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