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