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