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