/*

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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <exec/memory.h>
#include <exec/types.h>

#include <libraries/icq.h>

#include <proto/dos.h>
#include <proto/exec.h>

#include "STRICQMUI.h"


#define BCOPY_L(b)   L = BREV_L(b); memcpy(Buf+Len,&L,4); Len += 4
#define BCOPY_W(b)   W = BREV_W(b); memcpy(Buf+Len,&W,2); Len += 2
#define BCOPY_B(b)   memcpy(Buf+Len,&b,1); Len++
#define BCOPY_S(s)   S = strlen(s)+1; BCOPY_W(S); strcpy(Buf+Len,(s)); Len += S
#define BCOPY_M(b,l) memcpy(Buf+Len,(b),(l)); Len += (l);

#define ACOPY_L(b)   memcpy(&L,Buf+Len,4); b = BREV_L(L); Len += 4
#define ACOPY_W(b)   memcpy(&W,Buf+Len,2); b = BREV_W(W); Len += 2
#define ACOPY_B(b)   memcpy(&b,Buf+Len,1); Len++
#define ACOPY_S(s)   ACOPY_W(S); strcpy((s),Buf+Len); Len += S
#define ACOPY_P(s)   ACOPY_W(S); (s) = Buf+Len; Len += S

#define FCOPY_L(b)   memcpy(Buf+Len,&b,4); Len += 4;
#define FCOPY_W(b)   memcpy(Buf+Len,&b,2); Len += 2;

#define ECOPY_L(b)   memcpy(&b,Buf+Len,4); Len += 4;
#define ECOPY_W(b)   memcpy(&b,Buf+Len,2); Len += 2;

void  MSG_ParseMessage(UWORD, UBYTE *, APTR);

UWORD TCP_ParseTCPInit(struct TCP_Init *, UBYTE *);

UWORD TCP_CreateMsgPacket(struct TCP_Message *, UBYTE **, ULONG *, UWORD *);
void  TCP_FreeMsgPacket(UBYTE *Buf);
void  TCP_ParseMsgPacket(struct TCP_Message *, UBYTE *, ULONG *);

UWORD TCP_CreateFilePacket(UBYTE, APTR, UBYTE *, UWORD *);

UWORD TCP_CreateChatPacket(UBYTE, APTR, UBYTE *, UWORD *);
UBYTE TCP_ParseChatPacket(void **, UBYTE *);
UWORD TCP_CreateChatCmd(UBYTE, APTR, UBYTE *);

UWORD UDP_CreateHeader(struct UDP_Header *, UBYTE *);
void  UDP_ParseHeader(UBYTE *, struct UDP_Header *);
UWORD UDP_CreatePacket(struct UDP_Header *, APTR, UBYTE *);
BOOL  UDP_EncryptPacket(UBYTE *, UBYTE *, UWORD);
void  UDP_ParsePacket(APTR, UBYTE *, UWORD, struct UDP_Header *);
void  UDP_ParseMeta(APTR, struct S_Meta *);

void  UDP_NextMulti(struct S_Multi *);

char *ICQ_CmdToText(UWORD);

ULONG CalcCheckCode(UBYTE *Buf, UWORD Len);
ULONG ScrambleCheckCode(ULONG CheckCode);
void  PrintHex(UBYTE, char *, UBYTE *, UWORD);

extern void HandleError(UBYTE, char *, ...);


/*
** The following data constitutes fair use for compatibility.
*/

UBYTE UDP_Table[] = {
 0x59, 0x60, 0x37 , 0x6B , 0x65 , 0x62 , 0x46 , 0x48 , 0x53 , 0x61 , 0x4C , 0x59 , 0x60 , 0x57 , 0x5B , 0x3D,
 0x5E, 0x34, 0x6D , 0x36 , 0x50 , 0x3F , 0x6F , 0x67 , 0x53 , 0x61 , 0x4C , 0x59 , 0x40 , 0x47 , 0x63 , 0x39,
 0x50, 0x5F, 0x5F , 0x3F , 0x6F , 0x47 , 0x43 , 0x69 , 0x48 , 0x33 , 0x31 , 0x64 , 0x35 , 0x5A , 0x4A , 0x42,
 0x56, 0x40, 0x67 , 0x53 , 0x41 , 0x07 , 0x6C , 0x49 , 0x58 , 0x3B , 0x4D , 0x46 , 0x68 , 0x43 , 0x69 , 0x48,
 0x33, 0x31, 0x44 , 0x65 , 0x62 , 0x46 , 0x48 , 0x53 , 0x41 , 0x07 , 0x6C , 0x69 , 0x48 , 0x33 , 0x51 , 0x54,
 0x5D, 0x4E, 0x6C , 0x49 , 0x38 , 0x4B , 0x55 , 0x4A , 0x62 , 0x46 , 0x48 , 0x33 , 0x51 , 0x34 , 0x6D , 0x36,
 0x50, 0x5F, 0x5F , 0x5F , 0x3F , 0x6F , 0x47 , 0x63 , 0x59 , 0x40 , 0x67 , 0x33 , 0x31 , 0x64 , 0x35 , 0x5A,
 0x6A, 0x52, 0x6E , 0x3C , 0x51 , 0x34 , 0x6D , 0x36 , 0x50 , 0x5F , 0x5F , 0x3F , 0x4F , 0x37 , 0x4B , 0x35,
 0x5A, 0x4A, 0x62 , 0x66 , 0x58 , 0x3B , 0x4D , 0x66 , 0x58 , 0x5B , 0x5D , 0x4E , 0x6C , 0x49 , 0x58 , 0x3B,
 0x4D, 0x66, 0x58 , 0x3B , 0x4D , 0x46 , 0x48 , 0x53 , 0x61 , 0x4C , 0x59 , 0x40 , 0x67 , 0x33 , 0x31 , 0x64,
 0x55, 0x6A, 0x32 , 0x3E , 0x44 , 0x45 , 0x52 , 0x6E , 0x3C , 0x31 , 0x64 , 0x55 , 0x6A , 0x52 , 0x4E , 0x6C,
 0x69, 0x48, 0x53 , 0x61 , 0x4C , 0x39 , 0x30 , 0x6F , 0x47 , 0x63 , 0x59 , 0x60 , 0x57 , 0x5B , 0x3D , 0x3E,
 0x64, 0x35, 0x3A , 0x3A , 0x5A , 0x6A , 0x52 , 0x4E , 0x6C , 0x69 , 0x48 , 0x53 , 0x61 , 0x6C , 0x49 , 0x58,
 0x3B, 0x4D, 0x46 , 0x68 , 0x63 , 0x39 , 0x50 , 0x5F , 0x5F , 0x3F , 0x6F , 0x67 , 0x53 , 0x41 , 0x25 , 0x41,
 0x3C, 0x51, 0x54 , 0x3D , 0x5E , 0x54 , 0x5D , 0x4E , 0x4C , 0x39 , 0x50 , 0x5F , 0x5F , 0x5F , 0x3F , 0x6F,
 0x47, 0x43, 0x69 , 0x48 , 0x33 , 0x51 , 0x54 , 0x5D , 0x6E , 0x3C , 0x31 , 0x64 , 0x35 , 0x5A , 0x00 , 0x00,
};

char version_icq[] = "$VER: icq.c 0.36 (06.12.99)\n\nCopyright © 1998-2000 Douglas F. McLaughlin\nICQ Protocol Copyright © 1996 Mirabilis LTD.\n\nAll Rights Reserved.";
extern char err_buf[];

UBYTE TCP_Flag = TCP_FLAG_CAPABLE;

ULONG L, TCP_Sequence = 0xFFFFFFFE;

UWORD W, S, Len, Sequence, Sequence2, TCP_Version = TCP_VERSION_CURRENT;


void MSG_ParseMessage(UWORD Type, UBYTE *Msg, APTR Data)

{

char *c, *d;

switch(Type) {
  case MSG_MSG:
  case MSG_CHAT:
  case MSG_FILE:
  case MSG_DENY_AUTH:
  case MSG_GIVE_AUTH: {
    struct MSG_Message *Message = Data;
    Message->Message = Msg;
    break;
  }
  case MSG_URL: {
    struct MSG_URLDesc *URL = Data;
    URL->Desc = c = Msg;
    d = strchr(c,'\xFE');
    *d = '\0';
    URL->URL = d+1;
    break;
  }
  case MSG_REQ_AUTH:
  case MSG_ADDED:
  case MSG_WEB_PAGER:
  case MSG_EMAIL_PAGER: {
    struct MSG_Common *Common = Data;
    Common->Nick = c = Msg;
    d = strchr(c,'\xFE');
    *d = '\0';
    Common->First = c = d+1;
    d = strchr(c,'\xFE');
    *d = '\0';
    Common->Last = c = d+1;
    d = strchr(c,'\xFE');
    *d = '\0';
    Common->EMail = c = d+1;
    d = strchr(c,'\xFE');
    *d = '\0';
    Common->Authorize = (UBYTE)(*(d+1))&0x0F;
    c = d+1;
    d = strchr(c,'\xFE');
    if (!d) Common->Message = c+1;
    else Common->Message = d+1;
    break;
  }
  case MSG_ADDUIN: {
    struct MSG_AddUIN *AddUIN = Data;
    c = Msg;
    if (AddUIN->Total == 0) {
      d = strchr(c,'\xFE');
      *d = '\0';
      StrToLong(c,(LONG *)&AddUIN->Total);
      c = d+1;
    }
    d = strchr(c,'\xFE');
    *d = '\0';
    StrToLong(c,(LONG *)&AddUIN->UIN);
    AddUIN->Nick = c = d+1;
    d = strchr(c,'\xFE');
    *d = '\0';
    AddUIN->Next = d+1;
    break;
  }
}

return;    

}


UWORD TCP_ParseTCPInit(struct TCP_Init *Init, UBYTE *Buf)

{

UBYTE Ident;

UWORD PktLen;

Len = 0;

ACOPY_W(PktLen);
ACOPY_B(Ident);
ACOPY_W(Init->Version);

if (Ident != TCP_INIT_IDENT || Init->Version > TCP_VERSION_MAX) return(0);

ACOPY_W(Init->X1);                // This is probably a revision indicator...?
ACOPY_L(Init->TCP_Chat_File_Port);
ACOPY_L(Init->UIN);
ECOPY_L(Init->TCP_MsgIP);
ECOPY_L(Init->TCP_RealIP);
ACOPY_B(Init->TCP_Flag);
ACOPY_L(Init->TCP_MsgPort);

if (PktLen != Len-2) HandleError(DBG_TCP_ERRS,"TCP Init Packet passed with wrong size, received %d, expected %d",PktLen,Len-2);

return(PktLen);

}


UWORD TCP_CreateMsgPacket(struct TCP_Message *TCPMsg, UBYTE **Buffer, ULONG *Sequence, UWORD *RevLen)

{

UBYTE *Buf;

ULONG pkt_size;

pkt_size = 4+2+2+2+4+2+2+strlen(TCPMsg->Message)+1+4+4+4+1+2+2+4;

switch(TCPMsg->Type) {
  case MSG_CHAT:
    pkt_size += 2+strlen(TCPMsg->Chat_Session)+1+2+2+4;
    break;
  case MSG_FILE:
    pkt_size += 2+2+2+strlen(TCPMsg->File_Name)+1+4+4;
    break;
}

//HandleError(DBG_TCP_DBUG,"Created a TCP_Buf of %ld bytes.",pkt_size);

if (!(Buf = AllocVec(pkt_size,MEMF_CLEAR))) {
  HandleError(DBG_OTH_FATL,"Out of memory creating a TCP Message packet.");
  return(0);
}

Len = 0;

BCOPY_L(TCPMsg->UIN);
BCOPY_W(TCP_Version);
BCOPY_W(TCPMsg->Command);
BCOPY_W(TCPMsg->X1);
BCOPY_L(TCPMsg->UIN);
BCOPY_W(TCPMsg->Type);
BCOPY_S(TCPMsg->Message);
FCOPY_L(TCPMsg->TCP_MsgIP);
FCOPY_L(TCPMsg->TCP_RealIP);
BCOPY_L(TCPMsg->TCP_MsgPort);
BCOPY_B(TCP_Flag);
BCOPY_W(TCPMsg->TCP_Status);
BCOPY_W(TCPMsg->MsgCommand);

switch(TCPMsg->Type) {
  case MSG_CHAT:
    BCOPY_S(TCPMsg->Chat_Session);
    FCOPY_W(TCPMsg->Chat_RevPort);
    BCOPY_W(TCPMsg->Chat_X2);
    BCOPY_L(TCPMsg->Chat_Port);
    break;
  case MSG_FILE:
    FCOPY_W(TCPMsg->File_RevPort);
    BCOPY_W(TCPMsg->File_X3);
    BCOPY_S(TCPMsg->File_Name);
    BCOPY_L(TCPMsg->File_Size);
    BCOPY_L(TCPMsg->File_Port);
    break;
}

BCOPY_L(TCP_Sequence);

PrintHex(DBG_TCP_PKTS,"TCP Message Packet",Buf,Len);

*Sequence = TCP_Sequence--;

*RevLen = BREV_W(Len);

*Buffer = Buf;

return(Len);

}


void TCP_FreeMsgPacket(UBYTE *Buf)

{

FreeVec(Buf);

return;

}


void TCP_ParseMsgPacket(struct TCP_Message *tcpmsg, UBYTE *Buf, ULONG *Sequence)

{

ULONG Seq = 0;

UWORD slen = 0;

Len = 0;

BCOPY_L(tcpmsg->UIN);
BCOPY_W(tcpmsg->Command);
BCOPY_W(tcpmsg->X1);
BCOPY_W(tcpmsg->Type);
BCOPY_W(slen);
tcpmsg->Message = Buf+Len; Len += slen;
FCOPY_L(tcpmsg->TCP_MsgIP);
FCOPY_L(tcpmsg->TCP_RealIP);
BCOPY_L(tcpmsg->TCP_MsgPort);
BCOPY_W(tcpmsg->TCP_Status);
BCOPY_W(tcpmsg->MsgCommand);

switch(tcpmsg->Type) {
  case MSG_CHAT:
    BCOPY_W(slen);
    tcpmsg->Chat_Session = Buf+Len; Len += slen;
    FCOPY_W(tcpmsg->Chat_RevPort);
    BCOPY_W(tcpmsg->Chat_X2);
    BCOPY_L(tcpmsg->Chat_Port);
    break;
  case MSG_FILE:
    FCOPY_W(tcpmsg->File_RevPort);
    BCOPY_W(tcpmsg->File_X3);
    BCOPY_W(slen);
    tcpmsg->File_Name = Buf+Len; Len += slen;
    BCOPY_L(tcpmsg->File_Size);
    BCOPY_L(tcpmsg->File_Port);
    break;
}

BCOPY_L(Seq);

*Sequence = Seq;

return;

}


UWORD TCP_CreateFilePacket(UBYTE Cmd, APTR Data, UBYTE *Buf, UWORD *RevLen)

{

Len = 0;

switch(Cmd) {
  case TCP_FILE_BEGIN: {
    struct TCP_FileBegin *Begin = Data;
    BCOPY_B(Cmd);
    BCOPY_L(Begin->X1);
    BCOPY_L(Begin->TotalFiles);
    BCOPY_L(Begin->TotalSize);
    BCOPY_L(Begin->Speed);
    BCOPY_S(Begin->Nick);
    break;
  }
  case TCP_FILE_READY: {
    struct TCP_FileReady *Ready = Data;
    BCOPY_B(Cmd);
    BCOPY_B(Ready->X2);
    BCOPY_S(Ready->Filename);
    BCOPY_S(Ready->Text);
    BCOPY_L(Ready->Filesize);
    BCOPY_L(Ready->X2);
    BCOPY_L(Ready->Speed);
    break;
  }
  case TCP_FILE_SPEED: {
    struct TCP_FileSpeed *Speed = Data;
    BCOPY_B(Cmd);
    BCOPY_L(Speed->Speed);
    break;
  }
  case TCP_FILE_DATA: {
    struct TCP_FileData *DataPkt = Data;
    UWORD PktSize;
    PktSize = DataPkt->DataLen + 1;
    BCOPY_W(PktSize);
    BCOPY_B(Cmd);
    BCOPY_M(DataPkt->Data,DataPkt->DataLen);
    Len -= 2;
    break;
  }
}

*RevLen = BREV_W(Len);

if (Cmd != TCP_FILE_DATA) PrintHex(DBG_TCP_PKTS,"File Packet",Buf,(Cmd==TCP_FILE_DATA?32:Len));

return(Len);

}


UWORD TCP_CreateChatPacket(UBYTE Cmd, APTR Data, UBYTE *Buf, UWORD *RevLen)

{

Len = 0;

switch(Cmd) {
  case TCP_CHAT_1: {
    struct TCP_ChatInit *Init = Data;
    HandleError(DBG_TCP_DBUG,"Creating TCP_CHAT_1");
    BCOPY_L(Init->HandShake);
    BCOPY_L(Init->HiVersion);
    BCOPY_L(Init->UIN);
    BCOPY_S(Init->Nick);
    FCOPY_W(Init->RevPort);
    BCOPY_L(Init->TextColor);
    BCOPY_L(Init->BackColor);
    BCOPY_B(Init->X1);
    break;
  }
  case TCP_CHAT_2: {
    struct TCP_ChatTwo *Two = Data;
    struct TCP_ChatTwo_Ext *ext_p1;
    HandleError(DBG_TCP_DBUG,"Creating TCP_CHAT_2");
    BCOPY_L(Two->HandShake);
    BCOPY_L(Two->UIN);
    BCOPY_S(Two->Nick);
    BCOPY_L(Two->TextColor);
    BCOPY_L(Two->BackColor);
    BCOPY_W(Two->Version);
    BCOPY_W(Two->X1);
    BCOPY_L(Two->Port);
    FCOPY_L(Two->IP);
    FCOPY_L(Two->RealIP);
    BCOPY_B(Two->TCP_Flag);
    BCOPY_W(Two->X2);
    BCOPY_L(Two->FontSize);
    BCOPY_L(Two->FontFamily);
    BCOPY_S(Two->FontName);
    BCOPY_B(Two->X3);
    BCOPY_B(Two->X4);
    BCOPY_B(Two->Count);
    ext_p1 = Two->Ext.Next;
    while(ext_p1) {
      BCOPY_W(ext_p1->Version);
      BCOPY_W(ext_p1->X1);
      BCOPY_L(ext_p1->Port);
      BCOPY_L(ext_p1->UIN);
      FCOPY_L(ext_p1->IP);
      FCOPY_L(ext_p1->RealIP);
      BCOPY_B(ext_p1->TCP_Flag);
      BCOPY_W(ext_p1->RevPort);
      BCOPY_W(ext_p1->X2);
      BCOPY_L(ext_p1->HandShake);
      ext_p1 = ext_p1->Next;
    }
    HandleError(DBG_TCP_DBUG,"TCP_CHAT_2");
    break;
  }
  case TCP_CHAT_3: {
    struct TCP_ChatThree *Three = Data;
    HandleError(DBG_TCP_DBUG,"Creating TCP_CHAT_3");
    BCOPY_W(Three->Version);
    BCOPY_W(Three->X1);
    BCOPY_L(Three->Port);
    FCOPY_L(Three->IP);
    FCOPY_L(Three->RealIP);
    BCOPY_B(Three->TCP_Flag);
    BCOPY_W(Three->X2);
    BCOPY_L(Three->FontSize);
    BCOPY_L(Three->FontFamily);
    BCOPY_S(Three->FontName);
    BCOPY_W(Three->X3);
    break;
  }
}

*RevLen = BREV_W(Len);

PrintHex(DBG_TCP_PKTS,"Chat Initialization Packet",Buf,Len);

return(Len);

}


UBYTE TCP_ParseChatPacket(void **Data, UBYTE *Buf)

{

UBYTE Cmd = TCP_CHAT_ERROR;

ULONG HandShake, HiVersion;

UWORD PktLen;

Len = 0;

ACOPY_W(PktLen);
ACOPY_L(HandShake);
ACOPY_L(HiVersion);

HandleError(DBG_TCP_DBUG,"HandShake %ld, HiVersion %ld",HandShake,HiVersion);

if (HandShake != TCP_CHAT_HANDSHAKE && HandShake != TCP_CHAT_HANDSHAKE2) Cmd = TCP_CHAT_3;
else {
  if (HiVersion >= TCP_CHAT_VERSION) Cmd = TCP_CHAT_1;
  else Cmd = TCP_CHAT_2;
}

Len = 0;

ACOPY_W(PktLen);

switch(Cmd) {
  case TCP_CHAT_1: {
    struct TCP_ChatInit *Init;
    if (!(Init = (struct TCP_ChatInit *)AllocVec(sizeof(struct TCP_ChatInit),MEMF_CLEAR))) return(TCP_CHAT_ERROR);
    ACOPY_L(Init->HandShake);
    ACOPY_L(Init->HiVersion);
    ACOPY_L(Init->UIN);
    ACOPY_S(Init->Nick);
    ECOPY_W(Init->RevPort);
    ACOPY_L(Init->TextColor);
    ACOPY_L(Init->BackColor);
    ACOPY_B(Init->X1);
    *Data = Init;
    break;
  }
  case TCP_CHAT_2: {
    struct TCP_ChatTwo *Two;
    struct TCP_ChatTwo_Ext *ext_p1, *ext_p2;
    if (!(Two = (struct TCP_ChatTwo *)AllocVec(sizeof(struct TCP_ChatTwo),MEMF_CLEAR))) return(TCP_CHAT_ERROR);
    ACOPY_L(Two->HandShake);
    ACOPY_L(Two->UIN);
    ACOPY_S(Two->Nick);
    ACOPY_L(Two->TextColor);
    ACOPY_L(Two->BackColor);
    ACOPY_W(Two->Version);
    ACOPY_W(Two->X1);
    ACOPY_L(Two->Port);
    ECOPY_L(Two->IP);
    ECOPY_L(Two->RealIP);
    ACOPY_B(Two->TCP_Flag);
    ACOPY_W(Two->X2);
    ACOPY_L(Two->FontSize);
    ACOPY_L(Two->FontFamily);
    ACOPY_S(Two->FontName);
    ACOPY_B(Two->X3);
    ACOPY_B(Two->X4);
    ACOPY_B(Two->Count);
    ext_p2 = &Two->Ext;
    ext_p2->Next = NULL;
    while(PktLen > Len-2) {
      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;
      ACOPY_W(ext_p1->Version);
      ACOPY_W(ext_p1->X1);
      ACOPY_L(ext_p1->Port);
      ACOPY_L(ext_p1->UIN);
      ECOPY_L(ext_p1->IP);
      ECOPY_L(ext_p1->RealIP);
      ACOPY_B(ext_p1->TCP_Flag);
      ACOPY_W(ext_p1->RevPort);
      ACOPY_W(ext_p1->X2);
      ACOPY_L(ext_p1->HandShake);
      ext_p2 = ext_p1;
    }
    *Data = Two;
    break;
  }
  case TCP_CHAT_3: {
    struct TCP_ChatThree *Three;
    if (!(Three = (struct TCP_ChatThree *)AllocVec(sizeof(struct TCP_ChatThree),MEMF_CLEAR))) return(TCP_CHAT_ERROR);
    ACOPY_W(Three->Version);
    ACOPY_W(Three->X1);
    ACOPY_L(Three->Port);
    ECOPY_L(Three->IP);
    ECOPY_L(Three->RealIP);
    ACOPY_B(Three->TCP_Flag);
    ACOPY_W(Three->X2);
    ACOPY_L(Three->FontSize);
    ACOPY_L(Three->FontFamily);
    ACOPY_S(Three->FontName);
    ACOPY_W(Three->X3);
    *Data = Three;
    break;
  }
}

HandleError(DBG_TCP_DBUG,"Cmd is %d",Cmd);

return(Cmd);

}


UWORD TCP_CreateChatCmd(UBYTE Cmd, APTR Data, UBYTE *Buf)

{

ULONG tmp;

Len = 0;

switch(Cmd) {
  case TCP_CHAT_CMD_FCOLOR: {
    struct TCP_ChatColor *Color = Data;
    UBYTE Alpha = 0x00;
    BCOPY_B(Cmd);
    BCOPY_B(Color->Red);
    BCOPY_B(Color->Green);
    BCOPY_B(Color->Blue);
    BCOPY_B(Alpha);
    break;
  }
  case TCP_CHAT_CMD_STYLE: {
    struct TCP_ChatCmdStyle *Style = Data;
    BCOPY_B(Cmd);
    tmp = (Style->Bold?TCP_CHATF_BOLD:TCP_CHATF_NONE) | (Style->Italic?TCP_CHATF_ITALIC:TCP_CHATF_NONE) | (Style->Underline?TCP_CHATF_UNDERLINE:TCP_CHATF_NONE);
    BCOPY_L(tmp);
    break;
  }
}

return(Len);

}


UWORD UDP_CreatePacket(struct UDP_Header *Header, APTR Data, UBYTE *Buf)

{

if (Header->Command == C_LOGIN || Header->Command == C_NEW_USER_1) {
  switch(Header->Version) {
    case 0x0002:
      Sequence = 0x0000;
      break;
    case 0x0005:
      srand(time(NULL));
      Header->SessionID = rand();
      Sequence2 = rand() & 0x3FFF;
      Sequence  = 0x0000;
      Header->CheckCode = 0x00000000;
      break;
  }
}

Len = UDP_CreateHeader(Header,Buf);

switch(Header->Command) {
  case C_ACK: {
    ULONG Random = rand();
    switch(Header->Version) {
    case 0x0005:
      BCOPY_L(Random);
      break;
    }
    break;
  }
  case C_SEND_MESSAGE: {
    struct C_SendMessage *Msg = Data;
    BCOPY_L(Msg->ToUIN);
    BCOPY_W(Msg->Type);
    BCOPY_S(Msg->Message);
    break;
  }
  case C_LOGIN: {
    struct C_LogOn *LogOn = Data;
    switch(Header->Version) {
      case 0x0005:
        BCOPY_L(LogOn->Time);
        break;
    }
    BCOPY_L(LogOn->TCP_MsgPort);
    BCOPY_S(LogOn->Password);
    BCOPY_L(LogOn->X1);
    FCOPY_L(LogOn->TCP_MsgIP);
    BCOPY_B(LogOn->TCP_Flag);
    BCOPY_L(LogOn->Status);
    BCOPY_L(LogOn->X3);
    switch(Header->Version) {
      case 0x0002:
        BCOPY_W(LogOn->Seq_Login);
        break;
    }
    BCOPY_L(LogOn->X4);
    BCOPY_L(LogOn->X5);
    switch(Header->Version) {
      case 0x0005:
        BCOPY_L(LogOn->X6);
        BCOPY_L(LogOn->X7);
        BCOPY_L(LogOn->X8);
        break;
    }
    TCP_Flag = LogOn->TCP_Flag;
    break;
  }
  case C_NEW_USER_REG: {
    struct C_NewUser *NewUIN = Data;
    switch(Header->Version) {
      case 0x0002:
        BCOPY_W(NewUIN->X1);
        break;
    }
    BCOPY_S(NewUIN->Password);
    BCOPY_L(NewUIN->X2);
    BCOPY_W(NewUIN->Random);
    BCOPY_W(NewUIN->Pad);
    switch(Header->Version) {
      case 0x0005:
        BCOPY_L(NewUIN->X3);
        BCOPY_L(NewUIN->X4);
        break;
    }
    break;
  }
  case C_INVIS_LIST:
  case C_VIS_LIST:
  case C_CONTACT_LIST: {
    int i;
    struct C_ContactList *ContactList = Data;
    BCOPY_B(ContactList->UIN_Count);
    for(i = 0; i<ContactList->UIN_Count; i++) {
      BCOPY_L(ContactList->UIN[i]);
    }
    break;
  }
  case C_SEARCH_USER: {
    struct C_Search_User *Search = Data;
    BCOPY_W(Search->Sequence);
    BCOPY_S(Search->Nick);
    BCOPY_S(Search->First);
    BCOPY_S(Search->Last);
    BCOPY_S(Search->EMail);
    break;
  }
  case C_KEEP_ALIVE: {
    ULONG Random = rand();
    switch(Header->Version) {
    case 0x0005:
      BCOPY_L(Random);
      break;
    }
    break;
  }
  case C_SEND_TEXT_CODE: {
    struct C_LogOff *LogOff = Data;
    BCOPY_S(LogOff->Text);
    BCOPY_W(LogOff->X1);
    break;
  }
  case C_ACK_MSG: {
    ULONG Random = rand();
    switch(Header->Version) {
    case 0x0005:
      BCOPY_L(Random);
      break;
    }
    break;
  }
  case C_LOGIN_1: {
    ULONG Random = rand();
    switch(Header->Version) {
    case 0x0005:
      BCOPY_L(Random);
      break;
    }
    break;
  }
  case C_INFO_REQ:
  case C_EXT_INFO_REQ:{
    struct C_Info_Req *Info = Data;
    switch(Header->Version) {
      case 0x0002:
        BCOPY_W(Info->Sequence);
        Info->Sequence++;
        break;
    }
    BCOPY_L(Info->UIN);
    break;
  }
  case C_CHANGE_PASSWORD: {
    struct C_ChangePassword *Pass = Data;
    switch(Header->Version) {
      case 0x0002:
        BCOPY_W(Pass->Sequence);
        break;
    }
    BCOPY_S(Pass->Password);
    break;
  }
  case C_UPDATE_INFO:
  case C_NEW_USER_INFO: {
    struct C_Update_Basic_Info *Basic = Data;
    switch(Header->Version) {
      case 0x0002:
        BCOPY_W(Basic->Sequence);
        break;
    }
    BCOPY_S(Basic->Nick);
    BCOPY_S(Basic->First);
    BCOPY_S(Basic->Last);
    BCOPY_S(Basic->EMail);
    BCOPY_B(Basic->Authorize);
    break;
  }
  case C_UPDATE_EXT_INFO: {
    struct C_Update_Ext_Info *Extended = Data;
    switch(Header->Version) {
      case 0x0002:
        BCOPY_W(Extended->Sequence);
        break;
    }
    BCOPY_S(Extended->City);
    BCOPY_W(Extended->Country);
    BCOPY_B(Extended->Timezone);
    BCOPY_S(Extended->State);
    BCOPY_W(Extended->Age);
    BCOPY_B(Extended->Sex);
    BCOPY_S(Extended->Phone);
    BCOPY_S(Extended->URL);
    BCOPY_S(Extended->About);
    break;
  }
  case C_QUERY_STATUS: {
    struct C_ServerStat *SrvrStat = Data;
    BCOPY_L(SrvrStat->Index);
    break;
  }
  case C_LOGIN_3: {
    struct C_LogOn_3 *LogOn3 = Data;
    BCOPY_L(LogOn3->X1);
    break;
  }
  case C_STATUS_CHANGE: {
    struct C_StatusChange *Status = Data;
    BCOPY_L(Status->Status);
    break;
  }
  case C_SEND_URL_MESSAGE: {
    struct C_SendURLMsg *URLMsg = Data;
    BCOPY_L(URLMsg->Index);
    break;
  }
  case C_UPDATE_AUTH: {
    struct C_UpdateAuth *Auth = Data;
    BCOPY_W(Auth->Sequence);
    BCOPY_B(Auth->Authorize);
    break;
  }
  case C_LOGIN_2: {
    struct C_LogOn_2 *LogOn2 = Data;
    BCOPY_B(LogOn2->X1);
    break;
  }
  case C_NEW_USER_1:
    break;
  case C_META: {
    struct C_Meta *Meta = Data;
    BCOPY_W(Meta->Subcommand);
    BCOPY_L(Meta->UIN);
    break;
  }
}

return(Len);

}


BOOL UDP_EncryptPacket(UBYTE *Buf, UBYTE *OldBuf, UWORD Len)

{

ULONG code, data1, checkcode;
UWORD pos;

if (Len < 0x19) {
  HandleError(DBG_UDP_ERRS,"UDP_EncryptPacket() - Packet has no data!");
  return(FALSE);
}

memcpy(Buf,OldBuf,Len);

checkcode = CalcCheckCode(Buf,Len);

code = Len * 0x68656c6c + checkcode;

for(pos = 0x0a; pos < Len; pos += 4) {
  memcpy(&L,Buf + pos,4); data1 = BREV_L(L);
  data1 ^= code + UDP_Table[pos & 0xFF];
  L = BREV_L(data1); memcpy(Buf + pos,&L,4);
}

checkcode = ScrambleCheckCode(checkcode);

L = BREV_L(checkcode); memcpy(Buf + 0x14,&L,4);

return(TRUE);

}


ULONG CalcCheckCode(UBYTE *Buf, UWORD Len)

{

ULONG Number1, Number2, CheckCode;

UBYTE r1, r2;

Number1 = Buf[8];
Number1 <<= 8;
Number1 += Buf[4];
Number1 <<= 8;
Number1 += Buf[2];
Number1 <<= 8;
Number1 += Buf[6];

r1 = (rand() % (Len - 0x18)) + 0x18;
r2 = rand() & 0xFF;

Number2 = r1;
Number2 <<= 8;
Number2 += Buf[r1];
Number2 <<= 8;
Number2 += r2;
Number2 <<= 8;
Number2 += UDP_Table[r2];
Number2 ^= 0xFF00FF;

CheckCode = Number1 ^ Number2;

//HandleError(DBG_UDP_DBUG,"n1 = %08lx, r1 = %02x, r2 = %02x, Buf[r1] = %02x, Table[r2] = %02x, n2 = %08lx, CheckCode = %08x",Number1,r1,r2,Buf[r1],UDP_Table[r2],Number2,CheckCode);

return(CheckCode);

}


ULONG ScrambleCheckCode(ULONG CheckCode)

{

ULONG a[6];

a[1] = CheckCode & 0x0000001F;
a[2] = CheckCode & 0x03E003E0;
a[3] = CheckCode & 0xF8000400;
a[4] = CheckCode & 0x0000F800;
a[5] = CheckCode & 0x041F0000;

a[1] <<= 0x0C;
a[2] <<= 0x01;
a[3] >>= 0x0A;
a[4] <<= 0x10;
a[5] >>= 0x0F;

return(a[1] + a[2] + a[3] + a[4] + a[5]);

}


void UDP_ParsePacket(APTR Data, UBYTE *Buf, UWORD PktLen, struct UDP_Header *Header)

{

switch(Header->Version) {
  case 0x0002:
    Len = 6;
    break;
  case 0x0005:
    Len = 21;
    break;
}

switch(Header->Command) {
  case S_ACK:
    break;
  case S_GO_AWAY:
    PrintHex(DBG_UDP_ERRS,"S_GO_AWAY",Buf,PktLen);
    break;
  case S_NEW_USER_UIN:
    switch(Header->Version) {
      case 0x0002: {
        struct S_New_User_UIN *NewUIN = Data;
        ACOPY_W(NewUIN->X1);
        ACOPY_L(NewUIN->UIN);
        ACOPY_L(NewUIN->Random);
        break;
      }
      case 0x0005:
        PrintHex(DBG_UDP_ERRS,"S_NEW_USER_UIN",Buf,PktLen);
        break;
    }
    break;
  case S_LOGIN_REPLY: {
    struct S_Login_Reply *Login = Data;
    switch(Header->Version) {
      case 0x0002:
        ECOPY_L(Login->IP);
//      ACOPY_W(Login->X1);
//      ACOPY_W(Login->X2);
//      ACOPY_W(Login->X3);
//      ACOPY_L(Login->X4);
//      ACOPY_L(Login->X5);
//      ACOPY_W(Login->X6);
//      ACOPY_W(Login->X7);
//      ACOPY_W(Login->X8);
//      ACOPY_W(Login->X9);
//      ACOPY_W(Login->X10);
//      ACOPY_W(Login->X11);
//      ACOPY_L(Login->X12);
        break;
      case 0x0005:
        ACOPY_L(Login->X5);
        ACOPY_W(Login->X6);
        ACOPY_W(Login->X7);
        ACOPY_W(Login->X8);
        ACOPY_W(Login->X9);
        ECOPY_L(Login->IP);
//      ACOPY_L(Login->X12);
        break;
    }
    break;
  }
  case S_BAD_PASSWORD:
    PrintHex(DBG_UDP_ERRS,"S_BAD_PASSWORD",Buf,PktLen);
    break;
  case S_USER_ONLINE: {
    struct S_User_Online *Online = Data;
    ACOPY_L(Online->UIN);
    ECOPY_L(Online->IP);
    ACOPY_L(Online->TCP_MsgPort);
    ECOPY_L(Online->RealIP);
    ACOPY_B(Online->TCP_Flag);
    ACOPY_L(Online->Status);
    ACOPY_W(Online->TCP_Version);
    ACOPY_W(Online->TCP_Revision);
    break;
  }
  case S_USER_OFFLINE: {
    struct S_User_Offline *Offline = Data;
    ACOPY_L(Offline->UIN);
    break;
  }
  case S_QUERY_REPLY:
    break;
  case S_USER_FOUND:
  case S_INFO_REPLY: {
    struct S_Basic_Info *Basic = Data;
    switch(Header->Version) {
      case 0x0002:
        ACOPY_W(Basic->Sequence);
        break;
    }
    ACOPY_L(Basic->UIN);
    ACOPY_S(Basic->Nick);
    ACOPY_S(Basic->First);
    ACOPY_S(Basic->Last);
    ACOPY_S(Basic->EMail);
    ACOPY_B(Basic->Authorize);
    break;
  }
  case S_END_OF_SEARCH: {
    struct S_End_Of_Search *Search = Data;
    switch(Header->Version) {
      case 0x0002:
        ACOPY_W(Search->Sequence);
        break;
    }
    ACOPY_B(Search->TooMany);
    break;
  }
  case S_NEW_USER_REPLY:
  case S_UPDATE_EXT_REPLY:
    break;
  case S_RECEIVE_MESSAGE: {
    struct S_Receive_Message *Msg = Data;
    ACOPY_L(Msg->UIN);
    ACOPY_W(Msg->Year);
    ACOPY_B(Msg->Month);
    ACOPY_B(Msg->Day);
    ACOPY_B(Msg->Hour);
    ACOPY_B(Msg->Minute);
    ACOPY_W(Msg->Type);
    ACOPY_W(Msg->Length);
    Msg->MessageBuf = Buf+Len;
    break;
  }
  case S_END_OFFLINE_MSGS:
  case S_TOO_BUSY:
    break;
  case S_GET_MESSAGE: {
    struct S_Get_Message *Msg = Data;
    ACOPY_L(Msg->UIN);
    ACOPY_W(Msg->Type);
    ACOPY_W(Msg->Length);
    Msg->MessageBuf = Buf+Len;
    break;
  }
  case S_EXT_INFO_REPLY: {
    struct S_Ext_Info *Ext = Data;
    switch(Header->Version) {
      case 0x0002:
        ACOPY_W(Ext->Sequence);
        break;
    }
    ACOPY_L(Ext->UIN);
    ACOPY_S(Ext->City);
    ACOPY_W(Ext->Country);
    ACOPY_B(Ext->Timezone);
    ACOPY_S(Ext->State);
    ACOPY_W(Ext->Age);
    ACOPY_B(Ext->Sex);
    ACOPY_S(Ext->Phone);
    ACOPY_S(Ext->URL);
    ACOPY_S(Ext->About);
    break;
  }
  case S_INFO_NOT_FOUND:
  case S_EXT_INFO_NOT_FOUND:
    break;
  case S_TCP_REQUEST: {
    struct S_TCP_Request *Req = Data;
    switch(Header->Version) {
      case 0x0002:
        ACOPY_L(Req->Local_UIN);
        break;
    }
    ACOPY_L(Req->Remote_UIN);
    ECOPY_L(Req->IP);
    ACOPY_L(Req->Port);
    ACOPY_B(Req->TCP_Flag);
    ACOPY_L(Req->Port2);
    ACOPY_L(Req->Port3);
    ACOPY_W(Req->TCP_Version);
//  ACOPY_L(Req->X1);
//  ACOPY_L(Req->X2);
    break;
  }
  case S_STATUS_UPDATE : {
    struct S_Status_Update *Status = Data;
    ACOPY_L(Status->UIN);
    ACOPY_L(Status->Status);
    break;
  }
  case S_SYSTEM_MESSAGE: {
    struct S_System_Message *Sys = Data;
    switch(Header->Version) {
      case 0x0002:
        ACOPY_L(Sys->Index);
        ACOPY_S(Sys->Ad);
        ACOPY_S(Sys->URL);
        ACOPY_S(Sys->Button);
        break;
      case 0x0005:
        PrintHex(DBG_UDP_ERRS,"S_SYSTEM_MESSAGE",Buf,PktLen);
        break;
    }
    break;
  }
  case S_UPDATE_REPLY:
  case S_UPDATE_FAIL:
  case S_UPDATE_AUTH_REPLY:
    break;
  case S_META: {
    struct S_Meta *Meta = Data;
    ACOPY_W(Meta->Subcommand);
    ACOPY_B(Meta->Success);
    Meta->Packet = Buf + Len;
    break;
  }
  case S_REPLY_X1:
    PrintHex(DBG_UDP_ERRS,"S_REPLY_X1",Buf,PktLen);
    break;
  case S_MULTI: {
    struct S_Multi *Multi = Data;
    ACOPY_B(Multi->Count);
    ACOPY_W(Multi->Size);
    Multi->Packet = Buf + Len;
    break;
  }
}

return;

}


void UDP_NextMulti(struct S_Multi *Multi)

{

UBYTE *Buf;

UWORD Len = 0;

if ((--Multi->Count) == 0) {
  Multi->Size = 0;
  Multi->Packet = NULL;
  return;
}

Buf = Multi->Packet + Multi->Size;

ACOPY_W(Multi->Size);

Multi->Packet = Buf + Len;

return;

}


void  UDP_ParseMeta(APTR Data, struct S_Meta *Meta)

{

UBYTE *Buf = Meta->Packet;

Len = 0;

switch(Meta->Subcommand) {
  case META_INFO_HOME: {
    struct Meta_Info_Home *Home = Data;
    ACOPY_S(Home->Nick);
    ACOPY_S(Home->First);
    ACOPY_S(Home->Last);
    ACOPY_S(Home->EMail_Primary);
    ACOPY_S(Home->EMail_Secondary);
    ACOPY_S(Home->EMail_Old);
    ACOPY_S(Home->City);
    ACOPY_S(Home->State);
    ACOPY_S(Home->Phone);
    ACOPY_S(Home->FAX);
    ACOPY_S(Home->Street);
    ACOPY_S(Home->Cellular);
    ACOPY_S(Home->ZipCode);
    ACOPY_W(Home->Country);
    ACOPY_B(Home->Timezone);
    ACOPY_B(Home->Flag1);
    ACOPY_B(Home->Flag2);    /* Depending on the circumstance, these last three flags may not actually */
    ACOPY_B(Home->Flag3);    /*   have been sent by the server.  No harm in always copying them as the */
    ACOPY_B(Home->Flag4);    /*   calling program should always know which flags were actually sent.   */
    break;
  }
  case META_INFO_WORK: {
    struct Meta_Info_Work *Work = Data;
    ACOPY_S(Work->City);
    ACOPY_S(Work->State);
    ACOPY_S(Work->Phone);
    ACOPY_S(Work->FAX);
    ACOPY_S(Work->Address);
    ACOPY_S(Work->ZipCode);
    ACOPY_W(Work->Country);
    ACOPY_S(Work->Company);
    ACOPY_S(Work->Department);
    ACOPY_S(Work->Position);
    ACOPY_W(Work->Occupation);
    ACOPY_S(Work->URL);
    break;
  }
  case META_INFO_MORE: {
    struct Meta_Info_More *More = Data;
    ACOPY_W(More->Age);
    ACOPY_B(More->Sex);
    ACOPY_S(More->URL);
    ACOPY_W(More->BDay_Year);
    ACOPY_B(More->BDay_Month);
    ACOPY_B(More->BDay_Day);
    ACOPY_B(More->Language1);
    ACOPY_B(More->Language2);
    ACOPY_B(More->Language3);
    break;
  }
  case META_INFO_ABOUT: {
    struct Meta_Info_About *About = Data;
    ACOPY_S(About->About);
    break;
  }
  case META_INFO_INTERESTS: {
    struct Meta_Info_Interests *Interest = Data;
    ACOPY_B(Interest->Total_Categories);
    ACOPY_W(Interest->Topic);
    ACOPY_P(Interest->TopicText);
    Interest->Next_Category = Buf + Len;
    break;
  }
  case META_INFO_AFFILIATIONS: {
    struct Meta_Info_Affiliations *Affiliation = Data;
    ACOPY_B(Affiliation->Total_Backgrounds);
    if (Affiliation->Total_Backgrounds) {
      ACOPY_W(Affiliation->Background);
      ACOPY_P(Affiliation->BackgroundText);
      Affiliation->Next_Background = Buf + Len;
      break;
    }
    ACOPY_B(Affiliation->Total_Affiliations);
    if (Affiliation->Total_Affiliations) {
      ACOPY_W(Affiliation->Affiliation);
      ACOPY_P(Affiliation->AffiliationText);
      Affiliation->Next_Affiliation = Buf + Len;
    }
    break;
  }
  case META_INFO_URLCATEGORY: {
    struct Meta_Info_URLCategory *URLCat = Data;
    ACOPY_B(URLCat->Total_Categories);
    ACOPY_W(URLCat->Category);
    ACOPY_P(URLCat->AboutText);
    URLCat->Next_Category = Buf + Len;
    break;
  }
}

return;

}


UWORD UDP_CreateHeader(struct UDP_Header *Header, UBYTE *Buf)

{

UWORD Zero = 0;

Len = 0;

switch(Header->Version) {
  case 0x0002:
    if (Header->Command != C_ACK) Header->Sequence = ++Sequence;
    BCOPY_W(Header->Version);
    BCOPY_W(Header->Command);
    BCOPY_W(Header->Sequence);
    if (Header->Command != C_NEW_USER_1 && Header->Command != C_NEW_USER_REG) {
      BCOPY_L(Header->UIN);
    }
    break;
  case 0x0005:
    switch(Header->Command) {
      case C_ACK:
        break;
      case C_KEEP_ALIVE:
        Header->Sequence = 0;
        Header->Sequence2 = ++Sequence2;
        break;
      default:
        Header->Sequence  = ++Sequence;
        Header->Sequence2 = ++Sequence2;
    }
    BCOPY_W(Header->Version);
    BCOPY_L(Zero);
    BCOPY_L(Header->UIN);
    BCOPY_L(Header->SessionID);
    BCOPY_W(Header->Command);
    BCOPY_W(Header->Sequence2);
    BCOPY_W(Header->Sequence);
    BCOPY_L(Header->CheckCode);
    break;
}

return(Len);

}


void UDP_ParseHeader(UBYTE *Buf, struct UDP_Header *Header)

{

UBYTE X1;

Len = 0;

ACOPY_W(Header->Version);

switch(Header->Version) {
  case 0x0002:
    ACOPY_W(Header->Command);
    ACOPY_W(Header->Sequence);
    break;
  case 0x0005:
    ACOPY_B(X1);
    ACOPY_L(Header->SessionID);
    ACOPY_W(Header->Command);
    ACOPY_W(Header->Sequence2);
    ACOPY_W(Header->Sequence);
    ACOPY_L(Header->UIN);
    ACOPY_L(Header->CheckCode);
    break;
}

return;

}


void PrintHex(UBYTE type, char *txt, UBYTE *buf, UWORD len)

{

char line[80], tmp[5];

int i, j;

UBYTE c;

if (txt) HandleError(type,txt);

for(i = 0; i<len; i += 8) {
  line[0] = '\0';
  for(j = 0; j<8; j++) {
    if (i+j >= len) strcat(line,"   ");
    else {
      c = (unsigned char)buf[i+j];
      sprintf(tmp,"%02x ",c);
      strcat(line,tmp);
    }
  }
  for(j = 0; j<8; j++) {
    if (i+j >= len) break;
    c = (unsigned char)buf[i+j];
//  if (c >= ' ' && c <= 'z') sprintf(tmp,"%c",c);
    if (c >= ' ') sprintf(tmp,"%c",c);
    else sprintf(tmp,".");
    strcat(line,tmp);
  }
  HandleError(type,line);
  if (j < 8) break;
}

return;

}


char *ICQ_CmdToText(UWORD cmd)

{

static char txt[50];

switch(cmd) {
  case TCP_MESSAGE:
    strcpy(txt,"TCP Message");
    break;
  case C_ACK:
    strcpy(txt,"ACK");
    break;
  case C_ACK_MSG:
    strcpy(txt,"Del Offline Msgs");
    break;
  case C_SEND_MESSAGE:
    strcpy(txt,"Send Message");
    break;
  case C_LOGIN:
    strcpy(txt,"Login");
    break;
  case C_CONTACT_LIST:
    strcpy(txt,"Contact List");
    break;
  case C_SEND_TEXT_CODE:
    strcpy(txt,"Client Logout");
    break;
  case C_LOGIN_1:
    strcpy(txt,"Unknown (Login 1)");
    break;
  case C_LOGIN_2:
    strcpy(txt,"Unknown (Login 2)");
    break;
  case C_INFO_REQ:
    strcpy(txt,"Info Request");
    break;
  case C_SEARCH_USER:
    strcpy(txt,"User Search");
    break;
  case C_CHANGE_PASSWORD:
    strcpy(txt,"Change Password");
    break;
  case C_STATUS_CHANGE:
    strcpy(txt,"Status Change");
    break;
  case C_QUERY_STATUS:
    strcpy(txt,"Query Servers");
    break;
  case C_SEND_URL_MESSAGE:
    strcpy(txt,"Request URL Message");
    break;
  case C_EXT_INFO_REQ:
    strcpy(txt,"Ext. Info Request");
    break;
  case C_META:
    strcpy(txt,"Meta Command");
    break;
  case C_UPDATE_INFO:
    strcpy(txt,"Update Basic Info");
    break;
  case C_NEW_USER_INFO:
    strcpy(txt,"New User Info");
    break;
  case C_UPDATE_EXT_INFO:
    strcpy(txt,"Update Ext. Info");
    break;
  case C_NEW_USER_1:
    strcpy(txt,"Request New User");
    break;
  case C_NEW_USER_REG:
    strcpy(txt,"Register New User");
    break;
  case C_UPDATE_AUTH:
    strcpy(txt,"Update Authorize");
    break;
  case C_VIS_LIST:
    strcpy(txt,"Visible List");
    break;
  case C_INVIS_LIST:
    strcpy(txt,"Invisible List");
    break;
  case C_KEEP_ALIVE:
    strcpy(txt,"Ping Server");
    break;
  case S_LOGIN_REPLY:
    strcpy(txt,"Login Reply");
    break;
  case S_USER_ONLINE:
    strcpy(txt,"User Online");
    break;
  case S_USER_OFFLINE:
    strcpy(txt,"User Offline");
    break;
  case S_RECEIVE_MESSAGE:
    strcpy(txt,"Receive Offline Message");
    break;
  case S_GET_MESSAGE:
    strcpy(txt,"Receive Online Message");
    break;
  case S_STATUS_UPDATE :
    strcpy(txt,"Status Update");
    break;
  case S_GO_AWAY:
    strcpy(txt,"Server Logout");
    break;
  case S_REPLY_X1:
    strcpy(txt,"End Online User List");
    break;
  case S_END_OFFLINE_MSGS:
    strcpy(txt,"No More Messages");
    break;
  case S_INFO_REPLY:
    strcpy(txt,"Reply User Info");
    break;
  case S_USER_FOUND:
    strcpy(txt,"User Found");
    break;
  case S_END_OF_SEARCH:
    strcpy(txt,"End Of Search");
    break;
  case S_QUERY_REPLY:
    strcpy(txt,"Query Reply");
    break;
  case S_EXT_INFO_REPLY:
    strcpy(txt,"Ext. Info Reply");
    break;
  case S_UPDATE_REPLY:
    strcpy(txt,"Update Info Reply");
    break;
  case S_UPDATE_EXT_REPLY:
    strcpy(txt,"Update Ext. Reply");
    break;
  case S_NEW_USER_REPLY:
    strcpy(txt,"New User Info Reply");
    break;
  case S_BAD_PASSWORD:
    strcpy(txt,"Bad Password");
    break;
  case S_NEW_USER_UIN:
    strcpy(txt,"New User UIN");
    break;
  case S_UPDATE_AUTH_REPLY:
    strcpy(txt,"Update Auth. Reply");
    break;
  case S_TOO_BUSY:
    strcpy(txt,"Server is Busy");
    break;
  case S_TCP_REQUEST:
    strcpy(txt,"TCP Information");
    break;
  case S_INFO_NOT_FOUND:
    strcpy(txt,"Info Not Found");
    break;
  case S_EXT_INFO_NOT_FOUND:
    strcpy(txt,"Ext. Info Not Found");
    break;
  case S_MULTI:
    strcpy(txt,"Multi-Packet");
    break;
  case S_META:
    strcpy(txt,"Meta Command Reply");
    break;
  default:
    sprintf(txt,"0x%04x",cmd);
}

return(txt);

}
