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