/*

STRICQ - An Amiga, MUI Based, ICQ Clone.
Copyright (C) 1998-2000 Douglas F. McLaughlin

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Douglas F. McLaughlin - stricq@owlnet.net

*/

#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <time.h>

#include <libraries/asl.h>
#include <libraries/asyncio.h>
#include <libraries/icq.h>

#include <bsdsocket/socketbasetags.h>

#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#include <proto/asyncio.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/socket.h>
#include <proto/utility.h>

#include "STRICQMUI.h"

#define MUIM_STRICQ_AddSocks TAG_USER+1

extern void Add2Log(UBYTE, ULONG, UWORD, BOOL, UWORD, char *, ULONG);
extern void AddUser2CList(struct Contact *);
extern struct Contact *GetContact(ULONG);
extern void DelSockets(UWORD);
extern char *GetNick(ULONG);
extern struct Sockets *GetSocketFmContact(struct Contact *, UBYTE);
extern struct Sockets *GetSocketFmSocket(UWORD);
extern struct Sockets *GetSocketFmPort(UWORD);
extern void HandleError(UBYTE, char *, ...);
extern LONG OpenTCPSocket(struct Contact *, UBYTE, ULONG, ULONG);
extern void PrepNewContact(struct Contact *);
extern void PrepTCPMessage(struct TCP_Message *, ULONG);
extern void revmemcpy(char *, char *, int);
extern LONG SendTCPPacket(ULONG, UWORD, char *, UWORD, UWORD);

APTR __asm AddSession(REG(a2) APTR, REG(a1) struct ChatSession *);
void __asm RemSession(REG(a2) APTR, REG(a1) struct ChatSession *);
LONG __asm CmpSession(REG(a1) struct ChatSession *, REG(a2) struct ChatSession *);
void __asm DisplaySession(REG(a2) char **, REG(a1) struct ChatSession *);

ULONG __asm ChatDispatcher(REG(a0) struct IClass *cl, REG(a2) Object *obj, REG(a1) Msg msg);

void __asm OpenChatReqWin(REG(a2) APTR);
void __asm SendChatReq(REG(a2) APTR);
void __asm EndChat(REG(a2) APTR);
void __asm ChatWinState(REG(a2) APTR);
void __asm EndAllChat(REG(a2) APTR);
void __asm SetBIU(REG(a2) APTR);
void __asm SetLocalFColor(REG(a2) APTR);

void SendChatInit(UWORD);
BOOL StartChat(struct Contact *, struct Sockets *, BOOL);
void StartRemoteChat(struct Sockets *, struct Sockets *, struct Contact *);
char *GetSessionNicks(struct Sockets *);
int recv_chat(char *, UWORD);
void DoChatMode(char *, int, UWORD);
void UpdateChatSession(struct Contact *, struct Sockets *);
struct ChatSession *GetChatSessionPort(UWORD);
ULONG CountChatPorts(struct Sockets *);
void SaveChatText(struct Contact *, struct ChatWin *, struct RemoteChatObj *);
void SendText(UWORD, char *, UWORD);

void mChatHandleEvent(struct IClass *, Object *, struct MUIP_HandleEvent *);

enum {CB_SLEEP = 0, CB_OVERRIDE, CB_X1, CB_BACK, CB_TEXT, CB_X2, CB_BOLD, CB_ITALIC, CB_UNDERLINE, CB_X3,
      CB_CHATMODE, CB_X4, CB_KEYCLICK, CB_X5, CB_ACTIONS, CB_EMOTIONS};

enum {CB_COMMANDS = 0, CB_FREEZE, CB_CLEAR, CB_X10, CB_TIMEZONE};

extern char err_buf[], currentdir[], *ChatColorNames[];
extern LONG cmap[];
extern UBYTE ChatColors[16][3];
extern ULONG TCP_Seq, AsyncBuf;
extern UWORD TCPCodes[];
extern struct Colors Pens;
extern struct ObjApp *app;

struct MUIP_STRICQ_AddSocks {
  ULONG MethodID;
  struct Sockets *Socks;
};


APTR __asm AddSession(REG(a2) APTR pool, REG(a1) struct ChatSession *chat)

{

struct ChatSession *newchat = NULL;

if (!(newchat = AllocPooled(pool,sizeof(struct ChatSession)))) return(NULL);

strcpy(newchat->Session,chat->Session);

newchat->ChatListen = chat->ChatListen;

return(newchat);

}


void __asm RemSession(REG(a2) APTR pool, REG(a1) struct ChatSession *chat)

{

FreePooled(pool,chat,sizeof(struct ChatSession));

return;

}


LONG __asm CmpSession(REG(a1) struct ChatSession *a1, REG(a2) struct ChatSession *a2)

{

if (a1->ChatListen < a2->ChatListen) return(-1);
if (a1->ChatListen > a2->ChatListen) return(1);

return(0);

}


void __asm DisplaySession(REG(a2) char **array, REG(a1) struct ChatSession *chat)

{

static char buf[10];

if (chat) {
  sprintf(buf,"%d",chat->ChatListen);
  *array++ = buf;
  *array   = chat->Session;
}
else {
  *array++ = "Port";
  *array   = "Current Chat Sessions";
}

return;

}


void __asm OpenChatReqWin(REG(a2) APTR obj)

{

static char nick[21];

ULONG uin, total;

get(obj,MUIA_UserData,&uin);

strcpy(nick,GetNick(uin));

set(app->WI_ChatReqOut,MUIA_Window_Title,nick);

set(app->BT_SendChat,MUIA_UserData,uin);
set(app->BT_JoinChat,MUIA_UserData,uin);

get(app->LV_Sessions,MUIA_NList_Entries,&total);

if (total == 0) set(app->BT_JoinChat,MUIA_Disabled,TRUE);
else set(app->BT_JoinChat,MUIA_Disabled,FALSE);

set(app->WI_ChatReqOut,MUIA_Window_Open,TRUE);

return;

}


void __asm SendChatReq(REG(a2) APTR obj)

{

char *msg, *sess_nicks, *TCP_Buf = NULL;

LONG sel;

ULONG id, Seq;

UWORD len, RevLen, ret, session = 0;

struct ChatSession *chat;
struct Contact *user;
struct TCP_Message TCPMsg;

sess_nicks = LINE;

if (obj == app->BT_JoinChat) {
  get(app->LV_Sessions,MUIA_NList_Active,&sel);
  if (sel == MUIV_NList_Active_Off) {
    HandleError(DBG_OTH_FATL,"You must choose a chat session to 'Join'.");
    return;
  }
  DoMethod(app->LV_Sessions,MUIM_NList_GetEntry,sel,&chat);
  if (chat) {
    session = chat->ChatListen;
    sess_nicks = chat->Session;
  }
}

get(obj,MUIA_UserData,&id);

if (!(user = GetContact(id))) return;

if (!user->UWin) return;

user->ChatListen = session;

if (session) user->ConnectMe = TRUE;

get(app->TI_ChatReason,MUIA_Textinput_Contents,&msg);

set(app->WI_ChatReqOut,MUIA_Window_Open,FALSE);

TCPMsg.Command = TCP_MESSAGE;
TCPMsg.Type = MSG_CHAT;
TCPMsg.MsgCommand = TCP_MSG_REAL;

PrepTCPMessage(&TCPMsg,id);

TCPMsg.Message = msg;

TCPMsg.Chat_Session = sess_nicks;
TCPMsg.Chat_RevPort = TCPMsg.Chat_Port = user->ChatListen;
TCPMsg.Chat_X2 = 0;

len = TCP_CreateMsgPacket(&TCPMsg,&TCP_Buf,&Seq,&RevLen);

if (!(ret = SendTCPPacket(id,TCP_MESSAGE,TCP_Buf,len,RevLen))) {
  HandleError(DBG_TCP_ERRS,"Could not send Chat Request to %s.",user->Nick);
  TCP_FreeMsgPacket(TCP_Buf);
  return;
}

set(app->TI_ChatReason,MUIA_Textinput_Contents,NULL);

if (ret == -1) Add2Log(LOG_CLIENT,Seq,TCP_MESSAGE,FALSE,len,TCP_Buf,id);
else Add2Log(LOG_CLIENT,Seq,TCP_MESSAGE,FALSE,0,NULL,id);

TCP_FreeMsgPacket(TCP_Buf);

return;

}


void SendChatInit(UWORD sock)

{

char buf[50], *nick;

UWORD len, revlen;

struct Contact *user;
struct Sockets *socks;

struct TCP_ChatInit Init;

if (!(socks = GetSocketFmSocket(sock))) return;

if (!(user = GetContact(socks->UIN))) return;

get(app->STR_ICQUIN,MUIA_String_Integer,&Init.UIN);
get(app->STR_Nick,MUIA_String_Contents,&nick);

Init.HandShake = TCP_CHAT_HANDSHAKE;
Init.HiVersion = TCP_CHAT_VER_CURRENT;
strcpy(Init.Nick,nick);
Init.RevPort = user->ChatListen;
Init.TextColor = 0x00FFFFFF;
Init.BackColor = 0x00000000;
Init.X1 = 0x00;

HandleError(DBG_TCP_DBUG,"SendChatInit()");

len = TCP_CreateChatPacket(TCP_CHAT_1,&Init,buf,&revlen);

send(sock,(char *)&revlen,2,0);
send(sock,buf,len,0);

socks->Timehack = time(NULL);
socks->ConnectPort = user->ChatListen;

return;

}


void ParseChatPkt(char *buf, int len, UWORD sock)

{

APTR Data = NULL;

char cbuf[1024];

int i;

UBYTE Cmd;

ULONG opt = 1, sess_count;

UWORD RevLen, Len;

struct Contact *user, User;
struct Sockets *socks, *sock2, *primesocks;

struct TCP_Init Init;

if (socks = GetSocketFmSocket(sock)) {
  user = GetContact(socks->UIN);
  socks->Timehack = time(NULL);
}

PrintHex(DBG_TCP_PKTS,"Chat packet",buf,len);

if (!socks->HadInit) {
  if (!TCP_ParseTCPInit(&Init,buf)) return;
  if (!(user = GetContact(Init.UIN))) {
    PrepNewContact(&User);
    User.UIN = Init.UIN;
    sprintf(cbuf,"%ld",Init.UIN);
    User.Nick = cbuf;
    User.Status = STATUS_NEWUIN;
    User.CanTCP = Init.TCP_Flag;
    AddUser2CList(&User);
    user = GetContact(Init.UIN);
  }
  user->ChatListen = socks->ConnectPort;
  socks->TCP_Version = Init.Version;
  socks->TCP_Revision = Init.X1;
  socks->TCP_Listen_Msg_Port = Init.TCP_MsgPort;
  socks->TCP_Listen_Other_Port = Init.TCP_Chat_File_Port;
  socks->UIN = user->UIN;
  socks->TCP_Flag = Init.TCP_Flag;
  socks->HadInit = TRUE;
  DoMethod(app->LV_SockList,MUIM_NList_Redraw,MUIV_NList_Redraw_All);
  return;
}

primesocks = GetSocketFmPort(user->ChatListen);

Cmd = TCP_ParseChatPacket(&Data,buf);

switch(Cmd) {
  case TCP_CHAT_1: {
    char *nick;
//  int i;
    ULONG total;
//  struct Sockets *sock2;
    struct TCP_ChatInit *Init = Data;
    struct TCP_ChatTwo Two;
    struct TCP_ChatTwo_Ext *ext_p1, *ext_p2;
    socks->TCP_ChatHandShake = Init->HandShake;
    get(app->STR_ICQUIN,MUIA_String_Integer,&total);
    get(app->STR_Nick,MUIA_String_Contents,&nick);
    Two.HandShake = TCP_CHAT_HANDSHAKE;
    Two.UIN = total;
    strcpy(Two.Nick,nick);
    Two.TextColor = 0x00FFFFFF;
    Two.BackColor = 0x00000000;
    Two.Version = TCP_VERSION_CURRENT;
    Two.X1 = TCP_REVISION_CURRENT;
    Two.Port = socks->ConnectPort;
    Two.IP = socks->LocalAddr.sin_addr.s_addr;
    Two.RealIP = socks->LocalAddr.sin_addr.s_addr;
    Two.TCP_Flag = TCP_FLAG_CAPABLE;
    Two.X2 = 0x6A6C;
    Two.FontSize = 0x00000008;
    Two.FontFamily = 0x00000000;
    strcpy(Two.FontName,"Courier");
    Two.X3 = 0x00;
    Two.X4 = 0x02;
    Two.Ext.Next = NULL;
    ext_p2 = &Two.Ext;
    sess_count = CountChatPorts(primesocks);
    Two.Count = sess_count-1;
    if (sess_count > 1) {
      get(app->LV_SockList,MUIA_NList_Entries,&total);
      for(i = 0; i < total; i++) {
        DoMethod(app->LV_SockList,MUIM_NList_GetEntry,i,&sock2);
        if (sock2) {
          if (sock2->ConnectPort == primesocks->Port && sock2 != socks && sock2 != primesocks) {
            HandleError(DBG_TCP_DBUG,"Adding socket %d to the TCP_CHAT_2 struct",sock2->Socket);
            if (!(ext_p1 = (struct TCP_ChatTwo_Ext *)AllocVec(sizeof(struct TCP_ChatTwo_Ext),MEMF_CLEAR))) break;
            ext_p2->Next = ext_p1;
            ext_p1->Next = NULL;
            ext_p1->Version = sock2->TCP_Version;
            ext_p1->X1 = sock2->TCP_Revision;
            ext_p1->Port = sock2->TCP_Listen_Other_Port;
            ext_p1->UIN = sock2->UIN;
            ext_p1->IP = sock2->Address.sin_addr.s_addr;
            ext_p1->RealIP = sock2->Address.sin_addr.s_addr;
            ext_p1->RevPort = sock2->TCP_Listen_Other_Port;
            ext_p1->TCP_Flag = sock2->TCP_Flag;
            ext_p1->X2 = sock2->TCP_ChatData;
            ext_p1->HandShake = sock2->TCP_ChatHandShake;
            ext_p2 = ext_p1;
          }
        }
      }
    }
    HandleError(DBG_TCP_DBUG,"About to create TCP_CHAT_2");
    Len = TCP_CreateChatPacket(TCP_CHAT_2,&Two,cbuf,&RevLen);
    send(sock,(char *)&RevLen,2,0);
    send(sock,cbuf,Len,0);
    if (Two.Ext.Next) {
      ext_p1 = Two.Ext.Next;
      while(ext_p1) {
        ext_p2 = ext_p1->Next;
        FreeVec(ext_p1);
        ext_p1 = ext_p2;
      }
      Two.Ext.Next = NULL;
    }
    break;
  }
  case TCP_CHAT_2: {
    struct TCP_ChatThree Three;
    struct TCP_ChatTwo *Two = Data;
    struct TCP_ChatTwo_Ext *ext_p1, *ext_p2;
    socks->TCP_Version = Two->Version;
    socks->TCP_Revision = Two->X1;
    socks->TCP_Listen_Other_Port = Two->Port;
    socks->TCP_Flag = Two->TCP_Flag;
    socks->TCP_ChatData = Two->X2;
    socks->TCP_ChatHandShake = Two->HandShake;
    ext_p2 = &Two->Ext;
    while(ext_p1 = ext_p2->Next) {
      struct Contact *user2;
      if (!(user2 = GetContact(ext_p1->UIN))) {
        PrepNewContact(&User);
        User.UIN = ext_p1->UIN;
        sprintf(cbuf,"%ld",ext_p1->UIN);
        User.Nick = cbuf;
        User.Status = STATUS_NEWUIN;
        User.CanTCP = ext_p1->TCP_Flag;
        AddUser2CList(&User);
        user2 = GetContact(ext_p1->UIN);
      }
      if (!user2->ChatListen) {
        user2->ChatListen = socks->ConnectPort;
        user2->IP = ext_p1->IP;
        user2->RealIP = ext_p1->RealIP;
        HandleError(DBG_TCP_DBUG,"Also adding %s to multichat.",user2->Nick);
        if (!OpenTCPSocket(user2,SOCK_CHAT,user2->IP,ext_p1->RevPort)) {
          if (!OpenTCPSocket(user2,SOCK_CHAT,user2->RealIP,ext_p1->RevPort)) HandleError(DBG_TCP_ERRS,"Could not open a chat connection to %s.",user->Nick);
        }
      }
      ext_p2 = ext_p1;
    }
    Three.Version = TCP_VERSION_CURRENT;
    Three.X1 = TCP_REVISION_CURRENT;
    Three.Port = socks->ConnectPort;
    Three.IP = socks->LocalAddr.sin_addr.s_addr;
    Three.RealIP = socks->LocalAddr.sin_addr.s_addr;
    Three.TCP_Flag = TCP_FLAG_CAPABLE;
    Three.X2 = 0x6A6C;
    Three.FontSize = 0x00000008;
    Three.FontFamily = 0x00000000;
    strcpy(Three.FontName,"Courier");
    Three.X3 = 0x0002;
    HandleError(DBG_TCP_DBUG,"About to create TCP_CHAT_3");
    Len = TCP_CreateChatPacket(TCP_CHAT_3,&Three,cbuf,&RevLen);
    send(sock,(char *)&RevLen,2,0);
    send(sock,cbuf,Len,0);
    if (!primesocks->CWin) {
      if (!StartChat(user,primesocks,FALSE)) {
        DelSockets(sock);
        break;
      }
    }
    StartRemoteChat(socks,primesocks,user);
    setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,&opt,sizeof(opt));
    socks->ChatMode = user;
    break;
  }
  case TCP_CHAT_3: {
    struct TCP_ChatThree *Three = Data;
    socks->TCP_Version = Three->Version;
    socks->TCP_Revision = Three->X1;
    socks->TCP_Listen_Other_Port = Three->Port;
    socks->TCP_Flag = Three->TCP_Flag;
    socks->TCP_ChatData = Three->X2;
    send(sock,"\004",1,0);
    send(sock,"\003",1,0);
    if (!primesocks->CWin) {
      if (!StartChat(user,primesocks,FALSE)) {
        DelSockets(sock);
        break;
      }
    }
    StartRemoteChat(socks,primesocks,user);
    socks->ChatMode = user;
    setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,&opt,sizeof(opt));
    break;
  }
  default:
    PrintHex(DBG_TCP_ERRS,"Unknown Chat Init packet",buf,len);
}

if (Data) FreeVec(Data);

return;

}


BOOL StartChat(struct Contact *user, struct Sockets *primesocks, BOOL open)

{

char *nick;

static char Image_fl[MAXNAMLEN], Select_fl[MAXNAMLEN], Ghost_fl[MAXNAMLEN];

time_t t;

struct ChatWin *CWin;
struct Sockets *socks;

static struct MUIP_Toolbar_Description TBDesc[] = {
  {TDT_BUTTON, 's' , TDF_GHOSTED, "Sleep"    , "Sets your Chat Mode\nstatus to 'Away'"       , 0},
  {TDT_BUTTON, 'o' , TDF_GHOSTED, "Override" , "Overrides the other user's\nfont/color prefs", 0},
  {TDT_SPACE , NULL, NULL       , NULL       , NULL                                          , 0},
  {TDT_BUTTON, NULL, TDF_GHOSTED, "Back"     , "Set the background color\n(Has no effect)"   , 0},
  {TDT_BUTTON, 'f' , NULL       , "Text"     , "Set the current font color"                  , 0},
  {TDT_SPACE , NULL, NULL       , NULL       , NULL                                          , 0},
  {TDT_BUTTON, 'b' , TDF_TOGGLE , "Bold"     , "Set text to Bold"                            , 0},
  {TDT_BUTTON, 'i' , TDF_TOGGLE , "Italic"   , "Set text to Italic"                          , 0},
  {TDT_BUTTON, 'u' , TDF_TOGGLE , "Underline", "Set text to Underline"                       , 0},
  {TDT_SPACE , NULL, NULL       , NULL       , NULL                                          , 0},
  {TDT_BUTTON, 'w' , TDF_GHOSTED, "Chat Mode", "Set window mode to either\nSplit or IRC"     , 0}, 
  {TDT_SPACE , NULL, NULL       , NULL       , NULL                                          , 0},
  {TDT_BUTTON, 'c' , TDF_GHOSTED, "Keyclick" , "Toggle keyclick on/off"                      , 0},
  {TDT_SPACE , NULL, NULL       , NULL       , NULL                                          , 0},
  {TDT_BUTTON, 'a' , TDF_GHOSTED, "Actions"  , "Send an action line"                         , 0},
  {TDT_BUTTON, 'e' , TDF_GHOSTED, "Emotions" , "Send an emotion line"                        , 0},
  {TDT_BUTTON, 'm' , TDF_GHOSTED, "Smileys"  , "Send a smiley"                               , 0},

  {TDT_END, NULL, NULL, NULL, NULL, NULL},
};

HandleError(DBG_TCP_DBUG,"StartChat()");

socks = GetSocketFmContact(user,SOCK_CHAT);

if (primesocks->CWin) {
  if (open) set(primesocks->CWin->WI_ChatWin,MUIA_Window_Open,TRUE);
  return(TRUE);
}

if (!(CWin = (struct ChatWin *)AllocVec(sizeof(struct ChatWin),MEMF_CLEAR))) {
  HandleError(DBG_OTH_FATL,"Failed to AllocVec %d bytes (struct ChatWin)",sizeof(struct ChatWin));
  return(FALSE);
}

sprintf(Image_fl,"%sImages/ChatBarN.iff",currentdir);
sprintf(Select_fl,"%sImages/ChatBarS.iff",currentdir);
sprintf(Ghost_fl,"%sImages/ChatBarG.iff",currentdir);

get(app->STR_Nick,MUIA_String_Contents,&nick);

t = time(NULL);

strcpy(CWin->Title,nick);
sprintf(CWin->LocalTime,"Local Time: %.5s",ctime(&t)+11);

CWin->SC_ScrollBar = ScrollbarObject, End;

CWin->WI_ChatWin = WindowObject,
  MUIA_Window_Title, CWin->Title,
  MUIA_Window_ID, user->UIN | 0x20000000,
  MUIA_UserData, user->UIN,
  WindowContents, VGroup,
    Child, ScrollgroupObject,
      MUIA_Scrollgroup_FreeVert, FALSE,
      MUIA_Scrollgroup_Contents, HGroupV,
//      MUIA_Frame, MUIV_Frame_Virtual,
        Child, CWin->TB_ChatBar = ToolbarObject,
          MUIA_Toolbar_Description, TBDesc,
          MUIA_Toolbar_ImageType, MUIV_Toolbar_ImageType_File,
          MUIA_Toolbar_ImageNormal, Image_fl,
          MUIA_Toolbar_ImageSelect, Select_fl,
          MUIA_Toolbar_ImageGhost, Ghost_fl,
          MUIA_ShortHelp, TRUE,
        End,
        Child, CWin->POP_FColors = PopobjectObject,
          MUIA_Popstring_String, VSpace(0),
          MUIA_Popstring_Button, VSpace(0),
          MUIA_Popobject_ObjStrHook, &SetLocalFColorHook,
          MUIA_Popobject_Object, CWin->LV_FColorList = NListviewObject,
            MUIA_NListview_NList, NListObject,
              MUIA_Frame, MUIV_Frame_String,
              MUIA_NList_PrivateData, user->UIN,
              MUIA_NList_ConstructHook, MUIV_NList_ConstructHook_String,
              MUIA_NList_DestructHook, MUIV_NList_DestructHook_String,
              MUIA_NList_SourceArray, ChatColorNames,
              MUIA_NList_AdjustWidth, TRUE,
              MUIA_NList_AdjustHeight, TRUE,
            End,
          End,
        End,
        Child, Label("Font"),
        Child, CWin->POP_ChatFont = PopaslObject,
          MUIA_Popasl_Type, ASL_FontRequest,
          MUIA_Popstring_String, CWin->STR_ChatFont = String("",80),
          MUIA_Popstring_Button, PopButton(MUII_PopUp),
          ASLFO_TitleText, "Please select a font...",
        End,
      End,
    End,
    Child, VGroup,
      MUIA_Frame, MUIV_Frame_Group,
      Child, HGroup,
        Child, TextObject,
          MUIA_Text_Contents, "My Chat view",
        End,
        Child, CWin->TX_LocalTime = TextObject,
          MUIA_Text_PreParse, "\033r",
          MUIA_Text_Contents, CWin->LocalTime,
        End,
      End,
      Child, HGroup,
        MUIA_Group_Spacing, 0,
        Child, CWin->TE_ChatLocal = NewObject(mcc_Chat->mcc_Class,NULL,
          MUIA_Frame, MUIV_Frame_String,
//        MUIA_Font, MUIV_Font_Tiny,
          MUIA_CycleChain, TRUE,
          MUIA_TextEditor_ExportHook, MUIV_TextEditor_ExportHook_Plain,
          MUIA_TextEditor_Slider, CWin->SC_ScrollBar,
          MUIA_TextEditor_ColorMap, cmap,
        End,
        Child, CWin->SC_ScrollBar,
      End,
    End,
    Child, BalanceObject, End,
    Child, CWin->GP_ChatGroups = HGroup,
      Child, CWin->RT_Dummy = RectangleObject, End,
    End,
  End,
End;

if (!CWin->WI_ChatWin) {
  HandleError(DBG_OTH_FATL,"Could not create the initial chat window.");
  FreeVec(CWin);
  CWin = NULL;
  return(FALSE);
}

DoMethod(app->App,OM_ADDMEMBER,CWin->WI_ChatWin);

DoMethod(CWin->TE_ChatLocal,MUIM_STRICQ_AddSocks,primesocks);

DoMethod(CWin->WI_ChatWin,MUIM_Notify,MUIA_Window_CloseRequest,TRUE,CWin->WI_ChatWin,2,
  MUIM_CallHook,&EndAllChatHook);

DoMethod(CWin->WI_ChatWin,MUIM_Notify,MUIA_Window_Activate,MUIV_EveryTime,CWin->WI_ChatWin,2,
  MUIM_CallHook,&ChatWinStateHook);

DoMethod(CWin->TB_ChatBar,MUIM_Toolbar_Notify,CB_BOLD,MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime,CWin->TE_ChatLocal,3,
  MUIM_Set,MUIA_TextEditor_StyleBold,MUIV_TriggerValue);

DoMethod(CWin->TB_ChatBar,MUIM_Toolbar_Notify,CB_BOLD,MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime,CWin->WI_ChatWin,2,
  MUIM_CallHook,&SetBIUHook);

DoMethod(CWin->TB_ChatBar,MUIM_Toolbar_Notify,CB_ITALIC,MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime,CWin->TE_ChatLocal,3,
  MUIM_Set,MUIA_TextEditor_StyleItalic,MUIV_TriggerValue);

DoMethod(CWin->TB_ChatBar,MUIM_Toolbar_Notify,CB_ITALIC,MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime,CWin->WI_ChatWin,2,
  MUIM_CallHook,&SetBIUHook);

DoMethod(CWin->TB_ChatBar,MUIM_Toolbar_Notify,CB_UNDERLINE,MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime,CWin->TE_ChatLocal,3,
  MUIM_Set,MUIA_TextEditor_StyleUnderline,MUIV_TriggerValue);

DoMethod(CWin->TB_ChatBar,MUIM_Toolbar_Notify,CB_UNDERLINE,MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime,CWin->WI_ChatWin,2,
  MUIM_CallHook,&SetBIUHook);

DoMethod(CWin->TB_ChatBar,MUIM_Toolbar_Notify,CB_TEXT,MUIV_Toolbar_Notify_Pressed,FALSE,CWin->POP_FColors,1,
  MUIM_Popstring_Open);

DoMethod(CWin->LV_FColorList,MUIM_Notify,MUIA_NList_EntryClick,MUIV_EveryTime,CWin->POP_FColors,2,
  MUIM_Popstring_Close,TRUE);

set(CWin->WI_ChatWin,MUIA_Window_ActiveObject,CWin->TE_ChatLocal);

if (open) set(CWin->WI_ChatWin,MUIA_Window_Open,TRUE);

primesocks->CWin = CWin;

return(TRUE);

}


ULONG __asm ChatDispatcher(REG(a0) struct IClass *cl, REG(a2) Object *obj, REG(a1) Msg msg)

{

int i;

switch(msg->MethodID) {
  case MUIM_Setup: {
    struct ChatData *data = INST_DATA(cl,obj);
    data->Events.ehn_Priority = 0;
    data->Events.ehn_Flags = 0;
    data->Events.ehn_Events = IDCMP_VANILLAKEY;
    data->Events.ehn_Object = obj;
    data->Events.ehn_Class = cl;
    DoMethod(_win(obj),MUIM_Window_AddEventHandler,&data->Events);
    HandleError(DBG_TCP_DBUG,"Finished MUIM_Setup");
    break;
  }
  case MUIM_Show:
    for(i = 0; i < NUM_CHAT_COLORS; i++) cmap[i] = MUIPEN(Pens.ChatColors[i]);
    break;
  case MUIM_HandleEvent:
    mChatHandleEvent(cl,obj,(struct MUIP_HandleEvent *)msg);
    break;
  case MUIM_Cleanup: { 
    struct ChatData *data = INST_DATA(cl,obj);
    DoMethod(_win(obj),MUIM_Window_RemEventHandler,&data->Events);
    HandleError(DBG_TCP_DBUG,"Finished MUIM_Cleanup");
    break;
  }
  case MUIM_STRICQ_AddSocks: {
    struct ChatData *data = INST_DATA(cl,obj);
    data->Socks = ((struct MUIP_STRICQ_AddSocks *)msg)->Socks;
    HandleError(DBG_TCP_DBUG,"MUIM_STRICQ_AddSocks");
    return(TRUE);
  }
//default:
//  HandleError(DBG_TCP_DBUG,"Unknown ChatDispatcher Method");
}

return(DoSuperMethodA(cl,obj,msg));

}


void mChatHandleEvent(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)

{

BOOL flag = FALSE, *bold, *italic, *underline;

char key[2], text[16], *notifycmd;

LONG pen;

struct Sockets *primesocks;

struct ChatData *data = INST_DATA(cl,obj);

//HandleError(DBG_TCP_DBUG,"mChatHandleEvent()");

primesocks = data->Socks;

key[1] = '\0';

if (msg->imsg) {
  switch(msg->imsg->Class) {
    case IDCMP_VANILLAKEY:
      set(primesocks->CWin->WI_ChatWin,MUIA_Window_ActiveObject,primesocks->CWin->TE_ChatLocal);
      DoMethod(primesocks->CWin->TE_ChatLocal,MUIM_TextEditor_ARexxCmd,"POSITION EOF");
      key[0] = (char)msg->imsg->Code;
      switch(key[0]) {
        case 0x07:
          flag = TRUE;
          get(app->STR_ChatBeep,MUIA_String_Contents,&notifycmd);
          if (strlen(notifycmd) > 0) System(notifycmd,NULL);
          else DisplayBeep(NULL);
          break;
        case 0x08:
          flag = TRUE;
          DoMethod(primesocks->CWin->TE_ChatLocal,MUIM_TextEditor_ARexxCmd,"BACKSPACE");
          break;
        case 0x0d:
          flag = TRUE;
          key[0] = '\n';
          DoMethod(primesocks->CWin->TE_ChatLocal,MUIM_TextEditor_InsertText,key,MUIV_TextEditor_InsertText_Bottom);
          key[0] = '\x0d';
          break;
        default:
          if ((UBYTE)key[0] >= (UBYTE)' ') {
            flag = TRUE;
            text[0] = '\0';
            get(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_StyleBold,&bold);
            get(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_StyleItalic,&italic);
            get(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_StyleUnderline,&underline);
            get(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_Pen,&pen);
            if (bold) strcat(text,"\33b");
            if (italic) strcat(text,"\33i");
            if (underline) strcat(text,"\33u");
            sprintf(text,"%s\33p[%ld]%s\33n",text,pen,key);
            DoMethod(primesocks->CWin->TE_ChatLocal,MUIM_TextEditor_InsertText,text,MUIV_TextEditor_InsertText_Bottom);
          }
          break;
      }
      break;
  }
}

if (flag) SendText(primesocks->Port,key,1);

return;

}


void StartRemoteChat(struct Sockets *socks, struct Sockets *primesocks, struct Contact *user)

{

BOOL *open;

static char Image_fl[MAXNAMLEN], Select_fl[MAXNAMLEN], Ghost_fl[MAXNAMLEN];

time_t t;

struct RemoteChatObj *RCObj;

static struct MUIP_Toolbar_Description RTBDesc[] = {
  {TDT_BUTTON, NULL, TDF_GHOSTED, "Commands" , "Additional commands to\nperform with this UIN", 0},
  {TDT_BUTTON, NULL, TDF_TOGGLE , "Freeze"   , "Freeze incoming text"                         , 0},
  {TDT_BUTTON, NULL, NULL       , "Clear"    , "Clears the text"                              , 0},
  {TDT_SPACE , NULL, NULL       , NULL       , NULL                                           , 0},
  {TDT_BUTTON, NULL, TDF_GHOSTED, "Timezone" , "Sets remote user's timezone"                  , 0},

  {TDT_END, NULL, NULL, NULL, NULL, NULL},
};

HandleError(DBG_TCP_DBUG,"StartRemoteChat()");

get(primesocks->CWin->WI_ChatWin,MUIA_Window_Open,&open);

if (open) DoMethod(primesocks->CWin->GP_ChatGroups,MUIM_Group_InitChange);

if (socks->RCObj) HandleError(DBG_TCP_ERRS,"Found a RemoteChatObj in the beginning of StartRemoteChat().  Please report this.");

if (!(RCObj = (struct RemoteChatObj *)AllocVec(sizeof(struct RemoteChatObj),MEMF_CLEAR))) {
  HandleError(DBG_OTH_FATL,"Failed to AllocVec %d bytes (struct RemoteChatObj)",sizeof(struct RemoteChatObj));
  return;
}

sprintf(Image_fl,"%sImages/ChatBar2N.iff",currentdir);
sprintf(Select_fl,"%sImages/ChatBar2S.iff",currentdir);
sprintf(Ghost_fl,"%sImages/ChatBar2G.iff",currentdir);

t = time(NULL);

strcpy(RCObj->Nick,user->Nick);
sprintf(RCObj->RemoteTime,"Remote Time: %.5s",ctime(&t)+11);

RCObj->SC_ScrollBar = ScrollbarObject, End;

RCObj->GP_RemoteObj = VGroup,
  MUIA_Frame, MUIV_Frame_Group,
  MUIA_UserData, user->UIN,
  Child, HGroup,
    Child, RCObj->TX_Nick = TextObject,
      MUIA_Text_Contents, RCObj->Nick,
      MUIA_Weight, 0,
    End,
    Child, RCObj->TB_RemoteChatBar = ToolbarObject,
      MUIA_Toolbar_Description, RTBDesc,
      MUIA_Toolbar_ImageType, MUIV_Toolbar_ImageType_File,
      MUIA_Toolbar_ImageNormal, Image_fl,
      MUIA_Toolbar_ImageSelect, Select_fl,
      MUIA_Toolbar_ImageGhost, Ghost_fl,
      MUIA_ShortHelp, TRUE,
      MUIA_HorizDisappear, 2,
    End,
    Child, RCObj->TX_RemoteTime = TextObject,
      MUIA_Text_PreParse, "\033r",
      MUIA_Text_Contents, RCObj->RemoteTime,
      MUIA_HorizDisappear, 1,
    End,
  End,
  Child, HGroup,
    MUIA_Group_Spacing, 0,
    Child, RCObj->TE_ChatRemote = TextEditorObject,
      MUIA_Frame, MUIV_Frame_String,
//    MUIA_Font, MUIV_Font_Tiny,
      MUIA_TextEditor_Slider, RCObj->SC_ScrollBar,
      MUIA_TextEditor_ColorMap, cmap,
    End,
    Child, RCObj->SC_ScrollBar,
  End,
End;

if (!RCObj->GP_RemoteObj) {
  HandleError(DBG_OTH_FATL,"Could not create a Remote Chat Object");
  FreeVec(RCObj);
  RCObj = NULL;
  return;
}

set(primesocks->CWin->RT_Dummy,MUIA_ShowMe,FALSE);

DoMethod(primesocks->CWin->GP_ChatGroups,OM_ADDMEMBER,RCObj->GP_RemoteObj);

if (open) DoMethod(primesocks->CWin->GP_ChatGroups,MUIM_Group_ExitChange);
else set(primesocks->CWin->WI_ChatWin,MUIA_Window_Open,TRUE);

get(primesocks->CWin->WI_ChatWin,MUIA_Window_Open,&open);

if (!open) HandleError(DBG_OTH_FATL,"Chat window could not be opened!");

socks->RCObj = RCObj;

DoMethod(RCObj->TB_RemoteChatBar,MUIM_Toolbar_Notify,CB_FREEZE,MUIV_Toolbar_Notify_Pressed,MUIV_EveryTime,RCObj->TE_ChatRemote,3,
  MUIM_Set,MUIA_TextEditor_Quiet,MUIV_TriggerValue);

DoMethod(RCObj->TB_RemoteChatBar,MUIM_Toolbar_Notify,CB_CLEAR,MUIV_Toolbar_Notify_Pressed,FALSE,RCObj->TE_ChatRemote,1,
  MUIM_TextEditor_ClearText);

UpdateChatSession(user,primesocks);

return;

}


char *GetSessionNicks(struct Sockets *primesocks)

{

static char nicks[256];

char *nick;

int i;

ULONG total;

struct Sockets *socks;

HandleError(DBG_TCP_DBUG,"GetSessionNicks(%d)",primesocks->Port);

get(app->LV_SockList,MUIA_NList_Entries,&total);

if (total < 1) return(NULL);

get(app->STR_Nick,MUIA_String_Contents,&nick);

strcpy(nicks,nick);

for(i = 0; i < total; i++) {
  DoMethod(app->LV_SockList,MUIM_NList_GetEntry,i,&socks);
  if (socks) {
    if (socks->ConnectPort == primesocks->Port && socks != primesocks) {
      HandleError(DBG_TCP_DBUG,"Socket %d has ConnectPort %d",socks->Socket,socks->ConnectPort);
      sprintf(nicks,"%s, %s",nicks,GetNick(socks->UIN));
    }
  }
}

if (strlen(nicks) == 0) return(NULL);

return(nicks);

}


int recv_chat(char *buf, UWORD sock)

{

int i;

ULONG err;

i = recv(sock,buf,TCPBUFLEN,0);

if (i == 0) return(-1);

if (i < 0) {
  switch(err = Errno()) {
    case EWOULDBLOCK:
    case EBADF:
      i = 0;
      break;
    default:
      SocketBaseTags(SBTM_GETREF(SBTC_ERRNOSTRPTR),&err,TAG_DONE);
      HandleError(DBG_TCP_ERRS,"Error on chat socket %d: %s\n",sock,(char *)err);
  }
}

return(i);

}


void DoChatMode(char *buf, int len, UWORD sock)

{

BOOL *bold, *italic, *underline;

char *notifycmd, key[2], text[16], *font;

int i;

LONG pen;

UWORD flen;

struct Sockets *socks;

socks = GetSocketFmSocket(sock);

PrintHex(DBG_TCP_PKTS,"Chat mode data",buf,len);

key[1] = '\0';

for(i = 0; i<len; i++) {
  DoMethod(socks->RCObj->TE_ChatRemote,MUIM_TextEditor_ARexxCmd,"POSITION EOF");
  switch(buf[i]) {
    case 0x07: //Beep
      get(app->STR_ChatBeep,MUIA_String_Contents,&notifycmd);
      if (strlen(notifycmd) > 0) System(notifycmd,NULL);
      else DisplayBeep(NULL);
      break;
    case 0x12: //Font Size
      i += 4;
      break;
    case 0x01: //Background Color
      i += 4;
      break;
    case TCP_CHAT_CMD_FCOLOR: {
      int j;
      for(j = 0; j < NUM_CHAT_COLORS; j++) {
        if ((UBYTE)buf[i+1] == ChatColors[j][0] && (UBYTE)buf[i+2] == ChatColors[j][1] && (UBYTE)buf[i+3] == ChatColors[j][2]) break;
      }
      if (j == NUM_CHAT_COLORS) j = -1;
      set(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_Pen,j+1);
      i += 4;
      break;
    }
    case TCP_CHAT_CMD_STYLE: {
      struct TCP_ChatCmdStyle Style;
      Style.Bold = (buf[++i]&TCP_CHATF_BOLD?TRUE:FALSE);
      Style.Italic = (buf[i]&TCP_CHATF_ITALIC?TRUE:FALSE);
      Style.Underline = (buf[i]&TCP_CHATF_UNDERLINE?TRUE:FALSE);
      set(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_StyleBold,Style.Bold);
      set(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_StyleItalic,Style.Italic);
      set(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_StyleUnderline,Style.Underline);
      i += 3;
      break;
    }
    case 0x04: //Activate window
    case 0x03: //De-Activate window
      break;
    case 0x0b: //End Chat Mode
      EndChat(socks->RCObj->GP_RemoteObj);
      return;
    case 0x10: //Change Font Name
      i++;
      revmemcpy((char *)&flen,buf+i,2); i += 2;
      font = buf+i; i += (flen+1);
      break;
    case 0x08: //Backspace
      DoMethod(socks->RCObj->TE_ChatRemote,MUIM_TextEditor_ARexxCmd,"BACKSPACE");
      break;
    case 0x0d: //Return
      key[0] = '\n';
      DoMethod(socks->RCObj->TE_ChatRemote,MUIM_TextEditor_InsertText,key,MUIV_TextEditor_InsertText_Bottom);
      break;
    default:   //All remaining text
      key[0] = buf[i];
      if ((UBYTE)key[0] >= (UBYTE)' ') {
        text[0] = '\0';
        get(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_StyleBold,&bold);
        get(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_StyleItalic,&italic);
        get(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_StyleUnderline,&underline);
        get(socks->RCObj->TE_ChatRemote,MUIA_TextEditor_Pen,&pen);
        if (bold) strcat(text,"\33b");
        if (italic) strcat(text,"\33i");
        if (underline) strcat(text,"\33u");
        sprintf(text,"%s\33p[%ld]%s\33n",text,pen,key);
        DoMethod(socks->RCObj->TE_ChatRemote,MUIM_TextEditor_InsertText,text,MUIV_TextEditor_InsertText_Bottom);
      }
      break;
  }
}

socks->Timehack = time(NULL);

return;

}


void __asm EndChat(REG(a2) APTR obj)

{

int i;

ULONG winuin, total, sess_count;

struct ChatSession *session;
struct Contact *user;
struct RemoteChatObj *RCObj;
struct Sockets *socks, *primesocks;

HandleError(DBG_TCP_DBUG,"EndChat()");

get(obj,MUIA_UserData,&winuin);

if (!(user = GetContact(winuin))) return;

if (!(socks = GetSocketFmContact(user,SOCK_CHAT))) return;

if (socks->Closing) return;

if (!(primesocks = GetSocketFmPort(socks->ConnectPort))) {
  DelSockets(socks->Socket);
  return;
}

if (!socks->RCObj) {
  HandleError(DBG_TCP_DBUG,"Got an empty socks->RCObj");
  DelSockets(socks->Socket);
  return;
}

RCObj = socks->RCObj;

socks->RCObj = NULL;

DelSockets(socks->Socket);

user->ChatListen = 0;

SaveChatText(user,primesocks->CWin,RCObj);

if (RCObj) {
  DoMethod(primesocks->CWin->GP_ChatGroups,MUIM_Group_InitChange);
  DoMethod(primesocks->CWin->GP_ChatGroups,OM_REMMEMBER,RCObj->GP_RemoteObj);
  MUI_DisposeObject(RCObj->GP_RemoteObj);
  FreeVec(RCObj);
  RCObj = NULL;
  DoMethod(primesocks->CWin->GP_ChatGroups,MUIM_Group_ExitChange);
}

sess_count = CountChatPorts(primesocks);

if (sess_count == 0) {
  set(primesocks->CWin->WI_ChatWin,MUIA_Window_Open,FALSE);
  DoMethod(app->App,OM_REMMEMBER,primesocks->CWin->WI_ChatWin);
  MUI_DisposeObject(primesocks->CWin->WI_ChatWin);
  FreeVec(primesocks->CWin);
  primesocks->CWin = NULL;
  get(app->LV_Sessions,MUIA_NList_Entries,&total);
  if (total > 0) {
    for(i = 0; i < total; i++) {
      DoMethod(app->LV_Sessions,MUIM_NList_GetEntry,i,&session);
      if (session) {
        if (session->ChatListen == primesocks->Port) {
          DoMethod(app->LV_Sessions,MUIM_NList_Remove,i);
          break;
        }
      }
    }
  }
  DelSockets(primesocks->Socket);
}
else UpdateChatSession(user,primesocks);

return;

}


void __asm ChatWinState(REG(a2) APTR obj)

{

BOOL *active;

char key[2];

ULONG uin;

struct Contact *user;
struct Sockets *primesocks;

get(obj,MUIA_UserData,&uin);

if (!(user = GetContact(uin))) return;

if (!(primesocks = GetSocketFmPort(user->ChatListen))) return;

key[1] = '\0';

get(primesocks->CWin->WI_ChatWin,MUIA_Window_Activate,&active);

if (active) key[0] = '\x03';
else key[0] = '\x04';

SendText(primesocks->Port,key,1);

return;

}


void UpdateChatSession(struct Contact *user, struct Sockets *primesocks)

{

BOOL flag = FALSE;

char *nick;

int i;

ULONG total;

struct ChatSession *session, Sess;

get(app->STR_Nick,MUIA_String_Contents,&nick);

strcpy(primesocks->CWin->Title,GetSessionNicks(primesocks));

set(primesocks->CWin->WI_ChatWin,MUIA_Window_Title,primesocks->CWin->Title);

get(app->LV_Sessions,MUIA_NList_Entries,&total);

if (total > 0) {
  for(i = 0; i < total; i++) {
    DoMethod(app->LV_Sessions,MUIM_NList_GetEntry,i,&session);
    if (session) {
      if (session->ChatListen == primesocks->Port) {
        strcpy(session->Session,primesocks->CWin->Title);
        flag = TRUE;
        break;
      }
    }
  }
}

if (!flag) {
  Sess.ChatListen = primesocks->Port;
  strcpy(Sess.Session,primesocks->CWin->Title);
  DoMethod(app->LV_Sessions,MUIM_NList_InsertSingle,&Sess,MUIV_NList_Insert_Sorted);
}

return;

}


struct ChatSession *GetChatSessionPort(UWORD port)

{

int i;

ULONG total;

struct ChatSession *session;

get(app->LV_Sessions,MUIA_NList_Entries,&total);

if (total > 0) {
  for(i = 0; i < total; i++) {
    DoMethod(app->LV_Sessions,MUIM_NList_GetEntry,i,&session);
    if (session) if (session->ChatListen == port) return(session);
  }
}

return(NULL);

}


ULONG CountChatPorts(struct Sockets *primesocks)

{

int i;

ULONG total, count = 0;

struct Sockets *socks;

HandleError(DBG_TCP_DBUG,"CountChatPorts(%d)",primesocks->Port);

get(app->LV_SockList,MUIA_NList_Entries,&total);

for(i = 0; i < total; i++) {
  DoMethod(app->LV_SockList,MUIM_NList_GetEntry,i,&socks);
  if (socks) {
    if (socks->ConnectPort == primesocks->Port && socks != primesocks) {
      count++;
      HandleError(DBG_TCP_DBUG,"Socket %d has ConnectPort %d",socks->Socket,socks->ConnectPort);
    }
  }
}

HandleError(DBG_TCP_DBUG,"CountChatPorts() = %ld",count);

return(count);

}


void SaveChatText(struct Contact *user, struct ChatWin *CWin, struct RemoteChatObj *RCObj)

{

char uinlog[MAXNAMLEN], *rchat, *lchat;

ULONG uin;

struct AsyncFile *logfh;

rchat = (char *)DoMethod(RCObj->TE_ChatRemote,MUIM_TextEditor_ExportText);
lchat = (char *)DoMethod(CWin->TE_ChatLocal,MUIM_TextEditor_ExportText);

if (!rchat) rchat = LINE;
if (!lchat) lchat = LINE;

if (!strlen(rchat) && !strlen(lchat)) return;

/*

  MUI hits enforcer/cyberguard/muforce when the next line is executed.  I see this as an MUI bug, not STRICQ.

*/

if (MUI_Request(app->App,CWin->WI_ChatWin,0,NULL,"Save|Discard","Save Chat Contents?")) {
  get(app->STR_ICQUIN,MUIA_String_Integer,&uin);
  sprintf(uinlog,"%sUsers/%ld/Logs/%ld",currentdir,uin,user->UIN);
  if (logfh = OpenAsync(uinlog,MODE_APPEND,AsyncBuf)) {
//  for(c = rchat; *c; c++) if (*c == '\n') *c = ' ';
    WriteLineAsync(logfh,"Chat R: ");
    WriteLineAsync(logfh,rchat);
    WriteLineAsync(logfh,"\n\n");
    set(RCObj->TE_ChatRemote,MUIA_TextEditor_Contents,NULL);
    if (strlen(rchat)) FreeVec(rchat);
//  for(c = lchat; *c; c++) if (*c == '\n') *c = ' ';
    WriteLineAsync(logfh,"Chat S: ");
    WriteLineAsync(logfh,lchat);
    WriteLineAsync(logfh,"\n\n");
    set(CWin->TE_ChatLocal,MUIA_TextEditor_Contents,NULL);
    if (strlen(lchat)) FreeVec(lchat);
    CloseAsync(logfh);
    SetComment(uinlog,GetNick(user->UIN));
  }
}

return;

}


void __asm EndAllChat(REG(a2) APTR obj)

{

int i;

ULONG winuin, total;

struct ChatSession *session;
struct Contact *user;
struct Sockets *socks, *primesocks;

get(obj,MUIA_UserData,&winuin);

if (!(user = GetContact(winuin))) return;

if (!(primesocks = GetSocketFmPort(user->ChatListen))) return;

get(app->LV_SockList,MUIA_NList_Entries,&total);

for(i = 0; i < total; i++) {
  DoMethod(app->LV_SockList,MUIM_NList_GetEntry,i,&socks);
  if (socks) if (socks->ConnectPort == primesocks->Port && socks != primesocks) socks->Closing = TRUE;
}

if (!MUI_Request(app->App,primesocks->CWin->WI_ChatWin,0,NULL,"Yes|No","Exit Chat.  Are you sure?")) {
  for(i = 0; i < total; i++) {
    DoMethod(app->LV_SockList,MUIM_NList_GetEntry,i,&socks);
    if (socks) if (socks->ConnectPort == primesocks->Port && socks != primesocks) socks->Closing = FALSE;
  }
  return;
}

for(i = 0; i < total; i++) {
  DoMethod(app->LV_SockList,MUIM_NList_GetEntry,i,&socks);
  if (socks) {
    if (socks->ConnectPort == primesocks->Port && socks != primesocks) {
      user = GetContact(socks->UIN);
      user->ChatListen = 0;
      HandleError(DBG_TCP_DBUG,"Shutting down %s's chat socket %d",user->Nick,socks->Socket);
      if (socks->RCObj) {
        SaveChatText(user,primesocks->CWin,socks->RCObj);
        DoMethod(primesocks->CWin->GP_ChatGroups,MUIM_Group_InitChange);
        DoMethod(primesocks->CWin->GP_ChatGroups,OM_REMMEMBER,socks->RCObj->GP_RemoteObj);
        MUI_DisposeObject(socks->RCObj->GP_RemoteObj);
        FreeVec(socks->RCObj);
        socks->RCObj = NULL;
        DoMethod(primesocks->CWin->GP_ChatGroups,MUIM_Group_ExitChange);
      }
      send(socks->Socket,"\x0B",1,0);
      DelSockets(socks->Socket);
    }
  }
}

set(primesocks->CWin->WI_ChatWin,MUIA_Window_Open,FALSE);

DoMethod(app->App,OM_REMMEMBER,primesocks->CWin->WI_ChatWin);

MUI_DisposeObject(primesocks->CWin->WI_ChatWin);

FreeVec(primesocks->CWin);

primesocks->CWin = NULL;

get(app->LV_Sessions,MUIA_NList_Entries,&total);

if (total > 0) {
  for(i = 0; i < total; i++) {
    DoMethod(app->LV_Sessions,MUIM_NList_GetEntry,i,&session);
    if (session) {
      if (session->ChatListen == primesocks->Port) {
        DoMethod(app->LV_Sessions,MUIM_NList_Remove,i);
        break;
      }
    }
  }
}

DelSockets(primesocks->Socket);

return;

}


void __asm SetBIU(REG(a2) APTR obj)

{

BOOL *bold, *italic, *underline;

char buf[16];

ULONG uin;

UWORD len;

struct Contact *user;
struct Sockets *primesocks;
struct TCP_ChatCmdStyle Style;

get(obj,MUIA_UserData,&uin);

user = GetContact(uin);

primesocks = GetSocketFmPort(user->ChatListen);

get(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_StyleBold,&bold);
get(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_StyleItalic,&italic);
get(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_StyleUnderline,&underline);

Style.Bold = (bold?TRUE:FALSE);
Style.Italic = (italic?TRUE:FALSE);
Style.Underline = (underline?TRUE:FALSE);

len = TCP_CreateChatCmd(TCP_CHAT_CMD_STYLE,&Style,buf);

SendText(primesocks->Port,buf,len);

return;

}


void SendText(UWORD port, char *text, UWORD len)

{

int i;

ULONG total;

struct Sockets *socks;

get(app->LV_SockList,MUIA_NList_Entries,&total);

for(i = 0; i < total; i++) {
  DoMethod(app->LV_SockList,MUIM_NList_GetEntry,i,&socks);
  if (socks) {
    if (socks->ConnectPort == port && socks->Dir != TCP_DIR_LISTEN) {
      send(socks->Socket,text,len,0);
      socks->Timehack = time(NULL);
    }
  }
}

return;

}


void __asm SetLocalFColor(REG(a2) APTR obj)

{

char buf[16];

LONG entry;

ULONG uin;

UWORD len;

struct Contact *user;
struct Sockets *primesocks;
struct TCP_ChatColor Color;

get(obj,MUIA_NList_EntryClick,&entry);

if (entry < 0 || entry >= NUM_CHAT_COLORS) return;

get(obj,MUIA_NList_PrivateData,&uin);

user = GetContact(uin);

primesocks = GetSocketFmPort(user->ChatListen);

set(primesocks->CWin->TE_ChatLocal,MUIA_TextEditor_Pen,entry+1);

Color.Red = ChatColors[entry][0];
Color.Green = ChatColors[entry][1];
Color.Blue = ChatColors[entry][2];

len = TCP_CreateChatCmd(TCP_CHAT_CMD_FCOLOR,&Color,buf);

SendText(primesocks->Port,buf,len);

return;

}
