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.
11 Revision 1.1 1999/07/19 04:12:49 ajc
12 * serv_icq.c, serv_icq.mk: added (separate makefile is temporary)
14 Revision 1.16 1998/12/08 16:00:59 denis
15 Cleaned up a little before releasing
17 Revision 1.15 1998/11/25 19:18:16 denis
18 Added close icq_ProxySok in icq_Disconnect
20 Revision 1.14 1998/11/25 09:48:49 denis
21 icq_GetProxySok and icq_HandleProxyResponse methods added
22 Connection terminated support added
24 Revision 1.13 1998/11/19 12:22:48 denis
25 SOCKS support cleaned a little
26 icq_RecvUrl renamed to icq_RecvURL
27 icq_ProxyAuth added for Username/Password Authentication
28 URL/Description order inverted
29 icq_Quit splitted to icq_Logout and icq_Disconnect
30 icq_ProxyName and icq_ProxyPass range checking added
32 Revision 1.12 1998/11/18 16:21:29 denis
33 Fixed SOCKS5 proxy support
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <netinet/in.h>
56 #include "dynloader.h"
58 #include "citserver.h"
60 struct ctdl_icq_handle {
63 BYTE icq_ServMess[8192];
68 icq_ContactItem *icq_ContFirst;
79 DWORD icq_ProxyDestHost;
80 WORD icq_ProxyDestPort;
81 WORD icq_ProxyOurPort;
82 FILE *icq_MyConfigFile; /* ig */
83 time_t icq_LastKeepAlive; /* ig */
87 unsigned long SYM_CTDL_ICQ;
88 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
89 extern struct CitContext *ContextList;
90 #define MODULE_NAME "ICQ metaclient"
91 #define MODULE_AUTHOR "Art Cancro"
92 #define MODULE_EMAIL "ajc@uncnsrd.mt-kisco.ny.us"
93 #define MAJOR_VERSION 0
94 #define MINOR_VERSION 1
96 static struct DLModule_Info info =
107 void (*icq_Logged) (void);
108 void (*icq_Disconnected) (void);
109 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
110 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
111 void (*icq_RecvAdded) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email);
112 void (*icq_RecvAuthReq) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email, const char *reason);
113 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
114 void (*icq_SearchDone) (void);
115 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
116 void (*icq_UserOffline) (DWORD uin);
117 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
118 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
119 void (*icq_ExtInfoReply) (DWORD uin, const char *city, WORD country_code, char country_stat, const char *state, WORD age, char gender, const char *phone, const char *hp, const char *about);
120 void (*icq_Log) (time_t time, unsigned char level, const char *str);
121 void (*icq_SrvAck) (WORD seq);
124 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
125 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
126 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
127 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
128 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
129 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
130 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
131 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
134 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
135 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
136 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
137 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
138 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
139 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
140 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
141 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
143 static COUNTRY_CODE Country_Codes[] =
158 {"American Samoa", 684},
173 {"Czech Republic", 42},
176 {"El Salvador", 503},
180 {"French Antilles", 596},
181 {"French Polynesia", 689},
188 {"Guantanomo Bay", 53},
202 {"Ivory Coast", 225},
210 {"Liechtenstein", 41},
222 {"Netherlands Antilles", 599},
223 {"New Caledonia", 687},
230 {"Papua New Guinea", 675},
240 {"Saudia Arabia", 966},
245 {"South Africa", 27},
256 {"United Arab Emirates", 971},
258 {"Vatican City", 39},
265 {"Not entered", 0xffff}};
267 void icq_init_handle(struct ctdl_icq_handle *i)
269 memset(i, 0, sizeof(struct ctdl_icq_handle));
270 i->icq_Russian = TRUE;
272 i->icq_OurIp = 0x0100007f;
273 i->icq_Status = STATUS_OFFLINE;
276 i->icq_MyConfigFile = NULL;
279 int icq_SockWrite(int sok, const void *buf, size_t count)
282 if (!(ThisICQ->icq_UseProxy))
283 return write(sok, buf, count);
285 tmpbuf[0] = 0; /* reserved */
286 tmpbuf[1] = 0; /* reserved */
287 tmpbuf[2] = 0; /* standalone packet */
288 tmpbuf[3] = 1; /* address type IP v4 */
289 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
290 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
291 memcpy(&tmpbuf[10], buf, count);
292 return write(sok, tmpbuf, count + 10) - 10;
296 int icq_SockRead(int sok, void *buf, size_t count)
300 if (!(ThisICQ->icq_UseProxy))
301 return read(sok, buf, count);
303 res = read(sok, tmpbuf, count + 10);
304 memcpy(buf, &tmpbuf[10], res - 10);
309 /****************************************
310 This must be called every 2 min.
311 so the server knows we're still alive.
312 JAVA client sends two different commands
314 *****************************************/
319 Word_2_Chars(pak.head.ver, ICQ_VER);
320 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
321 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
322 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
323 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
325 Word_2_Chars(pak.head.ver, ICQ_VER);
326 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
327 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
328 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
329 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
331 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
332 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
335 /**********************************
336 This must be called to remove
337 messages from the server
338 ***********************************/
339 void icq_SendGotMessages()
343 Word_2_Chars(pak.head.ver, ICQ_VER);
344 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
345 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
346 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
348 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
351 /*************************************
352 this sends over the contact list
353 *************************************/
354 void icq_SendContactList()
360 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
362 Word_2_Chars(pak.head.ver, ICQ_VER);
363 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
364 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
365 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
371 DW_2_Chars(tmp, ptr->uin);
376 pak.data[0] = num_used;
377 size = ((int) tmp - (int) pak.data);
378 size += sizeof(pak.head);
379 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
382 void icq_SendNewUser(unsigned long uin)
387 Word_2_Chars(pak.head.ver, ICQ_VER);
388 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
389 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
390 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
391 DW_2_Chars(pak.data, uin);
392 size = sizeof(pak.head) + 4;
393 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
396 /*************************************
397 this sends over the visible list
398 that allows certain users to see you
400 *************************************/
401 void icq_SendVisibleList()
407 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
409 Word_2_Chars(pak.head.ver, ICQ_VER);
410 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
411 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
412 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
419 DW_2_Chars(tmp, ptr->uin);
426 pak.data[0] = num_used;
427 size = ((int) tmp - (int) pak.data);
428 size += sizeof(pak.head);
429 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
433 /**************************************
434 This sends the second login command
435 this is necessary to finish logging in.
436 ***************************************/
437 void icq_SendLogin1()
441 Word_2_Chars(pak.head.ver, ICQ_VER);
442 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
443 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
444 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
446 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
449 /************************************************
450 This is called when a user goes offline
451 *************************************************/
452 void icq_HandleUserOffline(srv_net_icq_pak pak)
457 remote_uin = Chars_2_DW(pak.data);
458 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
459 sprintf(buf, "User %lu logged off\n", remote_uin);
460 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
463 (*icq_UserOffline) (remote_uin);
464 icq_AckSrv(Chars_2_Word(pak.head.seq));
467 void icq_HandleUserOnline(srv_net_icq_pak pak)
469 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
470 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
471 icq_ContactItem *ptr;
474 remote_uin = Chars_2_DW(pak.data);
475 new_status = Chars_2_DW(&pak.data[17]);
476 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
477 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
478 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
479 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
480 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
481 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
484 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
485 icq_AckSrv(Chars_2_Word(pak.head.seq));
488 void icq_Status_Update(srv_net_icq_pak pak)
490 unsigned long remote_uin, new_status;
493 remote_uin = Chars_2_DW(pak.data);
494 new_status = Chars_2_DW(&pak.data[4]);
495 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
496 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
497 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
499 if (icq_UserStatusUpdate)
500 (*icq_UserStatusUpdate) (remote_uin, new_status);
501 icq_AckSrv(Chars_2_Word(pak.head.seq));
504 /************************************
505 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
506 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
507 It does NOT wait for any kind of a response.
508 *************************************/
509 void icq_Login(DWORD status)
516 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
517 Word_2_Chars(pak.head.ver, ICQ_VER);
518 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
519 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
520 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
522 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
523 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
525 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
526 DW_2_Chars(s2.status, status);
527 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
529 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
530 s2.X2[0] = LOGIN_X2_DEF;
531 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
532 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
533 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
535 memcpy(pak.data, &s1, sizeof(s1));
537 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
538 size += Chars_2_Word(s1.len);
539 memcpy(&pak.data[size], &s2, sizeof(s2));
541 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
544 /*******************************
545 This routine sends the aknowlegement cmd to the
546 server it appears that this must be done after
547 everything the server sends us
548 *******************************/
549 void icq_AckSrv(int seq)
554 Word_2_Chars(pak.head.ver, ICQ_VER);
555 Word_2_Chars(pak.head.cmd, CMD_ACK);
556 Word_2_Chars(pak.head.seq, seq);
557 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
558 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
559 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
560 for (i = 0; i < 6; i++)
561 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
564 void icq_HandleInfoReply(srv_net_icq_pak pak)
566 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
570 seq = Chars_2_Word(pak.data);
571 uin = Chars_2_DW(&pak.data[2]);
572 len = Chars_2_Word(&pak.data[6]);
574 icq_RusConv("wk", ptr1);
575 tmp = &pak.data[8 + len];
576 len = Chars_2_Word(tmp);
578 icq_RusConv("wk", ptr2);
580 len = Chars_2_Word(tmp);
582 icq_RusConv("wk", ptr3);
584 len = Chars_2_Word(tmp);
586 icq_RusConv("wk", ptr4);
588 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
589 sprintf(buf, "Info reply for %lu\n", uin);
590 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
593 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
594 icq_AckSrv(Chars_2_Word(pak.head.seq));
597 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
599 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
603 char cnt_stat, gender, buf[256];
604 uin = Chars_2_DW(&pak.data[2]);
605 len = Chars_2_Word(&pak.data[6]);
607 icq_RusConv("wk", ptr1);
608 cnt_code = Chars_2_Word(&pak.data[8 + len]);
609 cnt_stat = pak.data[len + 10];
610 tmp = &pak.data[11 + len];
611 len = Chars_2_Word(tmp);
612 icq_RusConv("wk", tmp + 2);
614 age = Chars_2_Word(tmp + 2 + len);
615 gender = *(tmp + len + 4);
617 len = Chars_2_Word(tmp);
618 icq_RusConv("wk", tmp + 2);
621 len = Chars_2_Word(tmp);
622 icq_RusConv("wk", tmp + 2);
625 len = Chars_2_Word(tmp);
626 icq_RusConv("wk", tmp + 2);
628 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
629 sprintf(buf, "Extended info reply for %lu\n", uin);
630 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
632 if (icq_ExtInfoReply)
633 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
634 icq_AckSrv(Chars_2_Word(pak.head.seq));
637 void icq_HandleSearchReply(srv_net_icq_pak pak)
639 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
643 uin = Chars_2_DW(&pak.data[2]);
644 len = Chars_2_Word(&pak.data[6]);
646 icq_RusConv("wk", ptr1);
647 tmp = &pak.data[8 + len];
648 len = Chars_2_Word(tmp);
650 icq_RusConv("wk", ptr2);
652 len = Chars_2_Word(tmp);
654 icq_RusConv("wk", ptr3);
656 len = Chars_2_Word(tmp);
658 icq_RusConv("wk", ptr4);
660 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
661 sprintf(buf, "User found %lu, Nick: %s, First Name: %s, Last Name: %s, EMail: %s, Auth: %s\n", uin, ptr1, ptr2, ptr3, ptr4, *tmp == 1 ? "no" : "yes");
662 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
665 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
666 icq_AckSrv(Chars_2_Word(pak.head.seq));
669 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
672 char *ptr1, *ptr2, *ptr3, *ptr4;
676 case USER_ADDED_MESS:
677 tmp = strchr(data, '\xFE');
679 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
680 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
687 tmp = strchr(tmp, '\xFE');
689 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
690 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
694 icq_RusConv("wk", data);
698 tmp = strchr(tmp, '\xFE');
700 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
701 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
705 icq_RusConv("wk", data);
709 tmp = strchr(tmp, '\xFE');
711 icq_RusConv("wk", data);
712 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
713 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
714 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
715 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
718 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
721 tmp = strchr(data, '\xFE');
726 tmp = strchr(tmp, '\xFE');
728 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
729 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
733 icq_RusConv("wk", data);
737 tmp = strchr(tmp, '\xFE');
739 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
740 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
744 icq_RusConv("wk", data);
748 tmp = strchr(tmp, '\xFE');
750 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
751 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
755 icq_RusConv("wk", data);
759 tmp = strchr(tmp, '\xFE');
761 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
762 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
768 tmp = strchr(tmp, '\x00');
770 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
771 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
775 icq_RusConv("wk", data);
776 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
777 sprintf(buf, "%lu has requested your authorization to be added to "
778 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
779 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
780 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
783 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
786 tmp = strchr(data, '\xFE');
788 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
789 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
793 icq_RusConv("wk", data);
797 icq_RusConv("wk", data);
798 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
799 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
800 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
803 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
806 icq_RusConv("wk", data);
807 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
808 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
809 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
812 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
816 /**********************************
817 Connects to hostname on port port
818 hostname can be DNS or nnn.nnn.nnn.nnn
819 write out messages to the FD aux
820 ***********************************/
821 int icq_Connect(const char *hostname, int port)
823 char buf[1024], un = 1;
824 char our_host[256], tmpbuf[256];
825 int conct, length, res;
826 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
827 struct hostent *host_struct; /* used in DNS llokup */
829 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
830 if ((ThisICQ->icq_Sok) == -1) {
831 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
832 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
835 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
836 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
837 sin.sin_addr.s_addr = INADDR_ANY;
838 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
840 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
841 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
842 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
845 length = sizeof(sin);
846 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
847 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
848 if ((ThisICQ->icq_UseProxy)) {
849 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
850 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
851 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
852 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
853 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
854 if (host_struct == 0L) {
855 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
856 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
857 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
861 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
863 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
864 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
865 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
866 if ((ThisICQ->icq_ProxySok) == -1) {
867 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
868 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
871 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
872 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
873 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
874 if (conct == -1) { /* did we connect ? */
875 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
876 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
879 buf[0] = 5; /* protocol version */
880 buf[1] = 1; /* number of methods */
881 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
882 buf[2] = 0; /* no authorization required */
884 buf[2] = 2; /* method username/password */
885 write((ThisICQ->icq_ProxySok), buf, 3);
886 res = read((ThisICQ->icq_ProxySok), buf, 2);
887 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
888 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
889 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
890 close((ThisICQ->icq_ProxySok));
893 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
894 buf[0] = 1; /* version of subnegotiation */
895 buf[1] = strlen((ThisICQ->icq_ProxyName));
896 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
897 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
898 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
899 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
900 res = read((ThisICQ->icq_ProxySok), buf, 2);
901 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
902 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
903 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
904 close((ThisICQ->icq_ProxySok));
908 buf[0] = 5; /* protocol version */
909 buf[1] = 3; /* command UDP associate */
910 buf[2] = 0; /* reserved */
911 buf[3] = 1; /* address type IP v4 */
916 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
917 write((ThisICQ->icq_ProxySok), buf, 10);
918 res = read((ThisICQ->icq_ProxySok), buf, 10);
919 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
922 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
923 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
926 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
927 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
930 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
931 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
934 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
935 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
938 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
939 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
942 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
943 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
946 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
947 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
950 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
951 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
954 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
955 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
958 close((ThisICQ->icq_ProxySok));
962 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
963 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
964 host_struct = gethostbyname(hostname);
965 if (host_struct == 0L) {
966 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
967 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
968 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
970 if ((ThisICQ->icq_UseProxy))
971 close((ThisICQ->icq_ProxySok));
974 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
976 if ((ThisICQ->icq_UseProxy)) {
977 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
978 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
980 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
981 sin.sin_port = htons(port); /* port */
982 if ((ThisICQ->icq_UseProxy)) {
983 (ThisICQ->icq_ProxyDestPort) = htons(port);
984 memcpy(&sin.sin_port, &buf[8], 2);
986 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
987 if (conct == -1) { /* did we connect ? */
988 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
989 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
990 if ((ThisICQ->icq_UseProxy))
991 close((ThisICQ->icq_ProxySok));
994 length = sizeof(sin);
995 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
996 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
997 (ThisICQ->icq_OurPort) = sin.sin_port;
998 return (ThisICQ->icq_Sok);
1001 void icq_HandleProxyResponse()
1005 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
1007 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1008 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
1009 if (icq_Disconnected)
1010 (*icq_Disconnected) ();
1011 SOCKCLOSE((ThisICQ->icq_Sok));
1012 SOCKCLOSE((ThisICQ->icq_ProxySok));
1016 /******************************************
1017 Handles packets that the server sends to us.
1018 *******************************************/
1019 void icq_HandleServerResponse()
1021 srv_net_icq_pak pak;
1022 SIMPLE_MESSAGE *s_mesg;
1023 RECV_MESSAGE *r_mesg;
1030 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1032 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1033 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1034 if (icq_Disconnected)
1035 (*icq_Disconnected) ();
1036 SOCKCLOSE((ThisICQ->icq_Sok));
1038 if ((icq_GetServMess(Chars_2_Word(pak.head.seq))) && (Chars_2_Word(pak.head.cmd) != SRV_NEW_UIN) && (Chars_2_Word(pak.head.cmd) != SRV_GO_AWAY)) {
1039 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1040 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1041 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1042 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1044 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1048 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1049 icq_SetServMess(Chars_2_Word(pak.head.seq));
1050 switch (Chars_2_Word(pak.head.cmd)) {
1052 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1053 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1055 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1058 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1059 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1060 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1063 case SRV_LOGIN_REPLY:
1064 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1065 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1066 sprintf(buf, "Login successful, UIN: %lu, IP: %u.%u.%u.%u\n", Chars_2_DW(pak.data), pak.data[4], pak.data[5], pak.data[6], pak.data[7]);
1067 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1069 icq_AckSrv(Chars_2_Word(pak.head.seq));
1071 icq_SendContactList();
1072 icq_SendVisibleList();
1076 case SRV_RECV_MESSAGE:
1077 r_mesg = (RECV_MESSAGE *) pak.data;
1078 icq_DoMsg(Chars_2_Word(r_mesg->type), Chars_2_Word(r_mesg->len), (char *) (r_mesg->len + 2), Chars_2_DW(r_mesg->uin), r_mesg->hour, r_mesg->minute, r_mesg->day, r_mesg->month, Chars_2_Word(r_mesg->year));
1079 icq_AckSrv(Chars_2_Word(pak.head.seq));
1081 case SRV_X1: /* unknown message sent after login */
1082 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1083 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1084 icq_AckSrv(Chars_2_Word(pak.head.seq));
1086 case SRV_X2: /* unknown message sent after login */
1087 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1088 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1089 icq_AckSrv(Chars_2_Word(pak.head.seq));
1090 icq_SendGotMessages();
1092 case SRV_INFO_REPLY:
1093 icq_HandleInfoReply(pak);
1095 case SRV_EXT_INFO_REPLY:
1096 icq_HandleExtInfoReply(pak);
1098 case SRV_USER_ONLINE:
1099 icq_HandleUserOnline(pak);
1101 case SRV_USER_OFFLINE:
1102 icq_HandleUserOffline(pak);
1105 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1106 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1107 icq_Login((ThisICQ->icq_Status));
1109 case SRV_STATUS_UPDATE:
1110 icq_Status_Update(pak);
1113 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1114 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1115 if (icq_Disconnected)
1116 (*icq_Disconnected) ();
1118 case SRV_END_OF_SEARCH:
1119 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1120 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1122 (*icq_SearchDone) ();
1123 icq_AckSrv(Chars_2_Word(pak.head.seq));
1125 case SRV_USER_FOUND:
1126 icq_HandleSearchReply(pak);
1128 case SRV_SYS_DELIVERED_MESS:
1129 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1130 cur_time = time(0L);
1131 tm_str = localtime(&cur_time);
1132 icq_DoMsg(Chars_2_Word(s_mesg->type), Chars_2_Word(s_mesg->len), (char *) (s_mesg->len + 2), Chars_2_DW(s_mesg->uin), tm_str->tm_hour, tm_str->tm_min, tm_str->tm_mday, tm_str->tm_mon + 1, tm_str->tm_year + 1900);
1133 icq_AckSrv(Chars_2_Word(pak.head.seq));
1135 default: /* commands we dont handle yet */
1136 len = s - (sizeof(pak.head));
1137 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1138 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1139 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1140 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1142 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1147 void icq_Init(DWORD uin, const char *password)
1149 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1150 (ThisICQ->icq_Uin) = uin;
1151 if ((ThisICQ->icq_Password))
1152 free((ThisICQ->icq_Password));
1153 (ThisICQ->icq_Password) = strdup(password);
1158 if ((ThisICQ->icq_Password))
1159 free((ThisICQ->icq_Password));
1162 /******************************
1163 Main function connects gets (ThisICQ->icq_Uin)
1164 and (ThisICQ->icq_Password) and logins in and sits
1165 in a loop waiting for server responses.
1166 *******************************/
1178 FD_SET((ThisICQ->icq_Sok), &readfds);
1179 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1180 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1181 icq_HandleServerResponse();
1185 } while (did_something);
1188 /********************************************************
1189 Russian language ICQ fix.
1190 Usual Windows ICQ users do use Windows 1251 encoding but
1191 unix users do use koi8 encoding, so we need to convert it.
1192 This function will convert string from windows 1251 to koi8
1193 or from koi8 to windows 1251.
1194 Andrew Frolov dron@ilm.net
1195 *********************************************************/
1196 void icq_RusConv(const char to[4], char *t_in)
1201 /* 6-17-1998 by Linux_Dude
1202 * Moved initialization of table out front of 'if' block to prevent compiler
1203 * warning. Improved error message, and now return without performing string
1204 * conversion to prevent addressing memory out of range (table pointer would
1205 * previously have remained uninitialized (= bad)).
1209 if (strcmp(to, "kw") == 0)
1211 else if (strcmp(to, "wk") != 0) {
1212 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1213 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1216 /* End Linux_Dude's changes ;) */
1218 if ((ThisICQ->icq_Russian)) {
1219 for (i = 0; t_in[i] != 0; i++) {
1222 t_in[i] = table[t_in[i] & 0177];
1227 /**************************************************
1228 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1230 ***************************************************/
1231 WORD icq_SendMessage(DWORD uin, const char *text)
1236 char buf[512]; /* message may be only 450 bytes long */
1238 strncpy(buf, text, 512);
1239 icq_RusConv("kw", buf);
1241 Word_2_Chars(pak.head.ver, ICQ_VER);
1242 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1243 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1244 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1245 DW_2_Chars(msg.uin, uin);
1246 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1247 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1248 memcpy(&pak.data, &msg, sizeof(msg));
1249 memcpy(&pak.data[8], buf, len + 1);
1250 size = sizeof(msg) + len + 1;
1251 for (i = 0; i < 6; i++)
1252 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1253 return (ThisICQ->icq_SeqNum) - 1;
1256 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1260 int size, len1, len2;
1261 char buf1[512], buf2[512];
1263 strncpy(buf1, descr, 512);
1264 strncpy(buf2, url, 512);
1265 /* Do we need to convert URL? */
1266 icq_RusConv("kw", buf2);
1267 len1 = strlen(buf1);
1268 len2 = strlen(buf2);
1269 Word_2_Chars(pak.head.ver, ICQ_VER);
1270 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1271 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1272 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1273 DW_2_Chars(msg.uin, uin);
1274 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1275 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1276 memcpy(&pak.data, &msg, sizeof(msg));
1277 memcpy(&pak.data[8], buf1, len1);
1278 pak.data[8 + len1] = 0xFE;
1279 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1280 size = sizeof(msg) + len1 + len2 + 2;
1281 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1282 return (ThisICQ->icq_SeqNum) - 1;
1285 /**************************************************
1286 Sends a authorization to the server so the Mirabilis
1287 client can add the user.
1288 ***************************************************/
1289 void icq_SendAuthMsg(DWORD uin)
1295 Word_2_Chars(pak.head.ver, ICQ_VER);
1296 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1297 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1298 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1299 DW_2_Chars(msg.uin, uin);
1300 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1301 Word_2_Chars(msg.len, 2);
1302 memcpy(&pak.data, &msg, sizeof(msg));
1303 pak.data[sizeof(msg)] = 0x03;
1304 pak.data[sizeof(msg) + 1] = 0x00;
1305 size = sizeof(msg) + 2;
1306 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1309 /**************************************************
1310 Changes the users status on the server
1311 ***************************************************/
1312 void icq_ChangeStatus(DWORD status)
1317 Word_2_Chars(pak.head.ver, ICQ_VER);
1318 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1319 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1320 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1321 DW_2_Chars(pak.data, status);
1322 (ThisICQ->icq_Status) = status;
1324 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1327 /**********************
1329 ***********************/
1335 Word_2_Chars(pak.head.ver, ICQ_VER);
1336 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1337 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1338 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1339 len = strlen("B_USER_DISCONNECTED") + 1;
1340 *(short *) pak.data = len;
1342 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1343 pak.data[2 + len] = 05;
1344 pak.data[3 + len] = 00;
1345 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1348 void icq_Disconnect()
1350 SOCKCLOSE((ThisICQ->icq_Sok));
1351 SOCKCLOSE((ThisICQ->icq_Sok));
1352 if ((ThisICQ->icq_UseProxy))
1353 SOCKCLOSE((ThisICQ->icq_ProxySok));
1356 /********************************************************
1357 Sends a request to the server for info on a specific user
1358 *********************************************************/
1359 WORD icq_SendInfoReq(DWORD uin)
1364 Word_2_Chars(pak.head.ver, ICQ_VER);
1365 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1366 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1367 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1368 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1369 DW_2_Chars(&pak.data[2], uin);
1371 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1372 return (ThisICQ->icq_SeqNum) - 1;
1375 /********************************************************
1376 Sends a request to the server for info on a specific user
1377 *********************************************************/
1378 WORD icq_SendExtInfoReq(DWORD uin)
1383 Word_2_Chars(pak.head.ver, ICQ_VER);
1384 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1385 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1386 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1387 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1388 DW_2_Chars(&pak.data[2], uin);
1390 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1391 return (ThisICQ->icq_SeqNum) - 1;
1394 /**************************************************************
1395 Initializes a server search for the information specified
1396 ***************************************************************/
1397 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1402 Word_2_Chars(pak.head.ver, ICQ_VER);
1403 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1404 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1405 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1406 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1408 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1410 strcpy(pak.data + size, nick);
1411 size += strlen(nick) + 1;
1412 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1414 strcpy(pak.data + size, first);
1415 size += strlen(first) + 1;
1416 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1418 strcpy(pak.data + size, last);
1419 size += strlen(last) + 1;
1420 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1422 strcpy(pak.data + size, email);
1423 size += strlen(email) + 1;
1424 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1427 /**************************************************************
1428 Initializes a server search for the information specified
1429 ***************************************************************/
1430 void icq_SendSearchUINReq(DWORD uin)
1435 Word_2_Chars(pak.head.ver, ICQ_VER);
1436 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1437 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1438 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1439 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1441 DW_2_Chars(&pak.data[size], uin);
1443 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1446 /**************************************************
1447 Registers a new uin in the ICQ network
1448 ***************************************************/
1449 void icq_RegNewUser(const char *pass)
1451 srv_net_icq_pak pak;
1456 Word_2_Chars(pak.head.ver, ICQ_VER);
1457 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1458 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1459 Word_2_Chars(len_buf, len);
1460 memcpy(&pak.data, "\x02\x00", 2);
1461 memcpy(&pak.data[2], &len_buf, 2);
1462 memcpy(&pak.data[4], pass, len + 1);
1463 DW_2_Chars(&pak.data[4 + len], 0x0072);
1464 DW_2_Chars(&pak.data[8 + len], 0x0000);
1466 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1469 void icq_UpdateUserInfo(USER_INFO * user)
1474 Word_2_Chars(pak.head.ver, ICQ_VER);
1475 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1476 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1477 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1478 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1480 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1482 strcpy(pak.data + size, user->nick);
1483 size += strlen(user->nick) + 1;
1484 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1486 strcpy(pak.data + size, user->first);
1487 size += strlen(user->first) + 1;
1488 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1490 strcpy(pak.data + size, user->last);
1491 size += strlen(user->last) + 1;
1492 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1494 strcpy(pak.data + size, user->email);
1495 size += strlen(user->email) + 1;
1496 pak.data[size] = user->auth;
1498 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1501 const char *icq_GetCountryName(int code)
1505 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1506 if (Country_Codes[i].code == code) {
1507 return Country_Codes[i].name;
1510 if (Country_Codes[i].code == code) {
1511 return Country_Codes[i].name;
1516 /********************************************
1517 returns a string describing the status or
1518 a "Error" if no such string exists
1519 *********************************************/
1520 const char *icq_ConvertStatus2Str(int status)
1522 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1525 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1526 switch (status & 0x01FF) {
1531 return "Do not disturb";
1536 case STATUS_OCCUPIED:
1540 return "Not available";
1542 case STATUS_INVISIBLE:
1545 case STATUS_INVISIBLE_2:
1546 return "Invisible mode 2";
1548 case STATUS_FREE_CHAT:
1549 return "Free for chat";
1557 void icq_InitNewUser(const char *hostname, DWORD port)
1559 srv_net_icq_pak pak;
1564 icq_Connect(hostname, port);
1565 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1566 printf("Couldn't establish connection\n");
1569 icq_RegNewUser((ThisICQ->icq_Password));
1572 tv.tv_usec = 500000;
1575 FD_SET((ThisICQ->icq_Sok), &readfds);
1577 /* don't care about writefds and exceptfds: */
1578 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1580 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1581 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1582 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1583 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1590 /********************************************
1591 Converts an intel endian character sequence to
1593 *********************************************/
1594 DWORD Chars_2_DW(unsigned char *buf)
1609 /********************************************
1610 Converts an intel endian character sequence to
1612 *********************************************/
1613 WORD Chars_2_Word(unsigned char *buf)
1624 /********************************************
1626 an intel endian character sequence
1627 *********************************************/
1628 void DW_2_Chars(unsigned char *buf, DWORD num)
1630 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1631 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1632 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1633 buf[0] = (unsigned char) (num) & 0x000000FF;
1636 /********************************************
1638 an intel endian character sequence
1639 *********************************************/
1640 void Word_2_Chars(unsigned char *buf, WORD num)
1642 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1643 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1646 /***************************
1647 ContactList functions
1648 ***************************/
1649 void icq_ContAddUser(DWORD cuin)
1651 icq_ContactItem *p = malloc(sizeof(icq_ContactItem));
1652 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1655 p->vis_list = FALSE;
1661 (ThisICQ->icq_ContFirst) = p;
1664 void icq_ContDelUser(DWORD cuin)
1666 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1669 if (ptr->uin == cuin) {
1670 (ThisICQ->icq_ContFirst) = ptr->next;
1672 ptr = (ThisICQ->icq_ContFirst);
1675 if (ptr->next->uin == cuin) {
1676 ptr->next = ptr->next->next;
1683 void icq_ContClear()
1685 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1690 (ThisICQ->icq_ContFirst) = ptr;
1694 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1696 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1700 if (ptr->uin == cuin)
1707 icq_ContactItem *icq_ContGetFirst()
1709 return (ThisICQ->icq_ContFirst);
1712 void icq_ContSetVis(DWORD cuin)
1714 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1718 if (ptr->uin == cuin)
1719 ptr->vis_list = TRUE;
1724 /************************
1725 (ThisICQ->icq_ServMess) functions
1726 *************************/
1727 BOOL icq_GetServMess(WORD num)
1729 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1732 void icq_SetServMess(WORD num)
1734 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1739 return (ThisICQ->icq_Sok);
1742 int icq_GetProxySok()
1744 return (ThisICQ->icq_ProxySok);
1747 /*******************
1748 SOCKS5 Proxy support
1749 ********************/
1750 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1752 if ((ThisICQ->icq_ProxyHost))
1753 free((ThisICQ->icq_ProxyHost));
1754 if ((ThisICQ->icq_ProxyName))
1755 free((ThisICQ->icq_ProxyName));
1756 if ((ThisICQ->icq_ProxyPass))
1757 free((ThisICQ->icq_ProxyPass));
1758 if (strlen(pname) > 255) {
1759 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1760 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1761 ThisICQ->icq_UseProxy = 0;
1764 if (strlen(ppass) > 255) {
1765 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1766 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1767 (ThisICQ->icq_UseProxy) = 0;
1770 (ThisICQ->icq_UseProxy) = 1;
1771 (ThisICQ->icq_ProxyHost) = strdup(phost);
1772 (ThisICQ->icq_ProxyPort) = pport;
1773 (ThisICQ->icq_ProxyAuth) = pauth;
1774 (ThisICQ->icq_ProxyName) = strdup(pname);
1775 (ThisICQ->icq_ProxyPass) = strdup(ppass);
1778 void icq_UnsetProxy()
1780 ThisICQ->icq_UseProxy = 0;
1785 /***********************************************************************/
1786 /* icqlib stuff ends here, Citadel module stuff begins */
1787 /***********************************************************************/
1790 /* config file manipulation routines ... probably temporary until we can
1791 * get something more robust written
1795 void CtdlICQ_Config_Delete(char *key) {
1796 long readpos, writepos;
1797 char buf[256], keyplusspace[256];
1800 if (ThisICQ->icq_MyConfigFile == NULL) return;
1803 sprintf(keyplusspace, "%s ", key);
1806 fseek(ThisICQ->icq_MyConfigFile, readpos, 0);
1807 ptr = fgets(buf, 256, ThisICQ->icq_MyConfigFile);
1808 readpos = ftell(ThisICQ->icq_MyConfigFile);
1810 fflush(ThisICQ->icq_MyConfigFile);
1811 ftruncate(fileno(ThisICQ->icq_MyConfigFile), writepos);
1814 if (strncasecmp(buf, keyplusspace,
1815 strlen(keyplusspace))) {
1816 fseek(ThisICQ->icq_MyConfigFile, writepos, 0);
1817 fprintf(ThisICQ->icq_MyConfigFile, "%s", buf);
1818 writepos = ftell(ThisICQ->icq_MyConfigFile);
1826 void CtdlICQ_Config_Write(char *key, char *contents) {
1827 CtdlICQ_Config_Delete(key);
1828 fseek(ThisICQ->icq_MyConfigFile, 0L, SEEK_END);
1829 fprintf(ThisICQ->icq_MyConfigFile, "%s %s\n", key, contents);
1834 void CtdlICQ_Config_Read(char *contents, char *key) {
1837 strcpy(contents, "");
1838 rewind(ThisICQ->icq_MyConfigFile);
1839 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1840 buf[strlen(buf)-1]=0;
1841 if ( (!strncasecmp(buf, key, strlen(key)))
1842 && (isspace(buf[strlen(key)])) ) {
1843 strcpy(contents, &buf[strlen(key)+1]);
1850 * Refresh the contact list
1852 void CtdlICQ_Refresh_Contact_List(void) {
1856 if (ThisICQ->icq_Sok < 0) return;
1859 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1860 buf[strlen(buf)-1]=0;
1862 contact_uin = atol(buf);
1863 if (contact_uin > 0L) {
1864 icq_ContAddUser(contact_uin);
1865 icq_ContSetVis(contact_uin);
1868 icq_SendContactList();
1875 * Utility routine to logout and disconnect from the ICQ server if we happen
1876 * to already be connected
1878 void CtdlICQ_Logout_If_Connected(void) {
1879 if (ThisICQ->icq_Sok >= 0) {
1882 ThisICQ->icq_Sok = (-1);
1888 * If we have an ICQ uin/password on file for this user, go ahead and try
1889 * to log on to the ICQ server.
1891 void CtdlICQ_Login_If_Possible(void) {
1896 CtdlICQ_Logout_If_Connected();
1898 CtdlICQ_Config_Read(buf, "uin");
1900 CtdlICQ_Config_Read(pass, "pass");
1902 if ( (uin > 0L) && (strlen(pass)>0) ) {
1903 icq_Init(uin, pass);
1904 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
1905 icq_Login(STATUS_ONLINE);
1906 CtdlICQ_Refresh_Contact_List();
1913 /* This merely hooks icqlib's log function into Citadel's log function. */
1914 void CtdlICQlog(time_t time, unsigned char level, const char *str)
1916 lprintf(level, "ICQ: %s", str);
1921 * At the start of each Citadel server session, we have to allocate some
1922 * space for the Citadel-ICQ session for this thread. It'll be automatically
1923 * freed by the server when the session ends.
1925 void CtdlICQ_session_startup_hook(void)
1927 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
1928 icq_init_handle(ThisICQ);
1933 * icq_Main() needs to be called as frequently as possible. We'll do it
1934 * following the completion of each Citadel server command.
1937 void CtdlICQ_after_cmd_hook(void)
1939 if (ThisICQ->icq_Sok >= 0) {
1940 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
1942 ThisICQ->icq_LastKeepAlive = time(NULL);
1950 * There are a couple of things we absolutely need to make sure of when we
1951 * kill off the session. One of them is that the sockets are all closed --
1952 * otherwise we could have a nasty file descriptor leak.
1954 void CtdlICQ_session_logout_hook(void)
1956 CtdlICQ_Logout_If_Connected();
1957 if (ThisICQ->icq_MyConfigFile != NULL)
1958 fclose(ThisICQ->icq_MyConfigFile);
1964 void CtdlICQ_session_login_hook(void)
1967 if (ThisICQ->icq_MyConfigFile != NULL)
1968 fclose(ThisICQ->icq_MyConfigFile);
1970 /* Open this user's ICQ config file; create it if it's not there */
1971 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1972 ThisICQ->icq_MyConfigFile = fopen(buf, "r+");
1973 if (ThisICQ->icq_MyConfigFile == NULL)
1974 ThisICQ->icq_MyConfigFile = fopen(buf, "w+");
1975 if (ThisICQ->icq_MyConfigFile == NULL)
1976 lprintf(2, "Cannot create %s: %s\n", buf, strerror(errno));
1978 /* Login to the ICQ server if we already have info on file. */
1979 CtdlICQ_Login_If_Possible();
1983 void cmd_icqa(char *argbuf) {
1986 extract(cuin, argbuf, 0);
1987 if (atol(cuin) > 0L) {
1988 CtdlICQ_Config_Write(cuin, "");
1989 icq_SendInfoReq(atol(cuin));
1990 cprintf("%d %s added to your ICQ contact list.\n", OK, cuin);
1992 cprintf("%d You must specify a numeric ICQ uin.\n", ERROR);
1995 CtdlICQ_Refresh_Contact_List();
2001 void cmd_icql(char *argbuf)
2006 extract(uin, argbuf, 0);
2007 extract(password, argbuf, 1);
2009 CtdlICQ_Config_Write("uin", uin);
2010 CtdlICQ_Config_Write("pass", password);
2012 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2013 CtdlICQ_Login_If_Possible();
2018 * When deleting a user from the server, be sure to delete
2019 * his/her/its ICQ config file as well.
2021 void CtdlICQ_DeleteUserConfigFile(char *uname, long unum) {
2024 sprintf(buf, "icq/%ld", unum);
2031 * Here's what we have to do when an ICQ message arrives!
2033 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2034 BYTE day, BYTE month, WORD year,
2040 sprintf(from, "%ld", uin);
2041 CtdlICQ_Config_Read(nick, from);
2042 if (strlen(nick) == 0) {
2043 icq_SendInfoReq(atol(from));
2046 sprintf(from, "%ld@icq (%s)", uin, nick);
2047 if (CtdlSendExpressMessageFunc) {
2048 CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
2053 lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
2059 CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2060 const char *first, const char *last,
2061 const char *email, char auth) {
2065 sprintf(str_uin, "%ld", uin);
2066 CtdlICQ_Config_Write(str_uin, nick);
2071 struct DLModule_Info *Dynamic_Module_Init(void)
2073 /* Create a directory to store ICQ stuff in.
2074 * It's ok if the directory is already there.
2076 if (mkdir("icq", 0700) != 0) if (errno != EEXIST) {
2077 lprintf(2, "Can't create icq subdirectory: %s\n",
2081 /* Make sure we've got a valid ThisICQ for each session */
2082 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2084 /* Tell the Citadel server about our wonderful ICQ hooks */
2085 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2086 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2087 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2088 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2089 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2090 CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add an ICQ contact");
2091 CtdlRegisterUserHook(CtdlICQ_DeleteUserConfigFile, EVT_PURGEUSER);
2093 /* Tell the code formerly known as icqlib about our callbacks */
2094 icq_Log = CtdlICQlog;
2095 icq_RecvMessage = CtdlICQ_Incoming_Message;
2096 icq_InfoReply = CtdlICQ_InfoReply;