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