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