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