]> code.citadel.org Git - citadel.git/blob - citadel/serv_icq.c
* EXTREME coolness. The server side of the ICQ metaclient is now working.
[citadel.git] / citadel / serv_icq.c
1 /* 
2  * serv_icq.c
3  *
4  * This is a modified version of Denis' ICQLIB, a very cleanly
5  * written implementation of the Mirabilis ICQ client protocol.  The library
6  * has been modified rather than merely utilized because we need it to be
7  * threadsafe in order to tie it into the Citadel server.
8  *
9  * Incomplete list of changes I made:
10  * * All globals placed into struct ctdl_icq_handle so we can do it per-thread
11  * * References to globals changed to ThisICQ->globalname
12  * * malloc->mallok, free->phree, strdup->strdoop, for memory leak checking
13  * * Added a bunch of #include's needed by Citadel
14  * * Most of the Citadel-specific code is appended to the end
15  *
16  * $Id$
17  */
18
19 #include "serv_icq.h"
20
21 #include <stdio.h>
22 #include <netdb.h>
23
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27
28 #include <time.h>
29 #include <string.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <limits.h>
34 #include "sysdep.h"
35 #ifdef HAVE_PTHREAD_H
36 #include <pthread.h>
37 #endif
38 #include "citadel.h"
39 #include "server.h"
40 #include "dynloader.h"
41 #include "tools.h"
42 #include "citserver.h"
43 #include "msgbase.h"
44
45 /*
46  * Contact list in memory
47  */
48 struct CtdlICQ_CL {
49         DWORD uin;
50         char name[32];
51         DWORD status;
52 };
53
54
55 struct ctdl_icq_handle {
56         int icq_Sok;
57         BOOL icq_Russian;
58         BYTE icq_ServMess[8192];
59         WORD icq_SeqNum;
60         DWORD icq_OurIp;
61         DWORD icq_OurPort;
62         DWORD icq_Uin;
63         icq_ContactItem *icq_ContFirst;
64         DWORD icq_Status;
65         char *icq_Password;
66         BYTE icq_LogLevel;
67         BYTE icq_UseProxy;
68         char *icq_ProxyHost;
69         WORD icq_ProxyPort;
70         int icq_ProxyAuth;
71         char *icq_ProxyName;
72         char *icq_ProxyPass;
73         int icq_ProxySok;
74         DWORD icq_ProxyDestHost;
75         WORD icq_ProxyDestPort;
76         WORD icq_ProxyOurPort;
77         time_t icq_LastKeepAlive;       /* ig */
78         char icq_config[256];           /* ig */
79         struct CtdlICQ_CL *icq_cl;      /* ig */
80         int icq_numcl;                  /* ig */
81 };
82
83 /* <ig> */
84
85 /* ICQROOM is the name of the room in which each user's ICQ configuration
86  * and contact lists will be stored.  (It's a personal room.)
87  */
88 #define ICQROOM         "My ICQ Config"
89
90 /* MIME types to use for storing ICQ stuff */
91 #define ICQMIME         "application/x-citadel-icq"     /* configuration */
92 #define ICQCLMIME       "application/x-citadel-icq-cl"  /* contact list */
93
94 /* Citadel server TSD symbol for use by serv_icq */
95 unsigned long SYM_CTDL_ICQ;
96 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
97
98 extern struct CitContext *ContextList;
99
100
101 /* </ig> */
102
103 void (*icq_Logged) (void);
104 void (*icq_Disconnected) (void);
105 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
106 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
107 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);
108 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);
109 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
110 void (*icq_SearchDone) (void);
111 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
112 void (*icq_UserOffline) (DWORD uin);
113 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
114 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
115 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);
116 void (*icq_Log) (time_t time, unsigned char level, const char *str);
117 void (*icq_SrvAck) (WORD seq);
118
119 BYTE kw[] =
120 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
121  144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
122  160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
123  176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
124  254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
125  239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
126  222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
127  207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
128
129 BYTE wk[] =
130 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
131  144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
132  160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
133  176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
134  225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
135  242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
136  193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
137  210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
138
139 static COUNTRY_CODE Country_Codes[] =
140 {
141         {"N/A", 0},
142         {"USA", 1},
143         {"Russia", 7},
144         {"Australia", 61},
145         {"Denmark", 45},
146         {"Sweden", 46},
147         {"Norway", 47},
148         {"Canada", 107},
149         {"Brazil", 55},
150         {"UK", 0x2c},
151         {"Finland", 358},
152         {"Iceland", 354},
153         {"Algeria", 213},
154         {"American Samoa", 684},
155         {"Argentina", 54},
156         {"Aruba", 297},
157         {"Austria", 43},
158         {"Bahrain", 973},
159         {"Bangladesh", 880},
160         {"Belgium", 32},
161         {"Belize", 501},
162         {"Bolivia", 591},
163         {"Cameroon", 237},
164         {"Chile", 56},
165         {"China", 86},
166         {"Columbia", 57},
167         {"Costa Rice", 506},
168         {"Cyprus", 357},
169         {"Czech Republic", 42},
170         {"Ecuador", 593},
171         {"Egypt", 20},
172         {"El Salvador", 503},
173         {"Ethiopia", 251},
174         {"Fiji", 679},
175         {"France", 33},
176         {"French Antilles", 596},
177         {"French Polynesia", 689},
178         {"Gabon", 241},
179         {"German", 49},
180         {"Ghana", 233},
181         {"Greece", 30},
182         {"Guadeloupe", 590},
183         {"Guam", 671},
184         {"Guantanomo Bay", 53},
185         {"Guatemala", 502},
186         {"Guyana", 592},
187         {"Haiti", 509},
188         {"Honduras", 504},
189         {"Hong Kong", 852},
190         {"Hungary", 36},
191         {"India", 91},
192         {"Indonesia", 62},
193         {"Iran", 98},
194         {"Iraq", 964},
195         {"Ireland", 353},
196         {"Israel", 972},
197         {"Italy", 39},
198         {"Ivory Coast", 225},
199         {"Japan", 81},
200         {"Jordan", 962},
201         {"Kenya", 254},
202         {"South Korea", 82},
203         {"Kuwait", 965},
204         {"Liberia", 231},
205         {"Libya", 218},
206         {"Liechtenstein", 41},
207         {"Luxembourg", 352},
208         {"Malawi", 265},
209         {"Malaysia", 60},
210         {"Mali", 223},
211         {"Malta", 356},
212         {"Mexico", 52},
213         {"Monaco", 33},
214         {"Morocco", 212},
215         {"Namibia", 264},
216         {"Nepal", 977},
217         {"Netherlands", 31},
218         {"Netherlands Antilles", 599},
219         {"New Caledonia", 687},
220         {"New Zealand", 64},
221         {"Nicaragua", 505},
222         {"Nigeria", 234},
223         {"Oman", 968},
224         {"Pakistan", 92},
225         {"Panama", 507},
226         {"Papua New Guinea", 675},
227         {"Paraguay", 595},
228         {"Peru", 51},
229         {"Philippines", 63},
230         {"Poland", 48},
231         {"Portugal", 351},
232         {"Qatar", 974},
233         {"Romania", 40},
234         {"Saipan", 670},
235         {"San Marino", 39},
236         {"Saudia Arabia", 966},
237         {"Saipan", 670},
238         {"Senegal", 221},
239         {"Singapore", 65},
240         {"Slovakia", 42},
241         {"South Africa", 27},
242         {"Spain", 34},
243         {"Sri Lanka", 94},
244         {"Suriname", 597},
245         {"Switzerland", 41},
246         {"Taiwan", 886},
247         {"Tanzania", 255},
248         {"Thailand", 66},
249         {"Tunisia", 216},
250         {"Turkey", 90},
251         {"Ukraine", 380},
252         {"United Arab Emirates", 971},
253         {"Uruguay", 598},
254         {"Vatican City", 39},
255         {"Venezuela", 58},
256         {"Vietnam", 84},
257         {"Yemen", 967},
258         {"Yugoslavia", 38},
259         {"Zaire", 243},
260         {"Zimbabwe", 263},
261         {"Not entered", 0xffff}};
262
263 void icq_init_handle(struct ctdl_icq_handle *i)
264 {
265         memset(i, 0, sizeof(struct ctdl_icq_handle));
266         i->icq_Russian = TRUE;
267         i->icq_SeqNum = 1;
268         i->icq_OurIp = 0x0100007f;
269         i->icq_Status = STATUS_OFFLINE;
270         i->icq_Sok = (-1);
271         i->icq_LogLevel = 9;
272 }
273
274 int icq_SockWrite(int sok, const void *buf, size_t count)
275 {
276         char tmpbuf[1024];
277         if (!(ThisICQ->icq_UseProxy))
278                 return write(sok, buf, count);
279         else {
280                 tmpbuf[0] = 0;  /* reserved */
281                 tmpbuf[1] = 0;  /* reserved */
282                 tmpbuf[2] = 0;  /* standalone packet */
283                 tmpbuf[3] = 1;  /* address type IP v4 */
284                 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
285                 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
286                 memcpy(&tmpbuf[10], buf, count);
287                 return write(sok, tmpbuf, count + 10) - 10;
288         }
289 }
290
291 int icq_SockRead(int sok, void *buf, size_t count)
292 {
293         int res;
294         char tmpbuf[1024];
295         if (!(ThisICQ->icq_UseProxy))
296                 return read(sok, buf, count);
297         else {
298                 res = read(sok, tmpbuf, count + 10);
299                 memcpy(buf, &tmpbuf[10], res - 10);
300                 return res - 10;
301         }
302 }
303
304 /****************************************
305 This must be called every 2 min.
306 so the server knows we're still alive.
307 JAVA client sends two different commands
308 so we do also :)
309 *****************************************/
310 void icq_KeepAlive()
311 {
312         net_icq_pak pak;
313
314         Word_2_Chars(pak.head.ver, ICQ_VER);
315         Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
316         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
317         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
318         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
319
320         Word_2_Chars(pak.head.ver, ICQ_VER);
321         Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
322         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
323         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
324         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
325
326         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
327                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
328 }
329
330 /**********************************
331 This must be called to remove
332 messages from the server
333 ***********************************/
334 void icq_SendGotMessages()
335 {
336         net_icq_pak pak;
337
338         Word_2_Chars(pak.head.ver, ICQ_VER);
339         Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
340         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
341         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
342
343         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
344 }
345
346 /*************************************
347 this sends over the contact list
348 *************************************/
349 void icq_SendContactList()
350 {
351         net_icq_pak pak;
352         char num_used;
353         int size;
354         char *tmp;
355         icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
356
357         Word_2_Chars(pak.head.ver, ICQ_VER);
358         Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
359         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
360         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
361
362         tmp = pak.data;
363         tmp++;
364         num_used = 0;
365         while (ptr) {
366                 DW_2_Chars(tmp, ptr->uin);
367                 tmp += 4;
368                 num_used++;
369                 ptr = ptr->next;
370         }
371         pak.data[0] = num_used;
372         size = ((int) tmp - (int) pak.data);
373         size += sizeof(pak.head);
374         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
375 }
376
377 void icq_SendNewUser(unsigned long uin)
378 {
379         net_icq_pak pak;
380         int size;
381
382         Word_2_Chars(pak.head.ver, ICQ_VER);
383         Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
384         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
385         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
386         DW_2_Chars(pak.data, uin);
387         size = sizeof(pak.head) + 4;
388         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
389 }
390
391 /*************************************
392 this sends over the visible list
393 that allows certain users to see you
394 if you're invisible.
395 *************************************/
396 void icq_SendVisibleList()
397 {
398         net_icq_pak pak;
399         char num_used;
400         int size;
401         char *tmp;
402         icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
403
404         Word_2_Chars(pak.head.ver, ICQ_VER);
405         Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
406         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
407         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
408
409         tmp = pak.data;
410         tmp++;
411         num_used = 0;
412         while (ptr) {
413                 if (ptr->vis_list) {
414                         DW_2_Chars(tmp, ptr->uin);
415                         tmp += 4;
416                         num_used++;
417                 }
418                 ptr = ptr->next;
419         }
420         if (num_used != 0) {
421                 pak.data[0] = num_used;
422                 size = ((int) tmp - (int) pak.data);
423                 size += sizeof(pak.head);
424                 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
425         }
426 }
427
428 /**************************************
429 This sends the second login command
430 this is necessary to finish logging in.
431 ***************************************/
432 void icq_SendLogin1()
433 {
434         net_icq_pak pak;
435
436         Word_2_Chars(pak.head.ver, ICQ_VER);
437         Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
438         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
439         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
440
441         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
442 }
443
444 /************************************************
445 This is called when a user goes offline
446 *************************************************/
447 void icq_HandleUserOffline(srv_net_icq_pak pak)
448 {
449         DWORD remote_uin;
450         char buf[256];
451
452         remote_uin = Chars_2_DW(pak.data);
453         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
454                 sprintf(buf, "User %lu logged off\n", remote_uin);
455                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
456         }
457         if (icq_UserOffline)
458                 (*icq_UserOffline) (remote_uin);
459         icq_AckSrv(Chars_2_Word(pak.head.seq));
460 }
461
462 void icq_HandleUserOnline(srv_net_icq_pak pak)
463 {
464         DWORD remote_uin, new_status, remote_ip, remote_real_ip;
465         DWORD remote_port;      /* Why Mirabilis used 4 bytes for port? */
466         icq_ContactItem *ptr;
467         char buf[256];
468
469         remote_uin = Chars_2_DW(pak.data);
470         new_status = Chars_2_DW(&pak.data[17]);
471         remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
472         remote_port = ntohl(Chars_2_DW(&pak.data[8]));
473         remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
474         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
475                 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
476                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
477         }
478         if (icq_UserOnline)
479                 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
480         icq_AckSrv(Chars_2_Word(pak.head.seq));
481 }
482
483 void icq_Status_Update(srv_net_icq_pak pak)
484 {
485         unsigned long remote_uin, new_status;
486         char buf[256];
487
488         remote_uin = Chars_2_DW(pak.data);
489         new_status = Chars_2_DW(&pak.data[4]);
490         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
491                 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
492                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
493         }
494         if (icq_UserStatusUpdate)
495                 (*icq_UserStatusUpdate) (remote_uin, new_status);
496         icq_AckSrv(Chars_2_Word(pak.head.seq));
497 }
498
499 /************************************
500 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
501 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
502 It does NOT wait for any kind of a response.
503 *************************************/
504 void icq_Login(DWORD status)
505 {
506         net_icq_pak pak;
507         int size, ret;
508         login_1 s1;
509         login_2 s2;
510
511         memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
512         Word_2_Chars(pak.head.ver, ICQ_VER);
513         Word_2_Chars(pak.head.cmd, CMD_LOGIN);
514         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
515         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
516
517         DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
518         Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
519
520         DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
521         DW_2_Chars(s2.status, status);
522         Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
523
524         DW_2_Chars(s2.X1, LOGIN_X1_DEF);
525         s2.X2[0] = LOGIN_X2_DEF;
526         DW_2_Chars(s2.X3, LOGIN_X3_DEF);
527         DW_2_Chars(s2.X4, LOGIN_X4_DEF);
528         DW_2_Chars(s2.X5, LOGIN_X5_DEF);
529
530         memcpy(pak.data, &s1, sizeof(s1));
531         size = 6;
532         memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
533         size += Chars_2_Word(s1.len);
534         memcpy(&pak.data[size], &s2, sizeof(s2));
535         size += sizeof(s2);
536         ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
537 }
538
539 /*******************************
540 This routine sends the aknowlegement cmd to the
541 server it appears that this must be done after
542 everything the server sends us
543 *******************************/
544 void icq_AckSrv(int seq)
545 {
546         int i;
547         net_icq_pak pak;
548
549         Word_2_Chars(pak.head.ver, ICQ_VER);
550         Word_2_Chars(pak.head.cmd, CMD_ACK);
551         Word_2_Chars(pak.head.seq, seq);
552         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
553         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
554                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
555         for (i = 0; i < 6; i++)
556                 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
557 }
558
559 void icq_HandleInfoReply(srv_net_icq_pak pak)
560 {
561         char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
562         int len;
563         DWORD uin;
564         WORD seq;
565         seq = Chars_2_Word(pak.data);
566         uin = Chars_2_DW(&pak.data[2]);
567         len = Chars_2_Word(&pak.data[6]);
568         ptr1 = &pak.data[8];
569         icq_RusConv("wk", ptr1);
570         tmp = &pak.data[8 + len];
571         len = Chars_2_Word(tmp);
572         ptr2 = tmp + 2;
573         icq_RusConv("wk", ptr2);
574         tmp += len + 2;
575         len = Chars_2_Word(tmp);
576         ptr3 = tmp + 2;
577         icq_RusConv("wk", ptr3);
578         tmp += len + 2;
579         len = Chars_2_Word(tmp);
580         ptr4 = tmp + 2;
581         icq_RusConv("wk", ptr4);
582         tmp += len + 2;
583         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
584                 sprintf(buf, "Info reply for %lu\n", uin);
585                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
586         }
587         if (icq_InfoReply)
588                 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
589         icq_AckSrv(Chars_2_Word(pak.head.seq));
590 }
591
592 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
593 {
594         unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
595         int len;
596         DWORD uin;
597         WORD cnt_code, age;
598         char cnt_stat, gender, buf[256];
599         uin = Chars_2_DW(&pak.data[2]);
600         len = Chars_2_Word(&pak.data[6]);
601         ptr1 = &pak.data[8];
602         icq_RusConv("wk", ptr1);
603         cnt_code = Chars_2_Word(&pak.data[8 + len]);
604         cnt_stat = pak.data[len + 10];
605         tmp = &pak.data[11 + len];
606         len = Chars_2_Word(tmp);
607         icq_RusConv("wk", tmp + 2);
608         ptr2 = tmp + 2;
609         age = Chars_2_Word(tmp + 2 + len);
610         gender = *(tmp + len + 4);
611         tmp += len + 5;
612         len = Chars_2_Word(tmp);
613         icq_RusConv("wk", tmp + 2);
614         ptr3 = tmp + 2;
615         tmp += len + 2;
616         len = Chars_2_Word(tmp);
617         icq_RusConv("wk", tmp + 2);
618         ptr4 = tmp + 2;
619         tmp += len + 2;
620         len = Chars_2_Word(tmp);
621         icq_RusConv("wk", tmp + 2);
622         ptr5 = tmp + 2;
623         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
624                 sprintf(buf, "Extended info reply for %lu\n", uin);
625                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
626         }
627         if (icq_ExtInfoReply)
628                 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
629         icq_AckSrv(Chars_2_Word(pak.head.seq));
630 }
631
632 void icq_HandleSearchReply(srv_net_icq_pak pak)
633 {
634         char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
635         int len;
636         char buf[512];
637         DWORD uin;
638         uin = Chars_2_DW(&pak.data[2]);
639         len = Chars_2_Word(&pak.data[6]);
640         ptr1 = &pak.data[8];
641         icq_RusConv("wk", ptr1);
642         tmp = &pak.data[8 + len];
643         len = Chars_2_Word(tmp);
644         ptr2 = tmp + 2;
645         icq_RusConv("wk", ptr2);
646         tmp += len + 2;
647         len = Chars_2_Word(tmp);
648         ptr3 = tmp + 2;
649         icq_RusConv("wk", ptr3);
650         tmp += len + 2;
651         len = Chars_2_Word(tmp);
652         ptr4 = tmp + 2;
653         icq_RusConv("wk", ptr4);
654         tmp += len + 2;
655         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
656                 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");
657                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
658         }
659         if (icq_UserFound)
660                 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
661         icq_AckSrv(Chars_2_Word(pak.head.seq));
662 }
663
664 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
665 {
666         char *tmp;
667         char *ptr1, *ptr2, *ptr3, *ptr4;
668         char buf[1024];
669
670         switch (type) {
671         case USER_ADDED_MESS:
672                 tmp = strchr(data, '\xFE');
673                 if (tmp == 0L) {
674                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
675                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
676                         return;
677                 }
678                 *tmp = 0;
679                 ptr1 = data;
680                 tmp++;
681                 data = tmp;
682                 tmp = strchr(tmp, '\xFE');
683                 if (tmp == 0L) {
684                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
685                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
686                         return;
687                 }
688                 *tmp = 0;
689                 icq_RusConv("wk", data);
690                 ptr2 = data;
691                 tmp++;
692                 data = tmp;
693                 tmp = strchr(tmp, '\xFE');
694                 if (tmp == 0L) {
695                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
696                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
697                         return;
698                 }
699                 *tmp = 0;
700                 icq_RusConv("wk", data);
701                 ptr3 = data;
702                 tmp++;
703                 data = tmp;
704                 tmp = strchr(tmp, '\xFE');
705                 *tmp = 0;
706                 icq_RusConv("wk", data);
707                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
708                         sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
709                                 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
710                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
711                 }
712                 if (icq_RecvAdded)
713                         (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
714                 break;
715         case AUTH_REQ_MESS:
716                 tmp = strchr(data, '\xFE');
717                 *tmp = 0;
718                 ptr1 = data;
719                 tmp++;
720                 data = tmp;
721                 tmp = strchr(tmp, '\xFE');
722                 if (tmp == 0L) {
723                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
724                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
725                         return;
726                 }
727                 *tmp = 0;
728                 icq_RusConv("wk", data);
729                 ptr2 = data;
730                 tmp++;
731                 data = tmp;
732                 tmp = strchr(tmp, '\xFE');
733                 if (tmp == 0L) {
734                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
735                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
736                         return;
737                 }
738                 *tmp = 0;
739                 icq_RusConv("wk", data);
740                 ptr3 = data;
741                 tmp++;
742                 data = tmp;
743                 tmp = strchr(tmp, '\xFE');
744                 if (tmp == 0L) {
745                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
746                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
747                         return;
748                 }
749                 *tmp = 0;
750                 icq_RusConv("wk", data);
751                 ptr4 = data;
752                 tmp++;
753                 data = tmp;
754                 tmp = strchr(tmp, '\xFE');
755                 if (tmp == 0L) {
756                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
757                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
758                         return;
759                 }
760                 *tmp = 0;
761                 tmp++;
762                 data = tmp;
763                 tmp = strchr(tmp, '\x00');
764                 if (tmp == 0L) {
765                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
766                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
767                         return;
768                 }
769                 *tmp = 0;
770                 icq_RusConv("wk", data);
771                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
772                         sprintf(buf, "%lu has requested your authorization to be added to "
773                                 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
774                                 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
775                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
776                 }
777                 if (icq_RecvAuthReq)
778                         (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
779                 break;
780         case URL_MESS:
781                 tmp = strchr(data, '\xFE');
782                 if (tmp == 0L) {
783                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
784                                 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
785                         return;
786                 }
787                 *tmp = 0;
788                 icq_RusConv("wk", data);
789                 ptr1 = data;
790                 tmp++;
791                 data = tmp;
792                 icq_RusConv("wk", data);
793                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
794                         sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
795                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
796                 }
797                 if (icq_RecvURL)
798                         (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
799                 break;
800         default:
801                 icq_RusConv("wk", data);
802                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
803                         sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
804                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
805                 }
806                 if (icq_RecvMessage)
807                         (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
808         }
809 }
810
811 /**********************************
812 Connects to hostname on port port
813 hostname can be DNS or nnn.nnn.nnn.nnn
814 write out messages to the FD aux
815 ***********************************/
816 int icq_Connect(const char *hostname, int port)
817 {
818         char buf[1024], un = 1;
819         char our_host[256], tmpbuf[256];
820         int conct, length, res;
821         struct sockaddr_in sin, prsin;  /* used to store inet addr stuff */
822         struct hostent *host_struct;    /* used in DNS llokup */
823
824         (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0);    /* create the unconnected socket */
825         if ((ThisICQ->icq_Sok) == -1) {
826                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
827                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
828                 return -1;
829         }
830         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
831                 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
832         sin.sin_addr.s_addr = INADDR_ANY;
833         sin.sin_family = AF_INET;       /* we're using the inet not appletalk */
834         sin.sin_port = 0;
835         if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
836                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
837                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
838                 return -1;
839         }
840         length = sizeof(sin);
841         getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
842         (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
843         if ((ThisICQ->icq_UseProxy)) {
844                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
845                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
846                 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
847                 if (prsin.sin_addr.s_addr == -1) {      /* name isn't n.n.n.n so must be DNS */
848                         host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
849                         if (host_struct == 0L) {
850                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
851                                         sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
852                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
853                                 }
854                                 return -1;
855                         }
856                         prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
857                 }
858                 prsin.sin_family = AF_INET;     /* we're using the inet not appletalk */
859                 prsin.sin_port = htons((ThisICQ->icq_ProxyPort));       /* port */
860                 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0);      /* create the unconnected socket */
861                 if ((ThisICQ->icq_ProxySok) == -1) {
862                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
863                                 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
864                         return -1;
865                 }
866                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
867                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
868                 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
869                 if (conct == -1) {      /* did we connect ? */
870                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
871                                 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
872                         return -1;
873                 }
874                 buf[0] = 5;     /* protocol version */
875                 buf[1] = 1;     /* number of methods */
876                 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
877                         buf[2] = 0;     /* no authorization required */
878                 else
879                         buf[2] = 2;     /* method username/password */
880                 write((ThisICQ->icq_ProxySok), buf, 3);
881                 res = read((ThisICQ->icq_ProxySok), buf, 2);
882                 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
883                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
884                                 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
885                         close((ThisICQ->icq_ProxySok));
886                         return -1;
887                 }
888                 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
889                         buf[0] = 1;     /* version of subnegotiation */
890                         buf[1] = strlen((ThisICQ->icq_ProxyName));
891                         memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
892                         buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
893                         memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
894                         write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
895                         res = read((ThisICQ->icq_ProxySok), buf, 2);
896                         if (res != 2 || buf[0] != 1 || buf[1] != 0) {
897                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
898                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
899                                 close((ThisICQ->icq_ProxySok));
900                                 return -1;
901                         }
902                 }
903                 buf[0] = 5;     /* protocol version */
904                 buf[1] = 3;     /* command UDP associate */
905                 buf[2] = 0;     /* reserved */
906                 buf[3] = 1;     /* address type IP v4 */
907                 buf[4] = (char) 0;
908                 buf[5] = (char) 0;
909                 buf[6] = (char) 0;
910                 buf[7] = (char) 0;
911                 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
912                 write((ThisICQ->icq_ProxySok), buf, 10);
913                 res = read((ThisICQ->icq_ProxySok), buf, 10);
914                 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
915                         switch (buf[1]) {
916                         case 1:
917                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
918                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
919                                 break;
920                         case 2:
921                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
922                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
923                                 break;
924                         case 3:
925                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
926                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
927                                 break;
928                         case 4:
929                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
930                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
931                                 break;
932                         case 5:
933                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
934                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
935                                 break;
936                         case 6:
937                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
938                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
939                                 break;
940                         case 7:
941                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
942                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
943                                 break;
944                         case 8:
945                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
946                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
947                                 break;
948                         default:
949                                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
950                                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
951                                 break;
952                         }
953                         close((ThisICQ->icq_ProxySok));
954                         return -1;
955                 }
956         }
957         sin.sin_addr.s_addr = inet_addr(hostname);      /* checks for n.n.n.n notation */
958         if (sin.sin_addr.s_addr == -1) {        /* name isn't n.n.n.n so must be DNS */
959                 host_struct = gethostbyname(hostname);
960                 if (host_struct == 0L) {
961                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
962                                 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
963                                 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
964                         }
965                         if ((ThisICQ->icq_UseProxy))
966                                 close((ThisICQ->icq_ProxySok));
967                         return -1;
968                 }
969                 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
970         }
971         if ((ThisICQ->icq_UseProxy)) {
972                 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
973                 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
974         }
975         sin.sin_family = AF_INET;       /* we're using the inet not appletalk */
976         sin.sin_port = htons(port);     /* port */
977         if ((ThisICQ->icq_UseProxy)) {
978                 (ThisICQ->icq_ProxyDestPort) = htons(port);
979                 memcpy(&sin.sin_port, &buf[8], 2);
980         }
981         conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
982         if (conct == -1) {      /* did we connect ? */
983                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
984                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
985                 if ((ThisICQ->icq_UseProxy))
986                         close((ThisICQ->icq_ProxySok));
987                 return -1;
988         }
989         length = sizeof(sin);
990         getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
991         (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
992         (ThisICQ->icq_OurPort) = sin.sin_port;
993         return (ThisICQ->icq_Sok);
994 }
995
996 void icq_HandleProxyResponse()
997 {
998         int s;
999         char buf[256];
1000         s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
1001         if (s <= 0) {
1002                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1003                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
1004                 if (icq_Disconnected)
1005                         (*icq_Disconnected) ();
1006                 SOCKCLOSE((ThisICQ->icq_Sok));
1007                 SOCKCLOSE((ThisICQ->icq_ProxySok));
1008         }
1009 }
1010
1011 /******************************************
1012 Handles packets that the server sends to us.
1013 *******************************************/
1014 void icq_HandleServerResponse()
1015 {
1016         srv_net_icq_pak pak;
1017         SIMPLE_MESSAGE *s_mesg;
1018         RECV_MESSAGE *r_mesg;
1019         time_t cur_time;
1020         struct tm *tm_str;
1021         int s, len;
1022         char buf[1024];
1023         cback acback;
1024
1025         s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1026         if (s <= 0) {
1027                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1028                         (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1029                 if (icq_Disconnected)
1030                         (*icq_Disconnected) ();
1031                 SOCKCLOSE((ThisICQ->icq_Sok));
1032         }
1033         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)) {
1034                 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) {    /* ACKs don't matter */
1035                         if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1036                                 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1037                                 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1038                         }
1039                         icq_AckSrv(Chars_2_Word(pak.head.seq));         /* LAGGGGG!! */
1040                         return;
1041                 }
1042         }
1043         if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1044                 icq_SetServMess(Chars_2_Word(pak.head.seq));
1045         switch (Chars_2_Word(pak.head.cmd)) {
1046         case SRV_ACK:
1047                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1048                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1049                 if (icq_SrvAck)
1050                         (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1051                 break;
1052         case SRV_NEW_UIN:
1053                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1054                         sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1055                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1056                 }
1057                 break;
1058         case SRV_LOGIN_REPLY:
1059                 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1060                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1061                         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]);
1062                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1063                 }
1064                 icq_AckSrv(Chars_2_Word(pak.head.seq));
1065                 icq_SendLogin1();
1066                 icq_SendContactList();
1067                 icq_SendVisibleList();
1068                 if (icq_Logged)
1069                         (*icq_Logged) ();
1070                 break;
1071         case SRV_RECV_MESSAGE:
1072                 r_mesg = (RECV_MESSAGE *) pak.data;
1073                 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));
1074                 icq_AckSrv(Chars_2_Word(pak.head.seq));
1075                 break;
1076         case SRV_X1:            /* unknown message  sent after login */
1077                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1078                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1079                 icq_AckSrv(Chars_2_Word(pak.head.seq));
1080                 break;
1081         case SRV_X2:            /* unknown message  sent after login */
1082                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1083                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1084                 icq_AckSrv(Chars_2_Word(pak.head.seq));
1085                 icq_SendGotMessages();
1086                 break;
1087         case SRV_INFO_REPLY:
1088                 icq_HandleInfoReply(pak);
1089                 break;
1090         case SRV_EXT_INFO_REPLY:
1091                 icq_HandleExtInfoReply(pak);
1092                 break;
1093         case SRV_USER_ONLINE:
1094                 icq_HandleUserOnline(pak);
1095                 break;
1096         case SRV_USER_OFFLINE:
1097                 icq_HandleUserOffline(pak);
1098                 break;
1099         case SRV_TRY_AGAIN:
1100                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1101                         (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1102                 icq_Login((ThisICQ->icq_Status));
1103                 break;
1104         case SRV_STATUS_UPDATE:
1105                 icq_Status_Update(pak);
1106                 break;
1107         case SRV_GO_AWAY:
1108                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1109                         (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1110                 if (icq_Disconnected)
1111                         (*icq_Disconnected) ();
1112                 break;
1113         case SRV_END_OF_SEARCH:
1114                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1115                         (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1116                 if (icq_SearchDone)
1117                         (*icq_SearchDone) ();
1118                 icq_AckSrv(Chars_2_Word(pak.head.seq));
1119                 break;
1120         case SRV_USER_FOUND:
1121                 icq_HandleSearchReply(pak);
1122                 break;
1123         case SRV_SYS_DELIVERED_MESS:
1124                 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1125                 cur_time = time(0L);
1126                 tm_str = localtime(&cur_time);
1127                 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);
1128                 icq_AckSrv(Chars_2_Word(pak.head.seq));
1129                 break;
1130         default:                /* commands we dont handle yet */
1131                 len = s - (sizeof(pak.head));
1132                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1133                         sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1134                                 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1135                         (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1136                 }
1137                 icq_AckSrv(Chars_2_Word(pak.head.seq));         /* fake like we know what we're doing */
1138                 break;
1139         }
1140 }
1141
1142 void icq_Init(DWORD uin, const char *password)
1143 {
1144         memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1145         (ThisICQ->icq_Uin) = uin;
1146         if ((ThisICQ->icq_Password))
1147                 phree((ThisICQ->icq_Password));
1148         (ThisICQ->icq_Password) = strdoop(password);
1149 }
1150
1151 void icq_Done(void)
1152 {
1153         if ((ThisICQ->icq_Password))
1154                 phree((ThisICQ->icq_Password));
1155 }
1156
1157 /******************************
1158 Main function connects gets (ThisICQ->icq_Uin)
1159 and (ThisICQ->icq_Password) and logins in and sits
1160 in a loop waiting for server responses.
1161 *******************************/
1162 void icq_Main()
1163 {
1164         struct timeval tv;
1165         fd_set readfds;
1166         int did_something;
1167
1168         do {
1169                 did_something = 0;
1170                 tv.tv_sec = 0;
1171                 tv.tv_usec = 0;
1172                 FD_ZERO(&readfds);
1173                 FD_SET((ThisICQ->icq_Sok), &readfds);
1174                 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1175                 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1176                         icq_HandleServerResponse();
1177                         did_something = 1;
1178                         /* sleep(1); */
1179                 }
1180         } while (did_something);
1181 }
1182
1183 /********************************************************
1184 Russian language ICQ fix.
1185 Usual Windows ICQ users do use Windows 1251 encoding but
1186 unix users do use koi8 encoding, so we need to convert it.
1187 This function will convert string from windows 1251 to koi8
1188 or from koi8 to windows 1251.
1189 Andrew Frolov dron@ilm.net
1190 *********************************************************/
1191 void icq_RusConv(const char to[4], char *t_in)
1192 {
1193         BYTE *table;
1194         int i;
1195
1196 /* 6-17-1998 by Linux_Dude
1197  * Moved initialization of table out front of 'if' block to prevent compiler
1198  * warning. Improved error message, and now return without performing string
1199  * conversion to prevent addressing memory out of range (table pointer would
1200  * previously have remained uninitialized (= bad)).
1201  */
1202
1203         table = wk;
1204         if (strcmp(to, "kw") == 0)
1205                 table = kw;
1206         else if (strcmp(to, "wk") != 0) {
1207                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1208                         (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1209                 return;
1210         }
1211 /* End Linux_Dude's changes ;) */
1212
1213         if ((ThisICQ->icq_Russian)) {
1214                 for (i = 0; t_in[i] != 0; i++) {
1215                         t_in[i] &= 0377;
1216                         if (t_in[i] & 0200)
1217                                 t_in[i] = table[t_in[i] & 0177];
1218                 }
1219         }
1220 }
1221
1222 /**************************************************
1223 Sends a message thru the server to (ThisICQ->icq_Uin).  Text is the
1224 message to send.
1225 ***************************************************/
1226 WORD icq_SendMessage(DWORD uin, const char *text)
1227 {
1228         SIMPLE_MESSAGE msg;
1229         net_icq_pak pak;
1230         int size, len, i;
1231         char buf[512];          /* message may be only 450 bytes long */
1232
1233         strncpy(buf, text, 512);
1234         icq_RusConv("kw", buf);
1235         len = strlen(buf);
1236         Word_2_Chars(pak.head.ver, ICQ_VER);
1237         Word_2_Chars(pak.head.cmd, CMD_SENDM);
1238         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1239         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1240         DW_2_Chars(msg.uin, uin);
1241         DW_2_Chars(msg.type, 0x0001);   /* A type 1 msg */
1242         Word_2_Chars(msg.len, len + 1);         /* length + the NULL */
1243         memcpy(&pak.data, &msg, sizeof(msg));
1244         memcpy(&pak.data[8], buf, len + 1);
1245         size = sizeof(msg) + len + 1;
1246         for (i = 0; i < 6; i++)
1247                 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1248         return (ThisICQ->icq_SeqNum) - 1;
1249 }
1250
1251 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1252 {
1253         SIMPLE_MESSAGE msg;
1254         net_icq_pak pak;
1255         int size, len1, len2;
1256         char buf1[512], buf2[512];
1257
1258         strncpy(buf1, descr, 512);
1259         strncpy(buf2, url, 512);
1260 /* Do we need to convert URL? */
1261         icq_RusConv("kw", buf2);
1262         len1 = strlen(buf1);
1263         len2 = strlen(buf2);
1264         Word_2_Chars(pak.head.ver, ICQ_VER);
1265         Word_2_Chars(pak.head.cmd, CMD_SENDM);
1266         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1267         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1268         DW_2_Chars(msg.uin, uin);
1269         DW_2_Chars(msg.type, 0x0004);   /* A type 4 msg */
1270         Word_2_Chars(msg.len, len1 + len2 + 2);         /* length + the NULL + 0xFE delimiter */
1271         memcpy(&pak.data, &msg, sizeof(msg));
1272         memcpy(&pak.data[8], buf1, len1);
1273         pak.data[8 + len1] = 0xFE;
1274         memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1275         size = sizeof(msg) + len1 + len2 + 2;
1276         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1277         return (ThisICQ->icq_SeqNum) - 1;
1278 }
1279
1280 /**************************************************
1281 Sends a authorization to the server so the Mirabilis
1282 client can add the user.
1283 ***************************************************/
1284 void icq_SendAuthMsg(DWORD uin)
1285 {
1286         SIMPLE_MESSAGE msg;
1287         net_icq_pak pak;
1288         int size;
1289
1290         Word_2_Chars(pak.head.ver, ICQ_VER);
1291         Word_2_Chars(pak.head.cmd, CMD_SENDM);
1292         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1293         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1294         DW_2_Chars(msg.uin, uin);
1295         DW_2_Chars(msg.type, AUTH_MESSAGE);     /* A type authorization msg */
1296         Word_2_Chars(msg.len, 2);
1297         memcpy(&pak.data, &msg, sizeof(msg));
1298         pak.data[sizeof(msg)] = 0x03;
1299         pak.data[sizeof(msg) + 1] = 0x00;
1300         size = sizeof(msg) + 2;
1301         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1302 }
1303
1304 /**************************************************
1305 Changes the users status on the server
1306 ***************************************************/
1307 void icq_ChangeStatus(DWORD status)
1308 {
1309         net_icq_pak pak;
1310         int size;
1311
1312         Word_2_Chars(pak.head.ver, ICQ_VER);
1313         Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1314         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1315         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1316         DW_2_Chars(pak.data, status);
1317         (ThisICQ->icq_Status) = status;
1318         size = 4;
1319         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1320 }
1321
1322 /**********************
1323 Logs off ICQ
1324 ***********************/
1325 void icq_Logout()
1326 {
1327         net_icq_pak pak;
1328         int size, len;
1329
1330         Word_2_Chars(pak.head.ver, ICQ_VER);
1331         Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1332         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1333         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1334         len = strlen("B_USER_DISCONNECTED") + 1;
1335         *(short *) pak.data = len;
1336         size = len + 4;
1337         memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1338         pak.data[2 + len] = 05;
1339         pak.data[3 + len] = 00;
1340         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1341 }
1342
1343 void icq_Disconnect()
1344 {
1345         SOCKCLOSE((ThisICQ->icq_Sok));
1346         SOCKCLOSE((ThisICQ->icq_Sok));
1347         if ((ThisICQ->icq_UseProxy))
1348                 SOCKCLOSE((ThisICQ->icq_ProxySok));
1349 }
1350
1351 /********************************************************
1352 Sends a request to the server for info on a specific user
1353 *********************************************************/
1354 WORD icq_SendInfoReq(DWORD uin)
1355 {
1356         net_icq_pak pak;
1357         int size;
1358
1359         Word_2_Chars(pak.head.ver, ICQ_VER);
1360         Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1361         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1362         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1363         Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1364         DW_2_Chars(&pak.data[2], uin);
1365         size = 6;
1366         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1367         return (ThisICQ->icq_SeqNum) - 1;
1368 }
1369
1370 /********************************************************
1371 Sends a request to the server for info on a specific user
1372 *********************************************************/
1373 WORD icq_SendExtInfoReq(DWORD uin)
1374 {
1375         net_icq_pak pak;
1376         int size;
1377
1378         Word_2_Chars(pak.head.ver, ICQ_VER);
1379         Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1380         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1381         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1382         Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1383         DW_2_Chars(&pak.data[2], uin);
1384         size = 6;
1385         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1386         return (ThisICQ->icq_SeqNum) - 1;
1387 }
1388
1389 /**************************************************************
1390 Initializes a server search for the information specified
1391 ***************************************************************/
1392 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1393 {
1394         net_icq_pak pak;
1395         int size;
1396
1397         Word_2_Chars(pak.head.ver, ICQ_VER);
1398         Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1399         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1400         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1401         Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1402         size = 2;
1403         Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1404         size += 2;
1405         strcpy(pak.data + size, nick);
1406         size += strlen(nick) + 1;
1407         Word_2_Chars(&pak.data[size], strlen(first) + 1);
1408         size += 2;
1409         strcpy(pak.data + size, first);
1410         size += strlen(first) + 1;
1411         Word_2_Chars(&pak.data[size], strlen(last) + 1);
1412         size += 2;
1413         strcpy(pak.data + size, last);
1414         size += strlen(last) + 1;
1415         Word_2_Chars(&pak.data[size], strlen(email) + 1);
1416         size += 2;
1417         strcpy(pak.data + size, email);
1418         size += strlen(email) + 1;
1419         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1420 }
1421
1422 /**************************************************************
1423 Initializes a server search for the information specified
1424 ***************************************************************/
1425 void icq_SendSearchUINReq(DWORD uin)
1426 {
1427         net_icq_pak pak;
1428         int size;
1429
1430         Word_2_Chars(pak.head.ver, ICQ_VER);
1431         Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1432         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1433         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1434         Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1435         size = 2;
1436         DW_2_Chars(&pak.data[size], uin);
1437         size += 4;
1438         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1439 }
1440
1441 /**************************************************
1442 Registers a new uin in the ICQ network
1443 ***************************************************/
1444 void icq_RegNewUser(const char *pass)
1445 {
1446         srv_net_icq_pak pak;
1447         BYTE len_buf[2];
1448         int size, len;
1449
1450         len = strlen(pass);
1451         Word_2_Chars(pak.head.ver, ICQ_VER);
1452         Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1453         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1454         Word_2_Chars(len_buf, len);
1455         memcpy(&pak.data, "\x02\x00", 2);
1456         memcpy(&pak.data[2], &len_buf, 2);
1457         memcpy(&pak.data[4], pass, len + 1);
1458         DW_2_Chars(&pak.data[4 + len], 0x0072);
1459         DW_2_Chars(&pak.data[8 + len], 0x0000);
1460         size = len + 12;
1461         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1462 }
1463
1464 void icq_UpdateUserInfo(USER_INFO * user)
1465 {
1466         net_icq_pak pak;
1467         int size;
1468
1469         Word_2_Chars(pak.head.ver, ICQ_VER);
1470         Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1471         Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1472         DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1473         Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1474         size = 2;
1475         Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1476         size += 2;
1477         strcpy(pak.data + size, user->nick);
1478         size += strlen(user->nick) + 1;
1479         Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1480         size += 2;
1481         strcpy(pak.data + size, user->first);
1482         size += strlen(user->first) + 1;
1483         Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1484         size += 2;
1485         strcpy(pak.data + size, user->last);
1486         size += strlen(user->last) + 1;
1487         Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1488         size += 2;
1489         strcpy(pak.data + size, user->email);
1490         size += strlen(user->email) + 1;
1491         pak.data[size] = user->auth;
1492         size++;
1493         icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1494 }
1495
1496 const char *icq_GetCountryName(int code)
1497 {
1498         int i;
1499
1500         for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1501                 if (Country_Codes[i].code == code) {
1502                         return Country_Codes[i].name;
1503                 }
1504         }
1505         if (Country_Codes[i].code == code) {
1506                 return Country_Codes[i].name;
1507         }
1508         return "N/A";
1509 }
1510
1511 /********************************************
1512 returns a string describing the status or
1513 a "Error" if no such string exists
1514 *********************************************/
1515 const char *icq_ConvertStatus2Str(int status)
1516 {
1517         if (STATUS_OFFLINE == status) {         /* this because -1 & 0x01FF is not -1 */
1518                 return "Offline";
1519         }
1520 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1521         switch (status & 0x01FF) {
1522         case STATUS_ONLINE:
1523                 return "Online";
1524                 break;
1525         case STATUS_DND:
1526                 return "Do not disturb";
1527                 break;
1528         case STATUS_AWAY:
1529                 return "Away";
1530                 break;
1531         case STATUS_OCCUPIED:
1532                 return "Occupied";
1533                 break;
1534         case STATUS_NA:
1535                 return "Not available";
1536                 break;
1537         case STATUS_INVISIBLE:
1538                 return "Invisible";
1539                 break;
1540         case STATUS_INVISIBLE_2:
1541                 return "Invisible mode 2";
1542                 break;
1543         case STATUS_FREE_CHAT:
1544                 return "Free for chat";
1545                 break;
1546         default:
1547                 return "Error";
1548                 break;
1549         }
1550 }
1551
1552 void icq_InitNewUser(const char *hostname, DWORD port)
1553 {
1554         srv_net_icq_pak pak;
1555         int s;
1556         struct timeval tv;
1557         fd_set readfds;
1558
1559         icq_Connect(hostname, port);
1560         if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1561                 printf("Couldn't establish connection\n");
1562                 exit(1);
1563         }
1564         icq_RegNewUser((ThisICQ->icq_Password));
1565         for (;;) {
1566                 tv.tv_sec = 2;
1567                 tv.tv_usec = 500000;
1568
1569                 FD_ZERO(&readfds);
1570                 FD_SET((ThisICQ->icq_Sok), &readfds);
1571
1572                 /* don't care about writefds and exceptfds: */
1573                 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1574
1575                 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1576                         s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1577                         if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1578                                 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1579                                 return;
1580                         }
1581                 }
1582         }
1583 }
1584
1585 /********************************************
1586 Converts an intel endian character sequence to
1587 a DWORD
1588 *********************************************/
1589 DWORD Chars_2_DW(unsigned char *buf)
1590 {
1591         DWORD i;
1592
1593         i = buf[3];
1594         i <<= 8;
1595         i += buf[2];
1596         i <<= 8;
1597         i += buf[1];
1598         i <<= 8;
1599         i += buf[0];
1600
1601         return i;
1602 }
1603
1604 /********************************************
1605 Converts an intel endian character sequence to
1606 a WORD
1607 *********************************************/
1608 WORD Chars_2_Word(unsigned char *buf)
1609 {
1610         WORD i;
1611
1612         i = buf[1];
1613         i <<= 8;
1614         i += buf[0];
1615
1616         return i;
1617 }
1618
1619 /********************************************
1620 Converts a DWORD to
1621 an intel endian character sequence 
1622 *********************************************/
1623 void DW_2_Chars(unsigned char *buf, DWORD num)
1624 {
1625         buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1626         buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1627         buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1628         buf[0] = (unsigned char) (num) & 0x000000FF;
1629 }
1630
1631 /********************************************
1632 Converts a WORD to
1633 an intel endian character sequence 
1634 *********************************************/
1635 void Word_2_Chars(unsigned char *buf, WORD num)
1636 {
1637         buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1638         buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1639 }
1640
1641 /***************************
1642 ContactList functions
1643 ***************************/
1644 void icq_ContAddUser(DWORD cuin)
1645 {
1646         icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1647         icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1648         p->uin = cuin;
1649         p->next = 0L;
1650         p->vis_list = FALSE;
1651         if (ptr) {
1652                 while (ptr->next)
1653                         ptr = ptr->next;
1654                 ptr->next = p;
1655         } else
1656                 (ThisICQ->icq_ContFirst) = p;
1657 }
1658
1659 void icq_ContDelUser(DWORD cuin)
1660 {
1661         icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1662         if (!ptr)
1663                 return;
1664         if (ptr->uin == cuin) {
1665                 (ThisICQ->icq_ContFirst) = ptr->next;
1666                 phree(ptr);
1667                 ptr = (ThisICQ->icq_ContFirst);
1668         }
1669         while (ptr->next) {
1670                 if (ptr->next->uin == cuin) {
1671                         ptr->next = ptr->next->next;
1672                         phree(ptr->next);
1673                 }
1674                 ptr = ptr->next;
1675         }
1676 }
1677
1678 void icq_ContClear()
1679 {
1680         icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1681         while (ptr) {
1682                 tmp = ptr->next;
1683                 phree(ptr);
1684                 ptr = tmp;
1685                 (ThisICQ->icq_ContFirst) = ptr;
1686         }
1687 }
1688
1689 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1690 {
1691         icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1692         if (!ptr)
1693                 return 0L;
1694         while (ptr) {
1695                 if (ptr->uin == cuin)
1696                         return ptr;
1697                 ptr = ptr->next;
1698         }
1699         return 0L;
1700 }
1701
1702 icq_ContactItem *icq_ContGetFirst()
1703 {
1704         return (ThisICQ->icq_ContFirst);
1705 }
1706
1707 void icq_ContSetVis(DWORD cuin)
1708 {
1709         icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1710         if (!ptr)
1711                 return;
1712         while (ptr) {
1713                 if (ptr->uin == cuin)
1714                         ptr->vis_list = TRUE;
1715                 ptr = ptr->next;
1716         }
1717 }
1718
1719 /************************
1720 (ThisICQ->icq_ServMess) functions
1721 *************************/
1722 BOOL icq_GetServMess(WORD num)
1723 {
1724         return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1725 }
1726
1727 void icq_SetServMess(WORD num)
1728 {
1729         (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1730 }
1731
1732 int icq_GetSok()
1733 {
1734         return (ThisICQ->icq_Sok);
1735 }
1736
1737 int icq_GetProxySok()
1738 {
1739         return (ThisICQ->icq_ProxySok);
1740 }
1741
1742 /*******************
1743 SOCKS5 Proxy support
1744 ********************/
1745 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1746 {
1747         if ((ThisICQ->icq_ProxyHost))
1748                 phree((ThisICQ->icq_ProxyHost));
1749         if ((ThisICQ->icq_ProxyName))
1750                 phree((ThisICQ->icq_ProxyName));
1751         if ((ThisICQ->icq_ProxyPass))
1752                 phree((ThisICQ->icq_ProxyPass));
1753         if (strlen(pname) > 255) {
1754                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1755                         (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1756                 ThisICQ->icq_UseProxy = 0;
1757                 return;
1758         }
1759         if (strlen(ppass) > 255) {
1760                 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1761                         (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1762                 (ThisICQ->icq_UseProxy) = 0;
1763                 return;
1764         }
1765         (ThisICQ->icq_UseProxy) = 1;
1766         (ThisICQ->icq_ProxyHost) = strdoop(phost);
1767         (ThisICQ->icq_ProxyPort) = pport;
1768         (ThisICQ->icq_ProxyAuth) = pauth;
1769         (ThisICQ->icq_ProxyName) = strdoop(pname);
1770         (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1771 }
1772
1773 void icq_UnsetProxy()
1774 {
1775         ThisICQ->icq_UseProxy = 0;
1776 }
1777
1778
1779
1780 /***********************************************************************/
1781 /* icqlib stuff ends here, Citadel module stuff begins                 */
1782 /***********************************************************************/
1783
1784
1785 /*
1786  * Callback function for CtdlICQ_Read_Config()
1787  */
1788 void CtdlICQ_Read_Config_Backend(long msgnum) {
1789         struct CtdlMessage *msg;
1790         int pos;
1791         char *ptr;
1792
1793         lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
1794         msg = CtdlFetchMessage(msgnum);
1795         if (msg != NULL) {
1796                 ptr = msg->cm_fields['M'];
1797                 pos = pattern2(ptr, "\n\n");
1798                 if (pos >= 0) {
1799                         while (pos > 0) {
1800                                 ++ptr;
1801                                 --pos;
1802                         }
1803                         ++ptr;
1804                         ++ptr;
1805                         safestrncpy(ThisICQ->icq_config, ptr, 256);
1806                 }
1807                 CtdlFreeMessage(msg);
1808         } else {
1809                 lprintf(9, "...it ain't there?\n");
1810         }
1811 }
1812
1813
1814 /*
1815  * If this user has an ICQ configuration on disk, read it into memory.
1816  */
1817 void CtdlICQ_Read_Config(void) {
1818         char hold_rm[ROOMNAMELEN];
1819         char icq_rm[ROOMNAMELEN];
1820         
1821         strcpy(hold_rm, CC->quickroom.QRname);
1822         MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1823         strcpy(ThisICQ->icq_config, "");
1824
1825         if (getroom(&CC->quickroom, icq_rm) != 0) {
1826                 getroom(&CC->quickroom, hold_rm);
1827                 return;
1828         }
1829
1830         /* We want the last (and probably only) config in this room */
1831         lprintf(9, "We're in <%s> looking for config\n", 
1832                 CC->quickroom.QRname);
1833         CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1834         getroom(&CC->quickroom, hold_rm);
1835         return;
1836 }
1837
1838
1839
1840 /*
1841  * Write our config to disk
1842  */
1843 void CtdlICQ_Write_Config(void) {
1844         char temp[PATH_MAX];
1845         FILE *fp;
1846
1847         strcpy(temp, tmpnam(NULL));
1848
1849         fp = fopen(temp, "w");
1850         if (fp == NULL) return;
1851         fprintf(fp, "%s|\n", ThisICQ->icq_config);
1852         fclose(fp);
1853
1854         /* this handy API function does all the work for us */
1855         CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
1856
1857         unlink(temp);
1858 }
1859
1860
1861
1862
1863
1864 /*
1865  * Write our contact list to disk
1866  */
1867 void CtdlICQ_Write_CL(void) {
1868         char temp[PATH_MAX];
1869         FILE *fp;
1870         int i;
1871
1872         strcpy(temp, tmpnam(NULL));
1873
1874         fp = fopen(temp, "w");
1875         if (fp == NULL) return;
1876
1877         if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1878                 fprintf(fp, "%ld|%s|\n",
1879                         ThisICQ->icq_cl[i].uin,
1880                         ThisICQ->icq_cl[i].name);
1881         }
1882         fclose(fp);
1883
1884         /* this handy API function does all the work for us */
1885         CtdlWriteObject(ICQROOM, ICQCLMIME, temp, 1, 0, 1);
1886
1887         unlink(temp);
1888 }
1889
1890
1891
1892
1893
1894 /*
1895  * Callback function for CtdlICQ_Read_CL()
1896  */
1897 void CtdlICQ_Read_CL_Backend(long msgnum) {
1898         struct CtdlMessage *msg;
1899         int pos;
1900         char *ptr, *cont;
1901         int i;
1902
1903         msg = CtdlFetchMessage(msgnum);
1904         if (msg != NULL) {
1905                 ptr = msg->cm_fields['M'];
1906                 pos = pattern2(ptr, "\n\n");
1907                 if (pos >= 0) {
1908                         while (pos > 0) {
1909                                 ++ptr;
1910                                 --pos;
1911                         }
1912                         ++ptr;
1913                         ++ptr;
1914                         for (i=0; i<strlen(ptr); ++i)
1915                                 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1916                         if (ThisICQ->icq_numcl) {
1917                                 ThisICQ->icq_cl = mallok(
1918                                         (ThisICQ->icq_numcl *
1919                                         sizeof (struct CtdlICQ_CL)));
1920                                 i=0;
1921                                 while (cont=strtok(ptr, "\n"), cont != NULL) {
1922                                         ThisICQ->icq_cl[i].uin =
1923                                                 extract_long(cont, 0);
1924                                         extract(ThisICQ->icq_cl[i].name,
1925                                                 cont, 1);
1926                                         ThisICQ->icq_cl[i].status =
1927                                                 STATUS_OFFLINE;
1928                                         ++i;
1929                                         ptr = NULL;
1930                                 }
1931                         }
1932                 }
1933                 CtdlFreeMessage(msg);
1934         }
1935 }
1936
1937
1938 /*
1939  * Read contact list into memory
1940  */
1941 void CtdlICQ_Read_CL(void) {
1942         char hold_rm[ROOMNAMELEN];
1943         char icq_rm[ROOMNAMELEN];
1944         
1945         strcpy(hold_rm, CC->quickroom.QRname);
1946         MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1947         strcpy(ThisICQ->icq_config, "");
1948
1949         if (getroom(&CC->quickroom, icq_rm) != 0) {
1950                 getroom(&CC->quickroom, hold_rm);
1951                 return;
1952         }
1953
1954         /* Free any contact list already in memory */
1955         if (ThisICQ->icq_numcl) {
1956                 phree(ThisICQ->icq_cl);
1957                 ThisICQ->icq_numcl = 0;
1958         }
1959
1960         /* We want the last (and probably only) list in this room */
1961         CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
1962         getroom(&CC->quickroom, hold_rm);
1963 }
1964
1965
1966 /* 
1967  * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
1968  * entry in the table if necessary
1969  */
1970 struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
1971         int i;
1972
1973         if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
1974                 if (ThisICQ->icq_cl[i].uin == uin)
1975                         return (&ThisICQ->icq_cl[i]);
1976
1977         ++ThisICQ->icq_numcl;
1978         ThisICQ->icq_cl = reallok(ThisICQ->icq_cl, 
1979                 (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
1980         memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
1981                 0, sizeof(struct CtdlICQ_CL));
1982         ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
1983         return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
1984 }
1985
1986
1987
1988 /*
1989  * Refresh the contact list
1990  */
1991 void CtdlICQ_Refresh_Contact_List(void) {
1992         int i;
1993
1994         if (ThisICQ->icq_Sok < 0) return;
1995         icq_ContClear();
1996
1997         if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1998                 if (ThisICQ->icq_cl[i].uin > 0L) {
1999                         icq_ContAddUser(ThisICQ->icq_cl[i].uin);
2000                         icq_ContSetVis(ThisICQ->icq_cl[i].uin);
2001                 }
2002         }
2003
2004         icq_SendContactList();
2005 }
2006
2007
2008
2009
2010 /* 
2011  * Utility routine to logout and disconnect from the ICQ server if we happen
2012  * to already be connected
2013  */
2014 void CtdlICQ_Logout_If_Connected(void) {
2015         if (ThisICQ->icq_Sok >= 0) {
2016                 icq_Logout();
2017                 icq_Disconnect();
2018                 ThisICQ->icq_Sok = (-1);
2019         }
2020 }
2021
2022
2023 /*
2024  * If we have an ICQ uin/password on file for this user, go ahead and try
2025  * to log on to the ICQ server.
2026  */
2027 void CtdlICQ_Login_If_Possible(void) {
2028         char buf[256];
2029         long uin;
2030         char pass[256];
2031
2032         CtdlICQ_Logout_If_Connected();
2033         CtdlICQ_Read_Config();
2034
2035         uin = extract_long(ThisICQ->icq_config, 0);
2036         extract(pass, ThisICQ->icq_config, 1);
2037
2038         lprintf(9, "Here's my config: %s\n", ThisICQ->icq_config);
2039
2040         if ( (uin > 0L) && (strlen(pass)>0) ) {
2041                 icq_Init(uin, pass);
2042                 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2043                         icq_Login(STATUS_ONLINE);
2044                         CtdlICQ_Refresh_Contact_List();
2045                 }
2046         }
2047 }
2048
2049
2050
2051 /* This merely hooks icqlib's log function into Citadel's log function. */
2052 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2053 {
2054         lprintf(level, "ICQ: %s", str);
2055 }
2056
2057
2058 /* 
2059  * At the start of each Citadel server session, we have to allocate some
2060  * space for the Citadel-ICQ session for this thread.  It'll be automatically
2061  * freed by the server when the session ends.
2062  */
2063 void CtdlICQ_session_startup_hook(void)
2064 {
2065         CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2066         icq_init_handle(ThisICQ);
2067 }
2068
2069
2070
2071 /* 
2072  * End-of-session cleanup
2073  */
2074 void CtdlICQ_session_stopdown_hook(void) {
2075         icq_Done();
2076 }
2077
2078
2079
2080 /*
2081  * icq_Main() needs to be called as frequently as possible.  We'll do it
2082  * following the completion of each Citadel server command.
2083  *
2084  */
2085 void CtdlICQ_after_cmd_hook(void)
2086 {
2087         if (ThisICQ->icq_Sok >= 0) {
2088                 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
2089                         icq_KeepAlive();
2090                         ThisICQ->icq_LastKeepAlive = time(NULL);
2091                 }
2092                 icq_Main();
2093         }
2094 }
2095
2096
2097 /*
2098  * There are a couple of things we absolutely need to make sure of when we
2099  * kill off the session.  One of them is that the sockets are all closed --
2100  * otherwise we could have a nasty file descriptor leak.
2101  */
2102 void CtdlICQ_session_logout_hook(void)
2103 {
2104         lprintf(9, "Shutting down ICQ\n");
2105         CtdlICQ_Logout_If_Connected();
2106
2107         /* Free the memory used by the contact list */
2108         if (ThisICQ->icq_numcl) {
2109                 phree(ThisICQ->icq_cl);
2110                 ThisICQ->icq_numcl = 0;
2111         }
2112 }
2113
2114
2115 /*
2116  */
2117 void CtdlICQ_session_login_hook(void)
2118 {
2119         /* If this user has an ICQ config on file, start it up. */
2120         CtdlICQ_Login_If_Possible();
2121 }
2122
2123
2124
2125
2126 void cmd_icql(char *argbuf)
2127 {
2128         safestrncpy(ThisICQ->icq_config, argbuf, 256);
2129         CtdlICQ_Write_Config();
2130         
2131         cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2132         CtdlICQ_Login_If_Possible();
2133 }
2134
2135
2136
2137
2138 /*
2139  * Here's what we have to do when an ICQ message arrives!
2140  */
2141 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2142                                 BYTE day, BYTE month, WORD year,
2143                                 const char *msg) {
2144         
2145         char from[256];
2146         int num_delivered;
2147
2148         sprintf(from, "%ld@icq", uin);
2149         num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2150         lprintf(9, "Delivered to %d users\n", num_delivered);
2151 }
2152
2153
2154
2155 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2156                 const char *first, const char *last,
2157                 const char *email, char auth) {
2158
2159         struct CtdlICQ_CL *ptr;
2160         
2161         ptr = CtdlICQ_CLent(uin);
2162         safestrncpy(ptr->name, nick, 32);
2163         ptr->status = STATUS_OFFLINE;
2164         lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
2165         CtdlICQ_Write_CL();
2166 }
2167
2168
2169
2170 /* send an icq */
2171
2172 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2173         int is_aticq = 0;
2174         int i;
2175         DWORD target_uin = 0L;
2176
2177
2178         /* If this is an incoming ICQ from someone on the contact list,
2179          * change the sender from "uin@icq" to the contact name.
2180          */
2181         is_aticq = 0;
2182         for (i=0; i<strlen(from); ++i)
2183                 if (!strcasecmp(&from[i], "@icq")) {
2184                         is_aticq = 1;
2185                 }
2186         if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
2187                 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2188                         if (ThisICQ->icq_cl[i].uin == atol(from))
2189                                 strcpy(from, ThisICQ->icq_cl[i].name);
2190                 }
2191         }
2192
2193
2194         /* Handle "uin@icq" syntax */
2195         is_aticq = 0;
2196         for (i=0; i<strlen(recp); ++i)
2197                 if (!strcasecmp(&recp[i], "@icq")) {
2198                         is_aticq = 1;
2199                 }
2200         if (is_aticq == 1) target_uin = atol(recp);
2201
2202         /* Handle "nick" syntax */
2203         if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
2204                 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2205                         if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
2206                                 target_uin = ThisICQ->icq_cl[i].uin;
2207                         }
2208                 }
2209         }
2210                 
2211
2212         if (target_uin == 0L) return(0);
2213
2214         if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
2215         return(1);
2216 }
2217
2218
2219
2220 void cmd_icqa(char *argbuf) {
2221         long uin;
2222
2223         uin = extract_long(argbuf, 0);
2224         if (uin <= 0L) {
2225                 cprintf("%d You must supply a uin.\n", ERROR);
2226                 return;
2227         }
2228
2229         CtdlICQ_Read_CL();
2230
2231         /* This function is normally used to obtain a relevant pointer, but
2232          * it also creates an entry in the contact list if one isn't there.
2233          */
2234         CtdlICQ_CLent(uin);
2235
2236         /* Save the new contact list and tell the ICQ server about it */
2237         CtdlICQ_Write_CL();
2238         CtdlICQ_Refresh_Contact_List();
2239
2240         /* Leave the user clueless as to what happened, because we really
2241          * don't know (and this is why I hate asynchronous protocols).
2242          */
2243         cprintf("%d Ok (maybe)\n", OK);
2244
2245         /* Request more info on this user from the server, which will arrive
2246          * at some time in the future.  Maybe.
2247          */
2248         icq_SendInfoReq(uin);
2249 }
2250
2251
2252 /* 
2253  * During an RWHO command, we want to append our ICQ information.
2254  */
2255 void CtdlICQ_rwho(void) {
2256         int i;
2257
2258         if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
2259            if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
2260                 cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
2261                         0,      /* no session ID */
2262                         ThisICQ->icq_cl[i].name,
2263                         icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
2264                         " ",    /* FIX add host */
2265                         " ",    /* no client */
2266                         time(NULL),     /* now? */
2267                         " ",    /* no last command */
2268                         "ICQ"   /* flags */
2269                         );
2270 }
2271  
2272
2273 void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
2274         struct CtdlICQ_CL *ptr;
2275         
2276         ptr = CtdlICQ_CLent(uin);
2277         ptr->status = status;
2278         if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
2279 }
2280
2281
2282 void CtdlICQ_Logged(void) {
2283         CtdlICQ_Read_CL();
2284         CtdlICQ_Refresh_Contact_List();
2285 }
2286
2287
2288 void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
2289                         DWORD port, DWORD realip) {
2290
2291         CtdlICQ_Status_Update(uin, status);
2292 }
2293
2294
2295 void CtdlICQ_UserOffline(DWORD uin) {
2296         CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
2297 }
2298
2299
2300 char *Dynamic_Module_Init(void)
2301 {
2302         /* Make sure we've got a valid ThisICQ for each session */
2303         SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2304
2305         /* Tell the Citadel server about our wonderful ICQ hooks */
2306         CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2307         CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2308         CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2309         CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2310         CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2311         CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
2312         CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2313         CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add ICQ contact");
2314         CtdlRegisterXmsgHook(CtdlICQ_Send_Msg);
2315
2316         /* Tell the code formerly known as icqlib about our callbacks */
2317         icq_Log = CtdlICQlog;
2318         icq_RecvMessage = CtdlICQ_Incoming_Message;
2319         icq_InfoReply = CtdlICQ_InfoReply;
2320         icq_Disconnected = CtdlICQ_Login_If_Possible;
2321         icq_Logged = CtdlICQ_Logged;
2322         icq_UserStatusUpdate = CtdlICQ_Status_Update;
2323         icq_UserOnline = CtdlICQ_UserOnline;
2324         icq_UserOffline = CtdlICQ_UserOffline;
2325
2326         return "$Id$";
2327 }