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.3 1999/07/24 21:26:01 ajc
14 Revision 1.2 1999/07/23 04:27:45 ajc
15 Added CtdlWriteObject() to store generic data in the msgbase
17 Revision 1.1 1999/07/19 04:12:49 ajc
18 * serv_icq.c, serv_icq.mk: added (separate makefile is temporary)
20 Revision 1.16 1998/12/08 16:00:59 denis
21 Cleaned up a little before releasing
23 Revision 1.15 1998/11/25 19:18:16 denis
24 Added close icq_ProxySok in icq_Disconnect
26 Revision 1.14 1998/11/25 09:48:49 denis
27 icq_GetProxySok and icq_HandleProxyResponse methods added
28 Connection terminated support added
30 Revision 1.13 1998/11/19 12:22:48 denis
31 SOCKS support cleaned a little
32 icq_RecvUrl renamed to icq_RecvURL
33 icq_ProxyAuth added for Username/Password Authentication
34 URL/Description order inverted
35 icq_Quit splitted to icq_Logout and icq_Disconnect
36 icq_ProxyName and icq_ProxyPass range checking added
38 Revision 1.12 1998/11/18 16:21:29 denis
39 Fixed SOCKS5 proxy support
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
62 #include "dynloader.h"
64 #include "citserver.h"
66 struct ctdl_icq_handle {
69 BYTE icq_ServMess[8192];
74 icq_ContactItem *icq_ContFirst;
85 DWORD icq_ProxyDestHost;
86 WORD icq_ProxyDestPort;
87 WORD icq_ProxyOurPort;
88 FILE *icq_MyConfigFile; /* ig */
89 time_t icq_LastKeepAlive; /* ig */
93 #define ICQROOM "My ICQ Config"
94 #define ICQMIME "application/x-citadel-icq"
95 unsigned long SYM_CTDL_ICQ;
96 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
97 extern struct CitContext *ContextList;
98 #define MODULE_NAME "ICQ metaclient"
99 #define MODULE_AUTHOR "Art Cancro"
100 #define MODULE_EMAIL "ajc@uncnsrd.mt-kisco.ny.us"
101 #define MAJOR_VERSION 0
102 #define MINOR_VERSION 1
104 static struct DLModule_Info info =
115 void (*icq_Logged) (void);
116 void (*icq_Disconnected) (void);
117 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
118 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
119 void (*icq_RecvAdded) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email);
120 void (*icq_RecvAuthReq) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email, const char *reason);
121 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
122 void (*icq_SearchDone) (void);
123 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
124 void (*icq_UserOffline) (DWORD uin);
125 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
126 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
127 void (*icq_ExtInfoReply) (DWORD uin, const char *city, WORD country_code, char country_stat, const char *state, WORD age, char gender, const char *phone, const char *hp, const char *about);
128 void (*icq_Log) (time_t time, unsigned char level, const char *str);
129 void (*icq_SrvAck) (WORD seq);
132 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
133 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
134 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
135 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
136 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
137 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
138 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
139 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
142 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
143 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
144 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
145 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
146 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
147 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
148 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
149 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
151 static COUNTRY_CODE Country_Codes[] =
166 {"American Samoa", 684},
181 {"Czech Republic", 42},
184 {"El Salvador", 503},
188 {"French Antilles", 596},
189 {"French Polynesia", 689},
196 {"Guantanomo Bay", 53},
210 {"Ivory Coast", 225},
218 {"Liechtenstein", 41},
230 {"Netherlands Antilles", 599},
231 {"New Caledonia", 687},
238 {"Papua New Guinea", 675},
248 {"Saudia Arabia", 966},
253 {"South Africa", 27},
264 {"United Arab Emirates", 971},
266 {"Vatican City", 39},
273 {"Not entered", 0xffff}};
275 void icq_init_handle(struct ctdl_icq_handle *i)
277 memset(i, 0, sizeof(struct ctdl_icq_handle));
278 i->icq_Russian = TRUE;
280 i->icq_OurIp = 0x0100007f;
281 i->icq_Status = STATUS_OFFLINE;
284 i->icq_MyConfigFile = NULL;
287 int icq_SockWrite(int sok, const void *buf, size_t count)
290 if (!(ThisICQ->icq_UseProxy))
291 return write(sok, buf, count);
293 tmpbuf[0] = 0; /* reserved */
294 tmpbuf[1] = 0; /* reserved */
295 tmpbuf[2] = 0; /* standalone packet */
296 tmpbuf[3] = 1; /* address type IP v4 */
297 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
298 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
299 memcpy(&tmpbuf[10], buf, count);
300 return write(sok, tmpbuf, count + 10) - 10;
304 int icq_SockRead(int sok, void *buf, size_t count)
308 if (!(ThisICQ->icq_UseProxy))
309 return read(sok, buf, count);
311 res = read(sok, tmpbuf, count + 10);
312 memcpy(buf, &tmpbuf[10], res - 10);
317 /****************************************
318 This must be called every 2 min.
319 so the server knows we're still alive.
320 JAVA client sends two different commands
322 *****************************************/
327 Word_2_Chars(pak.head.ver, ICQ_VER);
328 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
329 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
330 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
331 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
333 Word_2_Chars(pak.head.ver, ICQ_VER);
334 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
335 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
336 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
337 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
339 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
340 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
343 /**********************************
344 This must be called to remove
345 messages from the server
346 ***********************************/
347 void icq_SendGotMessages()
351 Word_2_Chars(pak.head.ver, ICQ_VER);
352 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
353 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
354 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
356 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
359 /*************************************
360 this sends over the contact list
361 *************************************/
362 void icq_SendContactList()
368 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
370 Word_2_Chars(pak.head.ver, ICQ_VER);
371 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
372 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
373 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
379 DW_2_Chars(tmp, ptr->uin);
384 pak.data[0] = num_used;
385 size = ((int) tmp - (int) pak.data);
386 size += sizeof(pak.head);
387 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
390 void icq_SendNewUser(unsigned long uin)
395 Word_2_Chars(pak.head.ver, ICQ_VER);
396 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
397 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
398 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
399 DW_2_Chars(pak.data, uin);
400 size = sizeof(pak.head) + 4;
401 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
404 /*************************************
405 this sends over the visible list
406 that allows certain users to see you
408 *************************************/
409 void icq_SendVisibleList()
415 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
417 Word_2_Chars(pak.head.ver, ICQ_VER);
418 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
419 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
420 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
427 DW_2_Chars(tmp, ptr->uin);
434 pak.data[0] = num_used;
435 size = ((int) tmp - (int) pak.data);
436 size += sizeof(pak.head);
437 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
441 /**************************************
442 This sends the second login command
443 this is necessary to finish logging in.
444 ***************************************/
445 void icq_SendLogin1()
449 Word_2_Chars(pak.head.ver, ICQ_VER);
450 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
451 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
452 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
454 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
457 /************************************************
458 This is called when a user goes offline
459 *************************************************/
460 void icq_HandleUserOffline(srv_net_icq_pak pak)
465 remote_uin = Chars_2_DW(pak.data);
466 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
467 sprintf(buf, "User %lu logged off\n", remote_uin);
468 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
471 (*icq_UserOffline) (remote_uin);
472 icq_AckSrv(Chars_2_Word(pak.head.seq));
475 void icq_HandleUserOnline(srv_net_icq_pak pak)
477 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
478 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
479 icq_ContactItem *ptr;
482 remote_uin = Chars_2_DW(pak.data);
483 new_status = Chars_2_DW(&pak.data[17]);
484 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
485 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
486 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
487 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
488 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
489 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
492 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
493 icq_AckSrv(Chars_2_Word(pak.head.seq));
496 void icq_Status_Update(srv_net_icq_pak pak)
498 unsigned long remote_uin, new_status;
501 remote_uin = Chars_2_DW(pak.data);
502 new_status = Chars_2_DW(&pak.data[4]);
503 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
504 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
505 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
507 if (icq_UserStatusUpdate)
508 (*icq_UserStatusUpdate) (remote_uin, new_status);
509 icq_AckSrv(Chars_2_Word(pak.head.seq));
512 /************************************
513 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
514 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
515 It does NOT wait for any kind of a response.
516 *************************************/
517 void icq_Login(DWORD status)
524 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
525 Word_2_Chars(pak.head.ver, ICQ_VER);
526 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
527 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
528 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
530 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
531 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
533 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
534 DW_2_Chars(s2.status, status);
535 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
537 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
538 s2.X2[0] = LOGIN_X2_DEF;
539 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
540 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
541 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
543 memcpy(pak.data, &s1, sizeof(s1));
545 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
546 size += Chars_2_Word(s1.len);
547 memcpy(&pak.data[size], &s2, sizeof(s2));
549 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
552 /*******************************
553 This routine sends the aknowlegement cmd to the
554 server it appears that this must be done after
555 everything the server sends us
556 *******************************/
557 void icq_AckSrv(int seq)
562 Word_2_Chars(pak.head.ver, ICQ_VER);
563 Word_2_Chars(pak.head.cmd, CMD_ACK);
564 Word_2_Chars(pak.head.seq, seq);
565 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
566 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
567 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
568 for (i = 0; i < 6; i++)
569 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
572 void icq_HandleInfoReply(srv_net_icq_pak pak)
574 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
578 seq = Chars_2_Word(pak.data);
579 uin = Chars_2_DW(&pak.data[2]);
580 len = Chars_2_Word(&pak.data[6]);
582 icq_RusConv("wk", ptr1);
583 tmp = &pak.data[8 + len];
584 len = Chars_2_Word(tmp);
586 icq_RusConv("wk", ptr2);
588 len = Chars_2_Word(tmp);
590 icq_RusConv("wk", ptr3);
592 len = Chars_2_Word(tmp);
594 icq_RusConv("wk", ptr4);
596 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
597 sprintf(buf, "Info reply for %lu\n", uin);
598 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
601 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
602 icq_AckSrv(Chars_2_Word(pak.head.seq));
605 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
607 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
611 char cnt_stat, gender, buf[256];
612 uin = Chars_2_DW(&pak.data[2]);
613 len = Chars_2_Word(&pak.data[6]);
615 icq_RusConv("wk", ptr1);
616 cnt_code = Chars_2_Word(&pak.data[8 + len]);
617 cnt_stat = pak.data[len + 10];
618 tmp = &pak.data[11 + len];
619 len = Chars_2_Word(tmp);
620 icq_RusConv("wk", tmp + 2);
622 age = Chars_2_Word(tmp + 2 + len);
623 gender = *(tmp + len + 4);
625 len = Chars_2_Word(tmp);
626 icq_RusConv("wk", tmp + 2);
629 len = Chars_2_Word(tmp);
630 icq_RusConv("wk", tmp + 2);
633 len = Chars_2_Word(tmp);
634 icq_RusConv("wk", tmp + 2);
636 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
637 sprintf(buf, "Extended info reply for %lu\n", uin);
638 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
640 if (icq_ExtInfoReply)
641 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
642 icq_AckSrv(Chars_2_Word(pak.head.seq));
645 void icq_HandleSearchReply(srv_net_icq_pak pak)
647 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
651 uin = Chars_2_DW(&pak.data[2]);
652 len = Chars_2_Word(&pak.data[6]);
654 icq_RusConv("wk", ptr1);
655 tmp = &pak.data[8 + len];
656 len = Chars_2_Word(tmp);
658 icq_RusConv("wk", ptr2);
660 len = Chars_2_Word(tmp);
662 icq_RusConv("wk", ptr3);
664 len = Chars_2_Word(tmp);
666 icq_RusConv("wk", ptr4);
668 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
669 sprintf(buf, "User found %lu, Nick: %s, First Name: %s, Last Name: %s, EMail: %s, Auth: %s\n", uin, ptr1, ptr2, ptr3, ptr4, *tmp == 1 ? "no" : "yes");
670 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
673 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
674 icq_AckSrv(Chars_2_Word(pak.head.seq));
677 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
680 char *ptr1, *ptr2, *ptr3, *ptr4;
684 case USER_ADDED_MESS:
685 tmp = strchr(data, '\xFE');
687 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
688 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
695 tmp = strchr(tmp, '\xFE');
697 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
698 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
702 icq_RusConv("wk", data);
706 tmp = strchr(tmp, '\xFE');
708 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
709 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
713 icq_RusConv("wk", data);
717 tmp = strchr(tmp, '\xFE');
719 icq_RusConv("wk", data);
720 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
721 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
722 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
723 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
726 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
729 tmp = strchr(data, '\xFE');
734 tmp = strchr(tmp, '\xFE');
736 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
737 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
741 icq_RusConv("wk", data);
745 tmp = strchr(tmp, '\xFE');
747 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
748 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
752 icq_RusConv("wk", data);
756 tmp = strchr(tmp, '\xFE');
758 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
759 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
763 icq_RusConv("wk", data);
767 tmp = strchr(tmp, '\xFE');
769 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
770 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
776 tmp = strchr(tmp, '\x00');
778 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
779 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
783 icq_RusConv("wk", data);
784 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
785 sprintf(buf, "%lu has requested your authorization to be added to "
786 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
787 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
788 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
791 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
794 tmp = strchr(data, '\xFE');
796 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
797 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
801 icq_RusConv("wk", data);
805 icq_RusConv("wk", data);
806 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
807 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
808 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
811 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
814 icq_RusConv("wk", data);
815 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
816 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
817 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
820 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
824 /**********************************
825 Connects to hostname on port port
826 hostname can be DNS or nnn.nnn.nnn.nnn
827 write out messages to the FD aux
828 ***********************************/
829 int icq_Connect(const char *hostname, int port)
831 char buf[1024], un = 1;
832 char our_host[256], tmpbuf[256];
833 int conct, length, res;
834 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
835 struct hostent *host_struct; /* used in DNS llokup */
837 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
838 if ((ThisICQ->icq_Sok) == -1) {
839 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
840 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
843 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
844 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
845 sin.sin_addr.s_addr = INADDR_ANY;
846 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
848 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
849 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
850 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
853 length = sizeof(sin);
854 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
855 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
856 if ((ThisICQ->icq_UseProxy)) {
857 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
858 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
859 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
860 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
861 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
862 if (host_struct == 0L) {
863 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
864 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
865 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
869 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
871 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
872 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
873 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
874 if ((ThisICQ->icq_ProxySok) == -1) {
875 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
876 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
879 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
880 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
881 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
882 if (conct == -1) { /* did we connect ? */
883 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
884 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
887 buf[0] = 5; /* protocol version */
888 buf[1] = 1; /* number of methods */
889 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
890 buf[2] = 0; /* no authorization required */
892 buf[2] = 2; /* method username/password */
893 write((ThisICQ->icq_ProxySok), buf, 3);
894 res = read((ThisICQ->icq_ProxySok), buf, 2);
895 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
896 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
897 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
898 close((ThisICQ->icq_ProxySok));
901 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
902 buf[0] = 1; /* version of subnegotiation */
903 buf[1] = strlen((ThisICQ->icq_ProxyName));
904 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
905 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
906 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
907 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
908 res = read((ThisICQ->icq_ProxySok), buf, 2);
909 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
910 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
911 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
912 close((ThisICQ->icq_ProxySok));
916 buf[0] = 5; /* protocol version */
917 buf[1] = 3; /* command UDP associate */
918 buf[2] = 0; /* reserved */
919 buf[3] = 1; /* address type IP v4 */
924 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
925 write((ThisICQ->icq_ProxySok), buf, 10);
926 res = read((ThisICQ->icq_ProxySok), buf, 10);
927 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
930 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
931 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
934 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
935 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
938 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
939 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
942 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
943 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
946 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
947 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
950 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
951 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
954 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
955 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
958 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
959 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
962 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
963 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
966 close((ThisICQ->icq_ProxySok));
970 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
971 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
972 host_struct = gethostbyname(hostname);
973 if (host_struct == 0L) {
974 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
975 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
976 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
978 if ((ThisICQ->icq_UseProxy))
979 close((ThisICQ->icq_ProxySok));
982 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
984 if ((ThisICQ->icq_UseProxy)) {
985 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
986 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
988 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
989 sin.sin_port = htons(port); /* port */
990 if ((ThisICQ->icq_UseProxy)) {
991 (ThisICQ->icq_ProxyDestPort) = htons(port);
992 memcpy(&sin.sin_port, &buf[8], 2);
994 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
995 if (conct == -1) { /* did we connect ? */
996 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
997 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
998 if ((ThisICQ->icq_UseProxy))
999 close((ThisICQ->icq_ProxySok));
1002 length = sizeof(sin);
1003 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
1004 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
1005 (ThisICQ->icq_OurPort) = sin.sin_port;
1006 return (ThisICQ->icq_Sok);
1009 void icq_HandleProxyResponse()
1013 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
1015 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1016 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
1017 if (icq_Disconnected)
1018 (*icq_Disconnected) ();
1019 SOCKCLOSE((ThisICQ->icq_Sok));
1020 SOCKCLOSE((ThisICQ->icq_ProxySok));
1024 /******************************************
1025 Handles packets that the server sends to us.
1026 *******************************************/
1027 void icq_HandleServerResponse()
1029 srv_net_icq_pak pak;
1030 SIMPLE_MESSAGE *s_mesg;
1031 RECV_MESSAGE *r_mesg;
1038 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1040 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1041 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1042 if (icq_Disconnected)
1043 (*icq_Disconnected) ();
1044 SOCKCLOSE((ThisICQ->icq_Sok));
1046 if ((icq_GetServMess(Chars_2_Word(pak.head.seq))) && (Chars_2_Word(pak.head.cmd) != SRV_NEW_UIN) && (Chars_2_Word(pak.head.cmd) != SRV_GO_AWAY)) {
1047 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1048 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1049 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1050 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1052 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1056 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1057 icq_SetServMess(Chars_2_Word(pak.head.seq));
1058 switch (Chars_2_Word(pak.head.cmd)) {
1060 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1061 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1063 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1066 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1067 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1068 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1071 case SRV_LOGIN_REPLY:
1072 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1073 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1074 sprintf(buf, "Login successful, UIN: %lu, IP: %u.%u.%u.%u\n", Chars_2_DW(pak.data), pak.data[4], pak.data[5], pak.data[6], pak.data[7]);
1075 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1077 icq_AckSrv(Chars_2_Word(pak.head.seq));
1079 icq_SendContactList();
1080 icq_SendVisibleList();
1084 case SRV_RECV_MESSAGE:
1085 r_mesg = (RECV_MESSAGE *) pak.data;
1086 icq_DoMsg(Chars_2_Word(r_mesg->type), Chars_2_Word(r_mesg->len), (char *) (r_mesg->len + 2), Chars_2_DW(r_mesg->uin), r_mesg->hour, r_mesg->minute, r_mesg->day, r_mesg->month, Chars_2_Word(r_mesg->year));
1087 icq_AckSrv(Chars_2_Word(pak.head.seq));
1089 case SRV_X1: /* unknown message sent after login */
1090 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1091 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1092 icq_AckSrv(Chars_2_Word(pak.head.seq));
1094 case SRV_X2: /* unknown message sent after login */
1095 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1096 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1097 icq_AckSrv(Chars_2_Word(pak.head.seq));
1098 icq_SendGotMessages();
1100 case SRV_INFO_REPLY:
1101 icq_HandleInfoReply(pak);
1103 case SRV_EXT_INFO_REPLY:
1104 icq_HandleExtInfoReply(pak);
1106 case SRV_USER_ONLINE:
1107 icq_HandleUserOnline(pak);
1109 case SRV_USER_OFFLINE:
1110 icq_HandleUserOffline(pak);
1113 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1114 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1115 icq_Login((ThisICQ->icq_Status));
1117 case SRV_STATUS_UPDATE:
1118 icq_Status_Update(pak);
1121 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1122 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1123 if (icq_Disconnected)
1124 (*icq_Disconnected) ();
1126 case SRV_END_OF_SEARCH:
1127 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1128 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1130 (*icq_SearchDone) ();
1131 icq_AckSrv(Chars_2_Word(pak.head.seq));
1133 case SRV_USER_FOUND:
1134 icq_HandleSearchReply(pak);
1136 case SRV_SYS_DELIVERED_MESS:
1137 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1138 cur_time = time(0L);
1139 tm_str = localtime(&cur_time);
1140 icq_DoMsg(Chars_2_Word(s_mesg->type), Chars_2_Word(s_mesg->len), (char *) (s_mesg->len + 2), Chars_2_DW(s_mesg->uin), tm_str->tm_hour, tm_str->tm_min, tm_str->tm_mday, tm_str->tm_mon + 1, tm_str->tm_year + 1900);
1141 icq_AckSrv(Chars_2_Word(pak.head.seq));
1143 default: /* commands we dont handle yet */
1144 len = s - (sizeof(pak.head));
1145 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1146 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1147 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1148 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1150 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1155 void icq_Init(DWORD uin, const char *password)
1157 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1158 (ThisICQ->icq_Uin) = uin;
1159 if ((ThisICQ->icq_Password))
1160 free((ThisICQ->icq_Password));
1161 (ThisICQ->icq_Password) = strdup(password);
1166 if ((ThisICQ->icq_Password))
1167 free((ThisICQ->icq_Password));
1170 /******************************
1171 Main function connects gets (ThisICQ->icq_Uin)
1172 and (ThisICQ->icq_Password) and logins in and sits
1173 in a loop waiting for server responses.
1174 *******************************/
1186 FD_SET((ThisICQ->icq_Sok), &readfds);
1187 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1188 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1189 icq_HandleServerResponse();
1193 } while (did_something);
1196 /********************************************************
1197 Russian language ICQ fix.
1198 Usual Windows ICQ users do use Windows 1251 encoding but
1199 unix users do use koi8 encoding, so we need to convert it.
1200 This function will convert string from windows 1251 to koi8
1201 or from koi8 to windows 1251.
1202 Andrew Frolov dron@ilm.net
1203 *********************************************************/
1204 void icq_RusConv(const char to[4], char *t_in)
1209 /* 6-17-1998 by Linux_Dude
1210 * Moved initialization of table out front of 'if' block to prevent compiler
1211 * warning. Improved error message, and now return without performing string
1212 * conversion to prevent addressing memory out of range (table pointer would
1213 * previously have remained uninitialized (= bad)).
1217 if (strcmp(to, "kw") == 0)
1219 else if (strcmp(to, "wk") != 0) {
1220 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1221 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1224 /* End Linux_Dude's changes ;) */
1226 if ((ThisICQ->icq_Russian)) {
1227 for (i = 0; t_in[i] != 0; i++) {
1230 t_in[i] = table[t_in[i] & 0177];
1235 /**************************************************
1236 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1238 ***************************************************/
1239 WORD icq_SendMessage(DWORD uin, const char *text)
1244 char buf[512]; /* message may be only 450 bytes long */
1246 strncpy(buf, text, 512);
1247 icq_RusConv("kw", buf);
1249 Word_2_Chars(pak.head.ver, ICQ_VER);
1250 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1251 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1252 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1253 DW_2_Chars(msg.uin, uin);
1254 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1255 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1256 memcpy(&pak.data, &msg, sizeof(msg));
1257 memcpy(&pak.data[8], buf, len + 1);
1258 size = sizeof(msg) + len + 1;
1259 for (i = 0; i < 6; i++)
1260 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1261 return (ThisICQ->icq_SeqNum) - 1;
1264 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1268 int size, len1, len2;
1269 char buf1[512], buf2[512];
1271 strncpy(buf1, descr, 512);
1272 strncpy(buf2, url, 512);
1273 /* Do we need to convert URL? */
1274 icq_RusConv("kw", buf2);
1275 len1 = strlen(buf1);
1276 len2 = strlen(buf2);
1277 Word_2_Chars(pak.head.ver, ICQ_VER);
1278 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1279 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1280 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1281 DW_2_Chars(msg.uin, uin);
1282 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1283 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1284 memcpy(&pak.data, &msg, sizeof(msg));
1285 memcpy(&pak.data[8], buf1, len1);
1286 pak.data[8 + len1] = 0xFE;
1287 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1288 size = sizeof(msg) + len1 + len2 + 2;
1289 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1290 return (ThisICQ->icq_SeqNum) - 1;
1293 /**************************************************
1294 Sends a authorization to the server so the Mirabilis
1295 client can add the user.
1296 ***************************************************/
1297 void icq_SendAuthMsg(DWORD uin)
1303 Word_2_Chars(pak.head.ver, ICQ_VER);
1304 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1305 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1306 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1307 DW_2_Chars(msg.uin, uin);
1308 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1309 Word_2_Chars(msg.len, 2);
1310 memcpy(&pak.data, &msg, sizeof(msg));
1311 pak.data[sizeof(msg)] = 0x03;
1312 pak.data[sizeof(msg) + 1] = 0x00;
1313 size = sizeof(msg) + 2;
1314 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1317 /**************************************************
1318 Changes the users status on the server
1319 ***************************************************/
1320 void icq_ChangeStatus(DWORD status)
1325 Word_2_Chars(pak.head.ver, ICQ_VER);
1326 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1327 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1328 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1329 DW_2_Chars(pak.data, status);
1330 (ThisICQ->icq_Status) = status;
1332 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1335 /**********************
1337 ***********************/
1343 Word_2_Chars(pak.head.ver, ICQ_VER);
1344 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1345 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1346 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1347 len = strlen("B_USER_DISCONNECTED") + 1;
1348 *(short *) pak.data = len;
1350 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1351 pak.data[2 + len] = 05;
1352 pak.data[3 + len] = 00;
1353 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1356 void icq_Disconnect()
1358 SOCKCLOSE((ThisICQ->icq_Sok));
1359 SOCKCLOSE((ThisICQ->icq_Sok));
1360 if ((ThisICQ->icq_UseProxy))
1361 SOCKCLOSE((ThisICQ->icq_ProxySok));
1364 /********************************************************
1365 Sends a request to the server for info on a specific user
1366 *********************************************************/
1367 WORD icq_SendInfoReq(DWORD uin)
1372 Word_2_Chars(pak.head.ver, ICQ_VER);
1373 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1374 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1375 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1376 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1377 DW_2_Chars(&pak.data[2], uin);
1379 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1380 return (ThisICQ->icq_SeqNum) - 1;
1383 /********************************************************
1384 Sends a request to the server for info on a specific user
1385 *********************************************************/
1386 WORD icq_SendExtInfoReq(DWORD uin)
1391 Word_2_Chars(pak.head.ver, ICQ_VER);
1392 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1393 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1394 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1395 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1396 DW_2_Chars(&pak.data[2], uin);
1398 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1399 return (ThisICQ->icq_SeqNum) - 1;
1402 /**************************************************************
1403 Initializes a server search for the information specified
1404 ***************************************************************/
1405 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1410 Word_2_Chars(pak.head.ver, ICQ_VER);
1411 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1412 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1413 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1414 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1416 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1418 strcpy(pak.data + size, nick);
1419 size += strlen(nick) + 1;
1420 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1422 strcpy(pak.data + size, first);
1423 size += strlen(first) + 1;
1424 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1426 strcpy(pak.data + size, last);
1427 size += strlen(last) + 1;
1428 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1430 strcpy(pak.data + size, email);
1431 size += strlen(email) + 1;
1432 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1435 /**************************************************************
1436 Initializes a server search for the information specified
1437 ***************************************************************/
1438 void icq_SendSearchUINReq(DWORD uin)
1443 Word_2_Chars(pak.head.ver, ICQ_VER);
1444 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1445 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1446 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1447 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1449 DW_2_Chars(&pak.data[size], uin);
1451 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1454 /**************************************************
1455 Registers a new uin in the ICQ network
1456 ***************************************************/
1457 void icq_RegNewUser(const char *pass)
1459 srv_net_icq_pak pak;
1464 Word_2_Chars(pak.head.ver, ICQ_VER);
1465 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1466 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1467 Word_2_Chars(len_buf, len);
1468 memcpy(&pak.data, "\x02\x00", 2);
1469 memcpy(&pak.data[2], &len_buf, 2);
1470 memcpy(&pak.data[4], pass, len + 1);
1471 DW_2_Chars(&pak.data[4 + len], 0x0072);
1472 DW_2_Chars(&pak.data[8 + len], 0x0000);
1474 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1477 void icq_UpdateUserInfo(USER_INFO * user)
1482 Word_2_Chars(pak.head.ver, ICQ_VER);
1483 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1484 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1485 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1486 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1488 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1490 strcpy(pak.data + size, user->nick);
1491 size += strlen(user->nick) + 1;
1492 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1494 strcpy(pak.data + size, user->first);
1495 size += strlen(user->first) + 1;
1496 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1498 strcpy(pak.data + size, user->last);
1499 size += strlen(user->last) + 1;
1500 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1502 strcpy(pak.data + size, user->email);
1503 size += strlen(user->email) + 1;
1504 pak.data[size] = user->auth;
1506 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1509 const char *icq_GetCountryName(int code)
1513 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1514 if (Country_Codes[i].code == code) {
1515 return Country_Codes[i].name;
1518 if (Country_Codes[i].code == code) {
1519 return Country_Codes[i].name;
1524 /********************************************
1525 returns a string describing the status or
1526 a "Error" if no such string exists
1527 *********************************************/
1528 const char *icq_ConvertStatus2Str(int status)
1530 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1533 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1534 switch (status & 0x01FF) {
1539 return "Do not disturb";
1544 case STATUS_OCCUPIED:
1548 return "Not available";
1550 case STATUS_INVISIBLE:
1553 case STATUS_INVISIBLE_2:
1554 return "Invisible mode 2";
1556 case STATUS_FREE_CHAT:
1557 return "Free for chat";
1565 void icq_InitNewUser(const char *hostname, DWORD port)
1567 srv_net_icq_pak pak;
1572 icq_Connect(hostname, port);
1573 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1574 printf("Couldn't establish connection\n");
1577 icq_RegNewUser((ThisICQ->icq_Password));
1580 tv.tv_usec = 500000;
1583 FD_SET((ThisICQ->icq_Sok), &readfds);
1585 /* don't care about writefds and exceptfds: */
1586 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1588 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1589 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1590 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1591 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1598 /********************************************
1599 Converts an intel endian character sequence to
1601 *********************************************/
1602 DWORD Chars_2_DW(unsigned char *buf)
1617 /********************************************
1618 Converts an intel endian character sequence to
1620 *********************************************/
1621 WORD Chars_2_Word(unsigned char *buf)
1632 /********************************************
1634 an intel endian character sequence
1635 *********************************************/
1636 void DW_2_Chars(unsigned char *buf, DWORD num)
1638 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1639 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1640 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1641 buf[0] = (unsigned char) (num) & 0x000000FF;
1644 /********************************************
1646 an intel endian character sequence
1647 *********************************************/
1648 void Word_2_Chars(unsigned char *buf, WORD num)
1650 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1651 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1654 /***************************
1655 ContactList functions
1656 ***************************/
1657 void icq_ContAddUser(DWORD cuin)
1659 icq_ContactItem *p = malloc(sizeof(icq_ContactItem));
1660 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1663 p->vis_list = FALSE;
1669 (ThisICQ->icq_ContFirst) = p;
1672 void icq_ContDelUser(DWORD cuin)
1674 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1677 if (ptr->uin == cuin) {
1678 (ThisICQ->icq_ContFirst) = ptr->next;
1680 ptr = (ThisICQ->icq_ContFirst);
1683 if (ptr->next->uin == cuin) {
1684 ptr->next = ptr->next->next;
1691 void icq_ContClear()
1693 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1698 (ThisICQ->icq_ContFirst) = ptr;
1702 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1704 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1708 if (ptr->uin == cuin)
1715 icq_ContactItem *icq_ContGetFirst()
1717 return (ThisICQ->icq_ContFirst);
1720 void icq_ContSetVis(DWORD cuin)
1722 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1726 if (ptr->uin == cuin)
1727 ptr->vis_list = TRUE;
1732 /************************
1733 (ThisICQ->icq_ServMess) functions
1734 *************************/
1735 BOOL icq_GetServMess(WORD num)
1737 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1740 void icq_SetServMess(WORD num)
1742 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1747 return (ThisICQ->icq_Sok);
1750 int icq_GetProxySok()
1752 return (ThisICQ->icq_ProxySok);
1755 /*******************
1756 SOCKS5 Proxy support
1757 ********************/
1758 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1760 if ((ThisICQ->icq_ProxyHost))
1761 free((ThisICQ->icq_ProxyHost));
1762 if ((ThisICQ->icq_ProxyName))
1763 free((ThisICQ->icq_ProxyName));
1764 if ((ThisICQ->icq_ProxyPass))
1765 free((ThisICQ->icq_ProxyPass));
1766 if (strlen(pname) > 255) {
1767 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1768 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1769 ThisICQ->icq_UseProxy = 0;
1772 if (strlen(ppass) > 255) {
1773 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1774 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1775 (ThisICQ->icq_UseProxy) = 0;
1778 (ThisICQ->icq_UseProxy) = 1;
1779 (ThisICQ->icq_ProxyHost) = strdup(phost);
1780 (ThisICQ->icq_ProxyPort) = pport;
1781 (ThisICQ->icq_ProxyAuth) = pauth;
1782 (ThisICQ->icq_ProxyName) = strdup(pname);
1783 (ThisICQ->icq_ProxyPass) = strdup(ppass);
1786 void icq_UnsetProxy()
1788 ThisICQ->icq_UseProxy = 0;
1793 /***********************************************************************/
1794 /* icqlib stuff ends here, Citadel module stuff begins */
1795 /***********************************************************************/
1798 /* config file manipulation routines ... probably temporary until we can
1799 * get something more robust written
1803 void CtdlICQ_Config_Delete(char *key) {
1804 long readpos, writepos;
1805 char buf[256], keyplusspace[256];
1808 if (ThisICQ->icq_MyConfigFile == NULL) return;
1811 sprintf(keyplusspace, "%s ", key);
1814 fseek(ThisICQ->icq_MyConfigFile, readpos, 0);
1815 ptr = fgets(buf, 256, ThisICQ->icq_MyConfigFile);
1816 readpos = ftell(ThisICQ->icq_MyConfigFile);
1818 fflush(ThisICQ->icq_MyConfigFile);
1819 ftruncate(fileno(ThisICQ->icq_MyConfigFile), writepos);
1822 if (strncasecmp(buf, keyplusspace,
1823 strlen(keyplusspace))) {
1824 fseek(ThisICQ->icq_MyConfigFile, writepos, 0);
1825 fprintf(ThisICQ->icq_MyConfigFile, "%s", buf);
1826 writepos = ftell(ThisICQ->icq_MyConfigFile);
1834 void CtdlICQ_Config_Write(char *key, char *contents) {
1836 char buf[256]; /* FIX */
1838 CtdlICQ_Config_Delete(key);
1839 fseek(ThisICQ->icq_MyConfigFile, 0L, SEEK_END);
1840 fprintf(ThisICQ->icq_MyConfigFile, "%s %s\n", key, contents);
1842 /****** FIX ****** TEMPORARY HACK TO SEE STUFF ***********/
1844 fflush(ThisICQ->icq_MyConfigFile);
1845 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1846 CtdlWriteObject(ICQROOM, ICQMIME, buf, 1, 0, 1);
1851 void CtdlICQ_Config_Read(char *contents, char *key) {
1854 strcpy(contents, "");
1855 rewind(ThisICQ->icq_MyConfigFile);
1856 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1857 buf[strlen(buf)-1]=0;
1858 if ( (!strncasecmp(buf, key, strlen(key)))
1859 && (isspace(buf[strlen(key)])) ) {
1860 strcpy(contents, &buf[strlen(key)+1]);
1867 * Refresh the contact list
1869 void CtdlICQ_Refresh_Contact_List(void) {
1873 if (ThisICQ->icq_Sok < 0) return;
1876 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1877 buf[strlen(buf)-1]=0;
1879 contact_uin = atol(buf);
1880 if (contact_uin > 0L) {
1881 icq_ContAddUser(contact_uin);
1882 icq_ContSetVis(contact_uin);
1885 icq_SendContactList();
1892 * Utility routine to logout and disconnect from the ICQ server if we happen
1893 * to already be connected
1895 void CtdlICQ_Logout_If_Connected(void) {
1896 if (ThisICQ->icq_Sok >= 0) {
1899 ThisICQ->icq_Sok = (-1);
1905 * If we have an ICQ uin/password on file for this user, go ahead and try
1906 * to log on to the ICQ server.
1908 void CtdlICQ_Login_If_Possible(void) {
1913 CtdlICQ_Logout_If_Connected();
1915 CtdlICQ_Config_Read(buf, "uin");
1917 CtdlICQ_Config_Read(pass, "pass");
1919 if ( (uin > 0L) && (strlen(pass)>0) ) {
1920 icq_Init(uin, pass);
1921 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
1922 icq_Login(STATUS_ONLINE);
1923 CtdlICQ_Refresh_Contact_List();
1930 /* This merely hooks icqlib's log function into Citadel's log function. */
1931 void CtdlICQlog(time_t time, unsigned char level, const char *str)
1933 lprintf(level, "ICQ: %s", str);
1938 * At the start of each Citadel server session, we have to allocate some
1939 * space for the Citadel-ICQ session for this thread. It'll be automatically
1940 * freed by the server when the session ends.
1942 void CtdlICQ_session_startup_hook(void)
1944 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
1945 icq_init_handle(ThisICQ);
1950 * icq_Main() needs to be called as frequently as possible. We'll do it
1951 * following the completion of each Citadel server command.
1954 void CtdlICQ_after_cmd_hook(void)
1956 if (ThisICQ->icq_Sok >= 0) {
1957 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
1959 ThisICQ->icq_LastKeepAlive = time(NULL);
1967 * There are a couple of things we absolutely need to make sure of when we
1968 * kill off the session. One of them is that the sockets are all closed --
1969 * otherwise we could have a nasty file descriptor leak.
1971 void CtdlICQ_session_logout_hook(void)
1973 CtdlICQ_Logout_If_Connected();
1974 if (ThisICQ->icq_MyConfigFile != NULL)
1975 fclose(ThisICQ->icq_MyConfigFile);
1981 void CtdlICQ_session_login_hook(void)
1984 if (ThisICQ->icq_MyConfigFile != NULL)
1985 fclose(ThisICQ->icq_MyConfigFile);
1987 /* Open this user's ICQ config file; create it if it's not there */
1988 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1989 ThisICQ->icq_MyConfigFile = fopen(buf, "r+");
1990 if (ThisICQ->icq_MyConfigFile == NULL)
1991 ThisICQ->icq_MyConfigFile = fopen(buf, "w+");
1992 if (ThisICQ->icq_MyConfigFile == NULL)
1993 lprintf(2, "Cannot create %s: %s\n", buf, strerror(errno));
1995 /* Login to the ICQ server if we already have info on file. */
1996 CtdlICQ_Login_If_Possible();
2000 void cmd_icqa(char *argbuf) {
2003 extract(cuin, argbuf, 0);
2004 if (atol(cuin) > 0L) {
2005 CtdlICQ_Config_Write(cuin, "");
2006 icq_SendInfoReq(atol(cuin));
2007 cprintf("%d %s added to your ICQ contact list.\n", OK, cuin);
2009 cprintf("%d You must specify a numeric ICQ uin.\n", ERROR);
2012 CtdlICQ_Refresh_Contact_List();
2018 void cmd_icql(char *argbuf)
2023 extract(uin, argbuf, 0);
2024 extract(password, argbuf, 1);
2026 CtdlICQ_Config_Write("uin", uin);
2027 CtdlICQ_Config_Write("pass", password);
2029 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2030 CtdlICQ_Login_If_Possible();
2035 * When deleting a user from the server, be sure to delete
2036 * his/her/its ICQ config file as well.
2038 void CtdlICQ_DeleteUserConfigFile(char *uname, long unum) {
2041 sprintf(buf, "icq/%ld", unum);
2048 * Here's what we have to do when an ICQ message arrives!
2050 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2051 BYTE day, BYTE month, WORD year,
2057 sprintf(from, "%ld", uin);
2058 CtdlICQ_Config_Read(nick, from);
2059 if (strlen(nick) == 0) {
2060 icq_SendInfoReq(atol(from));
2063 sprintf(from, "%ld@icq (%s)", uin, nick);
2064 if (CtdlSendExpressMessageFunc) {
2065 CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
2070 lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
2076 CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2077 const char *first, const char *last,
2078 const char *email, char auth) {
2082 sprintf(str_uin, "%ld", uin);
2083 CtdlICQ_Config_Write(str_uin, nick);
2088 struct DLModule_Info *Dynamic_Module_Init(void)
2090 /* Create a directory to store ICQ stuff in.
2091 * It's ok if the directory is already there.
2093 if (mkdir("icq", 0700) != 0) if (errno != EEXIST) {
2094 lprintf(2, "Can't create icq subdirectory: %s\n",
2098 /* Make sure we've got a valid ThisICQ for each session */
2099 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2101 /* Tell the Citadel server about our wonderful ICQ hooks */
2102 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2103 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2104 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2105 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2106 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2107 CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add an ICQ contact");
2108 CtdlRegisterUserHook(CtdlICQ_DeleteUserConfigFile, EVT_PURGEUSER);
2110 /* Tell the code formerly known as icqlib about our callbacks */
2111 icq_Log = CtdlICQlog;
2112 icq_RecvMessage = CtdlICQ_Incoming_Message;
2113 icq_InfoReply = CtdlICQ_InfoReply;