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