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.
9 * Incomplete list of changes I made:
10 * * All globals placed into struct ctdl_icq_handle so we can do it per-thread
11 * * References to globals changed to ThisICQ->globalname
12 * * malloc->mallok, free->phree, strdup->strdoop, for memory leak checking
13 * * Added a bunch of #include's needed by Citadel
14 * * Most of the Citadel-specific code is appended to the end
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
40 #include "dynloader.h"
42 #include "citserver.h"
46 * Contact list in memory
55 struct ctdl_icq_handle {
58 BYTE icq_ServMess[8192];
63 icq_ContactItem *icq_ContFirst;
74 DWORD icq_ProxyDestHost;
75 WORD icq_ProxyDestPort;
76 WORD icq_ProxyOurPort;
77 time_t icq_LastKeepAlive; /* ig */
78 char icq_config[256]; /* ig */
79 struct CtdlICQ_CL *icq_cl; /* ig */
80 int icq_numcl; /* ig */
85 /* ICQROOM is the name of the room in which each user's ICQ configuration
86 * and contact lists will be stored. (It's a personal room.)
88 #define ICQROOM "My ICQ Config"
90 /* MIME types to use for storing ICQ stuff */
91 #define ICQMIME "application/x-citadel-icq" /* configuration */
92 #define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
94 /* Citadel server TSD symbol for use by serv_icq */
95 unsigned long SYM_CTDL_ICQ;
96 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
98 extern struct CitContext *ContextList;
103 void (*icq_Logged) (void);
104 void (*icq_Disconnected) (void);
105 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
106 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
107 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);
108 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);
109 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
110 void (*icq_SearchDone) (void);
111 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
112 void (*icq_UserOffline) (DWORD uin);
113 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
114 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
115 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);
116 void (*icq_Log) (time_t time, unsigned char level, const char *str);
117 void (*icq_SrvAck) (WORD seq);
120 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
121 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
122 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
123 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
124 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
125 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
126 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
127 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
130 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
131 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
132 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
133 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
134 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
135 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
136 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
137 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
139 static COUNTRY_CODE Country_Codes[] =
154 {"American Samoa", 684},
169 {"Czech Republic", 42},
172 {"El Salvador", 503},
176 {"French Antilles", 596},
177 {"French Polynesia", 689},
184 {"Guantanomo Bay", 53},
198 {"Ivory Coast", 225},
206 {"Liechtenstein", 41},
218 {"Netherlands Antilles", 599},
219 {"New Caledonia", 687},
226 {"Papua New Guinea", 675},
236 {"Saudia Arabia", 966},
241 {"South Africa", 27},
252 {"United Arab Emirates", 971},
254 {"Vatican City", 39},
261 {"Not entered", 0xffff}};
263 void icq_init_handle(struct ctdl_icq_handle *i)
265 memset(i, 0, sizeof(struct ctdl_icq_handle));
266 i->icq_Russian = TRUE;
268 i->icq_OurIp = 0x0100007f;
269 i->icq_Status = STATUS_OFFLINE;
274 int icq_SockWrite(int sok, const void *buf, size_t count)
277 if (!(ThisICQ->icq_UseProxy))
278 return write(sok, buf, count);
280 tmpbuf[0] = 0; /* reserved */
281 tmpbuf[1] = 0; /* reserved */
282 tmpbuf[2] = 0; /* standalone packet */
283 tmpbuf[3] = 1; /* address type IP v4 */
284 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
285 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
286 memcpy(&tmpbuf[10], buf, count);
287 return write(sok, tmpbuf, count + 10) - 10;
291 int icq_SockRead(int sok, void *buf, size_t count)
295 if (!(ThisICQ->icq_UseProxy))
296 return read(sok, buf, count);
298 res = read(sok, tmpbuf, count + 10);
299 memcpy(buf, &tmpbuf[10], res - 10);
304 /****************************************
305 This must be called every 2 min.
306 so the server knows we're still alive.
307 JAVA client sends two different commands
309 *****************************************/
314 Word_2_Chars(pak.head.ver, ICQ_VER);
315 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
316 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
317 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
318 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
320 Word_2_Chars(pak.head.ver, ICQ_VER);
321 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
322 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
323 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
324 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
326 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
327 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
330 /**********************************
331 This must be called to remove
332 messages from the server
333 ***********************************/
334 void icq_SendGotMessages()
338 Word_2_Chars(pak.head.ver, ICQ_VER);
339 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
340 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
341 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
343 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
346 /*************************************
347 this sends over the contact list
348 *************************************/
349 void icq_SendContactList()
355 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
357 Word_2_Chars(pak.head.ver, ICQ_VER);
358 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
359 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
360 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
366 DW_2_Chars(tmp, ptr->uin);
371 pak.data[0] = num_used;
372 size = ((int) tmp - (int) pak.data);
373 size += sizeof(pak.head);
374 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
377 void icq_SendNewUser(unsigned long uin)
382 Word_2_Chars(pak.head.ver, ICQ_VER);
383 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
384 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
385 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
386 DW_2_Chars(pak.data, uin);
387 size = sizeof(pak.head) + 4;
388 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
391 /*************************************
392 this sends over the visible list
393 that allows certain users to see you
395 *************************************/
396 void icq_SendVisibleList()
402 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
404 Word_2_Chars(pak.head.ver, ICQ_VER);
405 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
406 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
407 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
414 DW_2_Chars(tmp, ptr->uin);
421 pak.data[0] = num_used;
422 size = ((int) tmp - (int) pak.data);
423 size += sizeof(pak.head);
424 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
428 /**************************************
429 This sends the second login command
430 this is necessary to finish logging in.
431 ***************************************/
432 void icq_SendLogin1()
436 Word_2_Chars(pak.head.ver, ICQ_VER);
437 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
438 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
439 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
441 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
444 /************************************************
445 This is called when a user goes offline
446 *************************************************/
447 void icq_HandleUserOffline(srv_net_icq_pak pak)
452 remote_uin = Chars_2_DW(pak.data);
453 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
454 sprintf(buf, "User %lu logged off\n", remote_uin);
455 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
458 (*icq_UserOffline) (remote_uin);
459 icq_AckSrv(Chars_2_Word(pak.head.seq));
462 void icq_HandleUserOnline(srv_net_icq_pak pak)
464 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
465 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
466 icq_ContactItem *ptr;
469 remote_uin = Chars_2_DW(pak.data);
470 new_status = Chars_2_DW(&pak.data[17]);
471 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
472 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
473 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
474 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
475 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
476 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
479 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
480 icq_AckSrv(Chars_2_Word(pak.head.seq));
483 void icq_Status_Update(srv_net_icq_pak pak)
485 unsigned long remote_uin, new_status;
488 remote_uin = Chars_2_DW(pak.data);
489 new_status = Chars_2_DW(&pak.data[4]);
490 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
491 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
492 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
494 if (icq_UserStatusUpdate)
495 (*icq_UserStatusUpdate) (remote_uin, new_status);
496 icq_AckSrv(Chars_2_Word(pak.head.seq));
499 /************************************
500 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
501 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
502 It does NOT wait for any kind of a response.
503 *************************************/
504 void icq_Login(DWORD status)
511 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
512 Word_2_Chars(pak.head.ver, ICQ_VER);
513 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
514 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
515 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
517 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
518 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
520 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
521 DW_2_Chars(s2.status, status);
522 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
524 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
525 s2.X2[0] = LOGIN_X2_DEF;
526 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
527 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
528 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
530 memcpy(pak.data, &s1, sizeof(s1));
532 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
533 size += Chars_2_Word(s1.len);
534 memcpy(&pak.data[size], &s2, sizeof(s2));
536 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
539 /*******************************
540 This routine sends the aknowlegement cmd to the
541 server it appears that this must be done after
542 everything the server sends us
543 *******************************/
544 void icq_AckSrv(int seq)
549 Word_2_Chars(pak.head.ver, ICQ_VER);
550 Word_2_Chars(pak.head.cmd, CMD_ACK);
551 Word_2_Chars(pak.head.seq, seq);
552 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
553 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
554 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
555 for (i = 0; i < 6; i++)
556 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
559 void icq_HandleInfoReply(srv_net_icq_pak pak)
561 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
565 seq = Chars_2_Word(pak.data);
566 uin = Chars_2_DW(&pak.data[2]);
567 len = Chars_2_Word(&pak.data[6]);
569 icq_RusConv("wk", ptr1);
570 tmp = &pak.data[8 + len];
571 len = Chars_2_Word(tmp);
573 icq_RusConv("wk", ptr2);
575 len = Chars_2_Word(tmp);
577 icq_RusConv("wk", ptr3);
579 len = Chars_2_Word(tmp);
581 icq_RusConv("wk", ptr4);
583 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
584 sprintf(buf, "Info reply for %lu\n", uin);
585 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
588 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
589 icq_AckSrv(Chars_2_Word(pak.head.seq));
592 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
594 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
598 char cnt_stat, gender, buf[256];
599 uin = Chars_2_DW(&pak.data[2]);
600 len = Chars_2_Word(&pak.data[6]);
602 icq_RusConv("wk", ptr1);
603 cnt_code = Chars_2_Word(&pak.data[8 + len]);
604 cnt_stat = pak.data[len + 10];
605 tmp = &pak.data[11 + len];
606 len = Chars_2_Word(tmp);
607 icq_RusConv("wk", tmp + 2);
609 age = Chars_2_Word(tmp + 2 + len);
610 gender = *(tmp + len + 4);
612 len = Chars_2_Word(tmp);
613 icq_RusConv("wk", tmp + 2);
616 len = Chars_2_Word(tmp);
617 icq_RusConv("wk", tmp + 2);
620 len = Chars_2_Word(tmp);
621 icq_RusConv("wk", tmp + 2);
623 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
624 sprintf(buf, "Extended info reply for %lu\n", uin);
625 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
627 if (icq_ExtInfoReply)
628 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
629 icq_AckSrv(Chars_2_Word(pak.head.seq));
632 void icq_HandleSearchReply(srv_net_icq_pak pak)
634 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
638 uin = Chars_2_DW(&pak.data[2]);
639 len = Chars_2_Word(&pak.data[6]);
641 icq_RusConv("wk", ptr1);
642 tmp = &pak.data[8 + len];
643 len = Chars_2_Word(tmp);
645 icq_RusConv("wk", ptr2);
647 len = Chars_2_Word(tmp);
649 icq_RusConv("wk", ptr3);
651 len = Chars_2_Word(tmp);
653 icq_RusConv("wk", ptr4);
655 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
656 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");
657 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
660 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
661 icq_AckSrv(Chars_2_Word(pak.head.seq));
664 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
667 char *ptr1, *ptr2, *ptr3, *ptr4;
671 case USER_ADDED_MESS:
672 tmp = strchr(data, '\xFE');
674 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
675 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
682 tmp = strchr(tmp, '\xFE');
684 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
685 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
689 icq_RusConv("wk", data);
693 tmp = strchr(tmp, '\xFE');
695 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
696 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
700 icq_RusConv("wk", data);
704 tmp = strchr(tmp, '\xFE');
706 icq_RusConv("wk", data);
707 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
708 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
709 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
710 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
713 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
716 tmp = strchr(data, '\xFE');
721 tmp = strchr(tmp, '\xFE');
723 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
724 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
728 icq_RusConv("wk", data);
732 tmp = strchr(tmp, '\xFE');
734 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
735 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
739 icq_RusConv("wk", data);
743 tmp = strchr(tmp, '\xFE');
745 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
746 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
750 icq_RusConv("wk", data);
754 tmp = strchr(tmp, '\xFE');
756 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
757 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
763 tmp = strchr(tmp, '\x00');
765 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
766 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
770 icq_RusConv("wk", data);
771 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
772 sprintf(buf, "%lu has requested your authorization to be added to "
773 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
774 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
775 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
778 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
781 tmp = strchr(data, '\xFE');
783 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
784 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
788 icq_RusConv("wk", data);
792 icq_RusConv("wk", data);
793 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
794 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
795 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
798 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
801 icq_RusConv("wk", data);
802 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
803 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
804 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
807 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
811 /**********************************
812 Connects to hostname on port port
813 hostname can be DNS or nnn.nnn.nnn.nnn
814 write out messages to the FD aux
815 ***********************************/
816 int icq_Connect(const char *hostname, int port)
818 char buf[1024], un = 1;
819 char our_host[256], tmpbuf[256];
820 int conct, length, res;
821 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
822 struct hostent *host_struct; /* used in DNS llokup */
824 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
825 if ((ThisICQ->icq_Sok) == -1) {
826 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
827 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
830 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
831 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
832 sin.sin_addr.s_addr = INADDR_ANY;
833 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
835 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
836 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
837 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
840 length = sizeof(sin);
841 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
842 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
843 if ((ThisICQ->icq_UseProxy)) {
844 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
845 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
846 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
847 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
848 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
849 if (host_struct == 0L) {
850 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
851 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
852 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
856 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
858 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
859 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
860 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
861 if ((ThisICQ->icq_ProxySok) == -1) {
862 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
863 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
866 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
867 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
868 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
869 if (conct == -1) { /* did we connect ? */
870 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
871 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
874 buf[0] = 5; /* protocol version */
875 buf[1] = 1; /* number of methods */
876 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
877 buf[2] = 0; /* no authorization required */
879 buf[2] = 2; /* method username/password */
880 write((ThisICQ->icq_ProxySok), buf, 3);
881 res = read((ThisICQ->icq_ProxySok), buf, 2);
882 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
883 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
884 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
885 close((ThisICQ->icq_ProxySok));
888 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
889 buf[0] = 1; /* version of subnegotiation */
890 buf[1] = strlen((ThisICQ->icq_ProxyName));
891 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
892 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
893 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
894 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
895 res = read((ThisICQ->icq_ProxySok), buf, 2);
896 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
897 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
898 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
899 close((ThisICQ->icq_ProxySok));
903 buf[0] = 5; /* protocol version */
904 buf[1] = 3; /* command UDP associate */
905 buf[2] = 0; /* reserved */
906 buf[3] = 1; /* address type IP v4 */
911 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
912 write((ThisICQ->icq_ProxySok), buf, 10);
913 res = read((ThisICQ->icq_ProxySok), buf, 10);
914 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
917 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
918 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
921 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
922 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
925 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
926 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
929 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
930 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
933 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
934 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
937 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
938 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
941 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
942 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
945 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
946 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
949 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
950 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
953 close((ThisICQ->icq_ProxySok));
957 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
958 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
959 host_struct = gethostbyname(hostname);
960 if (host_struct == 0L) {
961 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
962 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
963 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
965 if ((ThisICQ->icq_UseProxy))
966 close((ThisICQ->icq_ProxySok));
969 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
971 if ((ThisICQ->icq_UseProxy)) {
972 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
973 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
975 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
976 sin.sin_port = htons(port); /* port */
977 if ((ThisICQ->icq_UseProxy)) {
978 (ThisICQ->icq_ProxyDestPort) = htons(port);
979 memcpy(&sin.sin_port, &buf[8], 2);
981 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
982 if (conct == -1) { /* did we connect ? */
983 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
984 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
985 if ((ThisICQ->icq_UseProxy))
986 close((ThisICQ->icq_ProxySok));
989 length = sizeof(sin);
990 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
991 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
992 (ThisICQ->icq_OurPort) = sin.sin_port;
993 return (ThisICQ->icq_Sok);
996 void icq_HandleProxyResponse()
1000 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
1002 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1003 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
1004 if (icq_Disconnected)
1005 (*icq_Disconnected) ();
1006 SOCKCLOSE((ThisICQ->icq_Sok));
1007 SOCKCLOSE((ThisICQ->icq_ProxySok));
1011 /******************************************
1012 Handles packets that the server sends to us.
1013 *******************************************/
1014 void icq_HandleServerResponse()
1016 srv_net_icq_pak pak;
1017 SIMPLE_MESSAGE *s_mesg;
1018 RECV_MESSAGE *r_mesg;
1025 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1027 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1028 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1029 if (icq_Disconnected)
1030 (*icq_Disconnected) ();
1031 SOCKCLOSE((ThisICQ->icq_Sok));
1033 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)) {
1034 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1035 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1036 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1037 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1039 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1043 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1044 icq_SetServMess(Chars_2_Word(pak.head.seq));
1045 switch (Chars_2_Word(pak.head.cmd)) {
1047 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1048 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1050 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1053 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1054 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1055 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1058 case SRV_LOGIN_REPLY:
1059 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1060 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1061 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]);
1062 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1064 icq_AckSrv(Chars_2_Word(pak.head.seq));
1066 icq_SendContactList();
1067 icq_SendVisibleList();
1071 case SRV_RECV_MESSAGE:
1072 r_mesg = (RECV_MESSAGE *) pak.data;
1073 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));
1074 icq_AckSrv(Chars_2_Word(pak.head.seq));
1076 case SRV_X1: /* unknown message sent after login */
1077 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1078 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1079 icq_AckSrv(Chars_2_Word(pak.head.seq));
1081 case SRV_X2: /* 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_X2 (Done old messages)\n");
1084 icq_AckSrv(Chars_2_Word(pak.head.seq));
1085 icq_SendGotMessages();
1087 case SRV_INFO_REPLY:
1088 icq_HandleInfoReply(pak);
1090 case SRV_EXT_INFO_REPLY:
1091 icq_HandleExtInfoReply(pak);
1093 case SRV_USER_ONLINE:
1094 icq_HandleUserOnline(pak);
1096 case SRV_USER_OFFLINE:
1097 icq_HandleUserOffline(pak);
1100 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1101 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1102 icq_Login((ThisICQ->icq_Status));
1104 case SRV_STATUS_UPDATE:
1105 icq_Status_Update(pak);
1108 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1109 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1110 if (icq_Disconnected)
1111 (*icq_Disconnected) ();
1113 case SRV_END_OF_SEARCH:
1114 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1115 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1117 (*icq_SearchDone) ();
1118 icq_AckSrv(Chars_2_Word(pak.head.seq));
1120 case SRV_USER_FOUND:
1121 icq_HandleSearchReply(pak);
1123 case SRV_SYS_DELIVERED_MESS:
1124 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1125 cur_time = time(0L);
1126 tm_str = localtime(&cur_time);
1127 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);
1128 icq_AckSrv(Chars_2_Word(pak.head.seq));
1130 default: /* commands we dont handle yet */
1131 len = s - (sizeof(pak.head));
1132 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1133 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1134 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1135 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1137 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1142 void icq_Init(DWORD uin, const char *password)
1144 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1145 (ThisICQ->icq_Uin) = uin;
1146 if ((ThisICQ->icq_Password))
1147 phree((ThisICQ->icq_Password));
1148 (ThisICQ->icq_Password) = strdoop(password);
1153 if ((ThisICQ->icq_Password))
1154 phree((ThisICQ->icq_Password));
1157 /******************************
1158 Main function connects gets (ThisICQ->icq_Uin)
1159 and (ThisICQ->icq_Password) and logins in and sits
1160 in a loop waiting for server responses.
1161 *******************************/
1173 FD_SET((ThisICQ->icq_Sok), &readfds);
1174 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1175 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1176 icq_HandleServerResponse();
1180 } while (did_something);
1183 /********************************************************
1184 Russian language ICQ fix.
1185 Usual Windows ICQ users do use Windows 1251 encoding but
1186 unix users do use koi8 encoding, so we need to convert it.
1187 This function will convert string from windows 1251 to koi8
1188 or from koi8 to windows 1251.
1189 Andrew Frolov dron@ilm.net
1190 *********************************************************/
1191 void icq_RusConv(const char to[4], char *t_in)
1196 /* 6-17-1998 by Linux_Dude
1197 * Moved initialization of table out front of 'if' block to prevent compiler
1198 * warning. Improved error message, and now return without performing string
1199 * conversion to prevent addressing memory out of range (table pointer would
1200 * previously have remained uninitialized (= bad)).
1204 if (strcmp(to, "kw") == 0)
1206 else if (strcmp(to, "wk") != 0) {
1207 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1208 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1211 /* End Linux_Dude's changes ;) */
1213 if ((ThisICQ->icq_Russian)) {
1214 for (i = 0; t_in[i] != 0; i++) {
1217 t_in[i] = table[t_in[i] & 0177];
1222 /**************************************************
1223 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1225 ***************************************************/
1226 WORD icq_SendMessage(DWORD uin, const char *text)
1231 char buf[512]; /* message may be only 450 bytes long */
1233 strncpy(buf, text, 512);
1234 icq_RusConv("kw", buf);
1236 Word_2_Chars(pak.head.ver, ICQ_VER);
1237 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1238 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1239 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1240 DW_2_Chars(msg.uin, uin);
1241 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1242 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1243 memcpy(&pak.data, &msg, sizeof(msg));
1244 memcpy(&pak.data[8], buf, len + 1);
1245 size = sizeof(msg) + len + 1;
1246 for (i = 0; i < 6; i++)
1247 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1248 return (ThisICQ->icq_SeqNum) - 1;
1251 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1255 int size, len1, len2;
1256 char buf1[512], buf2[512];
1258 strncpy(buf1, descr, 512);
1259 strncpy(buf2, url, 512);
1260 /* Do we need to convert URL? */
1261 icq_RusConv("kw", buf2);
1262 len1 = strlen(buf1);
1263 len2 = strlen(buf2);
1264 Word_2_Chars(pak.head.ver, ICQ_VER);
1265 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1266 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1267 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1268 DW_2_Chars(msg.uin, uin);
1269 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1270 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1271 memcpy(&pak.data, &msg, sizeof(msg));
1272 memcpy(&pak.data[8], buf1, len1);
1273 pak.data[8 + len1] = 0xFE;
1274 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1275 size = sizeof(msg) + len1 + len2 + 2;
1276 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1277 return (ThisICQ->icq_SeqNum) - 1;
1280 /**************************************************
1281 Sends a authorization to the server so the Mirabilis
1282 client can add the user.
1283 ***************************************************/
1284 void icq_SendAuthMsg(DWORD uin)
1290 Word_2_Chars(pak.head.ver, ICQ_VER);
1291 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1292 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1293 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1294 DW_2_Chars(msg.uin, uin);
1295 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1296 Word_2_Chars(msg.len, 2);
1297 memcpy(&pak.data, &msg, sizeof(msg));
1298 pak.data[sizeof(msg)] = 0x03;
1299 pak.data[sizeof(msg) + 1] = 0x00;
1300 size = sizeof(msg) + 2;
1301 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1304 /**************************************************
1305 Changes the users status on the server
1306 ***************************************************/
1307 void icq_ChangeStatus(DWORD status)
1312 Word_2_Chars(pak.head.ver, ICQ_VER);
1313 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1314 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1315 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1316 DW_2_Chars(pak.data, status);
1317 (ThisICQ->icq_Status) = status;
1319 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1322 /**********************
1324 ***********************/
1330 Word_2_Chars(pak.head.ver, ICQ_VER);
1331 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1332 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1333 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1334 len = strlen("B_USER_DISCONNECTED") + 1;
1335 *(short *) pak.data = len;
1337 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1338 pak.data[2 + len] = 05;
1339 pak.data[3 + len] = 00;
1340 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1343 void icq_Disconnect()
1345 SOCKCLOSE((ThisICQ->icq_Sok));
1346 SOCKCLOSE((ThisICQ->icq_Sok));
1347 if ((ThisICQ->icq_UseProxy))
1348 SOCKCLOSE((ThisICQ->icq_ProxySok));
1351 /********************************************************
1352 Sends a request to the server for info on a specific user
1353 *********************************************************/
1354 WORD icq_SendInfoReq(DWORD uin)
1359 Word_2_Chars(pak.head.ver, ICQ_VER);
1360 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1361 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1362 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1363 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1364 DW_2_Chars(&pak.data[2], uin);
1366 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1367 return (ThisICQ->icq_SeqNum) - 1;
1370 /********************************************************
1371 Sends a request to the server for info on a specific user
1372 *********************************************************/
1373 WORD icq_SendExtInfoReq(DWORD uin)
1378 Word_2_Chars(pak.head.ver, ICQ_VER);
1379 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1380 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1381 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1382 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1383 DW_2_Chars(&pak.data[2], uin);
1385 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1386 return (ThisICQ->icq_SeqNum) - 1;
1389 /**************************************************************
1390 Initializes a server search for the information specified
1391 ***************************************************************/
1392 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1397 Word_2_Chars(pak.head.ver, ICQ_VER);
1398 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1399 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1400 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1401 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1403 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1405 strcpy(pak.data + size, nick);
1406 size += strlen(nick) + 1;
1407 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1409 strcpy(pak.data + size, first);
1410 size += strlen(first) + 1;
1411 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1413 strcpy(pak.data + size, last);
1414 size += strlen(last) + 1;
1415 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1417 strcpy(pak.data + size, email);
1418 size += strlen(email) + 1;
1419 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1422 /**************************************************************
1423 Initializes a server search for the information specified
1424 ***************************************************************/
1425 void icq_SendSearchUINReq(DWORD uin)
1430 Word_2_Chars(pak.head.ver, ICQ_VER);
1431 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1432 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1433 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1434 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1436 DW_2_Chars(&pak.data[size], uin);
1438 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1441 /**************************************************
1442 Registers a new uin in the ICQ network
1443 ***************************************************/
1444 void icq_RegNewUser(const char *pass)
1446 srv_net_icq_pak pak;
1451 Word_2_Chars(pak.head.ver, ICQ_VER);
1452 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1453 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1454 Word_2_Chars(len_buf, len);
1455 memcpy(&pak.data, "\x02\x00", 2);
1456 memcpy(&pak.data[2], &len_buf, 2);
1457 memcpy(&pak.data[4], pass, len + 1);
1458 DW_2_Chars(&pak.data[4 + len], 0x0072);
1459 DW_2_Chars(&pak.data[8 + len], 0x0000);
1461 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1464 void icq_UpdateUserInfo(USER_INFO * user)
1469 Word_2_Chars(pak.head.ver, ICQ_VER);
1470 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1471 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1472 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1473 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1475 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1477 strcpy(pak.data + size, user->nick);
1478 size += strlen(user->nick) + 1;
1479 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1481 strcpy(pak.data + size, user->first);
1482 size += strlen(user->first) + 1;
1483 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1485 strcpy(pak.data + size, user->last);
1486 size += strlen(user->last) + 1;
1487 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1489 strcpy(pak.data + size, user->email);
1490 size += strlen(user->email) + 1;
1491 pak.data[size] = user->auth;
1493 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1496 const char *icq_GetCountryName(int code)
1500 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1501 if (Country_Codes[i].code == code) {
1502 return Country_Codes[i].name;
1505 if (Country_Codes[i].code == code) {
1506 return Country_Codes[i].name;
1511 /********************************************
1512 returns a string describing the status or
1513 a "Error" if no such string exists
1514 *********************************************/
1515 const char *icq_ConvertStatus2Str(int status)
1517 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1520 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1521 switch (status & 0x01FF) {
1526 return "Do not disturb";
1531 case STATUS_OCCUPIED:
1535 return "Not available";
1537 case STATUS_INVISIBLE:
1540 case STATUS_INVISIBLE_2:
1541 return "Invisible mode 2";
1543 case STATUS_FREE_CHAT:
1544 return "Free for chat";
1552 void icq_InitNewUser(const char *hostname, DWORD port)
1554 srv_net_icq_pak pak;
1559 icq_Connect(hostname, port);
1560 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1561 printf("Couldn't establish connection\n");
1564 icq_RegNewUser((ThisICQ->icq_Password));
1567 tv.tv_usec = 500000;
1570 FD_SET((ThisICQ->icq_Sok), &readfds);
1572 /* don't care about writefds and exceptfds: */
1573 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1575 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1576 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1577 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1578 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1585 /********************************************
1586 Converts an intel endian character sequence to
1588 *********************************************/
1589 DWORD Chars_2_DW(unsigned char *buf)
1604 /********************************************
1605 Converts an intel endian character sequence to
1607 *********************************************/
1608 WORD Chars_2_Word(unsigned char *buf)
1619 /********************************************
1621 an intel endian character sequence
1622 *********************************************/
1623 void DW_2_Chars(unsigned char *buf, DWORD num)
1625 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1626 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1627 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1628 buf[0] = (unsigned char) (num) & 0x000000FF;
1631 /********************************************
1633 an intel endian character sequence
1634 *********************************************/
1635 void Word_2_Chars(unsigned char *buf, WORD num)
1637 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1638 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1641 /***************************
1642 ContactList functions
1643 ***************************/
1644 void icq_ContAddUser(DWORD cuin)
1646 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1647 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1650 p->vis_list = FALSE;
1656 (ThisICQ->icq_ContFirst) = p;
1659 void icq_ContDelUser(DWORD cuin)
1661 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1664 if (ptr->uin == cuin) {
1665 (ThisICQ->icq_ContFirst) = ptr->next;
1667 ptr = (ThisICQ->icq_ContFirst);
1670 if (ptr->next->uin == cuin) {
1671 ptr->next = ptr->next->next;
1678 void icq_ContClear()
1680 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1685 (ThisICQ->icq_ContFirst) = ptr;
1689 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1691 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1695 if (ptr->uin == cuin)
1702 icq_ContactItem *icq_ContGetFirst()
1704 return (ThisICQ->icq_ContFirst);
1707 void icq_ContSetVis(DWORD cuin)
1709 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1713 if (ptr->uin == cuin)
1714 ptr->vis_list = TRUE;
1719 /************************
1720 (ThisICQ->icq_ServMess) functions
1721 *************************/
1722 BOOL icq_GetServMess(WORD num)
1724 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1727 void icq_SetServMess(WORD num)
1729 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1734 return (ThisICQ->icq_Sok);
1737 int icq_GetProxySok()
1739 return (ThisICQ->icq_ProxySok);
1742 /*******************
1743 SOCKS5 Proxy support
1744 ********************/
1745 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1747 if ((ThisICQ->icq_ProxyHost))
1748 phree((ThisICQ->icq_ProxyHost));
1749 if ((ThisICQ->icq_ProxyName))
1750 phree((ThisICQ->icq_ProxyName));
1751 if ((ThisICQ->icq_ProxyPass))
1752 phree((ThisICQ->icq_ProxyPass));
1753 if (strlen(pname) > 255) {
1754 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1755 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1756 ThisICQ->icq_UseProxy = 0;
1759 if (strlen(ppass) > 255) {
1760 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1761 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1762 (ThisICQ->icq_UseProxy) = 0;
1765 (ThisICQ->icq_UseProxy) = 1;
1766 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1767 (ThisICQ->icq_ProxyPort) = pport;
1768 (ThisICQ->icq_ProxyAuth) = pauth;
1769 (ThisICQ->icq_ProxyName) = strdoop(pname);
1770 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1773 void icq_UnsetProxy()
1775 ThisICQ->icq_UseProxy = 0;
1780 /***********************************************************************/
1781 /* icqlib stuff ends here, Citadel module stuff begins */
1782 /***********************************************************************/
1786 * Callback function for CtdlICQ_Read_Config()
1788 void CtdlICQ_Read_Config_Backend(long msgnum) {
1789 struct CtdlMessage *msg;
1793 msg = CtdlFetchMessage(msgnum);
1795 ptr = msg->cm_fields['M'];
1796 pos = pattern2(ptr, "\n\n");
1804 safestrncpy(ThisICQ->icq_config, ptr, 256);
1806 CtdlFreeMessage(msg);
1812 * If this user has an ICQ configuration on disk, read it into memory.
1814 void CtdlICQ_Read_Config(void) {
1815 char hold_rm[ROOMNAMELEN];
1816 char icq_rm[ROOMNAMELEN];
1818 strcpy(hold_rm, CC->quickroom.QRname);
1819 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1820 strcpy(ThisICQ->icq_config, "");
1822 if (getroom(&CC->quickroom, icq_rm) != 0) {
1823 getroom(&CC->quickroom, hold_rm);
1827 /* We want the last (and probably only) config in this room */
1828 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1829 getroom(&CC->quickroom, hold_rm);
1836 * Write our config to disk
1838 void CtdlICQ_Write_Config(void) {
1839 char temp[PATH_MAX];
1842 strcpy(temp, tmpnam(NULL));
1844 fp = fopen(temp, "w");
1845 if (fp == NULL) return;
1846 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1849 /* this handy API function does all the work for us */
1850 CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
1860 * Write our contact list to disk
1862 void CtdlICQ_Write_CL(void) {
1863 char temp[PATH_MAX];
1867 strcpy(temp, tmpnam(NULL));
1869 fp = fopen(temp, "w");
1870 if (fp == NULL) return;
1872 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1873 fprintf(fp, "%ld|%s|\n",
1874 ThisICQ->icq_cl[i].uin,
1875 ThisICQ->icq_cl[i].name);
1879 /* this handy API function does all the work for us */
1880 CtdlWriteObject(ICQROOM, ICQCLMIME, temp, 1, 0, 1);
1890 * Callback function for CtdlICQ_Read_CL()
1892 void CtdlICQ_Read_CL_Backend(long msgnum) {
1893 struct CtdlMessage *msg;
1898 msg = CtdlFetchMessage(msgnum);
1900 ptr = msg->cm_fields['M'];
1901 pos = pattern2(ptr, "\n\n");
1909 for (i=0; i<strlen(ptr); ++i)
1910 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1911 if (ThisICQ->icq_numcl) {
1912 ThisICQ->icq_cl = mallok(
1913 (ThisICQ->icq_numcl *
1914 sizeof (struct CtdlICQ_CL)));
1916 while (cont=strtok(ptr, "\n"), cont != NULL) {
1917 ThisICQ->icq_cl[i].uin =
1918 extract_long(cont, 0);
1919 extract(ThisICQ->icq_cl[i].name,
1921 ThisICQ->icq_cl[i].status =
1927 CtdlFreeMessage(msg);
1933 * Read contact list into memory
1935 void CtdlICQ_Read_CL(void) {
1936 char hold_rm[ROOMNAMELEN];
1937 char icq_rm[ROOMNAMELEN];
1939 strcpy(hold_rm, CC->quickroom.QRname);
1940 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1941 strcpy(ThisICQ->icq_config, "");
1943 if (getroom(&CC->quickroom, icq_rm) != 0) {
1944 getroom(&CC->quickroom, hold_rm);
1948 /* Free any contact list already in memory */
1949 if (ThisICQ->icq_numcl) {
1950 phree(ThisICQ->icq_cl);
1951 ThisICQ->icq_numcl = 0;
1954 /* We want the last (and probably only) list in this room */
1955 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_CL_Backend);
1956 getroom(&CC->quickroom, hold_rm);
1962 * Refresh the contact list
1964 void CtdlICQ_Refresh_Contact_List(void) {
1967 if (ThisICQ->icq_Sok < 0) return;
1970 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1971 if (ThisICQ->icq_cl[i] > 0L) {
1972 icq_ContAddUser(ThisICQ->icq_cl[i]);
1973 icq_ContSetVis(ThisICQ->icq_cl[i]);
1977 icq_SendContactList();
1984 * Utility routine to logout and disconnect from the ICQ server if we happen
1985 * to already be connected
1987 void CtdlICQ_Logout_If_Connected(void) {
1988 if (ThisICQ->icq_Sok >= 0) {
1991 ThisICQ->icq_Sok = (-1);
1997 * If we have an ICQ uin/password on file for this user, go ahead and try
1998 * to log on to the ICQ server.
2000 void CtdlICQ_Login_If_Possible(void) {
2005 CtdlICQ_Logout_If_Connected();
2006 CtdlICQ_Read_Config();
2008 uin = extract_long(ThisICQ->icq_config, 0);
2009 extract(pass, ThisICQ->icq_config, 1);
2011 lprintf(9, "Here's my config: %s\n", ThisICQ->icq_config);
2013 if ( (uin > 0L) && (strlen(pass)>0) ) {
2014 icq_Init(uin, pass);
2015 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2016 icq_Login(STATUS_ONLINE);
2017 CtdlICQ_Refresh_Contact_List();
2024 /* This merely hooks icqlib's log function into Citadel's log function. */
2025 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2027 lprintf(level, "ICQ: %s", str);
2032 * At the start of each Citadel server session, we have to allocate some
2033 * space for the Citadel-ICQ session for this thread. It'll be automatically
2034 * freed by the server when the session ends.
2036 void CtdlICQ_session_startup_hook(void)
2038 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2039 icq_init_handle(ThisICQ);
2045 * End-of-session cleanup
2047 void CtdlICQ_session_stopdown_hook(void) {
2054 * icq_Main() needs to be called as frequently as possible. We'll do it
2055 * following the completion of each Citadel server command.
2058 void CtdlICQ_after_cmd_hook(void)
2060 if (ThisICQ->icq_Sok >= 0) {
2061 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
2063 ThisICQ->icq_LastKeepAlive = time(NULL);
2071 * There are a couple of things we absolutely need to make sure of when we
2072 * kill off the session. One of them is that the sockets are all closed --
2073 * otherwise we could have a nasty file descriptor leak.
2075 void CtdlICQ_session_logout_hook(void)
2077 lprintf(9, "Shutting down ICQ\n");
2078 CtdlICQ_Logout_If_Connected();
2080 /* Free the memory used by the contact list */
2081 if (ThisICQ->icq_numcl) {
2082 phree(ThisICQ->icq_cl);
2083 ThisICQ->icq_numcl = 0;
2090 void CtdlICQ_session_login_hook(void)
2092 /* If this user has an ICQ config on file, start it up. */
2093 CtdlICQ_Login_If_Possible();
2099 void cmd_icql(char *argbuf)
2101 safestrncpy(ThisICQ->icq_config, argbuf, 256);
2102 CtdlICQ_Write_Config();
2104 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2105 CtdlICQ_Login_If_Possible();
2112 * Here's what we have to do when an ICQ message arrives!
2114 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2115 BYTE day, BYTE month, WORD year,
2121 sprintf(from, "%ld@icq", uin);
2122 num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2123 lprintf(9, "Delivered to %d users\n", num_delivered);
2128 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2129 const char *first, const char *last,
2130 const char *email, char auth) {
2132 /* FIX do something with this info!! */
2141 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2145 /* Return quietly if this isn't an ICQ page */
2146 for (i=0; i<strlen(recp); ++i)
2147 if (!strcasecmp(&recp[i], "@icq")) is_icq = 1;
2148 if (atol(recp)==0L) is_icq = 0;
2149 if (is_icq == 0) return(0);
2151 if (strlen(msg) > 0) icq_SendMessage(atol(recp), msg);
2160 char *Dynamic_Module_Init(void)
2162 /* Make sure we've got a valid ThisICQ for each session */
2163 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2165 /* Tell the Citadel server about our wonderful ICQ hooks */
2166 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2167 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2168 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2169 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2170 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2171 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2172 CtdlRegisterXmsgHook(CtdlICQ_Send_Msg);
2174 /* Tell the code formerly known as icqlib about our callbacks */
2175 icq_Log = CtdlICQlog;
2176 icq_RecvMessage = CtdlICQ_Incoming_Message;
2177 icq_InfoReply = CtdlICQ_InfoReply;
2178 icq_Disconnected = CtdlICQ_Login_If_Possible;