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