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>
40 #include "dynloader.h"
42 #include "citserver.h"
44 #include "sysdep_decls.h"
50 * Contact list in memory
62 /* ICQROOM is the name of the room in which each user's ICQ configuration
63 * and contact lists will be stored. (It's a personal room.)
65 #define ICQROOM "My ICQ Config"
67 /* MIME types to use for storing ICQ stuff */
68 #define ICQMIME "application/x-citadel-icq" /* configuration */
69 #define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
71 /* Citadel server TSD symbol for use by serv_icq */
72 unsigned long SYM_CTDL_ICQ;
73 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
75 extern struct CitContext *ContextList;
80 void (*icq_Logged) (void);
81 void (*icq_Disconnected) (void);
82 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
83 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
84 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);
85 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);
86 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
87 void (*icq_SearchDone) (void);
88 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
89 void (*icq_UserOffline) (DWORD uin);
90 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
91 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
92 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);
93 void (*icq_Log) (time_t time, unsigned char level, const char *str);
94 void (*icq_SrvAck) (WORD seq);
97 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
98 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
99 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
100 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
101 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
102 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
103 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
104 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
107 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
108 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
109 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
110 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
111 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
112 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
113 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
114 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
116 static COUNTRY_CODE Country_Codes[] =
131 {"American Samoa", 684},
146 {"Czech Republic", 42},
149 {"El Salvador", 503},
153 {"French Antilles", 596},
154 {"French Polynesia", 689},
161 {"Guantanomo Bay", 53},
175 {"Ivory Coast", 225},
183 {"Liechtenstein", 41},
195 {"Netherlands Antilles", 599},
196 {"New Caledonia", 687},
203 {"Papua New Guinea", 675},
213 {"Saudia Arabia", 966},
218 {"South Africa", 27},
229 {"United Arab Emirates", 971},
231 {"Vatican City", 39},
238 {"Not entered", 0xffff}};
240 void icq_init_handle(struct ctdl_icq_handle *i)
242 memset(i, 0, sizeof(struct ctdl_icq_handle));
243 i->icq_Russian = TRUE;
245 i->icq_OurIp = 0x0100007f;
246 i->icq_Status = STATUS_OFFLINE;
251 int icq_SockWrite(int sok, const void *buf, size_t count)
254 if (!(ThisICQ->icq_UseProxy))
255 return write(sok, buf, count);
257 tmpbuf[0] = 0; /* reserved */
258 tmpbuf[1] = 0; /* reserved */
259 tmpbuf[2] = 0; /* standalone packet */
260 tmpbuf[3] = 1; /* address type IP v4 */
261 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
262 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
263 memcpy(&tmpbuf[10], buf, count);
264 return write(sok, tmpbuf, count + 10) - 10;
268 int icq_SockRead(int sok, void *buf, size_t count)
272 if (!(ThisICQ->icq_UseProxy))
273 return read(sok, buf, count);
275 res = read(sok, tmpbuf, count + 10);
276 memcpy(buf, &tmpbuf[10], res - 10);
281 /****************************************
282 This must be called every 2 min.
283 so the server knows we're still alive.
284 JAVA client sends two different commands
286 *****************************************/
291 Word_2_Chars(pak.head.ver, ICQ_VER);
292 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
293 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
294 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
295 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
297 Word_2_Chars(pak.head.ver, ICQ_VER);
298 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
299 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
300 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
301 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
303 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
304 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
307 /**********************************
308 This must be called to remove
309 messages from the server
310 ***********************************/
311 void icq_SendGotMessages()
315 Word_2_Chars(pak.head.ver, ICQ_VER);
316 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
317 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
318 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
320 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
323 /*************************************
324 this sends over the contact list
325 *************************************/
326 void icq_SendContactList()
332 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
334 Word_2_Chars(pak.head.ver, ICQ_VER);
335 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
336 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
337 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
343 DW_2_Chars(tmp, ptr->uin);
348 pak.data[0] = num_used;
349 size = ((int) tmp - (int) pak.data);
350 size += sizeof(pak.head);
351 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
354 void icq_SendNewUser(unsigned long uin)
359 Word_2_Chars(pak.head.ver, ICQ_VER);
360 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
361 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
362 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
363 DW_2_Chars(pak.data, uin);
364 size = sizeof(pak.head) + 4;
365 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
368 /*************************************
369 this sends over the visible list
370 that allows certain users to see you
372 *************************************/
373 void icq_SendVisibleList()
379 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
381 Word_2_Chars(pak.head.ver, ICQ_VER);
382 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
383 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
384 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
391 DW_2_Chars(tmp, ptr->uin);
398 pak.data[0] = num_used;
399 size = ((int) tmp - (int) pak.data);
400 size += sizeof(pak.head);
401 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
405 /**************************************
406 This sends the second login command
407 this is necessary to finish logging in.
408 ***************************************/
409 void icq_SendLogin1()
413 Word_2_Chars(pak.head.ver, ICQ_VER);
414 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
415 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
416 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
418 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
421 /************************************************
422 This is called when a user goes offline
423 *************************************************/
424 void icq_HandleUserOffline(srv_net_icq_pak pak)
429 remote_uin = Chars_2_DW(pak.data);
430 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
431 sprintf(buf, "User %lu logged off\n", remote_uin);
432 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
435 (*icq_UserOffline) (remote_uin);
436 icq_AckSrv(Chars_2_Word(pak.head.seq));
439 void icq_HandleUserOnline(srv_net_icq_pak pak)
441 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
442 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
445 remote_uin = Chars_2_DW(pak.data);
446 new_status = Chars_2_DW(&pak.data[17]);
447 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
448 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
449 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
450 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
451 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
452 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
455 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
456 icq_AckSrv(Chars_2_Word(pak.head.seq));
459 void icq_Status_Update(srv_net_icq_pak pak)
461 unsigned long remote_uin, new_status;
464 remote_uin = Chars_2_DW(pak.data);
465 new_status = Chars_2_DW(&pak.data[4]);
466 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
467 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
468 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
470 if (icq_UserStatusUpdate)
471 (*icq_UserStatusUpdate) (remote_uin, new_status);
472 icq_AckSrv(Chars_2_Word(pak.head.seq));
475 /************************************
476 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
477 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
478 It does NOT wait for any kind of a response.
479 *************************************/
480 void icq_Login(DWORD status)
487 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
488 Word_2_Chars(pak.head.ver, ICQ_VER);
489 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
490 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
491 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
493 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
494 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
496 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
497 DW_2_Chars(s2.status, status);
498 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
500 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
501 s2.X2[0] = LOGIN_X2_DEF;
502 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
503 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
504 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
506 memcpy(pak.data, &s1, sizeof(s1));
508 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
509 size += Chars_2_Word(s1.len);
510 memcpy(&pak.data[size], &s2, sizeof(s2));
512 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
515 /*******************************
516 This routine sends the aknowlegement cmd to the
517 server it appears that this must be done after
518 everything the server sends us
519 *******************************/
520 void icq_AckSrv(int seq)
525 Word_2_Chars(pak.head.ver, ICQ_VER);
526 Word_2_Chars(pak.head.cmd, CMD_ACK);
527 Word_2_Chars(pak.head.seq, seq);
528 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
529 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
530 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
531 for (i = 0; i < 6; i++)
532 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
535 void icq_HandleInfoReply(srv_net_icq_pak pak)
537 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
541 seq = Chars_2_Word(pak.data);
542 uin = Chars_2_DW(&pak.data[2]);
543 len = Chars_2_Word(&pak.data[6]);
545 icq_RusConv("wk", ptr1);
546 tmp = &pak.data[8 + len];
547 len = Chars_2_Word(tmp);
549 icq_RusConv("wk", ptr2);
551 len = Chars_2_Word(tmp);
553 icq_RusConv("wk", ptr3);
555 len = Chars_2_Word(tmp);
557 icq_RusConv("wk", ptr4);
559 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
560 sprintf(buf, "Info reply for %lu\n", uin);
561 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
564 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
565 icq_AckSrv(Chars_2_Word(pak.head.seq));
568 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
570 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
574 char cnt_stat, gender, buf[256];
575 uin = Chars_2_DW(&pak.data[2]);
576 len = Chars_2_Word(&pak.data[6]);
578 icq_RusConv("wk", ptr1);
579 cnt_code = Chars_2_Word(&pak.data[8 + len]);
580 cnt_stat = pak.data[len + 10];
581 tmp = &pak.data[11 + len];
582 len = Chars_2_Word(tmp);
583 icq_RusConv("wk", tmp + 2);
585 age = Chars_2_Word(tmp + 2 + len);
586 gender = *(tmp + len + 4);
588 len = Chars_2_Word(tmp);
589 icq_RusConv("wk", tmp + 2);
592 len = Chars_2_Word(tmp);
593 icq_RusConv("wk", tmp + 2);
596 len = Chars_2_Word(tmp);
597 icq_RusConv("wk", tmp + 2);
599 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
600 sprintf(buf, "Extended info reply for %lu\n", uin);
601 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
603 if (icq_ExtInfoReply)
604 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
605 icq_AckSrv(Chars_2_Word(pak.head.seq));
608 void icq_HandleSearchReply(srv_net_icq_pak pak)
610 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
614 uin = Chars_2_DW(&pak.data[2]);
615 len = Chars_2_Word(&pak.data[6]);
617 icq_RusConv("wk", ptr1);
618 tmp = &pak.data[8 + len];
619 len = Chars_2_Word(tmp);
621 icq_RusConv("wk", ptr2);
623 len = Chars_2_Word(tmp);
625 icq_RusConv("wk", ptr3);
627 len = Chars_2_Word(tmp);
629 icq_RusConv("wk", ptr4);
631 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
632 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");
633 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
636 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
637 icq_AckSrv(Chars_2_Word(pak.head.seq));
640 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
643 char *ptr1, *ptr2, *ptr3, *ptr4;
647 case USER_ADDED_MESS:
648 tmp = strchr(data, '\xFE');
650 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
651 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
658 tmp = strchr(tmp, '\xFE');
660 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
661 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
665 icq_RusConv("wk", data);
669 tmp = strchr(tmp, '\xFE');
671 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
672 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
676 icq_RusConv("wk", data);
680 tmp = strchr(tmp, '\xFE');
682 icq_RusConv("wk", data);
683 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
684 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
685 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
686 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
689 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
692 tmp = strchr(data, '\xFE');
697 tmp = strchr(tmp, '\xFE');
699 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
700 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
704 icq_RusConv("wk", data);
708 tmp = strchr(tmp, '\xFE');
710 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
711 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
715 icq_RusConv("wk", data);
719 tmp = strchr(tmp, '\xFE');
721 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
722 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
726 icq_RusConv("wk", data);
730 tmp = strchr(tmp, '\xFE');
732 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
733 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
739 tmp = strchr(tmp, '\x00');
741 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
742 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
746 icq_RusConv("wk", data);
747 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
748 sprintf(buf, "%lu has requested your authorization to be added to "
749 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
750 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
751 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
754 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
757 tmp = strchr(data, '\xFE');
759 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
760 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
764 icq_RusConv("wk", data);
768 icq_RusConv("wk", data);
769 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
770 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
771 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
774 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
777 icq_RusConv("wk", data);
778 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
779 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
780 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
783 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
787 /**********************************
788 Connects to hostname on port port
789 hostname can be DNS or nnn.nnn.nnn.nnn
790 write out messages to the FD aux
791 ***********************************/
792 int icq_Connect(const char *hostname, int port)
796 int conct, length, res;
797 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
798 struct hostent *host_struct; /* used in DNS llokup */
800 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
801 if ((ThisICQ->icq_Sok) == -1) {
802 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
803 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
806 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
807 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
808 sin.sin_addr.s_addr = INADDR_ANY;
809 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
811 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
812 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
813 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
816 length = sizeof(sin);
817 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
818 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
819 if ((ThisICQ->icq_UseProxy)) {
820 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
821 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
822 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
823 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
824 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
825 if (host_struct == 0L) {
826 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
827 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
828 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
832 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
834 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
835 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
836 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
837 if ((ThisICQ->icq_ProxySok) == -1) {
838 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
839 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
842 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
843 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
844 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
845 if (conct == -1) { /* did we connect ? */
846 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
847 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
850 buf[0] = 5; /* protocol version */
851 buf[1] = 1; /* number of methods */
852 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
853 buf[2] = 0; /* no authorization required */
855 buf[2] = 2; /* method username/password */
856 write((ThisICQ->icq_ProxySok), buf, 3);
857 res = read((ThisICQ->icq_ProxySok), buf, 2);
858 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
859 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
860 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
861 close((ThisICQ->icq_ProxySok));
864 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
865 buf[0] = 1; /* version of subnegotiation */
866 buf[1] = strlen((ThisICQ->icq_ProxyName));
867 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
868 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
869 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
870 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
871 res = read((ThisICQ->icq_ProxySok), buf, 2);
872 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
873 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
874 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
875 close((ThisICQ->icq_ProxySok));
879 buf[0] = 5; /* protocol version */
880 buf[1] = 3; /* command UDP associate */
881 buf[2] = 0; /* reserved */
882 buf[3] = 1; /* address type IP v4 */
887 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
888 write((ThisICQ->icq_ProxySok), buf, 10);
889 res = read((ThisICQ->icq_ProxySok), buf, 10);
890 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
893 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
894 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
897 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
898 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
901 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
902 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
905 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
906 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
909 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
910 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
913 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
914 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
917 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
918 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
921 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
922 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
925 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
926 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
929 close((ThisICQ->icq_ProxySok));
933 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
934 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
935 host_struct = gethostbyname(hostname);
936 if (host_struct == 0L) {
937 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
938 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
939 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
941 if ((ThisICQ->icq_UseProxy))
942 close((ThisICQ->icq_ProxySok));
945 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
947 if ((ThisICQ->icq_UseProxy)) {
948 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
949 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
951 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
952 sin.sin_port = htons(port); /* port */
953 if ((ThisICQ->icq_UseProxy)) {
954 (ThisICQ->icq_ProxyDestPort) = htons(port);
955 memcpy(&sin.sin_port, &buf[8], 2);
957 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
958 if (conct == -1) { /* did we connect ? */
959 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
960 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
961 if ((ThisICQ->icq_UseProxy))
962 close((ThisICQ->icq_ProxySok));
965 length = sizeof(sin);
966 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
967 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
968 (ThisICQ->icq_OurPort) = sin.sin_port;
969 return (ThisICQ->icq_Sok);
972 void icq_HandleProxyResponse()
976 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
978 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
979 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
980 if (icq_Disconnected)
981 (*icq_Disconnected) ();
982 SOCKCLOSE((ThisICQ->icq_Sok));
983 SOCKCLOSE((ThisICQ->icq_ProxySok));
987 /******************************************
988 Handles packets that the server sends to us.
989 *******************************************/
990 void icq_HandleServerResponse()
993 SIMPLE_MESSAGE *s_mesg;
994 RECV_MESSAGE *r_mesg;
1000 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1002 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1003 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1004 if (icq_Disconnected)
1005 (*icq_Disconnected) ();
1006 SOCKCLOSE((ThisICQ->icq_Sok));
1008 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)) {
1009 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1010 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1011 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1012 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1014 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1018 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1019 icq_SetServMess(Chars_2_Word(pak.head.seq));
1020 switch (Chars_2_Word(pak.head.cmd)) {
1022 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1023 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1025 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1028 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1029 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1030 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1033 case SRV_LOGIN_REPLY:
1034 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1035 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1036 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]);
1037 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1039 icq_AckSrv(Chars_2_Word(pak.head.seq));
1041 icq_SendContactList();
1042 icq_SendVisibleList();
1046 case SRV_RECV_MESSAGE:
1047 r_mesg = (RECV_MESSAGE *) pak.data;
1048 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));
1049 icq_AckSrv(Chars_2_Word(pak.head.seq));
1051 case SRV_X1: /* unknown message sent after login */
1052 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1053 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1054 icq_AckSrv(Chars_2_Word(pak.head.seq));
1056 case SRV_X2: /* unknown message sent after login */
1057 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1058 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1059 icq_AckSrv(Chars_2_Word(pak.head.seq));
1060 icq_SendGotMessages();
1062 case SRV_INFO_REPLY:
1063 icq_HandleInfoReply(pak);
1065 case SRV_EXT_INFO_REPLY:
1066 icq_HandleExtInfoReply(pak);
1068 case SRV_USER_ONLINE:
1069 icq_HandleUserOnline(pak);
1071 case SRV_USER_OFFLINE:
1072 icq_HandleUserOffline(pak);
1075 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1076 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1077 icq_Login((ThisICQ->icq_Status));
1079 case SRV_STATUS_UPDATE:
1080 icq_Status_Update(pak);
1083 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1084 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1085 if (icq_Disconnected)
1086 (*icq_Disconnected) ();
1088 case SRV_END_OF_SEARCH:
1089 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1090 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1092 (*icq_SearchDone) ();
1093 icq_AckSrv(Chars_2_Word(pak.head.seq));
1095 case SRV_USER_FOUND:
1096 icq_HandleSearchReply(pak);
1098 case SRV_SYS_DELIVERED_MESS:
1099 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1100 cur_time = time(0L);
1101 tm_str = localtime(&cur_time);
1102 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);
1103 icq_AckSrv(Chars_2_Word(pak.head.seq));
1105 default: /* commands we dont handle yet */
1106 len = s - (sizeof(pak.head));
1107 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1108 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1109 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1110 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1112 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1117 void icq_Init(DWORD uin, const char *password)
1119 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1120 (ThisICQ->icq_Uin) = uin;
1121 if ((ThisICQ->icq_Password))
1122 phree((ThisICQ->icq_Password));
1123 (ThisICQ->icq_Password) = strdoop(password);
1128 if ((ThisICQ->icq_Password))
1129 phree((ThisICQ->icq_Password));
1132 /******************************
1133 Main function connects gets (ThisICQ->icq_Uin)
1134 and (ThisICQ->icq_Password) and logins in and sits
1135 in a loop waiting for server responses.
1136 *******************************/
1148 FD_SET((ThisICQ->icq_Sok), &readfds);
1149 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1150 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1151 icq_HandleServerResponse();
1155 } while (did_something);
1158 /********************************************************
1159 Russian language ICQ fix.
1160 Usual Windows ICQ users do use Windows 1251 encoding but
1161 unix users do use koi8 encoding, so we need to convert it.
1162 This function will convert string from windows 1251 to koi8
1163 or from koi8 to windows 1251.
1164 Andrew Frolov dron@ilm.net
1165 *********************************************************/
1166 void icq_RusConv(const char to[4], char *t_in)
1171 /* 6-17-1998 by Linux_Dude
1172 * Moved initialization of table out front of 'if' block to prevent compiler
1173 * warning. Improved error message, and now return without performing string
1174 * conversion to prevent addressing memory out of range (table pointer would
1175 * previously have remained uninitialized (= bad)).
1179 if (strcmp(to, "kw") == 0)
1181 else if (strcmp(to, "wk") != 0) {
1182 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1183 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1186 /* End Linux_Dude's changes ;) */
1188 if ((ThisICQ->icq_Russian)) {
1189 for (i = 0; t_in[i] != 0; i++) {
1192 t_in[i] = table[t_in[i] & 0177];
1197 /**************************************************
1198 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1200 ***************************************************/
1201 WORD icq_SendMessage(DWORD uin, const char *text)
1206 char buf[512]; /* message may be only 450 bytes long */
1208 strncpy(buf, text, 512);
1209 icq_RusConv("kw", buf);
1211 Word_2_Chars(pak.head.ver, ICQ_VER);
1212 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1213 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1214 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1215 DW_2_Chars(msg.uin, uin);
1216 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1217 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1218 memcpy(&pak.data, &msg, sizeof(msg));
1219 memcpy(&pak.data[8], buf, len + 1);
1220 size = sizeof(msg) + len + 1;
1221 for (i = 0; i < 6; i++)
1222 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1223 return (ThisICQ->icq_SeqNum) - 1;
1226 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1230 int size, len1, len2;
1231 char buf1[512], buf2[512];
1233 strncpy(buf1, descr, 512);
1234 strncpy(buf2, url, 512);
1235 /* Do we need to convert URL? */
1236 icq_RusConv("kw", buf2);
1237 len1 = strlen(buf1);
1238 len2 = strlen(buf2);
1239 Word_2_Chars(pak.head.ver, ICQ_VER);
1240 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1241 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1242 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1243 DW_2_Chars(msg.uin, uin);
1244 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1245 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1246 memcpy(&pak.data, &msg, sizeof(msg));
1247 memcpy(&pak.data[8], buf1, len1);
1248 pak.data[8 + len1] = 0xFE;
1249 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1250 size = sizeof(msg) + len1 + len2 + 2;
1251 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1252 return (ThisICQ->icq_SeqNum) - 1;
1255 /**************************************************
1256 Sends a authorization to the server so the Mirabilis
1257 client can add the user.
1258 ***************************************************/
1259 void icq_SendAuthMsg(DWORD uin)
1265 Word_2_Chars(pak.head.ver, ICQ_VER);
1266 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1267 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1268 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1269 DW_2_Chars(msg.uin, uin);
1270 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1271 Word_2_Chars(msg.len, 2);
1272 memcpy(&pak.data, &msg, sizeof(msg));
1273 pak.data[sizeof(msg)] = 0x03;
1274 pak.data[sizeof(msg) + 1] = 0x00;
1275 size = sizeof(msg) + 2;
1276 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1279 /**************************************************
1280 Changes the users status on the server
1281 ***************************************************/
1282 void icq_ChangeStatus(DWORD status)
1287 Word_2_Chars(pak.head.ver, ICQ_VER);
1288 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1289 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1290 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1291 DW_2_Chars(pak.data, status);
1292 (ThisICQ->icq_Status) = status;
1294 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1297 /**********************
1299 ***********************/
1305 Word_2_Chars(pak.head.ver, ICQ_VER);
1306 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1307 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1308 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1309 len = strlen("B_USER_DISCONNECTED") + 1;
1310 *(short *) pak.data = len;
1312 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1313 pak.data[2 + len] = 05;
1314 pak.data[3 + len] = 00;
1315 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1318 void icq_Disconnect()
1320 SOCKCLOSE((ThisICQ->icq_Sok));
1321 SOCKCLOSE((ThisICQ->icq_Sok));
1322 if ((ThisICQ->icq_UseProxy))
1323 SOCKCLOSE((ThisICQ->icq_ProxySok));
1326 /********************************************************
1327 Sends a request to the server for info on a specific user
1328 *********************************************************/
1329 WORD icq_SendInfoReq(DWORD uin)
1334 Word_2_Chars(pak.head.ver, ICQ_VER);
1335 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1336 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1337 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1338 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1339 DW_2_Chars(&pak.data[2], uin);
1341 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1342 return (ThisICQ->icq_SeqNum) - 1;
1345 /********************************************************
1346 Sends a request to the server for info on a specific user
1347 *********************************************************/
1348 WORD icq_SendExtInfoReq(DWORD uin)
1353 Word_2_Chars(pak.head.ver, ICQ_VER);
1354 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1355 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1356 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1357 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1358 DW_2_Chars(&pak.data[2], uin);
1360 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1361 return (ThisICQ->icq_SeqNum) - 1;
1364 /**************************************************************
1365 Initializes a server search for the information specified
1366 ***************************************************************/
1367 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1372 Word_2_Chars(pak.head.ver, ICQ_VER);
1373 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1374 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1375 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1376 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1378 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1380 strcpy(pak.data + size, nick);
1381 size += strlen(nick) + 1;
1382 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1384 strcpy(pak.data + size, first);
1385 size += strlen(first) + 1;
1386 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1388 strcpy(pak.data + size, last);
1389 size += strlen(last) + 1;
1390 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1392 strcpy(pak.data + size, email);
1393 size += strlen(email) + 1;
1394 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1397 /**************************************************************
1398 Initializes a server search for the information specified
1399 ***************************************************************/
1400 void icq_SendSearchUINReq(DWORD uin)
1405 Word_2_Chars(pak.head.ver, ICQ_VER);
1406 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1407 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1408 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1409 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1411 DW_2_Chars(&pak.data[size], uin);
1413 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1416 /**************************************************
1417 Registers a new uin in the ICQ network
1418 ***************************************************/
1419 void icq_RegNewUser(const char *pass)
1421 srv_net_icq_pak pak;
1426 Word_2_Chars(pak.head.ver, ICQ_VER);
1427 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1428 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1429 Word_2_Chars(len_buf, len);
1430 memcpy(&pak.data, "\x02\x00", 2);
1431 memcpy(&pak.data[2], &len_buf, 2);
1432 memcpy(&pak.data[4], pass, len + 1);
1433 DW_2_Chars(&pak.data[4 + len], 0x0072);
1434 DW_2_Chars(&pak.data[8 + len], 0x0000);
1436 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1439 void icq_UpdateUserInfo(USER_INFO * user)
1444 Word_2_Chars(pak.head.ver, ICQ_VER);
1445 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1446 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1447 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1448 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1450 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1452 strcpy(pak.data + size, user->nick);
1453 size += strlen(user->nick) + 1;
1454 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1456 strcpy(pak.data + size, user->first);
1457 size += strlen(user->first) + 1;
1458 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1460 strcpy(pak.data + size, user->last);
1461 size += strlen(user->last) + 1;
1462 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1464 strcpy(pak.data + size, user->email);
1465 size += strlen(user->email) + 1;
1466 pak.data[size] = user->auth;
1468 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1471 const char *icq_GetCountryName(int code)
1475 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1476 if (Country_Codes[i].code == code) {
1477 return Country_Codes[i].name;
1480 if (Country_Codes[i].code == code) {
1481 return Country_Codes[i].name;
1486 /********************************************
1487 returns a string describing the status or
1488 a "Error" if no such string exists
1489 *********************************************/
1490 const char *icq_ConvertStatus2Str(int status)
1492 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1495 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1496 switch (status & 0x01FF) {
1501 return "Do not disturb";
1506 case STATUS_OCCUPIED:
1510 return "Not available";
1512 case STATUS_INVISIBLE:
1515 case STATUS_INVISIBLE_2:
1516 return "Invisible mode 2";
1518 case STATUS_FREE_CHAT:
1519 return "Free for chat";
1527 void icq_InitNewUser(const char *hostname, DWORD port)
1529 srv_net_icq_pak pak;
1534 icq_Connect(hostname, port);
1535 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1536 printf("Couldn't establish connection\n");
1539 icq_RegNewUser((ThisICQ->icq_Password));
1542 tv.tv_usec = 500000;
1545 FD_SET((ThisICQ->icq_Sok), &readfds);
1547 /* don't care about writefds and exceptfds: */
1548 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1550 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1551 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1552 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1553 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1560 /********************************************
1561 Converts an intel endian character sequence to
1563 *********************************************/
1564 DWORD Chars_2_DW(unsigned char *buf)
1579 /********************************************
1580 Converts an intel endian character sequence to
1582 *********************************************/
1583 WORD Chars_2_Word(unsigned char *buf)
1594 /********************************************
1596 an intel endian character sequence
1597 *********************************************/
1598 void DW_2_Chars(unsigned char *buf, DWORD num)
1600 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1601 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1602 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1603 buf[0] = (unsigned char) (num) & 0x000000FF;
1606 /********************************************
1608 an intel endian character sequence
1609 *********************************************/
1610 void Word_2_Chars(unsigned char *buf, WORD num)
1612 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1613 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1616 /***************************
1617 ContactList functions
1618 ***************************/
1619 void icq_ContAddUser(DWORD cuin)
1621 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1622 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1625 p->vis_list = FALSE;
1631 (ThisICQ->icq_ContFirst) = p;
1634 void icq_ContDelUser(DWORD cuin)
1636 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1639 if (ptr->uin == cuin) {
1640 (ThisICQ->icq_ContFirst) = ptr->next;
1642 ptr = (ThisICQ->icq_ContFirst);
1645 if (ptr->next->uin == cuin) {
1646 ptr->next = ptr->next->next;
1653 void icq_ContClear()
1655 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1660 (ThisICQ->icq_ContFirst) = ptr;
1664 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1666 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1670 if (ptr->uin == cuin)
1677 icq_ContactItem *icq_ContGetFirst()
1679 return (ThisICQ->icq_ContFirst);
1682 void icq_ContSetVis(DWORD cuin)
1684 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1688 if (ptr->uin == cuin)
1689 ptr->vis_list = TRUE;
1694 /************************
1695 (ThisICQ->icq_ServMess) functions
1696 *************************/
1697 BOOL icq_GetServMess(WORD num)
1699 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1702 void icq_SetServMess(WORD num)
1704 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1709 return (ThisICQ->icq_Sok);
1712 int icq_GetProxySok()
1714 return (ThisICQ->icq_ProxySok);
1717 /*******************
1718 SOCKS5 Proxy support
1719 ********************/
1720 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1722 if ((ThisICQ->icq_ProxyHost))
1723 phree((ThisICQ->icq_ProxyHost));
1724 if ((ThisICQ->icq_ProxyName))
1725 phree((ThisICQ->icq_ProxyName));
1726 if ((ThisICQ->icq_ProxyPass))
1727 phree((ThisICQ->icq_ProxyPass));
1728 if (strlen(pname) > 255) {
1729 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1730 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1731 ThisICQ->icq_UseProxy = 0;
1734 if (strlen(ppass) > 255) {
1735 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1736 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1737 (ThisICQ->icq_UseProxy) = 0;
1740 (ThisICQ->icq_UseProxy) = 1;
1741 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1742 (ThisICQ->icq_ProxyPort) = pport;
1743 (ThisICQ->icq_ProxyAuth) = pauth;
1744 (ThisICQ->icq_ProxyName) = strdoop(pname);
1745 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1748 void icq_UnsetProxy()
1750 ThisICQ->icq_UseProxy = 0;
1755 /***********************************************************************/
1756 /* icqlib stuff ends here, Citadel module stuff begins */
1757 /***********************************************************************/
1761 * Callback function for CtdlICQ_Read_Config()
1763 void CtdlICQ_Read_Config_Backend(long msgnum) {
1764 struct CtdlMessage *msg;
1768 lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
1769 msg = CtdlFetchMessage(msgnum);
1771 ptr = msg->cm_fields['M'];
1772 pos = pattern2(ptr, "\n\n");
1780 safestrncpy(ThisICQ->icq_config, ptr, 256);
1782 CtdlFreeMessage(msg);
1784 lprintf(9, "...it ain't there?\n");
1790 * If this user has an ICQ configuration on disk, read it into memory.
1792 void CtdlICQ_Read_Config(void) {
1793 char hold_rm[ROOMNAMELEN];
1794 char icq_rm[ROOMNAMELEN];
1796 strcpy(hold_rm, CC->quickroom.QRname);
1797 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1798 strcpy(ThisICQ->icq_config, "");
1800 if (getroom(&CC->quickroom, icq_rm) != 0) {
1801 getroom(&CC->quickroom, hold_rm);
1805 /* We want the last (and probably only) config in this room */
1806 lprintf(9, "We're in <%s> looking for config\n",
1807 CC->quickroom.QRname);
1808 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1809 getroom(&CC->quickroom, hold_rm);
1816 * Write our config to disk
1818 void CtdlICQ_Write_Config(void) {
1819 char temp[PATH_MAX];
1822 strcpy(temp, tmpnam(NULL));
1824 fp = fopen(temp, "w");
1825 if (fp == NULL) return;
1826 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1829 /* this handy API function does all the work for us */
1830 CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
1840 * Write our contact list to disk
1842 void CtdlICQ_Write_CL(void) {
1843 char temp[PATH_MAX];
1847 strcpy(temp, tmpnam(NULL));
1849 fp = fopen(temp, "w");
1850 if (fp == NULL) return;
1852 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1853 fprintf(fp, "%ld|%s|\n",
1854 ThisICQ->icq_cl[i].uin,
1855 ThisICQ->icq_cl[i].name);
1859 /* this handy API function does all the work for us */
1860 CtdlWriteObject(ICQROOM, ICQCLMIME, temp, 1, 0, 1);
1870 * Callback function for CtdlICQ_Read_CL()
1872 void CtdlICQ_Read_CL_Backend(long msgnum) {
1873 struct CtdlMessage *msg;
1878 msg = CtdlFetchMessage(msgnum);
1880 ptr = msg->cm_fields['M'];
1881 pos = pattern2(ptr, "\n\n");
1889 for (i=0; i<strlen(ptr); ++i)
1890 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1891 if (ThisICQ->icq_numcl) {
1892 ThisICQ->icq_cl = mallok(
1893 (ThisICQ->icq_numcl *
1894 sizeof (struct CtdlICQ_CL)));
1896 while (cont=strtok(ptr, "\n"), cont != NULL) {
1897 ThisICQ->icq_cl[i].uin =
1898 extract_long(cont, 0);
1899 extract(ThisICQ->icq_cl[i].name,
1901 ThisICQ->icq_cl[i].status =
1908 CtdlFreeMessage(msg);
1914 * Read contact list into memory
1916 void CtdlICQ_Read_CL(void) {
1917 char hold_rm[ROOMNAMELEN];
1918 char icq_rm[ROOMNAMELEN];
1920 strcpy(hold_rm, CC->quickroom.QRname);
1921 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1922 strcpy(ThisICQ->icq_config, "");
1924 if (getroom(&CC->quickroom, icq_rm) != 0) {
1925 getroom(&CC->quickroom, hold_rm);
1929 /* Free any contact list already in memory */
1930 if (ThisICQ->icq_numcl) {
1931 phree(ThisICQ->icq_cl);
1932 ThisICQ->icq_numcl = 0;
1935 /* We want the last (and probably only) list in this room */
1936 CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
1937 getroom(&CC->quickroom, hold_rm);
1942 * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
1943 * entry in the table if necessary
1945 struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
1948 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
1949 if (ThisICQ->icq_cl[i].uin == uin)
1950 return (&ThisICQ->icq_cl[i]);
1952 ++ThisICQ->icq_numcl;
1953 ThisICQ->icq_cl = reallok(ThisICQ->icq_cl,
1954 (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
1955 memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
1956 0, sizeof(struct CtdlICQ_CL));
1957 ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
1958 return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
1964 * Refresh the contact list
1966 void CtdlICQ_Refresh_Contact_List(void) {
1969 if (ThisICQ->icq_Sok < 0) return;
1973 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1974 if (ThisICQ->icq_cl[i].uin > 0L) {
1975 icq_ContAddUser(ThisICQ->icq_cl[i].uin);
1976 icq_ContSetVis(ThisICQ->icq_cl[i].uin);
1980 icq_SendContactList();
1988 * Utility routine to logout and disconnect from the ICQ server if we happen
1989 * to already be connected
1991 void CtdlICQ_Logout_If_Connected(void) {
1992 if (ThisICQ->icq_Sok >= 0) {
1995 ThisICQ->icq_Sok = (-1);
2001 * If we have an ICQ uin/password on file for this user, go ahead and try
2002 * to log on to the ICQ server.
2004 void CtdlICQ_Login_If_Possible(void) {
2008 CtdlICQ_Logout_If_Connected();
2009 CtdlICQ_Read_Config();
2011 uin = extract_long(ThisICQ->icq_config, 0);
2012 extract(pass, ThisICQ->icq_config, 1);
2014 if ( (uin > 0L) && (strlen(pass)>0) ) {
2015 icq_Init(uin, pass);
2016 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2017 icq_Login(STATUS_ONLINE);
2024 /* This merely hooks icqlib's log function into Citadel's log function. */
2025 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2027 lprintf(level, "ICQ: %s", str);
2032 * At the start of each Citadel server session, we have to allocate some
2033 * space for the Citadel-ICQ session for this thread. It'll be automatically
2034 * freed by the server when the session ends.
2036 void CtdlICQ_session_startup_hook(void)
2038 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2039 icq_init_handle(ThisICQ);
2045 * End-of-session cleanup
2047 void CtdlICQ_session_stopdown_hook(void) {
2054 * icq_Main() needs to be called as frequently as possible. We'll do it
2055 * following the completion of each Citadel server command.
2058 void CtdlICQ_after_cmd_hook(void)
2060 if (ThisICQ->icq_Sok >= 0) {
2061 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 60 ) {
2063 ThisICQ->icq_LastKeepAlive = time(NULL);
2071 * There are a couple of things we absolutely need to make sure of when we
2072 * kill off the session. One of them is that the sockets are all closed --
2073 * otherwise we could have a nasty file descriptor leak.
2075 void CtdlICQ_session_logout_hook(void)
2077 lprintf(9, "Shutting down ICQ\n");
2078 CtdlICQ_Logout_If_Connected();
2080 /* Free the memory used by the contact list */
2081 if (ThisICQ->icq_numcl) {
2082 phree(ThisICQ->icq_cl);
2083 ThisICQ->icq_numcl = 0;
2090 void CtdlICQ_session_login_hook(void)
2092 /* If this user has an ICQ config on file, start it up. */
2093 CtdlICQ_Login_If_Possible();
2102 * Here's what we have to do when an ICQ message arrives!
2104 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2105 BYTE day, BYTE month, WORD year,
2112 /* Default sender is 'uin@icq' syntax */
2113 sprintf(from, "%ld@icq", uin);
2115 /* Use the sender's name if we have it inthe contact list */
2116 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
2117 if (uin == ThisICQ->icq_cl[i].uin) {
2118 safestrncpy(from, ThisICQ->icq_cl[i].name, 256);
2122 num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2123 lprintf(9, "Delivered to %d users\n", num_delivered);
2128 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2129 const char *first, const char *last,
2130 const char *email, char auth) {
2132 struct CtdlICQ_CL *ptr;
2135 ptr = CtdlICQ_CLent(uin);
2136 safestrncpy(ptr->name, nick, 32);
2137 ptr->status = STATUS_OFFLINE;
2138 lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
2146 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2149 DWORD target_uin = 0L;
2152 /* If this is an incoming ICQ from someone on the contact list,
2153 * change the sender from "uin@icq" to the contact name.
2156 for (i=0; i<strlen(from); ++i)
2157 if (!strcasecmp(&from[i], "@icq")) {
2160 if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
2161 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2162 if (ThisICQ->icq_cl[i].uin == atol(from))
2163 strcpy(from, ThisICQ->icq_cl[i].name);
2168 /* Handle "uin@icq" syntax */
2170 for (i=0; i<strlen(recp); ++i)
2171 if (!strcasecmp(&recp[i], "@icq")) {
2174 if (is_aticq == 1) target_uin = atol(recp);
2176 /* Handle "nick" syntax */
2177 if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
2178 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2179 if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
2180 target_uin = ThisICQ->icq_cl[i].uin;
2186 if (target_uin == 0L) return(0);
2188 if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
2194 void cmd_cicq(char *argbuf) {
2201 if (!(CC->logged_in)) {
2202 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
2205 extract(cmd, argbuf, 0);
2207 /* "CICQ login" tells us how to log in. */
2208 if (!strcasecmp(cmd, "login")) {
2209 uin = extract_long(argbuf, 1);
2210 extract(pass, argbuf, 2);
2211 sprintf(ThisICQ->icq_config, "%ld|%s|", uin, pass);
2213 CtdlICQ_Write_Config();
2214 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2215 CtdlICQ_Login_If_Possible();
2217 cprintf("%d You must supply a UIN.\n", ERROR);
2222 /* "CICQ getcl" returns the contact list */
2223 if (!strcasecmp(cmd, "getcl")) {
2225 cprintf("%d Your ICQ contact list:\n", LISTING_FOLLOWS);
2226 if (ThisICQ->icq_numcl > 0) {
2227 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2228 cprintf("%ld|%s|%s|\n",
2229 ThisICQ->icq_cl[i].uin,
2230 ThisICQ->icq_cl[i].name,
2231 icq_ConvertStatus2Str(
2232 ThisICQ->icq_cl[i].status)
2240 /* "CICQ putcl" accepts a new contact list from the client */
2241 if (!strcasecmp(cmd, "putcl")) {
2242 cprintf("%d Send contact list\n", SEND_LISTING);
2243 ThisICQ->icq_numcl = 0;
2244 while (client_gets(buf), strcmp(buf, "000")) {
2245 uin = extract_long(buf, 0);
2251 CtdlICQ_Refresh_Contact_List();
2255 /* "CICQ status" returns the connected/notconnected status */
2256 if (!strcasecmp(cmd, "status")) {
2257 cprintf("%d %d\n", OK,
2258 ((ThisICQ->icq_Sok >= 0) ? 1 : 0) );
2262 cprintf("%d Invalid subcommand\n", ERROR);
2270 * During an RWHO command, we want to append our ICQ information.
2272 void CtdlICQ_rwho(void) {
2275 if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
2276 if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
2277 cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
2278 0, /* no session ID */
2279 ThisICQ->icq_cl[i].name,
2280 icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
2281 ThisICQ->icq_cl[i].host,
2282 " ", /* no client */
2283 time(NULL), /* now? */
2284 " ", /* no last command */
2290 void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
2291 struct CtdlICQ_CL *ptr;
2293 ptr = CtdlICQ_CLent(uin);
2294 ptr->status = status;
2295 if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
2299 void CtdlICQ_Logged(void) {
2300 CtdlICQ_Refresh_Contact_List();
2304 void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
2305 DWORD port, DWORD realip) {
2309 CtdlICQ_Status_Update(uin, status);
2310 decoded_ip = ntohl(ip);
2311 locate_host(CtdlICQ_CLent(uin)->host, &decoded_ip);
2315 void CtdlICQ_UserOffline(DWORD uin) {
2316 CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
2320 char *Dynamic_Module_Init(void)
2322 /* Make sure we've got a valid ThisICQ for each session */
2323 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2325 /* Tell the Citadel server about our wonderful ICQ hooks */
2326 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2327 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2328 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2329 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2330 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2331 CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
2332 CtdlRegisterProtoHook(cmd_cicq, "CICQ", "Configure Citadel ICQ");
2333 CtdlRegisterXmsgHook(CtdlICQ_Send_Msg, XMSG_PRI_FOREIGN);
2335 /* Tell the code formerly known as icqlib about our callbacks */
2336 icq_Log = CtdlICQlog;
2337 icq_RecvMessage = CtdlICQ_Incoming_Message;
2338 icq_InfoReply = CtdlICQ_InfoReply;
2339 icq_Disconnected = CtdlICQ_Login_If_Possible;
2340 icq_Logged = CtdlICQ_Logged;
2341 icq_UserStatusUpdate = CtdlICQ_Status_Update;
2342 icq_UserOnline = CtdlICQ_UserOnline;
2343 icq_UserOffline = CtdlICQ_UserOffline;