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