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