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
61 /* ICQROOM is the name of the room in which each user's ICQ configuration
62 * and contact lists will be stored. (It's a personal room.)
64 #define ICQROOM "My ICQ Config"
66 /* MIME types to use for storing ICQ stuff */
67 #define ICQMIME "application/x-citadel-icq" /* configuration */
68 #define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
70 /* Citadel server TSD symbol for use by serv_icq */
71 unsigned long SYM_CTDL_ICQ;
72 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
74 extern struct CitContext *ContextList;
79 void (*icq_Logged) (void);
80 void (*icq_Disconnected) (void);
81 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
82 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
83 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);
84 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);
85 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
86 void (*icq_SearchDone) (void);
87 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
88 void (*icq_UserOffline) (DWORD uin);
89 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
90 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
91 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);
92 void (*icq_Log) (time_t time, unsigned char level, const char *str);
93 void (*icq_SrvAck) (WORD seq);
96 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
97 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
98 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
99 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
100 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
101 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
102 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
103 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
106 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
107 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
108 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
109 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
110 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
111 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
112 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
113 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
115 static COUNTRY_CODE Country_Codes[] =
130 {"American Samoa", 684},
145 {"Czech Republic", 42},
148 {"El Salvador", 503},
152 {"French Antilles", 596},
153 {"French Polynesia", 689},
160 {"Guantanomo Bay", 53},
174 {"Ivory Coast", 225},
182 {"Liechtenstein", 41},
194 {"Netherlands Antilles", 599},
195 {"New Caledonia", 687},
202 {"Papua New Guinea", 675},
212 {"Saudia Arabia", 966},
217 {"South Africa", 27},
228 {"United Arab Emirates", 971},
230 {"Vatican City", 39},
237 {"Not entered", 0xffff}};
239 void icq_init_handle(struct ctdl_icq_handle *i)
241 memset(i, 0, sizeof(struct ctdl_icq_handle));
242 i->icq_Russian = TRUE;
244 i->icq_OurIp = 0x0100007f;
245 i->icq_Status = STATUS_OFFLINE;
250 int icq_SockWrite(int sok, const void *buf, size_t count)
253 if (!(ThisICQ->icq_UseProxy))
254 return write(sok, buf, count);
256 tmpbuf[0] = 0; /* reserved */
257 tmpbuf[1] = 0; /* reserved */
258 tmpbuf[2] = 0; /* standalone packet */
259 tmpbuf[3] = 1; /* address type IP v4 */
260 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
261 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
262 memcpy(&tmpbuf[10], buf, count);
263 return write(sok, tmpbuf, count + 10) - 10;
267 int icq_SockRead(int sok, void *buf, size_t count)
271 if (!(ThisICQ->icq_UseProxy))
272 return read(sok, buf, count);
274 res = read(sok, tmpbuf, count + 10);
275 memcpy(buf, &tmpbuf[10], res - 10);
280 /****************************************
281 This must be called every 2 min.
282 so the server knows we're still alive.
283 JAVA client sends two different commands
285 *****************************************/
290 Word_2_Chars(pak.head.ver, ICQ_VER);
291 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
292 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
293 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
294 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
296 Word_2_Chars(pak.head.ver, ICQ_VER);
297 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
298 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
299 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
300 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
302 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
303 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
306 /**********************************
307 This must be called to remove
308 messages from the server
309 ***********************************/
310 void icq_SendGotMessages()
314 Word_2_Chars(pak.head.ver, ICQ_VER);
315 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
316 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
317 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
319 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
322 /*************************************
323 this sends over the contact list
324 *************************************/
325 void icq_SendContactList()
331 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
333 Word_2_Chars(pak.head.ver, ICQ_VER);
334 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
335 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
336 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
342 DW_2_Chars(tmp, ptr->uin);
347 pak.data[0] = num_used;
348 size = ((int) tmp - (int) pak.data);
349 size += sizeof(pak.head);
350 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
353 void icq_SendNewUser(unsigned long uin)
358 Word_2_Chars(pak.head.ver, ICQ_VER);
359 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
360 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
361 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
362 DW_2_Chars(pak.data, uin);
363 size = sizeof(pak.head) + 4;
364 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
367 /*************************************
368 this sends over the visible list
369 that allows certain users to see you
371 *************************************/
372 void icq_SendVisibleList()
378 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
380 Word_2_Chars(pak.head.ver, ICQ_VER);
381 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
382 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
383 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
390 DW_2_Chars(tmp, ptr->uin);
397 pak.data[0] = num_used;
398 size = ((int) tmp - (int) pak.data);
399 size += sizeof(pak.head);
400 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
404 /**************************************
405 This sends the second login command
406 this is necessary to finish logging in.
407 ***************************************/
408 void icq_SendLogin1()
412 Word_2_Chars(pak.head.ver, ICQ_VER);
413 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
414 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
415 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
417 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
420 /************************************************
421 This is called when a user goes offline
422 *************************************************/
423 void icq_HandleUserOffline(srv_net_icq_pak pak)
428 remote_uin = Chars_2_DW(pak.data);
429 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
430 sprintf(buf, "User %lu logged off\n", remote_uin);
431 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
434 (*icq_UserOffline) (remote_uin);
435 icq_AckSrv(Chars_2_Word(pak.head.seq));
438 void icq_HandleUserOnline(srv_net_icq_pak pak)
440 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
441 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
444 remote_uin = Chars_2_DW(pak.data);
445 new_status = Chars_2_DW(&pak.data[17]);
446 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
447 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
448 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
449 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
450 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
451 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
454 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
455 icq_AckSrv(Chars_2_Word(pak.head.seq));
458 void icq_Status_Update(srv_net_icq_pak pak)
460 unsigned long remote_uin, new_status;
463 remote_uin = Chars_2_DW(pak.data);
464 new_status = Chars_2_DW(&pak.data[4]);
465 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
466 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
467 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
469 if (icq_UserStatusUpdate)
470 (*icq_UserStatusUpdate) (remote_uin, new_status);
471 icq_AckSrv(Chars_2_Word(pak.head.seq));
474 /************************************
475 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
476 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
477 It does NOT wait for any kind of a response.
478 *************************************/
479 void icq_Login(DWORD status)
486 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
487 Word_2_Chars(pak.head.ver, ICQ_VER);
488 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
489 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
490 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
492 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
493 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
495 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
496 DW_2_Chars(s2.status, status);
497 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
499 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
500 s2.X2[0] = LOGIN_X2_DEF;
501 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
502 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
503 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
505 memcpy(pak.data, &s1, sizeof(s1));
507 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
508 size += Chars_2_Word(s1.len);
509 memcpy(&pak.data[size], &s2, sizeof(s2));
511 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
514 /*******************************
515 This routine sends the aknowlegement cmd to the
516 server it appears that this must be done after
517 everything the server sends us
518 *******************************/
519 void icq_AckSrv(int seq)
524 Word_2_Chars(pak.head.ver, ICQ_VER);
525 Word_2_Chars(pak.head.cmd, CMD_ACK);
526 Word_2_Chars(pak.head.seq, seq);
527 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
528 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
529 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
530 for (i = 0; i < 6; i++)
531 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
534 void icq_HandleInfoReply(srv_net_icq_pak pak)
536 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
540 seq = Chars_2_Word(pak.data);
541 uin = Chars_2_DW(&pak.data[2]);
542 len = Chars_2_Word(&pak.data[6]);
544 icq_RusConv("wk", ptr1);
545 tmp = &pak.data[8 + len];
546 len = Chars_2_Word(tmp);
548 icq_RusConv("wk", ptr2);
550 len = Chars_2_Word(tmp);
552 icq_RusConv("wk", ptr3);
554 len = Chars_2_Word(tmp);
556 icq_RusConv("wk", ptr4);
558 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
559 sprintf(buf, "Info reply for %lu\n", uin);
560 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
563 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
564 icq_AckSrv(Chars_2_Word(pak.head.seq));
567 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
569 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
573 char cnt_stat, gender, buf[256];
574 uin = Chars_2_DW(&pak.data[2]);
575 len = Chars_2_Word(&pak.data[6]);
577 icq_RusConv("wk", ptr1);
578 cnt_code = Chars_2_Word(&pak.data[8 + len]);
579 cnt_stat = pak.data[len + 10];
580 tmp = &pak.data[11 + len];
581 len = Chars_2_Word(tmp);
582 icq_RusConv("wk", tmp + 2);
584 age = Chars_2_Word(tmp + 2 + len);
585 gender = *(tmp + len + 4);
587 len = Chars_2_Word(tmp);
588 icq_RusConv("wk", tmp + 2);
591 len = Chars_2_Word(tmp);
592 icq_RusConv("wk", tmp + 2);
595 len = Chars_2_Word(tmp);
596 icq_RusConv("wk", tmp + 2);
598 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
599 sprintf(buf, "Extended info reply for %lu\n", uin);
600 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
602 if (icq_ExtInfoReply)
603 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
604 icq_AckSrv(Chars_2_Word(pak.head.seq));
607 void icq_HandleSearchReply(srv_net_icq_pak pak)
609 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
613 uin = Chars_2_DW(&pak.data[2]);
614 len = Chars_2_Word(&pak.data[6]);
616 icq_RusConv("wk", ptr1);
617 tmp = &pak.data[8 + len];
618 len = Chars_2_Word(tmp);
620 icq_RusConv("wk", ptr2);
622 len = Chars_2_Word(tmp);
624 icq_RusConv("wk", ptr3);
626 len = Chars_2_Word(tmp);
628 icq_RusConv("wk", ptr4);
630 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
631 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");
632 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
635 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
636 icq_AckSrv(Chars_2_Word(pak.head.seq));
639 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
642 char *ptr1, *ptr2, *ptr3, *ptr4;
646 case USER_ADDED_MESS:
647 tmp = strchr(data, '\xFE');
649 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
650 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
657 tmp = strchr(tmp, '\xFE');
659 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
660 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
664 icq_RusConv("wk", data);
668 tmp = strchr(tmp, '\xFE');
670 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
671 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
675 icq_RusConv("wk", data);
679 tmp = strchr(tmp, '\xFE');
681 icq_RusConv("wk", data);
682 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
683 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
684 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
685 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
688 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
691 tmp = strchr(data, '\xFE');
696 tmp = strchr(tmp, '\xFE');
698 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
699 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
703 icq_RusConv("wk", data);
707 tmp = strchr(tmp, '\xFE');
709 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
710 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
714 icq_RusConv("wk", data);
718 tmp = strchr(tmp, '\xFE');
720 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
721 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
725 icq_RusConv("wk", data);
729 tmp = strchr(tmp, '\xFE');
731 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
732 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
738 tmp = strchr(tmp, '\x00');
740 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
741 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
745 icq_RusConv("wk", data);
746 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
747 sprintf(buf, "%lu has requested your authorization to be added to "
748 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
749 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
750 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
753 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
756 tmp = strchr(data, '\xFE');
758 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
759 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
763 icq_RusConv("wk", data);
767 icq_RusConv("wk", data);
768 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
769 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
770 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
773 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
776 icq_RusConv("wk", data);
777 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
778 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
779 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
782 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
786 /**********************************
787 Connects to hostname on port port
788 hostname can be DNS or nnn.nnn.nnn.nnn
789 write out messages to the FD aux
790 ***********************************/
791 int icq_Connect(const char *hostname, int port)
795 int conct, length, res;
796 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
797 struct hostent *host_struct; /* used in DNS llokup */
799 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
800 if ((ThisICQ->icq_Sok) == -1) {
801 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
802 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
805 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
806 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
807 sin.sin_addr.s_addr = INADDR_ANY;
808 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
810 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
811 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
812 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
815 length = sizeof(sin);
816 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
817 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
818 if ((ThisICQ->icq_UseProxy)) {
819 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
820 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
821 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
822 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
823 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
824 if (host_struct == 0L) {
825 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
826 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
827 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
831 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
833 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
834 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
835 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
836 if ((ThisICQ->icq_ProxySok) == -1) {
837 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
838 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
841 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
842 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
843 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
844 if (conct == -1) { /* did we connect ? */
845 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
846 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
849 buf[0] = 5; /* protocol version */
850 buf[1] = 1; /* number of methods */
851 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
852 buf[2] = 0; /* no authorization required */
854 buf[2] = 2; /* method username/password */
855 write((ThisICQ->icq_ProxySok), buf, 3);
856 res = read((ThisICQ->icq_ProxySok), buf, 2);
857 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
858 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
859 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
860 close((ThisICQ->icq_ProxySok));
863 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
864 buf[0] = 1; /* version of subnegotiation */
865 buf[1] = strlen((ThisICQ->icq_ProxyName));
866 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
867 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
868 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
869 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
870 res = read((ThisICQ->icq_ProxySok), buf, 2);
871 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
872 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
873 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
874 close((ThisICQ->icq_ProxySok));
878 buf[0] = 5; /* protocol version */
879 buf[1] = 3; /* command UDP associate */
880 buf[2] = 0; /* reserved */
881 buf[3] = 1; /* address type IP v4 */
886 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
887 write((ThisICQ->icq_ProxySok), buf, 10);
888 res = read((ThisICQ->icq_ProxySok), buf, 10);
889 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
892 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
893 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
896 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
897 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
900 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
901 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
904 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
905 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
908 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
909 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
912 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
913 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
916 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
917 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
920 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
921 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
924 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
925 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
928 close((ThisICQ->icq_ProxySok));
932 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
933 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
934 host_struct = gethostbyname(hostname);
935 if (host_struct == 0L) {
936 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
937 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
938 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
940 if ((ThisICQ->icq_UseProxy))
941 close((ThisICQ->icq_ProxySok));
944 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
946 if ((ThisICQ->icq_UseProxy)) {
947 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
948 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
950 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
951 sin.sin_port = htons(port); /* port */
952 if ((ThisICQ->icq_UseProxy)) {
953 (ThisICQ->icq_ProxyDestPort) = htons(port);
954 memcpy(&sin.sin_port, &buf[8], 2);
956 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
957 if (conct == -1) { /* did we connect ? */
958 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
959 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
960 if ((ThisICQ->icq_UseProxy))
961 close((ThisICQ->icq_ProxySok));
964 length = sizeof(sin);
965 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
966 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
967 (ThisICQ->icq_OurPort) = sin.sin_port;
968 return (ThisICQ->icq_Sok);
971 void icq_HandleProxyResponse()
975 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
977 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
978 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
979 if (icq_Disconnected)
980 (*icq_Disconnected) ();
981 SOCKCLOSE((ThisICQ->icq_Sok));
982 SOCKCLOSE((ThisICQ->icq_ProxySok));
986 /******************************************
987 Handles packets that the server sends to us.
988 *******************************************/
989 void icq_HandleServerResponse()
992 SIMPLE_MESSAGE *s_mesg;
993 RECV_MESSAGE *r_mesg;
999 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1001 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1002 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1003 if (icq_Disconnected)
1004 (*icq_Disconnected) ();
1005 SOCKCLOSE((ThisICQ->icq_Sok));
1007 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)) {
1008 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1009 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1010 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1011 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1013 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1017 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1018 icq_SetServMess(Chars_2_Word(pak.head.seq));
1019 switch (Chars_2_Word(pak.head.cmd)) {
1021 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1022 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1024 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1027 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1028 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1029 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1032 case SRV_LOGIN_REPLY:
1033 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1034 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1035 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]);
1036 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1038 icq_AckSrv(Chars_2_Word(pak.head.seq));
1040 icq_SendContactList();
1041 icq_SendVisibleList();
1045 case SRV_RECV_MESSAGE:
1046 r_mesg = (RECV_MESSAGE *) pak.data;
1047 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));
1048 icq_AckSrv(Chars_2_Word(pak.head.seq));
1050 case SRV_X1: /* unknown message sent after login */
1051 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1052 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1053 icq_AckSrv(Chars_2_Word(pak.head.seq));
1055 case SRV_X2: /* unknown message sent after login */
1056 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1057 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1058 icq_AckSrv(Chars_2_Word(pak.head.seq));
1059 icq_SendGotMessages();
1061 case SRV_INFO_REPLY:
1062 icq_HandleInfoReply(pak);
1064 case SRV_EXT_INFO_REPLY:
1065 icq_HandleExtInfoReply(pak);
1067 case SRV_USER_ONLINE:
1068 icq_HandleUserOnline(pak);
1070 case SRV_USER_OFFLINE:
1071 icq_HandleUserOffline(pak);
1074 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1075 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1076 icq_Login((ThisICQ->icq_Status));
1078 case SRV_STATUS_UPDATE:
1079 icq_Status_Update(pak);
1082 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1083 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1084 if (icq_Disconnected)
1085 (*icq_Disconnected) ();
1087 case SRV_END_OF_SEARCH:
1088 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1089 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1091 (*icq_SearchDone) ();
1092 icq_AckSrv(Chars_2_Word(pak.head.seq));
1094 case SRV_USER_FOUND:
1095 icq_HandleSearchReply(pak);
1097 case SRV_SYS_DELIVERED_MESS:
1098 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1099 cur_time = time(0L);
1100 tm_str = localtime(&cur_time);
1101 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);
1102 icq_AckSrv(Chars_2_Word(pak.head.seq));
1104 default: /* commands we dont handle yet */
1105 len = s - (sizeof(pak.head));
1106 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1107 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1108 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1109 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1111 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1116 void icq_Init(DWORD uin, const char *password)
1118 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1119 (ThisICQ->icq_Uin) = uin;
1120 if ((ThisICQ->icq_Password))
1121 phree((ThisICQ->icq_Password));
1122 (ThisICQ->icq_Password) = strdoop(password);
1127 if ((ThisICQ->icq_Password))
1128 phree((ThisICQ->icq_Password));
1131 /******************************
1132 Main function connects gets (ThisICQ->icq_Uin)
1133 and (ThisICQ->icq_Password) and logins in and sits
1134 in a loop waiting for server responses.
1135 *******************************/
1147 FD_SET((ThisICQ->icq_Sok), &readfds);
1148 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1149 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1150 icq_HandleServerResponse();
1154 } while (did_something);
1157 /********************************************************
1158 Russian language ICQ fix.
1159 Usual Windows ICQ users do use Windows 1251 encoding but
1160 unix users do use koi8 encoding, so we need to convert it.
1161 This function will convert string from windows 1251 to koi8
1162 or from koi8 to windows 1251.
1163 Andrew Frolov dron@ilm.net
1164 *********************************************************/
1165 void icq_RusConv(const char to[4], char *t_in)
1170 /* 6-17-1998 by Linux_Dude
1171 * Moved initialization of table out front of 'if' block to prevent compiler
1172 * warning. Improved error message, and now return without performing string
1173 * conversion to prevent addressing memory out of range (table pointer would
1174 * previously have remained uninitialized (= bad)).
1178 if (strcmp(to, "kw") == 0)
1180 else if (strcmp(to, "wk") != 0) {
1181 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1182 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1185 /* End Linux_Dude's changes ;) */
1187 if ((ThisICQ->icq_Russian)) {
1188 for (i = 0; t_in[i] != 0; i++) {
1191 t_in[i] = table[t_in[i] & 0177];
1196 /**************************************************
1197 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1199 ***************************************************/
1200 WORD icq_SendMessage(DWORD uin, const char *text)
1205 char buf[512]; /* message may be only 450 bytes long */
1207 strncpy(buf, text, 512);
1208 icq_RusConv("kw", buf);
1210 Word_2_Chars(pak.head.ver, ICQ_VER);
1211 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1212 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1213 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1214 DW_2_Chars(msg.uin, uin);
1215 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1216 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1217 memcpy(&pak.data, &msg, sizeof(msg));
1218 memcpy(&pak.data[8], buf, len + 1);
1219 size = sizeof(msg) + len + 1;
1220 for (i = 0; i < 6; i++)
1221 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1222 return (ThisICQ->icq_SeqNum) - 1;
1225 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1229 int size, len1, len2;
1230 char buf1[512], buf2[512];
1232 strncpy(buf1, descr, 512);
1233 strncpy(buf2, url, 512);
1234 /* Do we need to convert URL? */
1235 icq_RusConv("kw", buf2);
1236 len1 = strlen(buf1);
1237 len2 = strlen(buf2);
1238 Word_2_Chars(pak.head.ver, ICQ_VER);
1239 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1240 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1241 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1242 DW_2_Chars(msg.uin, uin);
1243 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1244 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1245 memcpy(&pak.data, &msg, sizeof(msg));
1246 memcpy(&pak.data[8], buf1, len1);
1247 pak.data[8 + len1] = 0xFE;
1248 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1249 size = sizeof(msg) + len1 + len2 + 2;
1250 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1251 return (ThisICQ->icq_SeqNum) - 1;
1254 /**************************************************
1255 Sends a authorization to the server so the Mirabilis
1256 client can add the user.
1257 ***************************************************/
1258 void icq_SendAuthMsg(DWORD uin)
1264 Word_2_Chars(pak.head.ver, ICQ_VER);
1265 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1266 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1267 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1268 DW_2_Chars(msg.uin, uin);
1269 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1270 Word_2_Chars(msg.len, 2);
1271 memcpy(&pak.data, &msg, sizeof(msg));
1272 pak.data[sizeof(msg)] = 0x03;
1273 pak.data[sizeof(msg) + 1] = 0x00;
1274 size = sizeof(msg) + 2;
1275 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1278 /**************************************************
1279 Changes the users status on the server
1280 ***************************************************/
1281 void icq_ChangeStatus(DWORD status)
1286 Word_2_Chars(pak.head.ver, ICQ_VER);
1287 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1288 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1289 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1290 DW_2_Chars(pak.data, status);
1291 (ThisICQ->icq_Status) = status;
1293 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1296 /**********************
1298 ***********************/
1304 Word_2_Chars(pak.head.ver, ICQ_VER);
1305 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1306 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1307 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1308 len = strlen("B_USER_DISCONNECTED") + 1;
1309 *(short *) pak.data = len;
1311 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1312 pak.data[2 + len] = 05;
1313 pak.data[3 + len] = 00;
1314 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1317 void icq_Disconnect()
1319 SOCKCLOSE((ThisICQ->icq_Sok));
1320 SOCKCLOSE((ThisICQ->icq_Sok));
1321 if ((ThisICQ->icq_UseProxy))
1322 SOCKCLOSE((ThisICQ->icq_ProxySok));
1325 /********************************************************
1326 Sends a request to the server for info on a specific user
1327 *********************************************************/
1328 WORD icq_SendInfoReq(DWORD uin)
1333 Word_2_Chars(pak.head.ver, ICQ_VER);
1334 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1335 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1336 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1337 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1338 DW_2_Chars(&pak.data[2], uin);
1340 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1341 return (ThisICQ->icq_SeqNum) - 1;
1344 /********************************************************
1345 Sends a request to the server for info on a specific user
1346 *********************************************************/
1347 WORD icq_SendExtInfoReq(DWORD uin)
1352 Word_2_Chars(pak.head.ver, ICQ_VER);
1353 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1354 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1355 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1356 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1357 DW_2_Chars(&pak.data[2], uin);
1359 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1360 return (ThisICQ->icq_SeqNum) - 1;
1363 /**************************************************************
1364 Initializes a server search for the information specified
1365 ***************************************************************/
1366 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1371 Word_2_Chars(pak.head.ver, ICQ_VER);
1372 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1373 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1374 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1375 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1377 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1379 strcpy(pak.data + size, nick);
1380 size += strlen(nick) + 1;
1381 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1383 strcpy(pak.data + size, first);
1384 size += strlen(first) + 1;
1385 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1387 strcpy(pak.data + size, last);
1388 size += strlen(last) + 1;
1389 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1391 strcpy(pak.data + size, email);
1392 size += strlen(email) + 1;
1393 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1396 /**************************************************************
1397 Initializes a server search for the information specified
1398 ***************************************************************/
1399 void icq_SendSearchUINReq(DWORD uin)
1404 Word_2_Chars(pak.head.ver, ICQ_VER);
1405 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1406 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1407 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1408 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1410 DW_2_Chars(&pak.data[size], uin);
1412 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1415 /**************************************************
1416 Registers a new uin in the ICQ network
1417 ***************************************************/
1418 void icq_RegNewUser(const char *pass)
1420 srv_net_icq_pak pak;
1425 Word_2_Chars(pak.head.ver, ICQ_VER);
1426 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1427 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1428 Word_2_Chars(len_buf, len);
1429 memcpy(&pak.data, "\x02\x00", 2);
1430 memcpy(&pak.data[2], &len_buf, 2);
1431 memcpy(&pak.data[4], pass, len + 1);
1432 DW_2_Chars(&pak.data[4 + len], 0x0072);
1433 DW_2_Chars(&pak.data[8 + len], 0x0000);
1435 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1438 void icq_UpdateUserInfo(USER_INFO * user)
1443 Word_2_Chars(pak.head.ver, ICQ_VER);
1444 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1445 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1446 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1447 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1449 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1451 strcpy(pak.data + size, user->nick);
1452 size += strlen(user->nick) + 1;
1453 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1455 strcpy(pak.data + size, user->first);
1456 size += strlen(user->first) + 1;
1457 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1459 strcpy(pak.data + size, user->last);
1460 size += strlen(user->last) + 1;
1461 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1463 strcpy(pak.data + size, user->email);
1464 size += strlen(user->email) + 1;
1465 pak.data[size] = user->auth;
1467 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1470 const char *icq_GetCountryName(int code)
1474 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1475 if (Country_Codes[i].code == code) {
1476 return Country_Codes[i].name;
1479 if (Country_Codes[i].code == code) {
1480 return Country_Codes[i].name;
1485 /********************************************
1486 returns a string describing the status or
1487 a "Error" if no such string exists
1488 *********************************************/
1489 const char *icq_ConvertStatus2Str(int status)
1491 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1494 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1495 switch (status & 0x01FF) {
1500 return "Do not disturb";
1505 case STATUS_OCCUPIED:
1509 return "Not available";
1511 case STATUS_INVISIBLE:
1514 case STATUS_INVISIBLE_2:
1515 return "Invisible mode 2";
1517 case STATUS_FREE_CHAT:
1518 return "Free for chat";
1526 void icq_InitNewUser(const char *hostname, DWORD port)
1528 srv_net_icq_pak pak;
1533 icq_Connect(hostname, port);
1534 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1535 printf("Couldn't establish connection\n");
1538 icq_RegNewUser((ThisICQ->icq_Password));
1541 tv.tv_usec = 500000;
1544 FD_SET((ThisICQ->icq_Sok), &readfds);
1546 /* don't care about writefds and exceptfds: */
1547 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1549 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1550 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1551 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1552 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1559 /********************************************
1560 Converts an intel endian character sequence to
1562 *********************************************/
1563 DWORD Chars_2_DW(unsigned char *buf)
1578 /********************************************
1579 Converts an intel endian character sequence to
1581 *********************************************/
1582 WORD Chars_2_Word(unsigned char *buf)
1593 /********************************************
1595 an intel endian character sequence
1596 *********************************************/
1597 void DW_2_Chars(unsigned char *buf, DWORD num)
1599 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1600 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1601 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1602 buf[0] = (unsigned char) (num) & 0x000000FF;
1605 /********************************************
1607 an intel endian character sequence
1608 *********************************************/
1609 void Word_2_Chars(unsigned char *buf, WORD num)
1611 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1612 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1615 /***************************
1616 ContactList functions
1617 ***************************/
1618 void icq_ContAddUser(DWORD cuin)
1620 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1621 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1624 p->vis_list = FALSE;
1630 (ThisICQ->icq_ContFirst) = p;
1633 void icq_ContDelUser(DWORD cuin)
1635 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1638 if (ptr->uin == cuin) {
1639 (ThisICQ->icq_ContFirst) = ptr->next;
1641 ptr = (ThisICQ->icq_ContFirst);
1644 if (ptr->next->uin == cuin) {
1645 ptr->next = ptr->next->next;
1652 void icq_ContClear()
1654 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1659 (ThisICQ->icq_ContFirst) = ptr;
1663 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1665 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1669 if (ptr->uin == cuin)
1676 icq_ContactItem *icq_ContGetFirst()
1678 return (ThisICQ->icq_ContFirst);
1681 void icq_ContSetVis(DWORD cuin)
1683 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1687 if (ptr->uin == cuin)
1688 ptr->vis_list = TRUE;
1693 /************************
1694 (ThisICQ->icq_ServMess) functions
1695 *************************/
1696 BOOL icq_GetServMess(WORD num)
1698 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1701 void icq_SetServMess(WORD num)
1703 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1708 return (ThisICQ->icq_Sok);
1711 int icq_GetProxySok()
1713 return (ThisICQ->icq_ProxySok);
1716 /*******************
1717 SOCKS5 Proxy support
1718 ********************/
1719 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1721 if ((ThisICQ->icq_ProxyHost))
1722 phree((ThisICQ->icq_ProxyHost));
1723 if ((ThisICQ->icq_ProxyName))
1724 phree((ThisICQ->icq_ProxyName));
1725 if ((ThisICQ->icq_ProxyPass))
1726 phree((ThisICQ->icq_ProxyPass));
1727 if (strlen(pname) > 255) {
1728 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1729 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1730 ThisICQ->icq_UseProxy = 0;
1733 if (strlen(ppass) > 255) {
1734 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1735 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1736 (ThisICQ->icq_UseProxy) = 0;
1739 (ThisICQ->icq_UseProxy) = 1;
1740 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1741 (ThisICQ->icq_ProxyPort) = pport;
1742 (ThisICQ->icq_ProxyAuth) = pauth;
1743 (ThisICQ->icq_ProxyName) = strdoop(pname);
1744 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1747 void icq_UnsetProxy()
1749 ThisICQ->icq_UseProxy = 0;
1754 /***********************************************************************/
1755 /* icqlib stuff ends here, Citadel module stuff begins */
1756 /***********************************************************************/
1760 * Callback function for CtdlICQ_Read_Config()
1762 void CtdlICQ_Read_Config_Backend(long msgnum) {
1763 struct CtdlMessage *msg;
1767 lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
1768 msg = CtdlFetchMessage(msgnum);
1770 ptr = msg->cm_fields['M'];
1771 pos = pattern2(ptr, "\n\n");
1779 safestrncpy(ThisICQ->icq_config, ptr, 256);
1781 CtdlFreeMessage(msg);
1783 lprintf(9, "...it ain't there?\n");
1789 * If this user has an ICQ configuration on disk, read it into memory.
1791 void CtdlICQ_Read_Config(void) {
1792 char hold_rm[ROOMNAMELEN];
1793 char icq_rm[ROOMNAMELEN];
1795 strcpy(hold_rm, CC->quickroom.QRname);
1796 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1797 strcpy(ThisICQ->icq_config, "");
1799 if (getroom(&CC->quickroom, icq_rm) != 0) {
1800 getroom(&CC->quickroom, hold_rm);
1804 /* We want the last (and probably only) config in this room */
1805 lprintf(9, "We're in <%s> looking for config\n",
1806 CC->quickroom.QRname);
1807 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1808 getroom(&CC->quickroom, hold_rm);
1815 * Write our config to disk
1817 void CtdlICQ_Write_Config(void) {
1818 char temp[PATH_MAX];
1821 strcpy(temp, tmpnam(NULL));
1823 fp = fopen(temp, "w");
1824 if (fp == NULL) return;
1825 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1828 /* this handy API function does all the work for us */
1829 CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
1839 * Write our contact list to disk
1841 void CtdlICQ_Write_CL(void) {
1842 char temp[PATH_MAX];
1846 strcpy(temp, tmpnam(NULL));
1848 fp = fopen(temp, "w");
1849 if (fp == NULL) return;
1851 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1852 fprintf(fp, "%ld|%s|\n",
1853 ThisICQ->icq_cl[i].uin,
1854 ThisICQ->icq_cl[i].name);
1858 /* this handy API function does all the work for us */
1859 CtdlWriteObject(ICQROOM, ICQCLMIME, temp, 1, 0, 1);
1869 * Callback function for CtdlICQ_Read_CL()
1871 void CtdlICQ_Read_CL_Backend(long msgnum) {
1872 struct CtdlMessage *msg;
1877 msg = CtdlFetchMessage(msgnum);
1879 ptr = msg->cm_fields['M'];
1880 pos = pattern2(ptr, "\n\n");
1888 for (i=0; i<strlen(ptr); ++i)
1889 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1890 if (ThisICQ->icq_numcl) {
1891 ThisICQ->icq_cl = mallok(
1892 (ThisICQ->icq_numcl *
1893 sizeof (struct CtdlICQ_CL)));
1895 while (cont=strtok(ptr, "\n"), cont != NULL) {
1896 ThisICQ->icq_cl[i].uin =
1897 extract_long(cont, 0);
1898 extract(ThisICQ->icq_cl[i].name,
1900 ThisICQ->icq_cl[i].status =
1907 CtdlFreeMessage(msg);
1913 * Read contact list into memory
1915 void CtdlICQ_Read_CL(void) {
1916 char hold_rm[ROOMNAMELEN];
1917 char icq_rm[ROOMNAMELEN];
1919 strcpy(hold_rm, CC->quickroom.QRname);
1920 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1921 strcpy(ThisICQ->icq_config, "");
1923 if (getroom(&CC->quickroom, icq_rm) != 0) {
1924 getroom(&CC->quickroom, hold_rm);
1928 /* Free any contact list already in memory */
1929 if (ThisICQ->icq_numcl) {
1930 phree(ThisICQ->icq_cl);
1931 ThisICQ->icq_numcl = 0;
1934 /* We want the last (and probably only) list in this room */
1935 CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
1936 getroom(&CC->quickroom, hold_rm);
1941 * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
1942 * entry in the table if necessary
1944 struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
1947 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
1948 if (ThisICQ->icq_cl[i].uin == uin)
1949 return (&ThisICQ->icq_cl[i]);
1951 ++ThisICQ->icq_numcl;
1952 ThisICQ->icq_cl = reallok(ThisICQ->icq_cl,
1953 (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
1954 memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
1955 0, sizeof(struct CtdlICQ_CL));
1956 ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
1957 return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
1963 * Refresh the contact list
1965 void CtdlICQ_Refresh_Contact_List(void) {
1968 if (ThisICQ->icq_Sok < 0) return;
1972 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1973 if (ThisICQ->icq_cl[i].uin > 0L) {
1974 icq_ContAddUser(ThisICQ->icq_cl[i].uin);
1975 icq_ContSetVis(ThisICQ->icq_cl[i].uin);
1979 icq_SendContactList();
1987 * Utility routine to logout and disconnect from the ICQ server if we happen
1988 * to already be connected
1990 void CtdlICQ_Logout_If_Connected(void) {
1991 if (ThisICQ->icq_Sok >= 0) {
1994 ThisICQ->icq_Sok = (-1);
2000 * If we have an ICQ uin/password on file for this user, go ahead and try
2001 * to log on to the ICQ server.
2003 void CtdlICQ_Login_If_Possible(void) {
2007 CtdlICQ_Logout_If_Connected();
2008 CtdlICQ_Read_Config();
2010 uin = extract_long(ThisICQ->icq_config, 0);
2011 extract(pass, ThisICQ->icq_config, 1);
2013 if ( (uin > 0L) && (strlen(pass)>0) ) {
2014 icq_Init(uin, pass);
2015 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2016 icq_Login(STATUS_ONLINE);
2023 /* This merely hooks icqlib's log function into Citadel's log function. */
2024 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2026 lprintf(level, "ICQ: %s", str);
2031 * At the start of each Citadel server session, we have to allocate some
2032 * space for the Citadel-ICQ session for this thread. It'll be automatically
2033 * freed by the server when the session ends.
2035 void CtdlICQ_session_startup_hook(void)
2037 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2038 icq_init_handle(ThisICQ);
2044 * End-of-session cleanup
2046 void CtdlICQ_session_stopdown_hook(void) {
2053 * icq_Main() needs to be called as frequently as possible. We'll do it
2054 * following the completion of each Citadel server command.
2057 void CtdlICQ_after_cmd_hook(void)
2059 if (ThisICQ->icq_Sok >= 0) {
2060 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 60 ) {
2062 ThisICQ->icq_LastKeepAlive = time(NULL);
2070 * There are a couple of things we absolutely need to make sure of when we
2071 * kill off the session. One of them is that the sockets are all closed --
2072 * otherwise we could have a nasty file descriptor leak.
2074 void CtdlICQ_session_logout_hook(void)
2076 lprintf(9, "Shutting down ICQ\n");
2077 CtdlICQ_Logout_If_Connected();
2079 /* Free the memory used by the contact list */
2080 if (ThisICQ->icq_numcl) {
2081 phree(ThisICQ->icq_cl);
2082 ThisICQ->icq_numcl = 0;
2089 void CtdlICQ_session_login_hook(void)
2091 /* If this user has an ICQ config on file, start it up. */
2092 CtdlICQ_Login_If_Possible();
2101 * Here's what we have to do when an ICQ message arrives!
2103 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2104 BYTE day, BYTE month, WORD year,
2111 /* Default sender is 'uin@icq' syntax */
2112 sprintf(from, "%ld@icq", uin);
2114 /* Use the sender's name if we have it inthe contact list */
2115 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
2116 if (uin == ThisICQ->icq_cl[i].uin) {
2117 safestrncpy(from, ThisICQ->icq_cl[i].name, 256);
2121 num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2122 lprintf(9, "Delivered to %d users\n", num_delivered);
2127 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2128 const char *first, const char *last,
2129 const char *email, char auth) {
2131 struct CtdlICQ_CL *ptr;
2134 ptr = CtdlICQ_CLent(uin);
2135 safestrncpy(ptr->name, nick, 32);
2136 ptr->status = STATUS_OFFLINE;
2137 lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
2145 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2148 DWORD target_uin = 0L;
2151 /* If this is an incoming ICQ from someone on the contact list,
2152 * change the sender from "uin@icq" to the contact name.
2155 for (i=0; i<strlen(from); ++i)
2156 if (!strcasecmp(&from[i], "@icq")) {
2159 if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
2160 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2161 if (ThisICQ->icq_cl[i].uin == atol(from))
2162 strcpy(from, ThisICQ->icq_cl[i].name);
2167 /* Handle "uin@icq" syntax */
2169 for (i=0; i<strlen(recp); ++i)
2170 if (!strcasecmp(&recp[i], "@icq")) {
2173 if (is_aticq == 1) target_uin = atol(recp);
2175 /* Handle "nick" syntax */
2176 if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
2177 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2178 if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
2179 target_uin = ThisICQ->icq_cl[i].uin;
2185 if (target_uin == 0L) return(0);
2187 if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
2193 void cmd_cicq(char *argbuf) {
2200 if (!(CC->logged_in)) {
2201 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
2204 extract(cmd, argbuf, 0);
2206 /* "CICQ login" tells us how to log in. */
2207 if (!strcasecmp(cmd, "login")) {
2208 uin = extract_long(argbuf, 1);
2209 extract(pass, argbuf, 2);
2210 sprintf(ThisICQ->icq_config, "%ld|%s|", uin, pass);
2212 CtdlICQ_Write_Config();
2213 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2214 CtdlICQ_Login_If_Possible();
2216 cprintf("%d You must supply a UIN.\n", ERROR);
2221 /* "CICQ getcl" returns the contact list */
2222 if (!strcasecmp(cmd, "getcl")) {
2224 cprintf("%d Your ICQ contact list:\n", LISTING_FOLLOWS);
2225 if (ThisICQ->icq_numcl > 0) {
2226 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2227 cprintf("%ld|%s|%s|\n",
2228 ThisICQ->icq_cl[i].uin,
2229 ThisICQ->icq_cl[i].name,
2230 icq_ConvertStatus2Str(
2231 ThisICQ->icq_cl[i].status)
2239 /* "CICQ putcl" accepts a new contact list from the client */
2240 if (!strcasecmp(cmd, "putcl")) {
2241 cprintf("%d Send contact list\n", SEND_LISTING);
2242 ThisICQ->icq_numcl = 0;
2243 while (client_gets(buf), strcmp(buf, "000")) {
2244 uin = extract_long(buf, 0);
2250 CtdlICQ_Refresh_Contact_List();
2254 /* "CICQ status" returns the connected/notconnected status */
2255 if (!strcasecmp(cmd, "status")) {
2256 cprintf("%d %d\n", OK,
2257 ((ThisICQ->icq_Sok >= 0) ? 1 : 0) );
2261 cprintf("%d Invalid subcommand\n", ERROR);
2269 * During an RWHO command, we want to append our ICQ information.
2271 void CtdlICQ_rwho(void) {
2274 if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
2275 if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
2276 cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
2277 0, /* no session ID */
2278 ThisICQ->icq_cl[i].name,
2279 icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
2280 " ", /* FIX add host */
2281 " ", /* no client */
2282 time(NULL), /* now? */
2283 " ", /* no last command */
2289 void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
2290 struct CtdlICQ_CL *ptr;
2292 ptr = CtdlICQ_CLent(uin);
2293 ptr->status = status;
2294 if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
2298 void CtdlICQ_Logged(void) {
2299 CtdlICQ_Refresh_Contact_List();
2303 void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
2304 DWORD port, DWORD realip) {
2306 CtdlICQ_Status_Update(uin, status);
2310 void CtdlICQ_UserOffline(DWORD uin) {
2311 CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
2315 char *Dynamic_Module_Init(void)
2317 /* Make sure we've got a valid ThisICQ for each session */
2318 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2320 /* Tell the Citadel server about our wonderful ICQ hooks */
2321 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2322 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2323 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2324 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2325 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2326 CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
2327 CtdlRegisterProtoHook(cmd_cicq, "CICQ", "Configure Citadel ICQ");
2328 CtdlRegisterXmsgHook(CtdlICQ_Send_Msg, XMSG_PRI_FOREIGN);
2330 /* Tell the code formerly known as icqlib about our callbacks */
2331 icq_Log = CtdlICQlog;
2332 icq_RecvMessage = CtdlICQ_Incoming_Message;
2333 icq_InfoReply = CtdlICQ_InfoReply;
2334 icq_Disconnected = CtdlICQ_Login_If_Possible;
2335 icq_Logged = CtdlICQ_Logged;
2336 icq_UserStatusUpdate = CtdlICQ_Status_Update;
2337 icq_UserOnline = CtdlICQ_UserOnline;
2338 icq_UserOffline = CtdlICQ_UserOffline;