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