]> code.citadel.org Git - citadel.git/commitdiff
* serv_icq.c, serv_icq.mk: added (separate makefile is temporary)
authorArt Cancro <ajc@citadel.org>
Mon, 19 Jul 1999 04:12:49 +0000 (04:12 +0000)
committerArt Cancro <ajc@citadel.org>
Mon, 19 Jul 1999 04:12:49 +0000 (04:12 +0000)
citadel/ChangeLog
citadel/serv_icq.c [new file with mode: 0644]
citadel/serv_icq.mk [new file with mode: 0644]

index c42b341e9e834d934efa715b445c171f1cdfad32..afb3a0db0bdf5127351fe849da09042be825f4a2 100644 (file)
@@ -1,5 +1,6 @@
 Sun Jul 18 14:53:16 EDT 1999 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us> 
        * Changes to dynloader et al to handle ICQ module being written
+       * serv_icq.c, serv_icq.mk: added (separate makefile is temporary)
 
 1999-07-17 Nathan Bryant <bryant@cs.usm.maine.edu>
        * chkpwd.c: DELETED CVS REVISION 1.3 (backed out Art's last change)
diff --git a/citadel/serv_icq.c b/citadel/serv_icq.c
new file mode 100644 (file)
index 0000000..93ba14b
--- /dev/null
@@ -0,0 +1,2099 @@
+/* 
+ * serv_icq.c
+ *
+ * This is a modified version of Denis' ICQLIB, a very cleanly
+ * written implementation of the Mirabilis ICQ client protocol.  The library
+ * has been modified rather than merely utilized because we need it to be
+ * threadsafe in order to tie it into the Citadel server.
+
+ $Id$
+ $Log$
+ Revision 1.1  1999/07/19 04:12:49  ajc
+         * serv_icq.c, serv_icq.mk: added (separate makefile is temporary)
+
+ Revision 1.16  1998/12/08 16:00:59  denis
+ Cleaned up a little before releasing
+
+ Revision 1.15  1998/11/25 19:18:16  denis
+ Added close icq_ProxySok in icq_Disconnect
+
+ Revision 1.14  1998/11/25 09:48:49  denis
+ icq_GetProxySok and icq_HandleProxyResponse methods added
+ Connection terminated support added
+
+ Revision 1.13  1998/11/19 12:22:48  denis
+ SOCKS support cleaned a little
+ icq_RecvUrl renamed to icq_RecvURL
+ icq_ProxyAuth added for Username/Password Authentication
+ URL/Description order inverted
+ icq_Quit splitted to icq_Logout and icq_Disconnect
+ icq_ProxyName and icq_ProxyPass range checking added
+
+ Revision 1.12  1998/11/18 16:21:29  denis
+ Fixed SOCKS5 proxy support
+ */
+
+#include "serv_icq.h"
+
+#include <stdio.h>
+#include <netdb.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "sysdep.h"
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#include "citadel.h"
+#include "server.h"
+#include "dynloader.h"
+#include "tools.h"
+#include "citserver.h"
+
+struct ctdl_icq_handle {
+       int icq_Sok;
+       BOOL icq_Russian;
+       BYTE icq_ServMess[8192];
+       WORD icq_SeqNum;
+       DWORD icq_OurIp;
+       DWORD icq_OurPort;
+       DWORD icq_Uin;
+       icq_ContactItem *icq_ContFirst;
+       DWORD icq_Status;
+       char *icq_Password;
+       BYTE icq_LogLevel;
+       BYTE icq_UseProxy;
+       char *icq_ProxyHost;
+       WORD icq_ProxyPort;
+       int icq_ProxyAuth;
+       char *icq_ProxyName;
+       char *icq_ProxyPass;
+       int icq_ProxySok;
+       DWORD icq_ProxyDestHost;
+       WORD icq_ProxyDestPort;
+       WORD icq_ProxyOurPort;
+       FILE *icq_MyConfigFile;         /* ig */
+       time_t icq_LastKeepAlive;       /* ig */
+};
+
+/* <ig> */
+unsigned long SYM_CTDL_ICQ;
+#define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
+extern struct CitContext *ContextList;
+#define MODULE_NAME    "ICQ metaclient"
+#define MODULE_AUTHOR  "Art Cancro"
+#define MODULE_EMAIL   "ajc@uncnsrd.mt-kisco.ny.us"
+#define MAJOR_VERSION  0
+#define MINOR_VERSION  1
+
+static struct DLModule_Info info =
+{
+       MODULE_NAME,
+       MODULE_AUTHOR,
+       MODULE_EMAIL,
+       MAJOR_VERSION,
+       MINOR_VERSION
+};
+
+/* </ig> */
+
+void (*icq_Logged) (void);
+void (*icq_Disconnected) (void);
+void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
+void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
+void (*icq_RecvAdded) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email);
+void (*icq_RecvAuthReq) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email, const char *reason);
+void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
+void (*icq_SearchDone) (void);
+void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
+void (*icq_UserOffline) (DWORD uin);
+void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
+void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
+void (*icq_ExtInfoReply) (DWORD uin, const char *city, WORD country_code, char country_stat, const char *state, WORD age, char gender, const char *phone, const char *hp, const char *about);
+void (*icq_Log) (time_t time, unsigned char level, const char *str);
+void (*icq_SrvAck) (WORD seq);
+
+BYTE kw[] =
+{128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
+ 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
+ 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
+ 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
+
+BYTE wk[] =
+{128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
+ 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
+ 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
+ 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
+ 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
+
+static COUNTRY_CODE Country_Codes[] =
+{
+       {"N/A", 0},
+       {"USA", 1},
+       {"Russia", 7},
+       {"Australia", 61},
+       {"Denmark", 45},
+       {"Sweden", 46},
+       {"Norway", 47},
+       {"Canada", 107},
+       {"Brazil", 55},
+       {"UK", 0x2c},
+       {"Finland", 358},
+       {"Iceland", 354},
+       {"Algeria", 213},
+       {"American Samoa", 684},
+       {"Argentina", 54},
+       {"Aruba", 297},
+       {"Austria", 43},
+       {"Bahrain", 973},
+       {"Bangladesh", 880},
+       {"Belgium", 32},
+       {"Belize", 501},
+       {"Bolivia", 591},
+       {"Cameroon", 237},
+       {"Chile", 56},
+       {"China", 86},
+       {"Columbia", 57},
+       {"Costa Rice", 506},
+       {"Cyprus", 357},
+       {"Czech Republic", 42},
+       {"Ecuador", 593},
+       {"Egypt", 20},
+       {"El Salvador", 503},
+       {"Ethiopia", 251},
+       {"Fiji", 679},
+       {"France", 33},
+       {"French Antilles", 596},
+       {"French Polynesia", 689},
+       {"Gabon", 241},
+       {"German", 49},
+       {"Ghana", 233},
+       {"Greece", 30},
+       {"Guadeloupe", 590},
+       {"Guam", 671},
+       {"Guantanomo Bay", 53},
+       {"Guatemala", 502},
+       {"Guyana", 592},
+       {"Haiti", 509},
+       {"Honduras", 504},
+       {"Hong Kong", 852},
+       {"Hungary", 36},
+       {"India", 91},
+       {"Indonesia", 62},
+       {"Iran", 98},
+       {"Iraq", 964},
+       {"Ireland", 353},
+       {"Israel", 972},
+       {"Italy", 39},
+       {"Ivory Coast", 225},
+       {"Japan", 81},
+       {"Jordan", 962},
+       {"Kenya", 254},
+       {"South Korea", 82},
+       {"Kuwait", 965},
+       {"Liberia", 231},
+       {"Libya", 218},
+       {"Liechtenstein", 41},
+       {"Luxembourg", 352},
+       {"Malawi", 265},
+       {"Malaysia", 60},
+       {"Mali", 223},
+       {"Malta", 356},
+       {"Mexico", 52},
+       {"Monaco", 33},
+       {"Morocco", 212},
+       {"Namibia", 264},
+       {"Nepal", 977},
+       {"Netherlands", 31},
+       {"Netherlands Antilles", 599},
+       {"New Caledonia", 687},
+       {"New Zealand", 64},
+       {"Nicaragua", 505},
+       {"Nigeria", 234},
+       {"Oman", 968},
+       {"Pakistan", 92},
+       {"Panama", 507},
+       {"Papua New Guinea", 675},
+       {"Paraguay", 595},
+       {"Peru", 51},
+       {"Philippines", 63},
+       {"Poland", 48},
+       {"Portugal", 351},
+       {"Qatar", 974},
+       {"Romania", 40},
+       {"Saipan", 670},
+       {"San Marino", 39},
+       {"Saudia Arabia", 966},
+       {"Saipan", 670},
+       {"Senegal", 221},
+       {"Singapore", 65},
+       {"Slovakia", 42},
+       {"South Africa", 27},
+       {"Spain", 34},
+       {"Sri Lanka", 94},
+       {"Suriname", 597},
+       {"Switzerland", 41},
+       {"Taiwan", 886},
+       {"Tanzania", 255},
+       {"Thailand", 66},
+       {"Tunisia", 216},
+       {"Turkey", 90},
+       {"Ukraine", 380},
+       {"United Arab Emirates", 971},
+       {"Uruguay", 598},
+       {"Vatican City", 39},
+       {"Venezuela", 58},
+       {"Vietnam", 84},
+       {"Yemen", 967},
+       {"Yugoslavia", 38},
+       {"Zaire", 243},
+       {"Zimbabwe", 263},
+       {"Not entered", 0xffff}};
+
+void icq_init_handle(struct ctdl_icq_handle *i)
+{
+       memset(i, 0, sizeof(struct ctdl_icq_handle));
+       i->icq_Russian = TRUE;
+       i->icq_SeqNum = 1;
+       i->icq_OurIp = 0x0100007f;
+       i->icq_Status = STATUS_OFFLINE;
+       i->icq_Sok = (-1);
+       i->icq_LogLevel = 9;
+       i->icq_MyConfigFile = NULL;
+}
+
+int icq_SockWrite(int sok, const void *buf, size_t count)
+{
+       char tmpbuf[1024];
+       if (!(ThisICQ->icq_UseProxy))
+               return write(sok, buf, count);
+       else {
+               tmpbuf[0] = 0;  /* reserved */
+               tmpbuf[1] = 0;  /* reserved */
+               tmpbuf[2] = 0;  /* standalone packet */
+               tmpbuf[3] = 1;  /* address type IP v4 */
+               memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
+               memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
+               memcpy(&tmpbuf[10], buf, count);
+               return write(sok, tmpbuf, count + 10) - 10;
+       }
+}
+
+int icq_SockRead(int sok, void *buf, size_t count)
+{
+       int res;
+       char tmpbuf[1024];
+       if (!(ThisICQ->icq_UseProxy))
+               return read(sok, buf, count);
+       else {
+               res = read(sok, tmpbuf, count + 10);
+               memcpy(buf, &tmpbuf[10], res - 10);
+               return res - 10;
+       }
+}
+
+/****************************************
+This must be called every 2 min.
+so the server knows we're still alive.
+JAVA client sends two different commands
+so we do also :)
+*****************************************/
+void icq_KeepAlive()
+{
+       net_icq_pak pak;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
+
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
+}
+
+/**********************************
+This must be called to remove
+messages from the server
+***********************************/
+void icq_SendGotMessages()
+{
+       net_icq_pak pak;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
+}
+
+/*************************************
+this sends over the contact list
+*************************************/
+void icq_SendContactList()
+{
+       net_icq_pak pak;
+       char num_used;
+       int size;
+       char *tmp;
+       icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+
+       tmp = pak.data;
+       tmp++;
+       num_used = 0;
+       while (ptr) {
+               DW_2_Chars(tmp, ptr->uin);
+               tmp += 4;
+               num_used++;
+               ptr = ptr->next;
+       }
+       pak.data[0] = num_used;
+       size = ((int) tmp - (int) pak.data);
+       size += sizeof(pak.head);
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
+}
+
+void icq_SendNewUser(unsigned long uin)
+{
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       DW_2_Chars(pak.data, uin);
+       size = sizeof(pak.head) + 4;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
+}
+
+/*************************************
+this sends over the visible list
+that allows certain users to see you
+if you're invisible.
+*************************************/
+void icq_SendVisibleList()
+{
+       net_icq_pak pak;
+       char num_used;
+       int size;
+       char *tmp;
+       icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+
+       tmp = pak.data;
+       tmp++;
+       num_used = 0;
+       while (ptr) {
+               if (ptr->vis_list) {
+                       DW_2_Chars(tmp, ptr->uin);
+                       tmp += 4;
+                       num_used++;
+               }
+               ptr = ptr->next;
+       }
+       if (num_used != 0) {
+               pak.data[0] = num_used;
+               size = ((int) tmp - (int) pak.data);
+               size += sizeof(pak.head);
+               icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
+       }
+}
+
+/**************************************
+This sends the second login command
+this is necessary to finish logging in.
+***************************************/
+void icq_SendLogin1()
+{
+       net_icq_pak pak;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
+}
+
+/************************************************
+This is called when a user goes offline
+*************************************************/
+void icq_HandleUserOffline(srv_net_icq_pak pak)
+{
+       DWORD remote_uin;
+       char buf[256];
+
+       remote_uin = Chars_2_DW(pak.data);
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+               sprintf(buf, "User %lu logged off\n", remote_uin);
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+       }
+       if (icq_UserOffline)
+               (*icq_UserOffline) (remote_uin);
+       icq_AckSrv(Chars_2_Word(pak.head.seq));
+}
+
+void icq_HandleUserOnline(srv_net_icq_pak pak)
+{
+       DWORD remote_uin, new_status, remote_ip, remote_real_ip;
+       DWORD remote_port;      /* Why Mirabilis used 4 bytes for port? */
+       icq_ContactItem *ptr;
+       char buf[256];
+
+       remote_uin = Chars_2_DW(pak.data);
+       new_status = Chars_2_DW(&pak.data[17]);
+       remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
+       remote_port = ntohl(Chars_2_DW(&pak.data[8]));
+       remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+               sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+       }
+       if (icq_UserOnline)
+               (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
+       icq_AckSrv(Chars_2_Word(pak.head.seq));
+}
+
+void icq_Status_Update(srv_net_icq_pak pak)
+{
+       unsigned long remote_uin, new_status;
+       char buf[256];
+
+       remote_uin = Chars_2_DW(pak.data);
+       new_status = Chars_2_DW(&pak.data[4]);
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+               sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+       }
+       if (icq_UserStatusUpdate)
+               (*icq_UserStatusUpdate) (remote_uin, new_status);
+       icq_AckSrv(Chars_2_Word(pak.head.seq));
+}
+
+/************************************
+This procedure logins into the server with (ThisICQ->icq_Uin) and pass
+on the socket (ThisICQ->icq_Sok) and gives our ip and port.
+It does NOT wait for any kind of a response.
+*************************************/
+void icq_Login(DWORD status)
+{
+       net_icq_pak pak;
+       int size, ret;
+       login_1 s1;
+       login_2 s2;
+
+       memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_LOGIN);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+
+       DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
+       Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
+
+       DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
+       DW_2_Chars(s2.status, status);
+       Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
+
+       DW_2_Chars(s2.X1, LOGIN_X1_DEF);
+       s2.X2[0] = LOGIN_X2_DEF;
+       DW_2_Chars(s2.X3, LOGIN_X3_DEF);
+       DW_2_Chars(s2.X4, LOGIN_X4_DEF);
+       DW_2_Chars(s2.X5, LOGIN_X5_DEF);
+
+       memcpy(pak.data, &s1, sizeof(s1));
+       size = 6;
+       memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
+       size += Chars_2_Word(s1.len);
+       memcpy(&pak.data[size], &s2, sizeof(s2));
+       size += sizeof(s2);
+       ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+/*******************************
+This routine sends the aknowlegement cmd to the
+server it appears that this must be done after
+everything the server sends us
+*******************************/
+void icq_AckSrv(int seq)
+{
+       int i;
+       net_icq_pak pak;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_ACK);
+       Word_2_Chars(pak.head.seq, seq);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
+       for (i = 0; i < 6; i++)
+               icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
+}
+
+void icq_HandleInfoReply(srv_net_icq_pak pak)
+{
+       char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
+       int len;
+       DWORD uin;
+       WORD seq;
+       seq = Chars_2_Word(pak.data);
+       uin = Chars_2_DW(&pak.data[2]);
+       len = Chars_2_Word(&pak.data[6]);
+       ptr1 = &pak.data[8];
+       icq_RusConv("wk", ptr1);
+       tmp = &pak.data[8 + len];
+       len = Chars_2_Word(tmp);
+       ptr2 = tmp + 2;
+       icq_RusConv("wk", ptr2);
+       tmp += len + 2;
+       len = Chars_2_Word(tmp);
+       ptr3 = tmp + 2;
+       icq_RusConv("wk", ptr3);
+       tmp += len + 2;
+       len = Chars_2_Word(tmp);
+       ptr4 = tmp + 2;
+       icq_RusConv("wk", ptr4);
+       tmp += len + 2;
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+               sprintf(buf, "Info reply for %lu\n", uin);
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+       }
+       if (icq_InfoReply)
+               (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
+       icq_AckSrv(Chars_2_Word(pak.head.seq));
+}
+
+void icq_HandleExtInfoReply(srv_net_icq_pak pak)
+{
+       unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
+       int len;
+       DWORD uin;
+       WORD cnt_code, age;
+       char cnt_stat, gender, buf[256];
+       uin = Chars_2_DW(&pak.data[2]);
+       len = Chars_2_Word(&pak.data[6]);
+       ptr1 = &pak.data[8];
+       icq_RusConv("wk", ptr1);
+       cnt_code = Chars_2_Word(&pak.data[8 + len]);
+       cnt_stat = pak.data[len + 10];
+       tmp = &pak.data[11 + len];
+       len = Chars_2_Word(tmp);
+       icq_RusConv("wk", tmp + 2);
+       ptr2 = tmp + 2;
+       age = Chars_2_Word(tmp + 2 + len);
+       gender = *(tmp + len + 4);
+       tmp += len + 5;
+       len = Chars_2_Word(tmp);
+       icq_RusConv("wk", tmp + 2);
+       ptr3 = tmp + 2;
+       tmp += len + 2;
+       len = Chars_2_Word(tmp);
+       icq_RusConv("wk", tmp + 2);
+       ptr4 = tmp + 2;
+       tmp += len + 2;
+       len = Chars_2_Word(tmp);
+       icq_RusConv("wk", tmp + 2);
+       ptr5 = tmp + 2;
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+               sprintf(buf, "Extended info reply for %lu\n", uin);
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+       }
+       if (icq_ExtInfoReply)
+               (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
+       icq_AckSrv(Chars_2_Word(pak.head.seq));
+}
+
+void icq_HandleSearchReply(srv_net_icq_pak pak)
+{
+       char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
+       int len;
+       char buf[512];
+       DWORD uin;
+       uin = Chars_2_DW(&pak.data[2]);
+       len = Chars_2_Word(&pak.data[6]);
+       ptr1 = &pak.data[8];
+       icq_RusConv("wk", ptr1);
+       tmp = &pak.data[8 + len];
+       len = Chars_2_Word(tmp);
+       ptr2 = tmp + 2;
+       icq_RusConv("wk", ptr2);
+       tmp += len + 2;
+       len = Chars_2_Word(tmp);
+       ptr3 = tmp + 2;
+       icq_RusConv("wk", ptr3);
+       tmp += len + 2;
+       len = Chars_2_Word(tmp);
+       ptr4 = tmp + 2;
+       icq_RusConv("wk", ptr4);
+       tmp += len + 2;
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+               sprintf(buf, "User found %lu, Nick: %s, First Name: %s, Last Name: %s, EMail: %s, Auth: %s\n", uin, ptr1, ptr2, ptr3, ptr4, *tmp == 1 ? "no" : "yes");
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+       }
+       if (icq_UserFound)
+               (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
+       icq_AckSrv(Chars_2_Word(pak.head.seq));
+}
+
+void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
+{
+       char *tmp;
+       char *ptr1, *ptr2, *ptr3, *ptr4;
+       char buf[1024];
+
+       switch (type) {
+       case USER_ADDED_MESS:
+               tmp = strchr(data, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               ptr1 = data;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               ptr2 = data;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               ptr3 = data;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\xFE');
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+                       sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
+                               "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+               }
+               if (icq_RecvAdded)
+                       (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
+               break;
+       case AUTH_REQ_MESS:
+               tmp = strchr(data, '\xFE');
+               *tmp = 0;
+               ptr1 = data;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               ptr2 = data;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               ptr3 = data;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               ptr4 = data;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               tmp++;
+               data = tmp;
+               tmp = strchr(tmp, '\x00');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+                       sprintf(buf, "%lu has requested your authorization to be added to "
+                               "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
+                               "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+               }
+               if (icq_RecvAuthReq)
+                       (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
+               break;
+       case URL_MESS:
+               tmp = strchr(data, '\xFE');
+               if (tmp == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                               (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
+                       return;
+               }
+               *tmp = 0;
+               icq_RusConv("wk", data);
+               ptr1 = data;
+               tmp++;
+               data = tmp;
+               icq_RusConv("wk", data);
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+                       sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+               }
+               if (icq_RecvURL)
+                       (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
+               break;
+       default:
+               icq_RusConv("wk", data);
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+                       sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+               }
+               if (icq_RecvMessage)
+                       (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
+       }
+}
+
+/**********************************
+Connects to hostname on port port
+hostname can be DNS or nnn.nnn.nnn.nnn
+write out messages to the FD aux
+***********************************/
+int icq_Connect(const char *hostname, int port)
+{
+       char buf[1024], un = 1;
+       char our_host[256], tmpbuf[256];
+       int conct, length, res;
+       struct sockaddr_in sin, prsin;  /* used to store inet addr stuff */
+       struct hostent *host_struct;    /* used in DNS llokup */
+
+       (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0);    /* create the unconnected socket */
+       if ((ThisICQ->icq_Sok) == -1) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
+               return -1;
+       }
+       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+               (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
+       sin.sin_addr.s_addr = INADDR_ANY;
+       sin.sin_family = AF_INET;       /* we're using the inet not appletalk */
+       sin.sin_port = 0;
+       if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
+               return -1;
+       }
+       length = sizeof(sin);
+       getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
+       (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
+       if ((ThisICQ->icq_UseProxy)) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
+               prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
+               if (prsin.sin_addr.s_addr == -1) {      /* name isn't n.n.n.n so must be DNS */
+                       host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
+                       if (host_struct == 0L) {
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
+                                       sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
+                               }
+                               return -1;
+                       }
+                       prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
+               }
+               prsin.sin_family = AF_INET;     /* we're using the inet not appletalk */
+               prsin.sin_port = htons((ThisICQ->icq_ProxyPort));       /* port */
+               (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0);      /* create the unconnected socket */
+               if ((ThisICQ->icq_ProxySok) == -1) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                               (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
+                       return -1;
+               }
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
+               conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
+               if (conct == -1) {      /* did we connect ? */
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                               (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
+                       return -1;
+               }
+               buf[0] = 5;     /* protocol version */
+               buf[1] = 1;     /* number of methods */
+               if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
+                       buf[2] = 0;     /* no authorization required */
+               else
+                       buf[2] = 2;     /* method username/password */
+               write((ThisICQ->icq_ProxySok), buf, 3);
+               res = read((ThisICQ->icq_ProxySok), buf, 2);
+               if (res != 2 || buf[0] != 5 || buf[1] != 2) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                               (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
+                       close((ThisICQ->icq_ProxySok));
+                       return -1;
+               }
+               if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
+                       buf[0] = 1;     /* version of subnegotiation */
+                       buf[1] = strlen((ThisICQ->icq_ProxyName));
+                       memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
+                       buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
+                       memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
+                       write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
+                       res = read((ThisICQ->icq_ProxySok), buf, 2);
+                       if (res != 2 || buf[0] != 1 || buf[1] != 0) {
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
+                               close((ThisICQ->icq_ProxySok));
+                               return -1;
+                       }
+               }
+               buf[0] = 5;     /* protocol version */
+               buf[1] = 3;     /* command UDP associate */
+               buf[2] = 0;     /* reserved */
+               buf[3] = 1;     /* address type IP v4 */
+               buf[4] = (char) 0;
+               buf[5] = (char) 0;
+               buf[6] = (char) 0;
+               buf[7] = (char) 0;
+               memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
+               write((ThisICQ->icq_ProxySok), buf, 10);
+               res = read((ThisICQ->icq_ProxySok), buf, 10);
+               if (res != 10 || buf[0] != 5 || buf[1] != 0) {
+                       switch (buf[1]) {
+                       case 1:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
+                               break;
+                       case 2:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
+                               break;
+                       case 3:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
+                               break;
+                       case 4:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
+                               break;
+                       case 5:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
+                               break;
+                       case 6:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
+                               break;
+                       case 7:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
+                               break;
+                       case 8:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
+                               break;
+                       default:
+                               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
+                               break;
+                       }
+                       close((ThisICQ->icq_ProxySok));
+                       return -1;
+               }
+       }
+       sin.sin_addr.s_addr = inet_addr(hostname);      /* checks for n.n.n.n notation */
+       if (sin.sin_addr.s_addr == -1) {        /* name isn't n.n.n.n so must be DNS */
+               host_struct = gethostbyname(hostname);
+               if (host_struct == 0L) {
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
+                               sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
+                               (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
+                       }
+                       if ((ThisICQ->icq_UseProxy))
+                               close((ThisICQ->icq_ProxySok));
+                       return -1;
+               }
+               sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
+       }
+       if ((ThisICQ->icq_UseProxy)) {
+               (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
+               memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
+       }
+       sin.sin_family = AF_INET;       /* we're using the inet not appletalk */
+       sin.sin_port = htons(port);     /* port */
+       if ((ThisICQ->icq_UseProxy)) {
+               (ThisICQ->icq_ProxyDestPort) = htons(port);
+               memcpy(&sin.sin_port, &buf[8], 2);
+       }
+       conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
+       if (conct == -1) {      /* did we connect ? */
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
+               if ((ThisICQ->icq_UseProxy))
+                       close((ThisICQ->icq_ProxySok));
+               return -1;
+       }
+       length = sizeof(sin);
+       getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
+       (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
+       (ThisICQ->icq_OurPort) = sin.sin_port;
+       return (ThisICQ->icq_Sok);
+}
+
+void icq_HandleProxyResponse()
+{
+       int s;
+       char buf[256];
+       s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
+       if (s <= 0) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
+               if (icq_Disconnected)
+                       (*icq_Disconnected) ();
+               SOCKCLOSE((ThisICQ->icq_Sok));
+               SOCKCLOSE((ThisICQ->icq_ProxySok));
+       }
+}
+
+/******************************************
+Handles packets that the server sends to us.
+*******************************************/
+void icq_HandleServerResponse()
+{
+       srv_net_icq_pak pak;
+       SIMPLE_MESSAGE *s_mesg;
+       RECV_MESSAGE *r_mesg;
+       time_t cur_time;
+       struct tm *tm_str;
+       int s, len;
+       char buf[1024];
+       cback acback;
+
+       s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
+       if (s <= 0) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
+                       (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
+               if (icq_Disconnected)
+                       (*icq_Disconnected) ();
+               SOCKCLOSE((ThisICQ->icq_Sok));
+       }
+       if ((icq_GetServMess(Chars_2_Word(pak.head.seq))) && (Chars_2_Word(pak.head.cmd) != SRV_NEW_UIN) && (Chars_2_Word(pak.head.cmd) != SRV_GO_AWAY)) {
+               if (Chars_2_Word(pak.head.cmd) != SRV_ACK) {    /* ACKs don't matter */
+                       if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
+                               sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
+                               (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
+                       }
+                       icq_AckSrv(Chars_2_Word(pak.head.seq));         /* LAGGGGG!! */
+                       return;
+               }
+       }
+       if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
+               icq_SetServMess(Chars_2_Word(pak.head.seq));
+       switch (Chars_2_Word(pak.head.cmd)) {
+       case SRV_ACK:
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
+               if (icq_SrvAck)
+                       (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
+               break;
+       case SRV_NEW_UIN:
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+                       sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+               }
+               break;
+       case SRV_LOGIN_REPLY:
+               (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
+                       sprintf(buf, "Login successful, UIN: %lu, IP: %u.%u.%u.%u\n", Chars_2_DW(pak.data), pak.data[4], pak.data[5], pak.data[6], pak.data[7]);
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
+               }
+               icq_AckSrv(Chars_2_Word(pak.head.seq));
+               icq_SendLogin1();
+               icq_SendContactList();
+               icq_SendVisibleList();
+               if (icq_Logged)
+                       (*icq_Logged) ();
+               break;
+       case SRV_RECV_MESSAGE:
+               r_mesg = (RECV_MESSAGE *) pak.data;
+               icq_DoMsg(Chars_2_Word(r_mesg->type), Chars_2_Word(r_mesg->len), (char *) (r_mesg->len + 2), Chars_2_DW(r_mesg->uin), r_mesg->hour, r_mesg->minute, r_mesg->day, r_mesg->month, Chars_2_Word(r_mesg->year));
+               icq_AckSrv(Chars_2_Word(pak.head.seq));
+               break;
+       case SRV_X1:            /* unknown message  sent after login */
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
+               icq_AckSrv(Chars_2_Word(pak.head.seq));
+               break;
+       case SRV_X2:            /* unknown message  sent after login */
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
+               icq_AckSrv(Chars_2_Word(pak.head.seq));
+               icq_SendGotMessages();
+               break;
+       case SRV_INFO_REPLY:
+               icq_HandleInfoReply(pak);
+               break;
+       case SRV_EXT_INFO_REPLY:
+               icq_HandleExtInfoReply(pak);
+               break;
+       case SRV_USER_ONLINE:
+               icq_HandleUserOnline(pak);
+               break;
+       case SRV_USER_OFFLINE:
+               icq_HandleUserOffline(pak);
+               break;
+       case SRV_TRY_AGAIN:
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
+                       (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
+               icq_Login((ThisICQ->icq_Status));
+               break;
+       case SRV_STATUS_UPDATE:
+               icq_Status_Update(pak);
+               break;
+       case SRV_GO_AWAY:
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                       (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
+               if (icq_Disconnected)
+                       (*icq_Disconnected) ();
+               break;
+       case SRV_END_OF_SEARCH:
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
+                       (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
+               if (icq_SearchDone)
+                       (*icq_SearchDone) ();
+               icq_AckSrv(Chars_2_Word(pak.head.seq));
+               break;
+       case SRV_USER_FOUND:
+               icq_HandleSearchReply(pak);
+               break;
+       case SRV_SYS_DELIVERED_MESS:
+               s_mesg = (SIMPLE_MESSAGE *) pak.data;
+               cur_time = time(0L);
+               tm_str = localtime(&cur_time);
+               icq_DoMsg(Chars_2_Word(s_mesg->type), Chars_2_Word(s_mesg->len), (char *) (s_mesg->len + 2), Chars_2_DW(s_mesg->uin), tm_str->tm_hour, tm_str->tm_min, tm_str->tm_mday, tm_str->tm_mon + 1, tm_str->tm_year + 1900);
+               icq_AckSrv(Chars_2_Word(pak.head.seq));
+               break;
+       default:                /* commands we dont handle yet */
+               len = s - (sizeof(pak.head));
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
+                       sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
+                               Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
+                       (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
+               }
+               icq_AckSrv(Chars_2_Word(pak.head.seq));         /* fake like we know what we're doing */
+               break;
+       }
+}
+
+void icq_Init(DWORD uin, const char *password)
+{
+       memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
+       (ThisICQ->icq_Uin) = uin;
+       if ((ThisICQ->icq_Password))
+               free((ThisICQ->icq_Password));
+       (ThisICQ->icq_Password) = strdup(password);
+}
+
+void icq_Done(void)
+{
+       if ((ThisICQ->icq_Password))
+               free((ThisICQ->icq_Password));
+}
+
+/******************************
+Main function connects gets (ThisICQ->icq_Uin)
+and (ThisICQ->icq_Password) and logins in and sits
+in a loop waiting for server responses.
+*******************************/
+void icq_Main()
+{
+       struct timeval tv;
+       fd_set readfds;
+       int did_something;
+
+       do {
+               did_something = 0;
+               tv.tv_sec = 0;
+               tv.tv_usec = 0;
+               FD_ZERO(&readfds);
+               FD_SET((ThisICQ->icq_Sok), &readfds);
+               select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
+               if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
+                       icq_HandleServerResponse();
+                       did_something = 1;
+                       sleep(1);
+               }
+       } while (did_something);
+}
+
+/********************************************************
+Russian language ICQ fix.
+Usual Windows ICQ users do use Windows 1251 encoding but
+unix users do use koi8 encoding, so we need to convert it.
+This function will convert string from windows 1251 to koi8
+or from koi8 to windows 1251.
+Andrew Frolov dron@ilm.net
+*********************************************************/
+void icq_RusConv(const char to[4], char *t_in)
+{
+       BYTE *table;
+       int i;
+
+/* 6-17-1998 by Linux_Dude
+ * Moved initialization of table out front of 'if' block to prevent compiler
+ * warning. Improved error message, and now return without performing string
+ * conversion to prevent addressing memory out of range (table pointer would
+ * previously have remained uninitialized (= bad)).
+ */
+
+       table = wk;
+       if (strcmp(to, "kw") == 0)
+               table = kw;
+       else if (strcmp(to, "wk") != 0) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                       (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
+               return;
+       }
+/* End Linux_Dude's changes ;) */
+
+       if ((ThisICQ->icq_Russian)) {
+               for (i = 0; t_in[i] != 0; i++) {
+                       t_in[i] &= 0377;
+                       if (t_in[i] & 0200)
+                               t_in[i] = table[t_in[i] & 0177];
+               }
+       }
+}
+
+/**************************************************
+Sends a message thru the server to (ThisICQ->icq_Uin).  Text is the
+message to send.
+***************************************************/
+WORD icq_SendMessage(DWORD uin, const char *text)
+{
+       SIMPLE_MESSAGE msg;
+       net_icq_pak pak;
+       int size, len, i;
+       char buf[512];          /* message may be only 450 bytes long */
+
+       strncpy(buf, text, 512);
+       icq_RusConv("kw", buf);
+       len = strlen(buf);
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_SENDM);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       DW_2_Chars(msg.uin, uin);
+       DW_2_Chars(msg.type, 0x0001);   /* A type 1 msg */
+       Word_2_Chars(msg.len, len + 1);         /* length + the NULL */
+       memcpy(&pak.data, &msg, sizeof(msg));
+       memcpy(&pak.data[8], buf, len + 1);
+       size = sizeof(msg) + len + 1;
+       for (i = 0; i < 6; i++)
+               icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+       return (ThisICQ->icq_SeqNum) - 1;
+}
+
+WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
+{
+       SIMPLE_MESSAGE msg;
+       net_icq_pak pak;
+       int size, len1, len2;
+       char buf1[512], buf2[512];
+
+       strncpy(buf1, descr, 512);
+       strncpy(buf2, url, 512);
+/* Do we need to convert URL? */
+       icq_RusConv("kw", buf2);
+       len1 = strlen(buf1);
+       len2 = strlen(buf2);
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_SENDM);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       DW_2_Chars(msg.uin, uin);
+       DW_2_Chars(msg.type, 0x0004);   /* A type 4 msg */
+       Word_2_Chars(msg.len, len1 + len2 + 2);         /* length + the NULL + 0xFE delimiter */
+       memcpy(&pak.data, &msg, sizeof(msg));
+       memcpy(&pak.data[8], buf1, len1);
+       pak.data[8 + len1] = 0xFE;
+       memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
+       size = sizeof(msg) + len1 + len2 + 2;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+       return (ThisICQ->icq_SeqNum) - 1;
+}
+
+/**************************************************
+Sends a authorization to the server so the Mirabilis
+client can add the user.
+***************************************************/
+void icq_SendAuthMsg(DWORD uin)
+{
+       SIMPLE_MESSAGE msg;
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_SENDM);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       DW_2_Chars(msg.uin, uin);
+       DW_2_Chars(msg.type, AUTH_MESSAGE);     /* A type authorization msg */
+       Word_2_Chars(msg.len, 2);
+       memcpy(&pak.data, &msg, sizeof(msg));
+       pak.data[sizeof(msg)] = 0x03;
+       pak.data[sizeof(msg) + 1] = 0x00;
+       size = sizeof(msg) + 2;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+/**************************************************
+Changes the users status on the server
+***************************************************/
+void icq_ChangeStatus(DWORD status)
+{
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       DW_2_Chars(pak.data, status);
+       (ThisICQ->icq_Status) = status;
+       size = 4;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+/**********************
+Logs off ICQ
+***********************/
+void icq_Logout()
+{
+       net_icq_pak pak;
+       int size, len;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       len = strlen("B_USER_DISCONNECTED") + 1;
+       *(short *) pak.data = len;
+       size = len + 4;
+       memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
+       pak.data[2 + len] = 05;
+       pak.data[3 + len] = 00;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+void icq_Disconnect()
+{
+       SOCKCLOSE((ThisICQ->icq_Sok));
+       SOCKCLOSE((ThisICQ->icq_Sok));
+       if ((ThisICQ->icq_UseProxy))
+               SOCKCLOSE((ThisICQ->icq_ProxySok));
+}
+
+/********************************************************
+Sends a request to the server for info on a specific user
+*********************************************************/
+WORD icq_SendInfoReq(DWORD uin)
+{
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(&pak.data[2], uin);
+       size = 6;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+       return (ThisICQ->icq_SeqNum) - 1;
+}
+
+/********************************************************
+Sends a request to the server for info on a specific user
+*********************************************************/
+WORD icq_SendExtInfoReq(DWORD uin)
+{
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(&pak.data[2], uin);
+       size = 6;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+       return (ThisICQ->icq_SeqNum) - 1;
+}
+
+/**************************************************************
+Initializes a server search for the information specified
+***************************************************************/
+void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
+{
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
+       size = 2;
+       Word_2_Chars(&pak.data[size], strlen(nick) + 1);
+       size += 2;
+       strcpy(pak.data + size, nick);
+       size += strlen(nick) + 1;
+       Word_2_Chars(&pak.data[size], strlen(first) + 1);
+       size += 2;
+       strcpy(pak.data + size, first);
+       size += strlen(first) + 1;
+       Word_2_Chars(&pak.data[size], strlen(last) + 1);
+       size += 2;
+       strcpy(pak.data + size, last);
+       size += strlen(last) + 1;
+       Word_2_Chars(&pak.data[size], strlen(email) + 1);
+       size += 2;
+       strcpy(pak.data + size, email);
+       size += strlen(email) + 1;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+/**************************************************************
+Initializes a server search for the information specified
+***************************************************************/
+void icq_SendSearchUINReq(DWORD uin)
+{
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
+       size = 2;
+       DW_2_Chars(&pak.data[size], uin);
+       size += 4;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+/**************************************************
+Registers a new uin in the ICQ network
+***************************************************/
+void icq_RegNewUser(const char *pass)
+{
+       srv_net_icq_pak pak;
+       BYTE len_buf[2];
+       int size, len;
+
+       len = strlen(pass);
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       Word_2_Chars(len_buf, len);
+       memcpy(&pak.data, "\x02\x00", 2);
+       memcpy(&pak.data[2], &len_buf, 2);
+       memcpy(&pak.data[4], pass, len + 1);
+       DW_2_Chars(&pak.data[4 + len], 0x0072);
+       DW_2_Chars(&pak.data[8 + len], 0x0000);
+       size = len + 12;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+void icq_UpdateUserInfo(USER_INFO * user)
+{
+       net_icq_pak pak;
+       int size;
+
+       Word_2_Chars(pak.head.ver, ICQ_VER);
+       Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
+       Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
+       DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
+       Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
+       size = 2;
+       Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
+       size += 2;
+       strcpy(pak.data + size, user->nick);
+       size += strlen(user->nick) + 1;
+       Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
+       size += 2;
+       strcpy(pak.data + size, user->first);
+       size += strlen(user->first) + 1;
+       Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
+       size += 2;
+       strcpy(pak.data + size, user->last);
+       size += strlen(user->last) + 1;
+       Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
+       size += 2;
+       strcpy(pak.data + size, user->email);
+       size += strlen(user->email) + 1;
+       pak.data[size] = user->auth;
+       size++;
+       icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
+}
+
+const char *icq_GetCountryName(int code)
+{
+       int i;
+
+       for (i = 0; Country_Codes[i].code != 0xffff; i++) {
+               if (Country_Codes[i].code == code) {
+                       return Country_Codes[i].name;
+               }
+       }
+       if (Country_Codes[i].code == code) {
+               return Country_Codes[i].name;
+       }
+       return "N/A";
+}
+
+/********************************************
+returns a string describing the status or
+a "Error" if no such string exists
+*********************************************/
+const char *icq_ConvertStatus2Str(int status)
+{
+       if (STATUS_OFFLINE == status) {         /* this because -1 & 0x01FF is not -1 */
+               return "Offline";
+       }
+/* To handle icq99a statuses 0xFFFF changed to 0x01FF */
+       switch (status & 0x01FF) {
+       case STATUS_ONLINE:
+               return "Online";
+               break;
+       case STATUS_DND:
+               return "Do not disturb";
+               break;
+       case STATUS_AWAY:
+               return "Away";
+               break;
+       case STATUS_OCCUPIED:
+               return "Occupied";
+               break;
+       case STATUS_NA:
+               return "Not available";
+               break;
+       case STATUS_INVISIBLE:
+               return "Invisible";
+               break;
+       case STATUS_INVISIBLE_2:
+               return "Invisible mode 2";
+               break;
+       case STATUS_FREE_CHAT:
+               return "Free for chat";
+               break;
+       default:
+               return "Error";
+               break;
+       }
+}
+
+void icq_InitNewUser(const char *hostname, DWORD port)
+{
+       srv_net_icq_pak pak;
+       int s;
+       struct timeval tv;
+       fd_set readfds;
+
+       icq_Connect(hostname, port);
+       if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
+               printf("Couldn't establish connection\n");
+               exit(1);
+       }
+       icq_RegNewUser((ThisICQ->icq_Password));
+       for (;;) {
+               tv.tv_sec = 2;
+               tv.tv_usec = 500000;
+
+               FD_ZERO(&readfds);
+               FD_SET((ThisICQ->icq_Sok), &readfds);
+
+               /* don't care about writefds and exceptfds: */
+               select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
+
+               if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
+                       s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
+                       if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
+                               (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
+                               return;
+                       }
+               }
+       }
+}
+
+/********************************************
+Converts an intel endian character sequence to
+a DWORD
+*********************************************/
+DWORD Chars_2_DW(unsigned char *buf)
+{
+       DWORD i;
+
+       i = buf[3];
+       i <<= 8;
+       i += buf[2];
+       i <<= 8;
+       i += buf[1];
+       i <<= 8;
+       i += buf[0];
+
+       return i;
+}
+
+/********************************************
+Converts an intel endian character sequence to
+a WORD
+*********************************************/
+WORD Chars_2_Word(unsigned char *buf)
+{
+       WORD i;
+
+       i = buf[1];
+       i <<= 8;
+       i += buf[0];
+
+       return i;
+}
+
+/********************************************
+Converts a DWORD to
+an intel endian character sequence 
+*********************************************/
+void DW_2_Chars(unsigned char *buf, DWORD num)
+{
+       buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
+       buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
+       buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
+       buf[0] = (unsigned char) (num) & 0x000000FF;
+}
+
+/********************************************
+Converts a WORD to
+an intel endian character sequence 
+*********************************************/
+void Word_2_Chars(unsigned char *buf, WORD num)
+{
+       buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
+       buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
+}
+
+/***************************
+ContactList functions
+***************************/
+void icq_ContAddUser(DWORD cuin)
+{
+       icq_ContactItem *p = malloc(sizeof(icq_ContactItem));
+       icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
+       p->uin = cuin;
+       p->next = 0L;
+       p->vis_list = FALSE;
+       if (ptr) {
+               while (ptr->next)
+                       ptr = ptr->next;
+               ptr->next = p;
+       } else
+               (ThisICQ->icq_ContFirst) = p;
+}
+
+void icq_ContDelUser(DWORD cuin)
+{
+       icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
+       if (!ptr)
+               return;
+       if (ptr->uin == cuin) {
+               (ThisICQ->icq_ContFirst) = ptr->next;
+               free(ptr);
+               ptr = (ThisICQ->icq_ContFirst);
+       }
+       while (ptr->next) {
+               if (ptr->next->uin == cuin) {
+                       ptr->next = ptr->next->next;
+                       free(ptr->next);
+               }
+               ptr = ptr->next;
+       }
+}
+
+void icq_ContClear()
+{
+       icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
+       while (ptr) {
+               tmp = ptr->next;
+               free(ptr);
+               ptr = tmp;
+               (ThisICQ->icq_ContFirst) = ptr;
+       }
+}
+
+icq_ContactItem *icq_ContFindByUin(DWORD cuin)
+{
+       icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
+       if (!ptr)
+               return 0L;
+       while (ptr) {
+               if (ptr->uin == cuin)
+                       return ptr;
+               ptr = ptr->next;
+       }
+       return 0L;
+}
+
+icq_ContactItem *icq_ContGetFirst()
+{
+       return (ThisICQ->icq_ContFirst);
+}
+
+void icq_ContSetVis(DWORD cuin)
+{
+       icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
+       if (!ptr)
+               return;
+       while (ptr) {
+               if (ptr->uin == cuin)
+                       ptr->vis_list = TRUE;
+               ptr = ptr->next;
+       }
+}
+
+/************************
+(ThisICQ->icq_ServMess) functions
+*************************/
+BOOL icq_GetServMess(WORD num)
+{
+       return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
+}
+
+void icq_SetServMess(WORD num)
+{
+       (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
+}
+
+int icq_GetSok()
+{
+       return (ThisICQ->icq_Sok);
+}
+
+int icq_GetProxySok()
+{
+       return (ThisICQ->icq_ProxySok);
+}
+
+/*******************
+SOCKS5 Proxy support
+********************/
+void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
+{
+       if ((ThisICQ->icq_ProxyHost))
+               free((ThisICQ->icq_ProxyHost));
+       if ((ThisICQ->icq_ProxyName))
+               free((ThisICQ->icq_ProxyName));
+       if ((ThisICQ->icq_ProxyPass))
+               free((ThisICQ->icq_ProxyPass));
+       if (strlen(pname) > 255) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                       (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
+               ThisICQ->icq_UseProxy = 0;
+               return;
+       }
+       if (strlen(ppass) > 255) {
+               if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
+                       (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
+               (ThisICQ->icq_UseProxy) = 0;
+               return;
+       }
+       (ThisICQ->icq_UseProxy) = 1;
+       (ThisICQ->icq_ProxyHost) = strdup(phost);
+       (ThisICQ->icq_ProxyPort) = pport;
+       (ThisICQ->icq_ProxyAuth) = pauth;
+       (ThisICQ->icq_ProxyName) = strdup(pname);
+       (ThisICQ->icq_ProxyPass) = strdup(ppass);
+}
+
+void icq_UnsetProxy()
+{
+       ThisICQ->icq_UseProxy = 0;
+}
+
+
+
+/***********************************************************************/
+/* icqlib stuff ends here, Citadel module stuff begins                 */
+/***********************************************************************/
+
+
+/* config file manipulation routines ... probably temporary until we can
+ * get something more robust written
+ */
+
+/* Delete a key */
+void CtdlICQ_Config_Delete(char *key) {
+       long readpos, writepos;
+       char buf[256], keyplusspace[256];
+       char *ptr;
+
+       if (ThisICQ->icq_MyConfigFile == NULL) return;
+       readpos = 0L;
+       writepos = 0L;
+       sprintf(keyplusspace, "%s ", key);
+
+       while(1) {
+               fseek(ThisICQ->icq_MyConfigFile, readpos, 0);
+               ptr = fgets(buf, 256, ThisICQ->icq_MyConfigFile);
+               readpos = ftell(ThisICQ->icq_MyConfigFile);
+               if (ptr == NULL) {
+                       fflush(ThisICQ->icq_MyConfigFile);
+                       ftruncate(fileno(ThisICQ->icq_MyConfigFile), writepos);
+                       return;
+               } else {
+                       if (strncasecmp(buf, keyplusspace,
+                          strlen(keyplusspace))) {
+                               fseek(ThisICQ->icq_MyConfigFile, writepos, 0);
+                               fprintf(ThisICQ->icq_MyConfigFile, "%s", buf);
+                               writepos = ftell(ThisICQ->icq_MyConfigFile);
+                       }
+               }
+       }
+}
+
+
+/* Write a key */
+void CtdlICQ_Config_Write(char *key, char *contents) {
+       CtdlICQ_Config_Delete(key);
+       fseek(ThisICQ->icq_MyConfigFile, 0L, SEEK_END);
+       fprintf(ThisICQ->icq_MyConfigFile, "%s %s\n", key, contents);
+}
+
+
+/* Read a key */
+void CtdlICQ_Config_Read(char *contents, char *key) {
+       char buf[256];
+
+       strcpy(contents, "");
+       rewind(ThisICQ->icq_MyConfigFile);
+       while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
+               buf[strlen(buf)-1]=0;
+               if ( (!strncasecmp(buf, key, strlen(key)))
+                  && (isspace(buf[strlen(key)])) ) {
+                       strcpy(contents, &buf[strlen(key)+1]);
+               }
+       }
+}
+
+
+/*
+ * Refresh the contact list
+ */
+void CtdlICQ_Refresh_Contact_List(void) {
+       char buf[256];
+       long contact_uin;
+
+       if (ThisICQ->icq_Sok < 0) return;
+
+       icq_ContClear();
+       while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
+               buf[strlen(buf)-1]=0;
+
+               contact_uin = atol(buf);
+               if (contact_uin > 0L) {
+                       icq_ContAddUser(contact_uin);
+                       icq_ContSetVis(contact_uin);
+               }
+       }
+       icq_SendContactList();
+}
+
+
+
+
+/* 
+ * Utility routine to logout and disconnect from the ICQ server if we happen
+ * to already be connected
+ */
+void CtdlICQ_Logout_If_Connected(void) {
+       if (ThisICQ->icq_Sok >= 0) {
+               icq_Logout();
+               icq_Disconnect();
+               ThisICQ->icq_Sok = (-1);
+       }
+}
+
+
+/*
+ * If we have an ICQ uin/password on file for this user, go ahead and try
+ * to log on to the ICQ server.
+ */
+void CtdlICQ_Login_If_Possible(void) {
+       char buf[256];
+       long uin;
+       char pass[256];
+
+       CtdlICQ_Logout_If_Connected();
+
+       CtdlICQ_Config_Read(buf, "uin");
+       uin = atol(buf);
+       CtdlICQ_Config_Read(pass, "pass");
+
+       if ( (uin > 0L) && (strlen(pass)>0) ) {
+               icq_Init(uin, pass);
+               if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
+                       icq_Login(STATUS_ONLINE);
+                       CtdlICQ_Refresh_Contact_List();
+               }
+       }
+}
+
+
+
+/* This merely hooks icqlib's log function into Citadel's log function. */
+void CtdlICQlog(time_t time, unsigned char level, const char *str)
+{
+       lprintf(level, "ICQ: %s", str);
+}
+
+
+/* 
+ * At the start of each Citadel server session, we have to allocate some
+ * space for the Citadel-ICQ session for this thread.  It'll be automatically
+ * freed by the server when the session ends.
+ */
+void CtdlICQ_session_startup_hook(void)
+{
+       CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
+       icq_init_handle(ThisICQ);
+}
+
+
+/*
+ * icq_Main() needs to be called as frequently as possible.  We'll do it
+ * following the completion of each Citadel server command.
+ *
+ */
+void CtdlICQ_after_cmd_hook(void)
+{
+       if (ThisICQ->icq_Sok >= 0) {
+               if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
+                       icq_KeepAlive();
+                       ThisICQ->icq_LastKeepAlive = time(NULL);
+               }
+               icq_Main();
+       }
+}
+
+
+/*
+ * There are a couple of things we absolutely need to make sure of when we
+ * kill off the session.  One of them is that the sockets are all closed --
+ * otherwise we could have a nasty file descriptor leak.
+ */
+void CtdlICQ_session_logout_hook(void)
+{
+       CtdlICQ_Logout_If_Connected();
+       if (ThisICQ->icq_MyConfigFile != NULL)
+               fclose(ThisICQ->icq_MyConfigFile);
+}
+
+
+/*
+ */
+void CtdlICQ_session_login_hook(void)
+{
+       char buf[256];
+       if (ThisICQ->icq_MyConfigFile != NULL)
+               fclose(ThisICQ->icq_MyConfigFile);
+
+       /* Open this user's ICQ config file; create it if it's not there */
+       sprintf(buf, "icq/%ld", CC->usersupp.usernum);
+       ThisICQ->icq_MyConfigFile = fopen(buf, "r+");
+       if (ThisICQ->icq_MyConfigFile == NULL)
+               ThisICQ->icq_MyConfigFile = fopen(buf, "w+");
+       if (ThisICQ->icq_MyConfigFile == NULL)
+               lprintf(2, "Cannot create %s: %s\n", buf, strerror(errno));
+
+       /* Login to the ICQ server if we already have info on file. */
+       CtdlICQ_Login_If_Possible();
+}
+
+
+void cmd_icqa(char *argbuf) {
+       char cuin[256];
+
+       extract(cuin, argbuf, 0);
+       if (atol(cuin) > 0L) {
+               CtdlICQ_Config_Write(cuin, "");
+               icq_SendInfoReq(atol(cuin));
+               cprintf("%d %s added to your ICQ contact list.\n", OK, cuin);
+       } else {
+               cprintf("%d You must specify a numeric ICQ uin.\n", ERROR);
+       }
+
+       CtdlICQ_Refresh_Contact_List();
+}
+
+
+
+
+void cmd_icql(char *argbuf)
+{
+       char uin[256];
+       char password[256];
+
+       extract(uin, argbuf, 0);
+       extract(password, argbuf, 1);
+
+       CtdlICQ_Config_Write("uin", uin);
+       CtdlICQ_Config_Write("pass", password);
+
+       cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
+       CtdlICQ_Login_If_Possible();
+}
+
+
+/*
+ * When deleting a user from the server, be sure to delete
+ * his/her/its ICQ config file as well.
+ */
+void CtdlICQ_DeleteUserConfigFile(char *uname, long unum) {
+       char buf[256];
+
+       sprintf(buf, "icq/%ld", unum);
+       unlink(buf);
+}
+
+
+
+/*
+ * Here's what we have to do when an ICQ message arrives!
+ */
+void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
+                               BYTE day, BYTE month, WORD year,
+                               const char *msg) {
+       
+       char from[256];
+       char nick[256];
+
+       sprintf(from, "%ld", uin);
+       CtdlICQ_Config_Read(nick, from);
+       if (strlen(nick) == 0) {
+               icq_SendInfoReq(atol(from));
+       }
+
+       sprintf(from, "%ld@icq (%s)", uin, nick);
+       if (CtdlSendExpressMessageFunc) {
+               CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
+
+
+
+       } else {
+               lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
+       }
+}
+
+
+
+CtdlICQ_InfoReply(unsigned long uin, const char *nick,
+               const char *first, const char *last,
+               const char *email, char auth) {
+
+       char str_uin[256];
+
+       sprintf(str_uin, "%ld", uin);
+       CtdlICQ_Config_Write(str_uin, nick);
+}
+
+
+
+struct DLModule_Info *Dynamic_Module_Init(void)
+{
+       /* Create a directory to store ICQ stuff in.
+        * It's ok if the directory is already there.
+        */
+       if (mkdir("icq", 0700) != 0) if (errno != EEXIST) {
+               lprintf(2, "Can't create icq subdirectory: %s\n",
+                       strerror(errno));
+       }
+
+       /* Make sure we've got a valid ThisICQ for each session */
+       SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
+
+       /* Tell the Citadel server about our wonderful ICQ hooks */
+       CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
+       CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
+       CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
+       CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
+       CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
+       CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add an ICQ contact");
+       CtdlRegisterUserHook(CtdlICQ_DeleteUserConfigFile, EVT_PURGEUSER);
+
+       /* Tell the code formerly known as icqlib about our callbacks */
+       icq_Log = CtdlICQlog;
+       icq_RecvMessage = CtdlICQ_Incoming_Message;
+       icq_InfoReply = CtdlICQ_InfoReply;
+
+       return &info;
+}
diff --git a/citadel/serv_icq.mk b/citadel/serv_icq.mk
new file mode 100644 (file)
index 0000000..5eb3f90
--- /dev/null
@@ -0,0 +1,6 @@
+modules/serv_icq.so: serv_icq.mo
+       gcc -shared -o modules/serv_icq.so serv_icq.mo
+
+serv_icq.mo: serv_icq.c
+       gcc -g -O2 -I. -DHAVE_CONFIG_H \
+       -D_REENTRANT -fPIC -DPIC -c serv_icq.c -o serv_icq.mo