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.2 1999/07/23 04:27:45 ajc
12 Added CtdlWriteObject() to store generic data in the msgbase
14 Revision 1.1 1999/07/19 04:12:49 ajc
15 * serv_icq.c, serv_icq.mk: added (separate makefile is temporary)
17 Revision 1.16 1998/12/08 16:00:59 denis
18 Cleaned up a little before releasing
20 Revision 1.15 1998/11/25 19:18:16 denis
21 Added close icq_ProxySok in icq_Disconnect
23 Revision 1.14 1998/11/25 09:48:49 denis
24 icq_GetProxySok and icq_HandleProxyResponse methods added
25 Connection terminated support added
27 Revision 1.13 1998/11/19 12:22:48 denis
28 SOCKS support cleaned a little
29 icq_RecvUrl renamed to icq_RecvURL
30 icq_ProxyAuth added for Username/Password Authentication
31 URL/Description order inverted
32 icq_Quit splitted to icq_Logout and icq_Disconnect
33 icq_ProxyName and icq_ProxyPass range checking added
35 Revision 1.12 1998/11/18 16:21:29 denis
36 Fixed SOCKS5 proxy support
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
59 #include "dynloader.h"
61 #include "citserver.h"
63 struct ctdl_icq_handle {
66 BYTE icq_ServMess[8192];
71 icq_ContactItem *icq_ContFirst;
82 DWORD icq_ProxyDestHost;
83 WORD icq_ProxyDestPort;
84 WORD icq_ProxyOurPort;
85 FILE *icq_MyConfigFile; /* ig */
86 time_t icq_LastKeepAlive; /* ig */
90 #define ICQROOM "My ICQ Config"
91 #define ICQMIME "application/x-citadel-icq"
92 unsigned long SYM_CTDL_ICQ;
93 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
94 extern struct CitContext *ContextList;
95 #define MODULE_NAME "ICQ metaclient"
96 #define MODULE_AUTHOR "Art Cancro"
97 #define MODULE_EMAIL "ajc@uncnsrd.mt-kisco.ny.us"
98 #define MAJOR_VERSION 0
99 #define MINOR_VERSION 1
101 static struct DLModule_Info info =
112 void (*icq_Logged) (void);
113 void (*icq_Disconnected) (void);
114 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
115 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
116 void (*icq_RecvAdded) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email);
117 void (*icq_RecvAuthReq) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email, const char *reason);
118 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
119 void (*icq_SearchDone) (void);
120 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
121 void (*icq_UserOffline) (DWORD uin);
122 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
123 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
124 void (*icq_ExtInfoReply) (DWORD uin, const char *city, WORD country_code, char country_stat, const char *state, WORD age, char gender, const char *phone, const char *hp, const char *about);
125 void (*icq_Log) (time_t time, unsigned char level, const char *str);
126 void (*icq_SrvAck) (WORD seq);
129 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
130 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
131 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
132 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
133 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
134 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
135 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
136 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
139 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
140 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
141 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
142 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
143 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
144 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
145 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
146 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
148 static COUNTRY_CODE Country_Codes[] =
163 {"American Samoa", 684},
178 {"Czech Republic", 42},
181 {"El Salvador", 503},
185 {"French Antilles", 596},
186 {"French Polynesia", 689},
193 {"Guantanomo Bay", 53},
207 {"Ivory Coast", 225},
215 {"Liechtenstein", 41},
227 {"Netherlands Antilles", 599},
228 {"New Caledonia", 687},
235 {"Papua New Guinea", 675},
245 {"Saudia Arabia", 966},
250 {"South Africa", 27},
261 {"United Arab Emirates", 971},
263 {"Vatican City", 39},
270 {"Not entered", 0xffff}};
272 void icq_init_handle(struct ctdl_icq_handle *i)
274 memset(i, 0, sizeof(struct ctdl_icq_handle));
275 i->icq_Russian = TRUE;
277 i->icq_OurIp = 0x0100007f;
278 i->icq_Status = STATUS_OFFLINE;
281 i->icq_MyConfigFile = NULL;
284 int icq_SockWrite(int sok, const void *buf, size_t count)
287 if (!(ThisICQ->icq_UseProxy))
288 return write(sok, buf, count);
290 tmpbuf[0] = 0; /* reserved */
291 tmpbuf[1] = 0; /* reserved */
292 tmpbuf[2] = 0; /* standalone packet */
293 tmpbuf[3] = 1; /* address type IP v4 */
294 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
295 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
296 memcpy(&tmpbuf[10], buf, count);
297 return write(sok, tmpbuf, count + 10) - 10;
301 int icq_SockRead(int sok, void *buf, size_t count)
305 if (!(ThisICQ->icq_UseProxy))
306 return read(sok, buf, count);
308 res = read(sok, tmpbuf, count + 10);
309 memcpy(buf, &tmpbuf[10], res - 10);
314 /****************************************
315 This must be called every 2 min.
316 so the server knows we're still alive.
317 JAVA client sends two different commands
319 *****************************************/
324 Word_2_Chars(pak.head.ver, ICQ_VER);
325 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
326 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
327 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
328 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
330 Word_2_Chars(pak.head.ver, ICQ_VER);
331 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
332 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
333 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
334 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
336 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
337 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
340 /**********************************
341 This must be called to remove
342 messages from the server
343 ***********************************/
344 void icq_SendGotMessages()
348 Word_2_Chars(pak.head.ver, ICQ_VER);
349 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
350 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
351 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
353 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
356 /*************************************
357 this sends over the contact list
358 *************************************/
359 void icq_SendContactList()
365 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
367 Word_2_Chars(pak.head.ver, ICQ_VER);
368 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
369 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
370 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
376 DW_2_Chars(tmp, ptr->uin);
381 pak.data[0] = num_used;
382 size = ((int) tmp - (int) pak.data);
383 size += sizeof(pak.head);
384 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
387 void icq_SendNewUser(unsigned long uin)
392 Word_2_Chars(pak.head.ver, ICQ_VER);
393 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
394 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
395 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
396 DW_2_Chars(pak.data, uin);
397 size = sizeof(pak.head) + 4;
398 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
401 /*************************************
402 this sends over the visible list
403 that allows certain users to see you
405 *************************************/
406 void icq_SendVisibleList()
412 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
414 Word_2_Chars(pak.head.ver, ICQ_VER);
415 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
416 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
417 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
424 DW_2_Chars(tmp, ptr->uin);
431 pak.data[0] = num_used;
432 size = ((int) tmp - (int) pak.data);
433 size += sizeof(pak.head);
434 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
438 /**************************************
439 This sends the second login command
440 this is necessary to finish logging in.
441 ***************************************/
442 void icq_SendLogin1()
446 Word_2_Chars(pak.head.ver, ICQ_VER);
447 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
448 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
449 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
451 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
454 /************************************************
455 This is called when a user goes offline
456 *************************************************/
457 void icq_HandleUserOffline(srv_net_icq_pak pak)
462 remote_uin = Chars_2_DW(pak.data);
463 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
464 sprintf(buf, "User %lu logged off\n", remote_uin);
465 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
468 (*icq_UserOffline) (remote_uin);
469 icq_AckSrv(Chars_2_Word(pak.head.seq));
472 void icq_HandleUserOnline(srv_net_icq_pak pak)
474 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
475 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
476 icq_ContactItem *ptr;
479 remote_uin = Chars_2_DW(pak.data);
480 new_status = Chars_2_DW(&pak.data[17]);
481 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
482 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
483 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
484 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
485 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
486 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
489 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
490 icq_AckSrv(Chars_2_Word(pak.head.seq));
493 void icq_Status_Update(srv_net_icq_pak pak)
495 unsigned long remote_uin, new_status;
498 remote_uin = Chars_2_DW(pak.data);
499 new_status = Chars_2_DW(&pak.data[4]);
500 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
501 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
502 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
504 if (icq_UserStatusUpdate)
505 (*icq_UserStatusUpdate) (remote_uin, new_status);
506 icq_AckSrv(Chars_2_Word(pak.head.seq));
509 /************************************
510 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
511 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
512 It does NOT wait for any kind of a response.
513 *************************************/
514 void icq_Login(DWORD status)
521 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
522 Word_2_Chars(pak.head.ver, ICQ_VER);
523 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
524 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
525 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
527 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
528 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
530 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
531 DW_2_Chars(s2.status, status);
532 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
534 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
535 s2.X2[0] = LOGIN_X2_DEF;
536 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
537 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
538 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
540 memcpy(pak.data, &s1, sizeof(s1));
542 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
543 size += Chars_2_Word(s1.len);
544 memcpy(&pak.data[size], &s2, sizeof(s2));
546 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
549 /*******************************
550 This routine sends the aknowlegement cmd to the
551 server it appears that this must be done after
552 everything the server sends us
553 *******************************/
554 void icq_AckSrv(int seq)
559 Word_2_Chars(pak.head.ver, ICQ_VER);
560 Word_2_Chars(pak.head.cmd, CMD_ACK);
561 Word_2_Chars(pak.head.seq, seq);
562 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
563 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
564 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
565 for (i = 0; i < 6; i++)
566 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
569 void icq_HandleInfoReply(srv_net_icq_pak pak)
571 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
575 seq = Chars_2_Word(pak.data);
576 uin = Chars_2_DW(&pak.data[2]);
577 len = Chars_2_Word(&pak.data[6]);
579 icq_RusConv("wk", ptr1);
580 tmp = &pak.data[8 + len];
581 len = Chars_2_Word(tmp);
583 icq_RusConv("wk", ptr2);
585 len = Chars_2_Word(tmp);
587 icq_RusConv("wk", ptr3);
589 len = Chars_2_Word(tmp);
591 icq_RusConv("wk", ptr4);
593 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
594 sprintf(buf, "Info reply for %lu\n", uin);
595 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
598 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
599 icq_AckSrv(Chars_2_Word(pak.head.seq));
602 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
604 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
608 char cnt_stat, gender, buf[256];
609 uin = Chars_2_DW(&pak.data[2]);
610 len = Chars_2_Word(&pak.data[6]);
612 icq_RusConv("wk", ptr1);
613 cnt_code = Chars_2_Word(&pak.data[8 + len]);
614 cnt_stat = pak.data[len + 10];
615 tmp = &pak.data[11 + len];
616 len = Chars_2_Word(tmp);
617 icq_RusConv("wk", tmp + 2);
619 age = Chars_2_Word(tmp + 2 + len);
620 gender = *(tmp + len + 4);
622 len = Chars_2_Word(tmp);
623 icq_RusConv("wk", tmp + 2);
626 len = Chars_2_Word(tmp);
627 icq_RusConv("wk", tmp + 2);
630 len = Chars_2_Word(tmp);
631 icq_RusConv("wk", tmp + 2);
633 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
634 sprintf(buf, "Extended info reply for %lu\n", uin);
635 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
637 if (icq_ExtInfoReply)
638 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
639 icq_AckSrv(Chars_2_Word(pak.head.seq));
642 void icq_HandleSearchReply(srv_net_icq_pak pak)
644 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
648 uin = Chars_2_DW(&pak.data[2]);
649 len = Chars_2_Word(&pak.data[6]);
651 icq_RusConv("wk", ptr1);
652 tmp = &pak.data[8 + len];
653 len = Chars_2_Word(tmp);
655 icq_RusConv("wk", ptr2);
657 len = Chars_2_Word(tmp);
659 icq_RusConv("wk", ptr3);
661 len = Chars_2_Word(tmp);
663 icq_RusConv("wk", ptr4);
665 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
666 sprintf(buf, "User found %lu, Nick: %s, First Name: %s, Last Name: %s, EMail: %s, Auth: %s\n", uin, ptr1, ptr2, ptr3, ptr4, *tmp == 1 ? "no" : "yes");
667 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
670 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
671 icq_AckSrv(Chars_2_Word(pak.head.seq));
674 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
677 char *ptr1, *ptr2, *ptr3, *ptr4;
681 case USER_ADDED_MESS:
682 tmp = strchr(data, '\xFE');
684 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
685 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
692 tmp = strchr(tmp, '\xFE');
694 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
695 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
699 icq_RusConv("wk", data);
703 tmp = strchr(tmp, '\xFE');
705 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
706 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
710 icq_RusConv("wk", data);
714 tmp = strchr(tmp, '\xFE');
716 icq_RusConv("wk", data);
717 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
718 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
719 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
720 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
723 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
726 tmp = strchr(data, '\xFE');
731 tmp = strchr(tmp, '\xFE');
733 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
734 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
738 icq_RusConv("wk", data);
742 tmp = strchr(tmp, '\xFE');
744 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
745 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
749 icq_RusConv("wk", data);
753 tmp = strchr(tmp, '\xFE');
755 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
756 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
760 icq_RusConv("wk", data);
764 tmp = strchr(tmp, '\xFE');
766 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
767 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
773 tmp = strchr(tmp, '\x00');
775 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
776 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
780 icq_RusConv("wk", data);
781 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
782 sprintf(buf, "%lu has requested your authorization to be added to "
783 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
784 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
785 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
788 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
791 tmp = strchr(data, '\xFE');
793 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
794 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
798 icq_RusConv("wk", data);
802 icq_RusConv("wk", data);
803 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
804 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
805 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
808 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
811 icq_RusConv("wk", data);
812 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
813 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
814 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
817 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
821 /**********************************
822 Connects to hostname on port port
823 hostname can be DNS or nnn.nnn.nnn.nnn
824 write out messages to the FD aux
825 ***********************************/
826 int icq_Connect(const char *hostname, int port)
828 char buf[1024], un = 1;
829 char our_host[256], tmpbuf[256];
830 int conct, length, res;
831 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
832 struct hostent *host_struct; /* used in DNS llokup */
834 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
835 if ((ThisICQ->icq_Sok) == -1) {
836 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
837 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
840 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
841 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
842 sin.sin_addr.s_addr = INADDR_ANY;
843 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
845 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
846 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
847 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
850 length = sizeof(sin);
851 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
852 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
853 if ((ThisICQ->icq_UseProxy)) {
854 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
855 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
856 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
857 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
858 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
859 if (host_struct == 0L) {
860 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
861 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
862 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
866 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
868 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
869 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
870 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
871 if ((ThisICQ->icq_ProxySok) == -1) {
872 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
873 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
876 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
877 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
878 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
879 if (conct == -1) { /* did we connect ? */
880 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
881 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
884 buf[0] = 5; /* protocol version */
885 buf[1] = 1; /* number of methods */
886 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
887 buf[2] = 0; /* no authorization required */
889 buf[2] = 2; /* method username/password */
890 write((ThisICQ->icq_ProxySok), buf, 3);
891 res = read((ThisICQ->icq_ProxySok), buf, 2);
892 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
893 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
894 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
895 close((ThisICQ->icq_ProxySok));
898 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
899 buf[0] = 1; /* version of subnegotiation */
900 buf[1] = strlen((ThisICQ->icq_ProxyName));
901 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
902 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
903 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
904 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
905 res = read((ThisICQ->icq_ProxySok), buf, 2);
906 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
907 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
908 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
909 close((ThisICQ->icq_ProxySok));
913 buf[0] = 5; /* protocol version */
914 buf[1] = 3; /* command UDP associate */
915 buf[2] = 0; /* reserved */
916 buf[3] = 1; /* address type IP v4 */
921 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
922 write((ThisICQ->icq_ProxySok), buf, 10);
923 res = read((ThisICQ->icq_ProxySok), buf, 10);
924 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
927 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
928 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
931 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
932 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
935 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
936 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
939 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
940 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
943 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
944 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
947 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
948 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
951 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
952 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
955 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
956 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
959 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
960 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
963 close((ThisICQ->icq_ProxySok));
967 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
968 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
969 host_struct = gethostbyname(hostname);
970 if (host_struct == 0L) {
971 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
972 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
973 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
975 if ((ThisICQ->icq_UseProxy))
976 close((ThisICQ->icq_ProxySok));
979 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
981 if ((ThisICQ->icq_UseProxy)) {
982 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
983 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
985 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
986 sin.sin_port = htons(port); /* port */
987 if ((ThisICQ->icq_UseProxy)) {
988 (ThisICQ->icq_ProxyDestPort) = htons(port);
989 memcpy(&sin.sin_port, &buf[8], 2);
991 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
992 if (conct == -1) { /* did we connect ? */
993 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
994 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
995 if ((ThisICQ->icq_UseProxy))
996 close((ThisICQ->icq_ProxySok));
999 length = sizeof(sin);
1000 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
1001 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
1002 (ThisICQ->icq_OurPort) = sin.sin_port;
1003 return (ThisICQ->icq_Sok);
1006 void icq_HandleProxyResponse()
1010 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
1012 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1013 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
1014 if (icq_Disconnected)
1015 (*icq_Disconnected) ();
1016 SOCKCLOSE((ThisICQ->icq_Sok));
1017 SOCKCLOSE((ThisICQ->icq_ProxySok));
1021 /******************************************
1022 Handles packets that the server sends to us.
1023 *******************************************/
1024 void icq_HandleServerResponse()
1026 srv_net_icq_pak pak;
1027 SIMPLE_MESSAGE *s_mesg;
1028 RECV_MESSAGE *r_mesg;
1035 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1037 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1038 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1039 if (icq_Disconnected)
1040 (*icq_Disconnected) ();
1041 SOCKCLOSE((ThisICQ->icq_Sok));
1043 if ((icq_GetServMess(Chars_2_Word(pak.head.seq))) && (Chars_2_Word(pak.head.cmd) != SRV_NEW_UIN) && (Chars_2_Word(pak.head.cmd) != SRV_GO_AWAY)) {
1044 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1045 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1046 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1047 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1049 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1053 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1054 icq_SetServMess(Chars_2_Word(pak.head.seq));
1055 switch (Chars_2_Word(pak.head.cmd)) {
1057 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1058 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1060 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1063 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1064 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1065 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1068 case SRV_LOGIN_REPLY:
1069 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1070 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1071 sprintf(buf, "Login successful, UIN: %lu, IP: %u.%u.%u.%u\n", Chars_2_DW(pak.data), pak.data[4], pak.data[5], pak.data[6], pak.data[7]);
1072 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1074 icq_AckSrv(Chars_2_Word(pak.head.seq));
1076 icq_SendContactList();
1077 icq_SendVisibleList();
1081 case SRV_RECV_MESSAGE:
1082 r_mesg = (RECV_MESSAGE *) pak.data;
1083 icq_DoMsg(Chars_2_Word(r_mesg->type), Chars_2_Word(r_mesg->len), (char *) (r_mesg->len + 2), Chars_2_DW(r_mesg->uin), r_mesg->hour, r_mesg->minute, r_mesg->day, r_mesg->month, Chars_2_Word(r_mesg->year));
1084 icq_AckSrv(Chars_2_Word(pak.head.seq));
1086 case SRV_X1: /* unknown message sent after login */
1087 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1088 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1089 icq_AckSrv(Chars_2_Word(pak.head.seq));
1091 case SRV_X2: /* unknown message sent after login */
1092 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1093 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1094 icq_AckSrv(Chars_2_Word(pak.head.seq));
1095 icq_SendGotMessages();
1097 case SRV_INFO_REPLY:
1098 icq_HandleInfoReply(pak);
1100 case SRV_EXT_INFO_REPLY:
1101 icq_HandleExtInfoReply(pak);
1103 case SRV_USER_ONLINE:
1104 icq_HandleUserOnline(pak);
1106 case SRV_USER_OFFLINE:
1107 icq_HandleUserOffline(pak);
1110 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1111 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1112 icq_Login((ThisICQ->icq_Status));
1114 case SRV_STATUS_UPDATE:
1115 icq_Status_Update(pak);
1118 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1119 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1120 if (icq_Disconnected)
1121 (*icq_Disconnected) ();
1123 case SRV_END_OF_SEARCH:
1124 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1125 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1127 (*icq_SearchDone) ();
1128 icq_AckSrv(Chars_2_Word(pak.head.seq));
1130 case SRV_USER_FOUND:
1131 icq_HandleSearchReply(pak);
1133 case SRV_SYS_DELIVERED_MESS:
1134 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1135 cur_time = time(0L);
1136 tm_str = localtime(&cur_time);
1137 icq_DoMsg(Chars_2_Word(s_mesg->type), Chars_2_Word(s_mesg->len), (char *) (s_mesg->len + 2), Chars_2_DW(s_mesg->uin), tm_str->tm_hour, tm_str->tm_min, tm_str->tm_mday, tm_str->tm_mon + 1, tm_str->tm_year + 1900);
1138 icq_AckSrv(Chars_2_Word(pak.head.seq));
1140 default: /* commands we dont handle yet */
1141 len = s - (sizeof(pak.head));
1142 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1143 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1144 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1145 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1147 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1152 void icq_Init(DWORD uin, const char *password)
1154 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1155 (ThisICQ->icq_Uin) = uin;
1156 if ((ThisICQ->icq_Password))
1157 free((ThisICQ->icq_Password));
1158 (ThisICQ->icq_Password) = strdup(password);
1163 if ((ThisICQ->icq_Password))
1164 free((ThisICQ->icq_Password));
1167 /******************************
1168 Main function connects gets (ThisICQ->icq_Uin)
1169 and (ThisICQ->icq_Password) and logins in and sits
1170 in a loop waiting for server responses.
1171 *******************************/
1183 FD_SET((ThisICQ->icq_Sok), &readfds);
1184 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1185 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1186 icq_HandleServerResponse();
1190 } while (did_something);
1193 /********************************************************
1194 Russian language ICQ fix.
1195 Usual Windows ICQ users do use Windows 1251 encoding but
1196 unix users do use koi8 encoding, so we need to convert it.
1197 This function will convert string from windows 1251 to koi8
1198 or from koi8 to windows 1251.
1199 Andrew Frolov dron@ilm.net
1200 *********************************************************/
1201 void icq_RusConv(const char to[4], char *t_in)
1206 /* 6-17-1998 by Linux_Dude
1207 * Moved initialization of table out front of 'if' block to prevent compiler
1208 * warning. Improved error message, and now return without performing string
1209 * conversion to prevent addressing memory out of range (table pointer would
1210 * previously have remained uninitialized (= bad)).
1214 if (strcmp(to, "kw") == 0)
1216 else if (strcmp(to, "wk") != 0) {
1217 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1218 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1221 /* End Linux_Dude's changes ;) */
1223 if ((ThisICQ->icq_Russian)) {
1224 for (i = 0; t_in[i] != 0; i++) {
1227 t_in[i] = table[t_in[i] & 0177];
1232 /**************************************************
1233 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1235 ***************************************************/
1236 WORD icq_SendMessage(DWORD uin, const char *text)
1241 char buf[512]; /* message may be only 450 bytes long */
1243 strncpy(buf, text, 512);
1244 icq_RusConv("kw", buf);
1246 Word_2_Chars(pak.head.ver, ICQ_VER);
1247 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1248 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1249 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1250 DW_2_Chars(msg.uin, uin);
1251 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1252 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1253 memcpy(&pak.data, &msg, sizeof(msg));
1254 memcpy(&pak.data[8], buf, len + 1);
1255 size = sizeof(msg) + len + 1;
1256 for (i = 0; i < 6; i++)
1257 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1258 return (ThisICQ->icq_SeqNum) - 1;
1261 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1265 int size, len1, len2;
1266 char buf1[512], buf2[512];
1268 strncpy(buf1, descr, 512);
1269 strncpy(buf2, url, 512);
1270 /* Do we need to convert URL? */
1271 icq_RusConv("kw", buf2);
1272 len1 = strlen(buf1);
1273 len2 = strlen(buf2);
1274 Word_2_Chars(pak.head.ver, ICQ_VER);
1275 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1276 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1277 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1278 DW_2_Chars(msg.uin, uin);
1279 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1280 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1281 memcpy(&pak.data, &msg, sizeof(msg));
1282 memcpy(&pak.data[8], buf1, len1);
1283 pak.data[8 + len1] = 0xFE;
1284 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1285 size = sizeof(msg) + len1 + len2 + 2;
1286 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1287 return (ThisICQ->icq_SeqNum) - 1;
1290 /**************************************************
1291 Sends a authorization to the server so the Mirabilis
1292 client can add the user.
1293 ***************************************************/
1294 void icq_SendAuthMsg(DWORD uin)
1300 Word_2_Chars(pak.head.ver, ICQ_VER);
1301 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1302 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1303 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1304 DW_2_Chars(msg.uin, uin);
1305 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1306 Word_2_Chars(msg.len, 2);
1307 memcpy(&pak.data, &msg, sizeof(msg));
1308 pak.data[sizeof(msg)] = 0x03;
1309 pak.data[sizeof(msg) + 1] = 0x00;
1310 size = sizeof(msg) + 2;
1311 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1314 /**************************************************
1315 Changes the users status on the server
1316 ***************************************************/
1317 void icq_ChangeStatus(DWORD status)
1322 Word_2_Chars(pak.head.ver, ICQ_VER);
1323 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1324 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1325 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1326 DW_2_Chars(pak.data, status);
1327 (ThisICQ->icq_Status) = status;
1329 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1332 /**********************
1334 ***********************/
1340 Word_2_Chars(pak.head.ver, ICQ_VER);
1341 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1342 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1343 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1344 len = strlen("B_USER_DISCONNECTED") + 1;
1345 *(short *) pak.data = len;
1347 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1348 pak.data[2 + len] = 05;
1349 pak.data[3 + len] = 00;
1350 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1353 void icq_Disconnect()
1355 SOCKCLOSE((ThisICQ->icq_Sok));
1356 SOCKCLOSE((ThisICQ->icq_Sok));
1357 if ((ThisICQ->icq_UseProxy))
1358 SOCKCLOSE((ThisICQ->icq_ProxySok));
1361 /********************************************************
1362 Sends a request to the server for info on a specific user
1363 *********************************************************/
1364 WORD icq_SendInfoReq(DWORD uin)
1369 Word_2_Chars(pak.head.ver, ICQ_VER);
1370 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1371 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1372 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1373 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1374 DW_2_Chars(&pak.data[2], uin);
1376 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1377 return (ThisICQ->icq_SeqNum) - 1;
1380 /********************************************************
1381 Sends a request to the server for info on a specific user
1382 *********************************************************/
1383 WORD icq_SendExtInfoReq(DWORD uin)
1388 Word_2_Chars(pak.head.ver, ICQ_VER);
1389 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1390 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1391 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1392 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1393 DW_2_Chars(&pak.data[2], uin);
1395 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1396 return (ThisICQ->icq_SeqNum) - 1;
1399 /**************************************************************
1400 Initializes a server search for the information specified
1401 ***************************************************************/
1402 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1407 Word_2_Chars(pak.head.ver, ICQ_VER);
1408 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1409 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1410 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1411 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1413 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1415 strcpy(pak.data + size, nick);
1416 size += strlen(nick) + 1;
1417 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1419 strcpy(pak.data + size, first);
1420 size += strlen(first) + 1;
1421 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1423 strcpy(pak.data + size, last);
1424 size += strlen(last) + 1;
1425 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1427 strcpy(pak.data + size, email);
1428 size += strlen(email) + 1;
1429 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1432 /**************************************************************
1433 Initializes a server search for the information specified
1434 ***************************************************************/
1435 void icq_SendSearchUINReq(DWORD uin)
1440 Word_2_Chars(pak.head.ver, ICQ_VER);
1441 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1442 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1443 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1444 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1446 DW_2_Chars(&pak.data[size], uin);
1448 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1451 /**************************************************
1452 Registers a new uin in the ICQ network
1453 ***************************************************/
1454 void icq_RegNewUser(const char *pass)
1456 srv_net_icq_pak pak;
1461 Word_2_Chars(pak.head.ver, ICQ_VER);
1462 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1463 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1464 Word_2_Chars(len_buf, len);
1465 memcpy(&pak.data, "\x02\x00", 2);
1466 memcpy(&pak.data[2], &len_buf, 2);
1467 memcpy(&pak.data[4], pass, len + 1);
1468 DW_2_Chars(&pak.data[4 + len], 0x0072);
1469 DW_2_Chars(&pak.data[8 + len], 0x0000);
1471 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1474 void icq_UpdateUserInfo(USER_INFO * user)
1479 Word_2_Chars(pak.head.ver, ICQ_VER);
1480 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1481 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1482 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1483 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1485 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1487 strcpy(pak.data + size, user->nick);
1488 size += strlen(user->nick) + 1;
1489 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1491 strcpy(pak.data + size, user->first);
1492 size += strlen(user->first) + 1;
1493 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1495 strcpy(pak.data + size, user->last);
1496 size += strlen(user->last) + 1;
1497 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1499 strcpy(pak.data + size, user->email);
1500 size += strlen(user->email) + 1;
1501 pak.data[size] = user->auth;
1503 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1506 const char *icq_GetCountryName(int code)
1510 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1511 if (Country_Codes[i].code == code) {
1512 return Country_Codes[i].name;
1515 if (Country_Codes[i].code == code) {
1516 return Country_Codes[i].name;
1521 /********************************************
1522 returns a string describing the status or
1523 a "Error" if no such string exists
1524 *********************************************/
1525 const char *icq_ConvertStatus2Str(int status)
1527 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1530 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1531 switch (status & 0x01FF) {
1536 return "Do not disturb";
1541 case STATUS_OCCUPIED:
1545 return "Not available";
1547 case STATUS_INVISIBLE:
1550 case STATUS_INVISIBLE_2:
1551 return "Invisible mode 2";
1553 case STATUS_FREE_CHAT:
1554 return "Free for chat";
1562 void icq_InitNewUser(const char *hostname, DWORD port)
1564 srv_net_icq_pak pak;
1569 icq_Connect(hostname, port);
1570 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1571 printf("Couldn't establish connection\n");
1574 icq_RegNewUser((ThisICQ->icq_Password));
1577 tv.tv_usec = 500000;
1580 FD_SET((ThisICQ->icq_Sok), &readfds);
1582 /* don't care about writefds and exceptfds: */
1583 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1585 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1586 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1587 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1588 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1595 /********************************************
1596 Converts an intel endian character sequence to
1598 *********************************************/
1599 DWORD Chars_2_DW(unsigned char *buf)
1614 /********************************************
1615 Converts an intel endian character sequence to
1617 *********************************************/
1618 WORD Chars_2_Word(unsigned char *buf)
1629 /********************************************
1631 an intel endian character sequence
1632 *********************************************/
1633 void DW_2_Chars(unsigned char *buf, DWORD num)
1635 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1636 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1637 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1638 buf[0] = (unsigned char) (num) & 0x000000FF;
1641 /********************************************
1643 an intel endian character sequence
1644 *********************************************/
1645 void Word_2_Chars(unsigned char *buf, WORD num)
1647 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1648 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1651 /***************************
1652 ContactList functions
1653 ***************************/
1654 void icq_ContAddUser(DWORD cuin)
1656 icq_ContactItem *p = malloc(sizeof(icq_ContactItem));
1657 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1660 p->vis_list = FALSE;
1666 (ThisICQ->icq_ContFirst) = p;
1669 void icq_ContDelUser(DWORD cuin)
1671 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1674 if (ptr->uin == cuin) {
1675 (ThisICQ->icq_ContFirst) = ptr->next;
1677 ptr = (ThisICQ->icq_ContFirst);
1680 if (ptr->next->uin == cuin) {
1681 ptr->next = ptr->next->next;
1688 void icq_ContClear()
1690 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1695 (ThisICQ->icq_ContFirst) = ptr;
1699 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1701 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1705 if (ptr->uin == cuin)
1712 icq_ContactItem *icq_ContGetFirst()
1714 return (ThisICQ->icq_ContFirst);
1717 void icq_ContSetVis(DWORD cuin)
1719 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1723 if (ptr->uin == cuin)
1724 ptr->vis_list = TRUE;
1729 /************************
1730 (ThisICQ->icq_ServMess) functions
1731 *************************/
1732 BOOL icq_GetServMess(WORD num)
1734 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1737 void icq_SetServMess(WORD num)
1739 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1744 return (ThisICQ->icq_Sok);
1747 int icq_GetProxySok()
1749 return (ThisICQ->icq_ProxySok);
1752 /*******************
1753 SOCKS5 Proxy support
1754 ********************/
1755 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1757 if ((ThisICQ->icq_ProxyHost))
1758 free((ThisICQ->icq_ProxyHost));
1759 if ((ThisICQ->icq_ProxyName))
1760 free((ThisICQ->icq_ProxyName));
1761 if ((ThisICQ->icq_ProxyPass))
1762 free((ThisICQ->icq_ProxyPass));
1763 if (strlen(pname) > 255) {
1764 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1765 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1766 ThisICQ->icq_UseProxy = 0;
1769 if (strlen(ppass) > 255) {
1770 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1771 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1772 (ThisICQ->icq_UseProxy) = 0;
1775 (ThisICQ->icq_UseProxy) = 1;
1776 (ThisICQ->icq_ProxyHost) = strdup(phost);
1777 (ThisICQ->icq_ProxyPort) = pport;
1778 (ThisICQ->icq_ProxyAuth) = pauth;
1779 (ThisICQ->icq_ProxyName) = strdup(pname);
1780 (ThisICQ->icq_ProxyPass) = strdup(ppass);
1783 void icq_UnsetProxy()
1785 ThisICQ->icq_UseProxy = 0;
1790 /***********************************************************************/
1791 /* icqlib stuff ends here, Citadel module stuff begins */
1792 /***********************************************************************/
1795 /* config file manipulation routines ... probably temporary until we can
1796 * get something more robust written
1800 void CtdlICQ_Config_Delete(char *key) {
1801 long readpos, writepos;
1802 char buf[256], keyplusspace[256];
1805 if (ThisICQ->icq_MyConfigFile == NULL) return;
1808 sprintf(keyplusspace, "%s ", key);
1811 fseek(ThisICQ->icq_MyConfigFile, readpos, 0);
1812 ptr = fgets(buf, 256, ThisICQ->icq_MyConfigFile);
1813 readpos = ftell(ThisICQ->icq_MyConfigFile);
1815 fflush(ThisICQ->icq_MyConfigFile);
1816 ftruncate(fileno(ThisICQ->icq_MyConfigFile), writepos);
1819 if (strncasecmp(buf, keyplusspace,
1820 strlen(keyplusspace))) {
1821 fseek(ThisICQ->icq_MyConfigFile, writepos, 0);
1822 fprintf(ThisICQ->icq_MyConfigFile, "%s", buf);
1823 writepos = ftell(ThisICQ->icq_MyConfigFile);
1831 void CtdlICQ_Config_Write(char *key, char *contents) {
1833 char buf[256]; /* FIX */
1835 CtdlICQ_Config_Delete(key);
1836 fseek(ThisICQ->icq_MyConfigFile, 0L, SEEK_END);
1837 fprintf(ThisICQ->icq_MyConfigFile, "%s %s\n", key, contents);
1839 /****** FIX ****** TEMPORARY HACK TO SEE STUFF ***********/
1841 fflush(ThisICQ->icq_MyConfigFile);
1842 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1843 CtdlWriteObject(ICQROOM, ICQMIME, buf, 1, 0, 1);
1848 void CtdlICQ_Config_Read(char *contents, char *key) {
1851 strcpy(contents, "");
1852 rewind(ThisICQ->icq_MyConfigFile);
1853 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1854 buf[strlen(buf)-1]=0;
1855 if ( (!strncasecmp(buf, key, strlen(key)))
1856 && (isspace(buf[strlen(key)])) ) {
1857 strcpy(contents, &buf[strlen(key)+1]);
1864 * Refresh the contact list
1866 void CtdlICQ_Refresh_Contact_List(void) {
1870 if (ThisICQ->icq_Sok < 0) return;
1873 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1874 buf[strlen(buf)-1]=0;
1876 contact_uin = atol(buf);
1877 if (contact_uin > 0L) {
1878 icq_ContAddUser(contact_uin);
1879 icq_ContSetVis(contact_uin);
1882 icq_SendContactList();
1889 * Utility routine to logout and disconnect from the ICQ server if we happen
1890 * to already be connected
1892 void CtdlICQ_Logout_If_Connected(void) {
1893 if (ThisICQ->icq_Sok >= 0) {
1896 ThisICQ->icq_Sok = (-1);
1902 * If we have an ICQ uin/password on file for this user, go ahead and try
1903 * to log on to the ICQ server.
1905 void CtdlICQ_Login_If_Possible(void) {
1910 CtdlICQ_Logout_If_Connected();
1912 CtdlICQ_Config_Read(buf, "uin");
1914 CtdlICQ_Config_Read(pass, "pass");
1916 if ( (uin > 0L) && (strlen(pass)>0) ) {
1917 icq_Init(uin, pass);
1918 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
1919 icq_Login(STATUS_ONLINE);
1920 CtdlICQ_Refresh_Contact_List();
1927 /* This merely hooks icqlib's log function into Citadel's log function. */
1928 void CtdlICQlog(time_t time, unsigned char level, const char *str)
1930 lprintf(level, "ICQ: %s", str);
1935 * At the start of each Citadel server session, we have to allocate some
1936 * space for the Citadel-ICQ session for this thread. It'll be automatically
1937 * freed by the server when the session ends.
1939 void CtdlICQ_session_startup_hook(void)
1941 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
1942 icq_init_handle(ThisICQ);
1947 * icq_Main() needs to be called as frequently as possible. We'll do it
1948 * following the completion of each Citadel server command.
1951 void CtdlICQ_after_cmd_hook(void)
1953 if (ThisICQ->icq_Sok >= 0) {
1954 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
1956 ThisICQ->icq_LastKeepAlive = time(NULL);
1964 * There are a couple of things we absolutely need to make sure of when we
1965 * kill off the session. One of them is that the sockets are all closed --
1966 * otherwise we could have a nasty file descriptor leak.
1968 void CtdlICQ_session_logout_hook(void)
1970 CtdlICQ_Logout_If_Connected();
1971 if (ThisICQ->icq_MyConfigFile != NULL)
1972 fclose(ThisICQ->icq_MyConfigFile);
1978 void CtdlICQ_session_login_hook(void)
1981 if (ThisICQ->icq_MyConfigFile != NULL)
1982 fclose(ThisICQ->icq_MyConfigFile);
1984 /* Open this user's ICQ config file; create it if it's not there */
1985 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1986 ThisICQ->icq_MyConfigFile = fopen(buf, "r+");
1987 if (ThisICQ->icq_MyConfigFile == NULL)
1988 ThisICQ->icq_MyConfigFile = fopen(buf, "w+");
1989 if (ThisICQ->icq_MyConfigFile == NULL)
1990 lprintf(2, "Cannot create %s: %s\n", buf, strerror(errno));
1992 /* Login to the ICQ server if we already have info on file. */
1993 CtdlICQ_Login_If_Possible();
1997 void cmd_icqa(char *argbuf) {
2000 extract(cuin, argbuf, 0);
2001 if (atol(cuin) > 0L) {
2002 CtdlICQ_Config_Write(cuin, "");
2003 icq_SendInfoReq(atol(cuin));
2004 cprintf("%d %s added to your ICQ contact list.\n", OK, cuin);
2006 cprintf("%d You must specify a numeric ICQ uin.\n", ERROR);
2009 CtdlICQ_Refresh_Contact_List();
2015 void cmd_icql(char *argbuf)
2020 extract(uin, argbuf, 0);
2021 extract(password, argbuf, 1);
2023 CtdlICQ_Config_Write("uin", uin);
2024 CtdlICQ_Config_Write("pass", password);
2026 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2027 CtdlICQ_Login_If_Possible();
2032 * When deleting a user from the server, be sure to delete
2033 * his/her/its ICQ config file as well.
2035 void CtdlICQ_DeleteUserConfigFile(char *uname, long unum) {
2038 sprintf(buf, "icq/%ld", unum);
2045 * Here's what we have to do when an ICQ message arrives!
2047 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2048 BYTE day, BYTE month, WORD year,
2054 sprintf(from, "%ld", uin);
2055 CtdlICQ_Config_Read(nick, from);
2056 if (strlen(nick) == 0) {
2057 icq_SendInfoReq(atol(from));
2060 sprintf(from, "%ld@icq (%s)", uin, nick);
2061 if (CtdlSendExpressMessageFunc) {
2062 CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
2067 lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
2073 CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2074 const char *first, const char *last,
2075 const char *email, char auth) {
2079 sprintf(str_uin, "%ld", uin);
2080 CtdlICQ_Config_Write(str_uin, nick);
2085 struct DLModule_Info *Dynamic_Module_Init(void)
2087 /* Create a directory to store ICQ stuff in.
2088 * It's ok if the directory is already there.
2090 if (mkdir("icq", 0700) != 0) if (errno != EEXIST) {
2091 lprintf(2, "Can't create icq subdirectory: %s\n",
2095 /* Make sure we've got a valid ThisICQ for each session */
2096 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2098 /* Tell the Citadel server about our wonderful ICQ hooks */
2099 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2100 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2101 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2102 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2103 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2104 CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add an ICQ contact");
2105 CtdlRegisterUserHook(CtdlICQ_DeleteUserConfigFile, EVT_PURGEUSER);
2107 /* Tell the code formerly known as icqlib about our callbacks */
2108 icq_Log = CtdlICQlog;
2109 icq_RecvMessage = CtdlICQ_Incoming_Message;
2110 icq_InfoReply = CtdlICQ_InfoReply;