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 lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
1794 msg = CtdlFetchMessage(msgnum);
1796 ptr = msg->cm_fields['M'];
1797 pos = pattern2(ptr, "\n\n");
1805 safestrncpy(ThisICQ->icq_config, ptr, 256);
1807 CtdlFreeMessage(msg);
1809 lprintf(9, "...it ain't there?\n");
1815 * If this user has an ICQ configuration on disk, read it into memory.
1817 void CtdlICQ_Read_Config(void) {
1818 char hold_rm[ROOMNAMELEN];
1819 char icq_rm[ROOMNAMELEN];
1821 strcpy(hold_rm, CC->quickroom.QRname);
1822 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1823 strcpy(ThisICQ->icq_config, "");
1825 if (getroom(&CC->quickroom, icq_rm) != 0) {
1826 getroom(&CC->quickroom, hold_rm);
1830 /* We want the last (and probably only) config in this room */
1831 lprintf(9, "We're in <%s> looking for config\n",
1832 CC->quickroom.QRname);
1833 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1834 getroom(&CC->quickroom, hold_rm);
1841 * Write our config to disk
1843 void CtdlICQ_Write_Config(void) {
1844 char temp[PATH_MAX];
1847 strcpy(temp, tmpnam(NULL));
1849 fp = fopen(temp, "w");
1850 if (fp == NULL) return;
1851 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1854 /* this handy API function does all the work for us */
1855 CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
1865 * Write our contact list to disk
1867 void CtdlICQ_Write_CL(void) {
1868 char temp[PATH_MAX];
1872 strcpy(temp, tmpnam(NULL));
1874 fp = fopen(temp, "w");
1875 if (fp == NULL) return;
1877 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1878 fprintf(fp, "%ld|%s|\n",
1879 ThisICQ->icq_cl[i].uin,
1880 ThisICQ->icq_cl[i].name);
1884 /* this handy API function does all the work for us */
1885 CtdlWriteObject(ICQROOM, ICQCLMIME, temp, 1, 0, 1);
1895 * Callback function for CtdlICQ_Read_CL()
1897 void CtdlICQ_Read_CL_Backend(long msgnum) {
1898 struct CtdlMessage *msg;
1903 msg = CtdlFetchMessage(msgnum);
1905 ptr = msg->cm_fields['M'];
1906 pos = pattern2(ptr, "\n\n");
1914 for (i=0; i<strlen(ptr); ++i)
1915 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1916 if (ThisICQ->icq_numcl) {
1917 ThisICQ->icq_cl = mallok(
1918 (ThisICQ->icq_numcl *
1919 sizeof (struct CtdlICQ_CL)));
1921 while (cont=strtok(ptr, "\n"), cont != NULL) {
1922 ThisICQ->icq_cl[i].uin =
1923 extract_long(cont, 0);
1924 extract(ThisICQ->icq_cl[i].name,
1926 ThisICQ->icq_cl[i].status =
1933 CtdlFreeMessage(msg);
1939 * Read contact list into memory
1941 void CtdlICQ_Read_CL(void) {
1942 char hold_rm[ROOMNAMELEN];
1943 char icq_rm[ROOMNAMELEN];
1945 strcpy(hold_rm, CC->quickroom.QRname);
1946 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1947 strcpy(ThisICQ->icq_config, "");
1949 if (getroom(&CC->quickroom, icq_rm) != 0) {
1950 getroom(&CC->quickroom, hold_rm);
1954 /* Free any contact list already in memory */
1955 if (ThisICQ->icq_numcl) {
1956 phree(ThisICQ->icq_cl);
1957 ThisICQ->icq_numcl = 0;
1960 /* We want the last (and probably only) list in this room */
1961 CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
1962 getroom(&CC->quickroom, hold_rm);
1967 * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
1968 * entry in the table if necessary
1970 struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
1973 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
1974 if (ThisICQ->icq_cl[i].uin == uin)
1975 return (&ThisICQ->icq_cl[i]);
1977 ++ThisICQ->icq_numcl;
1978 ThisICQ->icq_cl = reallok(ThisICQ->icq_cl,
1979 (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
1980 memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
1981 0, sizeof(struct CtdlICQ_CL));
1982 ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
1983 return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
1989 * Refresh the contact list
1991 void CtdlICQ_Refresh_Contact_List(void) {
1994 if (ThisICQ->icq_Sok < 0) return;
1997 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1998 if (ThisICQ->icq_cl[i].uin > 0L) {
1999 icq_ContAddUser(ThisICQ->icq_cl[i].uin);
2000 icq_ContSetVis(ThisICQ->icq_cl[i].uin);
2004 icq_SendContactList();
2011 * Utility routine to logout and disconnect from the ICQ server if we happen
2012 * to already be connected
2014 void CtdlICQ_Logout_If_Connected(void) {
2015 if (ThisICQ->icq_Sok >= 0) {
2018 ThisICQ->icq_Sok = (-1);
2024 * If we have an ICQ uin/password on file for this user, go ahead and try
2025 * to log on to the ICQ server.
2027 void CtdlICQ_Login_If_Possible(void) {
2032 CtdlICQ_Logout_If_Connected();
2033 CtdlICQ_Read_Config();
2035 uin = extract_long(ThisICQ->icq_config, 0);
2036 extract(pass, ThisICQ->icq_config, 1);
2038 lprintf(9, "Here's my config: %s\n", ThisICQ->icq_config);
2040 if ( (uin > 0L) && (strlen(pass)>0) ) {
2041 icq_Init(uin, pass);
2042 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2043 icq_Login(STATUS_ONLINE);
2044 CtdlICQ_Refresh_Contact_List();
2051 /* This merely hooks icqlib's log function into Citadel's log function. */
2052 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2054 lprintf(level, "ICQ: %s", str);
2059 * At the start of each Citadel server session, we have to allocate some
2060 * space for the Citadel-ICQ session for this thread. It'll be automatically
2061 * freed by the server when the session ends.
2063 void CtdlICQ_session_startup_hook(void)
2065 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2066 icq_init_handle(ThisICQ);
2072 * End-of-session cleanup
2074 void CtdlICQ_session_stopdown_hook(void) {
2081 * icq_Main() needs to be called as frequently as possible. We'll do it
2082 * following the completion of each Citadel server command.
2085 void CtdlICQ_after_cmd_hook(void)
2087 if (ThisICQ->icq_Sok >= 0) {
2088 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
2090 ThisICQ->icq_LastKeepAlive = time(NULL);
2098 * There are a couple of things we absolutely need to make sure of when we
2099 * kill off the session. One of them is that the sockets are all closed --
2100 * otherwise we could have a nasty file descriptor leak.
2102 void CtdlICQ_session_logout_hook(void)
2104 lprintf(9, "Shutting down ICQ\n");
2105 CtdlICQ_Logout_If_Connected();
2107 /* Free the memory used by the contact list */
2108 if (ThisICQ->icq_numcl) {
2109 phree(ThisICQ->icq_cl);
2110 ThisICQ->icq_numcl = 0;
2117 void CtdlICQ_session_login_hook(void)
2119 /* If this user has an ICQ config on file, start it up. */
2120 CtdlICQ_Login_If_Possible();
2126 void cmd_icql(char *argbuf)
2128 safestrncpy(ThisICQ->icq_config, argbuf, 256);
2129 CtdlICQ_Write_Config();
2131 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2132 CtdlICQ_Login_If_Possible();
2139 * Here's what we have to do when an ICQ message arrives!
2141 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2142 BYTE day, BYTE month, WORD year,
2148 sprintf(from, "%ld@icq", uin);
2149 num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2150 lprintf(9, "Delivered to %d users\n", num_delivered);
2155 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2156 const char *first, const char *last,
2157 const char *email, char auth) {
2159 struct CtdlICQ_CL *ptr;
2161 ptr = CtdlICQ_CLent(uin);
2162 safestrncpy(ptr->name, nick, 32);
2163 ptr->status = STATUS_OFFLINE;
2164 lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
2172 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2175 DWORD target_uin = 0L;
2178 /* If this is an incoming ICQ from someone on the contact list,
2179 * change the sender from "uin@icq" to the contact name.
2182 for (i=0; i<strlen(from); ++i)
2183 if (!strcasecmp(&from[i], "@icq")) {
2186 if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
2187 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2188 if (ThisICQ->icq_cl[i].uin == atol(from))
2189 strcpy(from, ThisICQ->icq_cl[i].name);
2194 /* Handle "uin@icq" syntax */
2196 for (i=0; i<strlen(recp); ++i)
2197 if (!strcasecmp(&recp[i], "@icq")) {
2200 if (is_aticq == 1) target_uin = atol(recp);
2202 /* Handle "nick" syntax */
2203 if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
2204 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2205 if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
2206 target_uin = ThisICQ->icq_cl[i].uin;
2212 if (target_uin == 0L) return(0);
2214 if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
2220 void cmd_icqa(char *argbuf) {
2223 uin = extract_long(argbuf, 0);
2225 cprintf("%d You must supply a uin.\n", ERROR);
2231 /* This function is normally used to obtain a relevant pointer, but
2232 * it also creates an entry in the contact list if one isn't there.
2236 /* Save the new contact list and tell the ICQ server about it */
2238 CtdlICQ_Refresh_Contact_List();
2240 /* Leave the user clueless as to what happened, because we really
2241 * don't know (and this is why I hate asynchronous protocols).
2243 cprintf("%d Ok (maybe)\n", OK);
2245 /* Request more info on this user from the server, which will arrive
2246 * at some time in the future. Maybe.
2248 icq_SendInfoReq(uin);
2253 * During an RWHO command, we want to append our ICQ information.
2255 void CtdlICQ_rwho(void) {
2258 if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
2259 if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
2260 cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
2261 0, /* no session ID */
2262 ThisICQ->icq_cl[i].name,
2263 icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
2264 " ", /* FIX add host */
2265 " ", /* no client */
2266 time(NULL), /* now? */
2267 " ", /* no last command */
2273 void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
2274 struct CtdlICQ_CL *ptr;
2276 ptr = CtdlICQ_CLent(uin);
2277 ptr->status = status;
2278 if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
2282 void CtdlICQ_Logged(void) {
2284 CtdlICQ_Refresh_Contact_List();
2288 void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
2289 DWORD port, DWORD realip) {
2291 CtdlICQ_Status_Update(uin, status);
2295 void CtdlICQ_UserOffline(DWORD uin) {
2296 CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
2300 char *Dynamic_Module_Init(void)
2302 /* Make sure we've got a valid ThisICQ for each session */
2303 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2305 /* Tell the Citadel server about our wonderful ICQ hooks */
2306 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2307 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2308 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2309 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2310 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2311 CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
2312 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2313 CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add ICQ contact");
2314 CtdlRegisterXmsgHook(CtdlICQ_Send_Msg);
2316 /* Tell the code formerly known as icqlib about our callbacks */
2317 icq_Log = CtdlICQlog;
2318 icq_RecvMessage = CtdlICQ_Incoming_Message;
2319 icq_InfoReply = CtdlICQ_InfoReply;
2320 icq_Disconnected = CtdlICQ_Login_If_Possible;
2321 icq_Logged = CtdlICQ_Logged;
2322 icq_UserStatusUpdate = CtdlICQ_Status_Update;
2323 icq_UserOnline = CtdlICQ_UserOnline;
2324 icq_UserOffline = CtdlICQ_UserOffline;