4 * This is a modified version of Denis V. Dmitrienko's 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>
27 #include <arpa/inet.h>
41 #include "dynloader.h"
43 #include "citserver.h"
44 #include "locate_host.h"
46 #include "sysdep_decls.h"
52 * Contact list in memory
64 /* ICQROOM is the name of the room in which each user's ICQ configuration
65 * and contact lists will be stored. (It's a personal room.)
67 #define ICQROOM "My ICQ Config"
69 /* MIME types to use for storing ICQ stuff */
70 #define ICQMIME "application/x-citadel-icq" /* configuration */
71 #define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
73 /* Citadel server TSD symbol for use by serv_icq */
74 unsigned long SYM_CTDL_ICQ;
75 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
77 extern struct CitContext *ContextList;
82 void (*icq_Logged) (void);
83 void (*icq_Disconnected) (void);
84 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
85 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
86 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);
87 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);
88 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
89 void (*icq_SearchDone) (void);
90 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
91 void (*icq_UserOffline) (DWORD uin);
92 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
93 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
94 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);
95 void (*icq_Log) (time_t time, unsigned char level, const char *str);
96 void (*icq_SrvAck) (WORD seq);
99 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
100 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
101 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
102 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
103 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
104 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
105 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
106 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
109 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
110 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
111 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
112 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
113 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
114 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
115 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
116 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
118 static COUNTRY_CODE Country_Codes[] =
133 {"American Samoa", 684},
148 {"Czech Republic", 42},
151 {"El Salvador", 503},
155 {"French Antilles", 596},
156 {"French Polynesia", 689},
163 {"Guantanomo Bay", 53},
177 {"Ivory Coast", 225},
185 {"Liechtenstein", 41},
197 {"Netherlands Antilles", 599},
198 {"New Caledonia", 687},
205 {"Papua New Guinea", 675},
215 {"Saudia Arabia", 966},
220 {"South Africa", 27},
231 {"United Arab Emirates", 971},
233 {"Vatican City", 39},
240 {"Not entered", 0xffff}};
242 void icq_init_handle(struct ctdl_icq_handle *i)
244 memset(i, 0, sizeof(struct ctdl_icq_handle));
245 i->icq_Russian = TRUE;
247 i->icq_OurIp = 0x0100007f;
248 i->icq_Status = STATUS_OFFLINE;
253 int icq_SockWrite(int sok, const void *buf, size_t count)
256 if (!(ThisICQ->icq_UseProxy))
257 return write(sok, buf, count);
259 tmpbuf[0] = 0; /* reserved */
260 tmpbuf[1] = 0; /* reserved */
261 tmpbuf[2] = 0; /* standalone packet */
262 tmpbuf[3] = 1; /* address type IP v4 */
263 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
264 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
265 memcpy(&tmpbuf[10], buf, count);
266 return write(sok, tmpbuf, count + 10) - 10;
270 int icq_SockRead(int sok, void *buf, size_t count)
274 if (!(ThisICQ->icq_UseProxy))
275 return read(sok, buf, count);
277 res = read(sok, tmpbuf, count + 10);
278 memcpy(buf, &tmpbuf[10], res - 10);
283 /****************************************
284 This must be called every 2 min.
285 so the server knows we're still alive.
286 JAVA client sends two different commands
288 *****************************************/
293 Word_2_Chars(pak.head.ver, ICQ_VER);
294 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
295 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
296 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
297 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
299 Word_2_Chars(pak.head.ver, ICQ_VER);
300 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
301 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
302 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
303 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
305 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
306 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
309 /**********************************
310 This must be called to remove
311 messages from the server
312 ***********************************/
313 void icq_SendGotMessages()
317 Word_2_Chars(pak.head.ver, ICQ_VER);
318 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
319 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
320 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
322 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
325 /*************************************
326 this sends over the contact list
327 *************************************/
328 void icq_SendContactList()
334 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
336 Word_2_Chars(pak.head.ver, ICQ_VER);
337 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
338 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
339 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
345 DW_2_Chars(tmp, ptr->uin);
350 pak.data[0] = num_used;
351 size = ((int) tmp - (int) pak.data);
352 size += sizeof(pak.head);
353 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
356 void icq_SendNewUser(unsigned long uin)
361 Word_2_Chars(pak.head.ver, ICQ_VER);
362 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
363 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
364 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
365 DW_2_Chars(pak.data, uin);
366 size = sizeof(pak.head) + 4;
367 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
370 /*************************************
371 this sends over the visible list
372 that allows certain users to see you
374 *************************************/
375 void icq_SendVisibleList()
381 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
383 Word_2_Chars(pak.head.ver, ICQ_VER);
384 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
385 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
386 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
393 DW_2_Chars(tmp, ptr->uin);
400 pak.data[0] = num_used;
401 size = ((int) tmp - (int) pak.data);
402 size += sizeof(pak.head);
403 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
407 /**************************************
408 This sends the second login command
409 this is necessary to finish logging in.
410 ***************************************/
411 void icq_SendLogin1()
415 Word_2_Chars(pak.head.ver, ICQ_VER);
416 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
417 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
418 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
420 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
423 /************************************************
424 This is called when a user goes offline
425 *************************************************/
426 void icq_HandleUserOffline(srv_net_icq_pak pak)
431 remote_uin = Chars_2_DW(pak.data);
432 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
433 sprintf(buf, "User %lu logged off\n", remote_uin);
434 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
437 (*icq_UserOffline) (remote_uin);
438 icq_AckSrv(Chars_2_Word(pak.head.seq));
441 void icq_HandleUserOnline(srv_net_icq_pak pak)
443 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
444 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
447 remote_uin = Chars_2_DW(pak.data);
448 new_status = Chars_2_DW(&pak.data[17]);
449 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
450 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
451 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
452 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
453 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
454 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
457 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
458 icq_AckSrv(Chars_2_Word(pak.head.seq));
461 void icq_Status_Update(srv_net_icq_pak pak)
463 unsigned long remote_uin, new_status;
466 remote_uin = Chars_2_DW(pak.data);
467 new_status = Chars_2_DW(&pak.data[4]);
468 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
469 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
470 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
472 if (icq_UserStatusUpdate)
473 (*icq_UserStatusUpdate) (remote_uin, new_status);
474 icq_AckSrv(Chars_2_Word(pak.head.seq));
477 /************************************
478 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
479 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
480 It does NOT wait for any kind of a response.
481 *************************************/
482 void icq_Login(DWORD status)
489 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
490 Word_2_Chars(pak.head.ver, ICQ_VER);
491 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
492 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
493 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
495 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
496 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
498 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
499 DW_2_Chars(s2.status, status);
500 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
502 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
503 s2.X2[0] = LOGIN_X2_DEF;
504 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
505 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
506 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
508 memcpy(pak.data, &s1, sizeof(s1));
510 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
511 size += Chars_2_Word(s1.len);
512 memcpy(&pak.data[size], &s2, sizeof(s2));
514 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
517 /*******************************
518 This routine sends the aknowlegement cmd to the
519 server it appears that this must be done after
520 everything the server sends us
521 *******************************/
522 void icq_AckSrv(int seq)
527 Word_2_Chars(pak.head.ver, ICQ_VER);
528 Word_2_Chars(pak.head.cmd, CMD_ACK);
529 Word_2_Chars(pak.head.seq, seq);
530 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
531 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
532 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
533 for (i = 0; i < 6; i++)
534 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
537 void icq_HandleInfoReply(srv_net_icq_pak pak)
539 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
543 seq = Chars_2_Word(pak.data);
544 uin = Chars_2_DW(&pak.data[2]);
545 len = Chars_2_Word(&pak.data[6]);
547 icq_RusConv("wk", ptr1);
548 tmp = &pak.data[8 + len];
549 len = Chars_2_Word(tmp);
551 icq_RusConv("wk", ptr2);
553 len = Chars_2_Word(tmp);
555 icq_RusConv("wk", ptr3);
557 len = Chars_2_Word(tmp);
559 icq_RusConv("wk", ptr4);
561 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
562 sprintf(buf, "Info reply for %lu\n", uin);
563 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
566 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
567 icq_AckSrv(Chars_2_Word(pak.head.seq));
570 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
572 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
576 char cnt_stat, gender, buf[256];
577 uin = Chars_2_DW(&pak.data[2]);
578 len = Chars_2_Word(&pak.data[6]);
580 icq_RusConv("wk", ptr1);
581 cnt_code = Chars_2_Word(&pak.data[8 + len]);
582 cnt_stat = pak.data[len + 10];
583 tmp = &pak.data[11 + len];
584 len = Chars_2_Word(tmp);
585 icq_RusConv("wk", tmp + 2);
587 age = Chars_2_Word(tmp + 2 + len);
588 gender = *(tmp + len + 4);
590 len = Chars_2_Word(tmp);
591 icq_RusConv("wk", tmp + 2);
594 len = Chars_2_Word(tmp);
595 icq_RusConv("wk", tmp + 2);
598 len = Chars_2_Word(tmp);
599 icq_RusConv("wk", tmp + 2);
601 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
602 sprintf(buf, "Extended info reply for %lu\n", uin);
603 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
605 if (icq_ExtInfoReply)
606 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
607 icq_AckSrv(Chars_2_Word(pak.head.seq));
610 void icq_HandleSearchReply(srv_net_icq_pak pak)
612 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
616 uin = Chars_2_DW(&pak.data[2]);
617 len = Chars_2_Word(&pak.data[6]);
619 icq_RusConv("wk", ptr1);
620 tmp = &pak.data[8 + len];
621 len = Chars_2_Word(tmp);
623 icq_RusConv("wk", ptr2);
625 len = Chars_2_Word(tmp);
627 icq_RusConv("wk", ptr3);
629 len = Chars_2_Word(tmp);
631 icq_RusConv("wk", ptr4);
633 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
634 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");
635 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
638 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
639 icq_AckSrv(Chars_2_Word(pak.head.seq));
642 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
645 char *ptr1, *ptr2, *ptr3, *ptr4;
649 case USER_ADDED_MESS:
650 tmp = strchr(data, '\xFE');
652 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
653 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
660 tmp = strchr(tmp, '\xFE');
662 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
663 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
667 icq_RusConv("wk", data);
671 tmp = strchr(tmp, '\xFE');
673 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
674 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
678 icq_RusConv("wk", data);
682 tmp = strchr(tmp, '\xFE');
684 icq_RusConv("wk", data);
685 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
686 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
687 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
688 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
691 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
694 tmp = strchr(data, '\xFE');
699 tmp = strchr(tmp, '\xFE');
701 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
702 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
706 icq_RusConv("wk", data);
710 tmp = strchr(tmp, '\xFE');
712 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
713 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
717 icq_RusConv("wk", data);
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");
741 tmp = strchr(tmp, '\x00');
743 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
744 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
748 icq_RusConv("wk", data);
749 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
750 sprintf(buf, "%lu has requested your authorization to be added to "
751 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
752 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
753 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
756 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
759 tmp = strchr(data, '\xFE');
761 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
762 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
766 icq_RusConv("wk", data);
770 icq_RusConv("wk", data);
771 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
772 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
773 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
776 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
779 icq_RusConv("wk", data);
780 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
781 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
782 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
785 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
789 /**********************************
790 Connects to hostname on port port
791 hostname can be DNS or nnn.nnn.nnn.nnn
792 write out messages to the FD aux
793 ***********************************/
794 int icq_Connect(const char *hostname, int port)
798 int conct, length, res;
799 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
800 struct hostent *host_struct; /* used in DNS llokup */
802 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
803 if ((ThisICQ->icq_Sok) == -1) {
804 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
805 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
808 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
809 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
810 sin.sin_addr.s_addr = INADDR_ANY;
811 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
813 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
814 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
815 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
818 length = sizeof(sin);
819 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
820 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
821 if ((ThisICQ->icq_UseProxy)) {
822 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
823 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
824 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
825 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
826 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
827 if (host_struct == 0L) {
828 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
829 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
830 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
834 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
836 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
837 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
838 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
839 if ((ThisICQ->icq_ProxySok) == -1) {
840 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
841 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
844 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
845 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
846 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
847 if (conct == -1) { /* did we connect ? */
848 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
849 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
852 buf[0] = 5; /* protocol version */
853 buf[1] = 1; /* number of methods */
854 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
855 buf[2] = 0; /* no authorization required */
857 buf[2] = 2; /* method username/password */
858 write((ThisICQ->icq_ProxySok), buf, 3);
859 res = read((ThisICQ->icq_ProxySok), buf, 2);
860 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
861 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
862 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
863 close((ThisICQ->icq_ProxySok));
866 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
867 buf[0] = 1; /* version of subnegotiation */
868 buf[1] = strlen((ThisICQ->icq_ProxyName));
869 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
870 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
871 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
872 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
873 res = read((ThisICQ->icq_ProxySok), buf, 2);
874 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
875 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
876 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
877 close((ThisICQ->icq_ProxySok));
881 buf[0] = 5; /* protocol version */
882 buf[1] = 3; /* command UDP associate */
883 buf[2] = 0; /* reserved */
884 buf[3] = 1; /* address type IP v4 */
889 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
890 write((ThisICQ->icq_ProxySok), buf, 10);
891 res = read((ThisICQ->icq_ProxySok), buf, 10);
892 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
895 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
896 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
899 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
900 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
903 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
904 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
907 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
908 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
911 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
912 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
915 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
916 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
919 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
920 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
923 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
924 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
927 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
928 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
931 close((ThisICQ->icq_ProxySok));
935 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
936 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
937 host_struct = gethostbyname(hostname);
938 if (host_struct == 0L) {
939 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
940 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
941 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
943 if ((ThisICQ->icq_UseProxy))
944 close((ThisICQ->icq_ProxySok));
947 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
949 if ((ThisICQ->icq_UseProxy)) {
950 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
951 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
953 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
954 sin.sin_port = htons(port); /* port */
955 if ((ThisICQ->icq_UseProxy)) {
956 (ThisICQ->icq_ProxyDestPort) = htons(port);
957 memcpy(&sin.sin_port, &buf[8], 2);
959 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
960 if (conct == -1) { /* did we connect ? */
961 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
962 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
963 if ((ThisICQ->icq_UseProxy))
964 close((ThisICQ->icq_ProxySok));
967 length = sizeof(sin);
968 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
969 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
970 (ThisICQ->icq_OurPort) = sin.sin_port;
971 return (ThisICQ->icq_Sok);
974 void icq_HandleProxyResponse()
978 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
980 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
981 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
982 if (icq_Disconnected)
983 (*icq_Disconnected) ();
984 SOCKCLOSE((ThisICQ->icq_Sok));
985 SOCKCLOSE((ThisICQ->icq_ProxySok));
989 /******************************************
990 Handles packets that the server sends to us.
991 *******************************************/
992 void icq_HandleServerResponse()
995 SIMPLE_MESSAGE *s_mesg;
996 RECV_MESSAGE *r_mesg;
1002 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1004 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1005 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1006 if (icq_Disconnected)
1007 (*icq_Disconnected) ();
1008 SOCKCLOSE((ThisICQ->icq_Sok));
1010 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)) {
1011 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1012 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1013 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1014 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1016 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1020 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1021 icq_SetServMess(Chars_2_Word(pak.head.seq));
1022 switch (Chars_2_Word(pak.head.cmd)) {
1024 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1025 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1027 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1030 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1031 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1032 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1035 case SRV_LOGIN_REPLY:
1036 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1037 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1038 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]);
1039 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1041 icq_AckSrv(Chars_2_Word(pak.head.seq));
1043 icq_SendContactList();
1044 icq_SendVisibleList();
1048 case SRV_RECV_MESSAGE:
1049 r_mesg = (RECV_MESSAGE *) pak.data;
1050 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));
1051 icq_AckSrv(Chars_2_Word(pak.head.seq));
1053 case SRV_X1: /* unknown message sent after login */
1054 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1055 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1056 icq_AckSrv(Chars_2_Word(pak.head.seq));
1058 case SRV_X2: /* unknown message sent after login */
1059 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1060 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1061 icq_AckSrv(Chars_2_Word(pak.head.seq));
1062 icq_SendGotMessages();
1064 case SRV_INFO_REPLY:
1065 icq_HandleInfoReply(pak);
1067 case SRV_EXT_INFO_REPLY:
1068 icq_HandleExtInfoReply(pak);
1070 case SRV_USER_ONLINE:
1071 icq_HandleUserOnline(pak);
1073 case SRV_USER_OFFLINE:
1074 icq_HandleUserOffline(pak);
1077 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1078 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1079 icq_Login((ThisICQ->icq_Status));
1081 case SRV_STATUS_UPDATE:
1082 icq_Status_Update(pak);
1085 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1086 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1087 if (icq_Disconnected)
1088 (*icq_Disconnected) ();
1090 case SRV_END_OF_SEARCH:
1091 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1092 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1094 (*icq_SearchDone) ();
1095 icq_AckSrv(Chars_2_Word(pak.head.seq));
1097 case SRV_USER_FOUND:
1098 icq_HandleSearchReply(pak);
1100 case SRV_SYS_DELIVERED_MESS:
1101 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1102 cur_time = time(0L);
1103 tm_str = localtime(&cur_time);
1104 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);
1105 icq_AckSrv(Chars_2_Word(pak.head.seq));
1107 default: /* commands we dont handle yet */
1108 len = s - (sizeof(pak.head));
1109 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1110 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1111 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1112 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1114 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1119 void icq_Init(DWORD uin, const char *password)
1121 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1122 (ThisICQ->icq_Uin) = uin;
1123 if ((ThisICQ->icq_Password))
1124 phree((ThisICQ->icq_Password));
1125 (ThisICQ->icq_Password) = strdoop(password);
1130 if ((ThisICQ->icq_Password))
1131 phree((ThisICQ->icq_Password));
1134 /******************************
1135 Main function connects gets (ThisICQ->icq_Uin)
1136 and (ThisICQ->icq_Password) and logins in and sits
1137 in a loop waiting for server responses.
1138 *******************************/
1150 FD_SET((ThisICQ->icq_Sok), &readfds);
1151 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1152 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1153 icq_HandleServerResponse();
1157 } while (did_something);
1160 /********************************************************
1161 Russian language ICQ fix.
1162 Usual Windows ICQ users do use Windows 1251 encoding but
1163 unix users do use koi8 encoding, so we need to convert it.
1164 This function will convert string from windows 1251 to koi8
1165 or from koi8 to windows 1251.
1166 Andrew Frolov dron@ilm.net
1167 *********************************************************/
1168 void icq_RusConv(const char to[4], char *t_in)
1173 /* 6-17-1998 by Linux_Dude
1174 * Moved initialization of table out front of 'if' block to prevent compiler
1175 * warning. Improved error message, and now return without performing string
1176 * conversion to prevent addressing memory out of range (table pointer would
1177 * previously have remained uninitialized (= bad)).
1181 if (strcmp(to, "kw") == 0)
1183 else if (strcmp(to, "wk") != 0) {
1184 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1185 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1188 /* End Linux_Dude's changes ;) */
1190 if ((ThisICQ->icq_Russian)) {
1191 for (i = 0; t_in[i] != 0; i++) {
1194 t_in[i] = table[t_in[i] & 0177];
1199 /**************************************************
1200 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1202 ***************************************************/
1203 WORD icq_SendMessage(DWORD uin, const char *text)
1208 char buf[512]; /* message may be only 450 bytes long */
1210 strncpy(buf, text, 512);
1211 icq_RusConv("kw", buf);
1213 Word_2_Chars(pak.head.ver, ICQ_VER);
1214 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1215 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1216 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1217 DW_2_Chars(msg.uin, uin);
1218 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1219 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1220 memcpy(&pak.data, &msg, sizeof(msg));
1221 memcpy(&pak.data[8], buf, len + 1);
1222 size = sizeof(msg) + len + 1;
1223 for (i = 0; i < 6; i++)
1224 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1225 return (ThisICQ->icq_SeqNum) - 1;
1228 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1232 int size, len1, len2;
1233 char buf1[512], buf2[512];
1235 strncpy(buf1, descr, 512);
1236 strncpy(buf2, url, 512);
1237 /* Do we need to convert URL? */
1238 icq_RusConv("kw", buf2);
1239 len1 = strlen(buf1);
1240 len2 = strlen(buf2);
1241 Word_2_Chars(pak.head.ver, ICQ_VER);
1242 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1243 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1244 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1245 DW_2_Chars(msg.uin, uin);
1246 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1247 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1248 memcpy(&pak.data, &msg, sizeof(msg));
1249 memcpy(&pak.data[8], buf1, len1);
1250 pak.data[8 + len1] = 0xFE;
1251 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1252 size = sizeof(msg) + len1 + len2 + 2;
1253 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1254 return (ThisICQ->icq_SeqNum) - 1;
1257 /**************************************************
1258 Sends a authorization to the server so the Mirabilis
1259 client can add the user.
1260 ***************************************************/
1261 void icq_SendAuthMsg(DWORD uin)
1267 Word_2_Chars(pak.head.ver, ICQ_VER);
1268 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1269 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1270 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1271 DW_2_Chars(msg.uin, uin);
1272 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1273 Word_2_Chars(msg.len, 2);
1274 memcpy(&pak.data, &msg, sizeof(msg));
1275 pak.data[sizeof(msg)] = 0x03;
1276 pak.data[sizeof(msg) + 1] = 0x00;
1277 size = sizeof(msg) + 2;
1278 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1281 /**************************************************
1282 Changes the users status on the server
1283 ***************************************************/
1284 void icq_ChangeStatus(DWORD status)
1289 Word_2_Chars(pak.head.ver, ICQ_VER);
1290 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1291 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1292 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1293 DW_2_Chars(pak.data, status);
1294 (ThisICQ->icq_Status) = status;
1296 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1299 /**********************
1301 ***********************/
1307 Word_2_Chars(pak.head.ver, ICQ_VER);
1308 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1309 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1310 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1311 len = strlen("B_USER_DISCONNECTED") + 1;
1312 *(short *) pak.data = len;
1314 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1315 pak.data[2 + len] = 05;
1316 pak.data[3 + len] = 00;
1317 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1320 void icq_Disconnect()
1322 SOCKCLOSE((ThisICQ->icq_Sok));
1323 SOCKCLOSE((ThisICQ->icq_Sok));
1324 if ((ThisICQ->icq_UseProxy))
1325 SOCKCLOSE((ThisICQ->icq_ProxySok));
1328 /********************************************************
1329 Sends a request to the server for info on a specific user
1330 *********************************************************/
1331 WORD icq_SendInfoReq(DWORD uin)
1336 Word_2_Chars(pak.head.ver, ICQ_VER);
1337 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1338 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1339 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1340 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1341 DW_2_Chars(&pak.data[2], uin);
1343 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1344 return (ThisICQ->icq_SeqNum) - 1;
1347 /********************************************************
1348 Sends a request to the server for info on a specific user
1349 *********************************************************/
1350 WORD icq_SendExtInfoReq(DWORD uin)
1355 Word_2_Chars(pak.head.ver, ICQ_VER);
1356 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1357 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1358 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1359 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1360 DW_2_Chars(&pak.data[2], uin);
1362 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1363 return (ThisICQ->icq_SeqNum) - 1;
1366 /**************************************************************
1367 Initializes a server search for the information specified
1368 ***************************************************************/
1369 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1374 Word_2_Chars(pak.head.ver, ICQ_VER);
1375 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1376 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1377 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1378 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1380 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1382 strcpy(pak.data + size, nick);
1383 size += strlen(nick) + 1;
1384 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1386 strcpy(pak.data + size, first);
1387 size += strlen(first) + 1;
1388 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1390 strcpy(pak.data + size, last);
1391 size += strlen(last) + 1;
1392 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1394 strcpy(pak.data + size, email);
1395 size += strlen(email) + 1;
1396 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1399 /**************************************************************
1400 Initializes a server search for the information specified
1401 ***************************************************************/
1402 void icq_SendSearchUINReq(DWORD uin)
1407 Word_2_Chars(pak.head.ver, ICQ_VER);
1408 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1409 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1410 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1411 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1413 DW_2_Chars(&pak.data[size], uin);
1415 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1418 /**************************************************
1419 Registers a new uin in the ICQ network
1420 ***************************************************/
1421 void icq_RegNewUser(const char *pass)
1423 srv_net_icq_pak pak;
1428 Word_2_Chars(pak.head.ver, ICQ_VER);
1429 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1430 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1431 Word_2_Chars(len_buf, len);
1432 memcpy(&pak.data, "\x02\x00", 2);
1433 memcpy(&pak.data[2], &len_buf, 2);
1434 memcpy(&pak.data[4], pass, len + 1);
1435 DW_2_Chars(&pak.data[4 + len], 0x0072);
1436 DW_2_Chars(&pak.data[8 + len], 0x0000);
1438 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1441 void icq_UpdateUserInfo(USER_INFO * user)
1446 Word_2_Chars(pak.head.ver, ICQ_VER);
1447 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1448 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1449 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1450 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1452 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1454 strcpy(pak.data + size, user->nick);
1455 size += strlen(user->nick) + 1;
1456 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1458 strcpy(pak.data + size, user->first);
1459 size += strlen(user->first) + 1;
1460 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1462 strcpy(pak.data + size, user->last);
1463 size += strlen(user->last) + 1;
1464 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1466 strcpy(pak.data + size, user->email);
1467 size += strlen(user->email) + 1;
1468 pak.data[size] = user->auth;
1470 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1473 const char *icq_GetCountryName(int code)
1477 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1478 if (Country_Codes[i].code == code) {
1479 return Country_Codes[i].name;
1482 if (Country_Codes[i].code == code) {
1483 return Country_Codes[i].name;
1488 /********************************************
1489 returns a string describing the status or
1490 a "Error" if no such string exists
1491 *********************************************/
1492 const char *icq_ConvertStatus2Str(int status)
1494 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1497 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1498 switch (status & 0x01FF) {
1503 return "Do not disturb";
1508 case STATUS_OCCUPIED:
1512 return "Not available";
1514 case STATUS_INVISIBLE:
1517 case STATUS_INVISIBLE_2:
1518 return "Invisible mode 2";
1520 case STATUS_FREE_CHAT:
1521 return "Free for chat";
1529 void icq_InitNewUser(const char *hostname, DWORD port)
1531 srv_net_icq_pak pak;
1536 icq_Connect(hostname, port);
1537 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1538 printf("Couldn't establish connection\n");
1541 icq_RegNewUser((ThisICQ->icq_Password));
1544 tv.tv_usec = 500000;
1547 FD_SET((ThisICQ->icq_Sok), &readfds);
1549 /* don't care about writefds and exceptfds: */
1550 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1552 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1553 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1554 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1555 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1562 /********************************************
1563 Converts an intel endian character sequence to
1565 *********************************************/
1566 DWORD Chars_2_DW(unsigned char *buf)
1581 /********************************************
1582 Converts an intel endian character sequence to
1584 *********************************************/
1585 WORD Chars_2_Word(unsigned char *buf)
1596 /********************************************
1598 an intel endian character sequence
1599 *********************************************/
1600 void DW_2_Chars(unsigned char *buf, DWORD num)
1602 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1603 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1604 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1605 buf[0] = (unsigned char) (num) & 0x000000FF;
1608 /********************************************
1610 an intel endian character sequence
1611 *********************************************/
1612 void Word_2_Chars(unsigned char *buf, WORD num)
1614 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1615 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1618 /***************************
1619 ContactList functions
1620 ***************************/
1621 void icq_ContAddUser(DWORD cuin)
1623 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1624 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1627 p->vis_list = FALSE;
1633 (ThisICQ->icq_ContFirst) = p;
1636 void icq_ContDelUser(DWORD cuin)
1638 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1641 if (ptr->uin == cuin) {
1642 (ThisICQ->icq_ContFirst) = ptr->next;
1644 ptr = (ThisICQ->icq_ContFirst);
1647 if (ptr->next->uin == cuin) {
1648 ptr->next = ptr->next->next;
1655 void icq_ContClear()
1657 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1662 (ThisICQ->icq_ContFirst) = ptr;
1666 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1668 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1672 if (ptr->uin == cuin)
1679 icq_ContactItem *icq_ContGetFirst()
1681 return (ThisICQ->icq_ContFirst);
1684 void icq_ContSetVis(DWORD cuin)
1686 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1690 if (ptr->uin == cuin)
1691 ptr->vis_list = TRUE;
1696 /************************
1697 (ThisICQ->icq_ServMess) functions
1698 *************************/
1699 BOOL icq_GetServMess(WORD num)
1701 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1704 void icq_SetServMess(WORD num)
1706 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1711 return (ThisICQ->icq_Sok);
1714 int icq_GetProxySok()
1716 return (ThisICQ->icq_ProxySok);
1719 /*******************
1720 SOCKS5 Proxy support
1721 ********************/
1722 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1724 if ((ThisICQ->icq_ProxyHost))
1725 phree((ThisICQ->icq_ProxyHost));
1726 if ((ThisICQ->icq_ProxyName))
1727 phree((ThisICQ->icq_ProxyName));
1728 if ((ThisICQ->icq_ProxyPass))
1729 phree((ThisICQ->icq_ProxyPass));
1730 if (strlen(pname) > 255) {
1731 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1732 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1733 ThisICQ->icq_UseProxy = 0;
1736 if (strlen(ppass) > 255) {
1737 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1738 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1739 (ThisICQ->icq_UseProxy) = 0;
1742 (ThisICQ->icq_UseProxy) = 1;
1743 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1744 (ThisICQ->icq_ProxyPort) = pport;
1745 (ThisICQ->icq_ProxyAuth) = pauth;
1746 (ThisICQ->icq_ProxyName) = strdoop(pname);
1747 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1750 void icq_UnsetProxy()
1752 ThisICQ->icq_UseProxy = 0;
1757 /***********************************************************************/
1758 /* icqlib stuff ends here, Citadel module stuff begins */
1759 /***********************************************************************/
1763 * Callback function for CtdlICQ_Read_Config()
1765 void CtdlICQ_Read_Config_Backend(long msgnum) {
1766 struct CtdlMessage *msg;
1770 lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
1771 msg = CtdlFetchMessage(msgnum);
1773 ptr = msg->cm_fields['M'];
1774 pos = pattern2(ptr, "\n\n");
1782 safestrncpy(ThisICQ->icq_config, ptr, 256);
1784 CtdlFreeMessage(msg);
1786 lprintf(9, "...it ain't there?\n");
1792 * If this user has an ICQ configuration on disk, read it into memory.
1794 void CtdlICQ_Read_Config(void) {
1795 char hold_rm[ROOMNAMELEN];
1796 char icq_rm[ROOMNAMELEN];
1798 strcpy(hold_rm, CC->quickroom.QRname);
1799 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1800 strcpy(ThisICQ->icq_config, "");
1802 if (getroom(&CC->quickroom, icq_rm) != 0) {
1803 getroom(&CC->quickroom, hold_rm);
1807 /* We want the last (and probably only) config in this room */
1808 lprintf(9, "We're in <%s> looking for config\n",
1809 CC->quickroom.QRname);
1810 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1811 getroom(&CC->quickroom, hold_rm);
1818 * Write our config to disk
1820 void CtdlICQ_Write_Config(void) {
1821 char temp[PATH_MAX];
1824 strcpy(temp, tmpnam(NULL));
1826 fp = fopen(temp, "w");
1827 if (fp == NULL) return;
1828 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1831 /* this handy API function does all the work for us */
1832 CtdlWriteObject(ICQROOM, ICQMIME, temp, &CC->usersupp, 0, 1);
1842 * Write our contact list to disk
1844 void CtdlICQ_Write_CL(void) {
1845 char temp[PATH_MAX];
1849 strcpy(temp, tmpnam(NULL));
1851 fp = fopen(temp, "w");
1852 if (fp == NULL) return;
1854 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1855 fprintf(fp, "%ld|%s|\n",
1856 ThisICQ->icq_cl[i].uin,
1857 ThisICQ->icq_cl[i].name);
1861 /* this handy API function does all the work for us */
1862 CtdlWriteObject(ICQROOM, ICQCLMIME, temp, &CC->usersupp, 0, 1);
1872 * Callback function for CtdlICQ_Read_CL()
1874 void CtdlICQ_Read_CL_Backend(long msgnum) {
1875 struct CtdlMessage *msg;
1880 msg = CtdlFetchMessage(msgnum);
1882 ptr = msg->cm_fields['M'];
1883 pos = pattern2(ptr, "\n\n");
1891 for (i=0; i<strlen(ptr); ++i)
1892 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1893 if (ThisICQ->icq_numcl) {
1894 ThisICQ->icq_cl = mallok(
1895 (ThisICQ->icq_numcl *
1896 sizeof (struct CtdlICQ_CL)));
1898 while (cont=strtok(ptr, "\n"), cont != NULL) {
1899 ThisICQ->icq_cl[i].uin =
1900 extract_long(cont, 0);
1901 extract(ThisICQ->icq_cl[i].name,
1903 ThisICQ->icq_cl[i].status =
1910 CtdlFreeMessage(msg);
1916 * Read contact list into memory
1918 void CtdlICQ_Read_CL(void) {
1919 char hold_rm[ROOMNAMELEN];
1920 char icq_rm[ROOMNAMELEN];
1922 strcpy(hold_rm, CC->quickroom.QRname);
1923 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1924 strcpy(ThisICQ->icq_config, "");
1926 if (getroom(&CC->quickroom, icq_rm) != 0) {
1927 getroom(&CC->quickroom, hold_rm);
1931 /* Free any contact list already in memory */
1932 if (ThisICQ->icq_numcl) {
1933 phree(ThisICQ->icq_cl);
1934 ThisICQ->icq_numcl = 0;
1937 /* We want the last (and probably only) list in this room */
1938 CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
1939 getroom(&CC->quickroom, hold_rm);
1944 * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
1945 * entry in the table if necessary
1947 struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
1950 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
1951 if (ThisICQ->icq_cl[i].uin == uin)
1952 return (&ThisICQ->icq_cl[i]);
1954 ++ThisICQ->icq_numcl;
1955 ThisICQ->icq_cl = reallok(ThisICQ->icq_cl,
1956 (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
1957 memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
1958 0, sizeof(struct CtdlICQ_CL));
1959 ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
1960 return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
1966 * Refresh the contact list
1968 void CtdlICQ_Refresh_Contact_List(void) {
1971 if (ThisICQ->icq_Sok < 0) return;
1975 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1976 if (ThisICQ->icq_cl[i].uin > 0L) {
1977 icq_ContAddUser(ThisICQ->icq_cl[i].uin);
1978 icq_ContSetVis(ThisICQ->icq_cl[i].uin);
1982 icq_SendContactList();
1990 * Utility routine to logout and disconnect from the ICQ server if we happen
1991 * to already be connected
1993 void CtdlICQ_Logout_If_Connected(void) {
1994 if (ThisICQ->icq_Sok >= 0) {
1997 ThisICQ->icq_Sok = (-1);
2003 * If we have an ICQ uin/password on file for this user, go ahead and try
2004 * to log on to the ICQ server.
2006 void CtdlICQ_Login_If_Possible(void) {
2010 CtdlICQ_Logout_If_Connected();
2011 CtdlICQ_Read_Config();
2013 uin = extract_long(ThisICQ->icq_config, 0);
2014 extract(pass, ThisICQ->icq_config, 1);
2016 if ( (uin > 0L) && (strlen(pass)>0) ) {
2017 icq_Init(uin, pass);
2018 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2019 icq_Login(STATUS_ONLINE);
2026 /* This merely hooks icqlib's log function into Citadel's log function. */
2027 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2029 lprintf(level, "ICQ: %s", str);
2034 * At the start of each Citadel server session, we have to allocate some
2035 * space for the Citadel-ICQ session for this thread. It'll be automatically
2036 * freed by the server when the session ends.
2038 void CtdlICQ_session_startup_hook(void)
2040 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2041 icq_init_handle(ThisICQ);
2047 * End-of-session cleanup
2049 void CtdlICQ_session_stopdown_hook(void) {
2056 * icq_Main() needs to be called as frequently as possible. We'll do it
2057 * following the completion of each Citadel server command.
2060 void CtdlICQ_after_cmd_hook(void)
2062 if (ThisICQ->icq_Sok >= 0) {
2063 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 60 ) {
2065 ThisICQ->icq_LastKeepAlive = time(NULL);
2073 * There are a couple of things we absolutely need to make sure of when we
2074 * kill off the session. One of them is that the sockets are all closed --
2075 * otherwise we could have a nasty file descriptor leak.
2077 void CtdlICQ_session_logout_hook(void)
2079 lprintf(9, "Shutting down ICQ\n");
2080 CtdlICQ_Logout_If_Connected();
2082 /* Free the memory used by the contact list */
2083 if (ThisICQ->icq_numcl) {
2084 phree(ThisICQ->icq_cl);
2085 ThisICQ->icq_numcl = 0;
2092 void CtdlICQ_session_login_hook(void)
2094 /* If this user has an ICQ config on file, start it up. */
2095 CtdlICQ_Login_If_Possible();
2104 * Here's what we have to do when an ICQ message arrives!
2106 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2107 BYTE day, BYTE month, WORD year,
2114 /* Default sender is 'uin@icq' syntax */
2115 sprintf(from, "%ld@icq", uin);
2117 /* Use the sender's name if we have it inthe contact list */
2118 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
2119 if (uin == ThisICQ->icq_cl[i].uin) {
2120 safestrncpy(from, ThisICQ->icq_cl[i].name, 256);
2124 num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2125 lprintf(9, "Delivered to %d users\n", num_delivered);
2130 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2131 const char *first, const char *last,
2132 const char *email, char auth) {
2134 struct CtdlICQ_CL *ptr;
2137 ptr = CtdlICQ_CLent(uin);
2138 safestrncpy(ptr->name, nick, 32);
2139 ptr->status = STATUS_OFFLINE;
2140 lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
2148 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2151 DWORD target_uin = 0L;
2154 /* If this is an incoming ICQ from someone on the contact list,
2155 * change the sender from "uin@icq" to the contact name.
2158 for (i=0; i<strlen(from); ++i)
2159 if (!strcasecmp(&from[i], "@icq")) {
2162 if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
2163 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2164 if (ThisICQ->icq_cl[i].uin == atol(from))
2165 strcpy(from, ThisICQ->icq_cl[i].name);
2170 /* Handle "uin@icq" syntax */
2172 for (i=0; i<strlen(recp); ++i)
2173 if (!strcasecmp(&recp[i], "@icq")) {
2176 if (is_aticq == 1) target_uin = atol(recp);
2178 /* Handle "nick" syntax */
2179 if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
2180 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2181 if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
2182 target_uin = ThisICQ->icq_cl[i].uin;
2188 if (target_uin == 0L) return(0);
2190 if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
2196 void cmd_cicq(char *argbuf) {
2203 if (!(CC->logged_in)) {
2204 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
2207 extract(cmd, argbuf, 0);
2209 /* "CICQ login" tells us how to log in. */
2210 if (!strcasecmp(cmd, "login")) {
2211 uin = extract_long(argbuf, 1);
2212 extract(pass, argbuf, 2);
2213 sprintf(ThisICQ->icq_config, "%ld|%s|", uin, pass);
2215 CtdlICQ_Write_Config();
2216 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2217 CtdlICQ_Login_If_Possible();
2219 cprintf("%d You must supply a UIN.\n", ERROR);
2224 /* "CICQ getcl" returns the contact list */
2225 if (!strcasecmp(cmd, "getcl")) {
2227 cprintf("%d Your ICQ contact list:\n", LISTING_FOLLOWS);
2228 if (ThisICQ->icq_numcl > 0) {
2229 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2230 cprintf("%ld|%s|%s|\n",
2231 ThisICQ->icq_cl[i].uin,
2232 ThisICQ->icq_cl[i].name,
2233 icq_ConvertStatus2Str(
2234 ThisICQ->icq_cl[i].status)
2242 /* "CICQ putcl" accepts a new contact list from the client */
2243 if (!strcasecmp(cmd, "putcl")) {
2244 cprintf("%d Send contact list\n", SEND_LISTING);
2245 ThisICQ->icq_numcl = 0;
2246 while (client_gets(buf), strcmp(buf, "000")) {
2247 uin = extract_long(buf, 0);
2253 CtdlICQ_Refresh_Contact_List();
2257 /* "CICQ status" returns the connected/notconnected status */
2258 if (!strcasecmp(cmd, "status")) {
2259 cprintf("%d %d\n", OK,
2260 ((ThisICQ->icq_Sok >= 0) ? 1 : 0) );
2264 cprintf("%d Invalid subcommand\n", ERROR);
2272 * During an RWHO command, we want to append our ICQ information.
2274 void CtdlICQ_rwho(void) {
2277 if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
2278 if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
2279 cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
2280 0, /* no session ID */
2281 ThisICQ->icq_cl[i].name,
2282 icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
2283 ThisICQ->icq_cl[i].host,
2284 " ", /* no client */
2285 time(NULL), /* now? */
2286 " ", /* no last command */
2292 void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
2293 struct CtdlICQ_CL *ptr;
2295 ptr = CtdlICQ_CLent(uin);
2296 ptr->status = status;
2297 if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
2301 void CtdlICQ_Logged(void) {
2302 CtdlICQ_Refresh_Contact_List();
2306 void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
2307 DWORD port, DWORD realip) {
2311 CtdlICQ_Status_Update(uin, status);
2312 decoded_ip = ntohl(ip);
2313 locate_host(CtdlICQ_CLent(uin)->host, (struct in_addr *)&decoded_ip);
2317 void CtdlICQ_UserOffline(DWORD uin) {
2318 CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
2322 char *Dynamic_Module_Init(void)
2324 /* Make sure we've got a valid ThisICQ for each session */
2325 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2327 /* Tell the Citadel server about our wonderful ICQ hooks */
2328 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2329 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2330 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2331 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2332 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2333 CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
2334 CtdlRegisterProtoHook(cmd_cicq, "CICQ", "Configure Citadel ICQ");
2335 CtdlRegisterXmsgHook(CtdlICQ_Send_Msg, XMSG_PRI_FOREIGN);
2337 /* Tell the code formerly known as icqlib about our callbacks */
2338 icq_Log = CtdlICQlog;
2339 icq_RecvMessage = CtdlICQ_Incoming_Message;
2340 icq_InfoReply = CtdlICQ_InfoReply;
2341 icq_Disconnected = CtdlICQ_Login_If_Possible;
2342 icq_Logged = CtdlICQ_Logged;
2343 icq_UserStatusUpdate = CtdlICQ_Status_Update;
2344 icq_UserOnline = CtdlICQ_UserOnline;
2345 icq_UserOffline = CtdlICQ_UserOffline;