4 * This is a modified version of Denis V. Dmitrienko's ICQLIB, a very cleanly
5 * written implementation of the Mirabilis ICQ client protocol. The library
6 * has been modified rather than merely utilized because we need it to be
7 * threadsafe in order to tie it into the Citadel server.
9 * Incomplete list of changes I made:
10 * * All globals placed into struct ctdl_icq_handle so we can do it per-thread
11 * * References to globals changed to ThisICQ->globalname
12 * * malloc->mallok, free->phree, strdup->strdoop, for memory leak checking
13 * * Added a bunch of #include's needed by Citadel
14 * * Most of the Citadel-specific code is appended to the end
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
41 #include "dynloader.h"
43 #include "citserver.h"
44 #include "locate_host.h"
46 #include "sysdep_decls.h"
52 * Contact list in memory
64 /* MIME types to use for storing ICQ stuff */
65 #define ICQMIME "application/x-citadel-icq" /* configuration */
66 #define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
68 /* Citadel server TSD symbol for use by serv_icq */
69 unsigned long SYM_CTDL_ICQ;
70 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
72 extern struct CitContext *ContextList;
77 void (*icq_Logged) (void);
78 void (*icq_Disconnected) (void);
79 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
80 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
81 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);
82 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);
83 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
84 void (*icq_SearchDone) (void);
85 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
86 void (*icq_UserOffline) (DWORD uin);
87 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
88 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
89 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);
90 void (*icq_Log) (time_t time, unsigned char level, const char *str);
91 void (*icq_SrvAck) (WORD seq);
94 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
95 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
96 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
97 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
98 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
99 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
100 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
101 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
104 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
105 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
106 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
107 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
108 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
109 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
110 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
111 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
113 static COUNTRY_CODE Country_Codes[] =
128 {"American Samoa", 684},
143 {"Czech Republic", 42},
146 {"El Salvador", 503},
150 {"French Antilles", 596},
151 {"French Polynesia", 689},
158 {"Guantanomo Bay", 53},
172 {"Ivory Coast", 225},
180 {"Liechtenstein", 41},
192 {"Netherlands Antilles", 599},
193 {"New Caledonia", 687},
200 {"Papua New Guinea", 675},
210 {"Saudia Arabia", 966},
215 {"South Africa", 27},
226 {"United Arab Emirates", 971},
228 {"Vatican City", 39},
235 {"Not entered", 0xffff}};
237 void icq_init_handle(struct ctdl_icq_handle *i)
239 memset(i, 0, sizeof(struct ctdl_icq_handle));
240 i->icq_Russian = TRUE;
242 i->icq_OurIp = 0x0100007f;
243 i->icq_Status = STATUS_OFFLINE;
248 int icq_SockWrite(int sok, const void *buf, size_t count)
251 if (!(ThisICQ->icq_UseProxy))
252 return write(sok, buf, count);
254 tmpbuf[0] = 0; /* reserved */
255 tmpbuf[1] = 0; /* reserved */
256 tmpbuf[2] = 0; /* standalone packet */
257 tmpbuf[3] = 1; /* address type IP v4 */
258 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
259 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
260 memcpy(&tmpbuf[10], buf, count);
261 return write(sok, tmpbuf, count + 10) - 10;
265 int icq_SockRead(int sok, void *buf, size_t count)
269 if (!(ThisICQ->icq_UseProxy))
270 return read(sok, buf, count);
272 res = read(sok, tmpbuf, count + 10);
273 memcpy(buf, &tmpbuf[10], res - 10);
278 /****************************************
279 This must be called every 2 min.
280 so the server knows we're still alive.
281 JAVA client sends two different commands
283 *****************************************/
288 Word_2_Chars(pak.head.ver, ICQ_VER);
289 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
290 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
291 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
292 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
294 Word_2_Chars(pak.head.ver, ICQ_VER);
295 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
296 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
297 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
298 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
300 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
301 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
304 /**********************************
305 This must be called to remove
306 messages from the server
307 ***********************************/
308 void icq_SendGotMessages()
312 Word_2_Chars(pak.head.ver, ICQ_VER);
313 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
314 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
315 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
317 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
320 /*************************************
321 this sends over the contact list
322 *************************************/
323 void icq_SendContactList()
329 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
331 Word_2_Chars(pak.head.ver, ICQ_VER);
332 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
333 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
334 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
340 DW_2_Chars(tmp, ptr->uin);
345 pak.data[0] = num_used;
346 size = ((int) tmp - (int) pak.data);
347 size += sizeof(pak.head);
348 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
351 void icq_SendNewUser(unsigned long uin)
356 Word_2_Chars(pak.head.ver, ICQ_VER);
357 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
358 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
359 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
360 DW_2_Chars(pak.data, uin);
361 size = sizeof(pak.head) + 4;
362 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
365 /*************************************
366 this sends over the visible list
367 that allows certain users to see you
369 *************************************/
370 void icq_SendVisibleList()
376 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
378 Word_2_Chars(pak.head.ver, ICQ_VER);
379 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
380 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
381 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
388 DW_2_Chars(tmp, ptr->uin);
395 pak.data[0] = num_used;
396 size = ((int) tmp - (int) pak.data);
397 size += sizeof(pak.head);
398 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
402 /**************************************
403 This sends the second login command
404 this is necessary to finish logging in.
405 ***************************************/
406 void icq_SendLogin1()
410 Word_2_Chars(pak.head.ver, ICQ_VER);
411 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
412 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
413 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
415 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
418 /************************************************
419 This is called when a user goes offline
420 *************************************************/
421 void icq_HandleUserOffline(srv_net_icq_pak pak)
426 remote_uin = Chars_2_DW(pak.data);
427 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
428 sprintf(buf, "User %lu logged off\n", remote_uin);
429 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
432 (*icq_UserOffline) (remote_uin);
433 icq_AckSrv(Chars_2_Word(pak.head.seq));
436 void icq_HandleUserOnline(srv_net_icq_pak pak)
438 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
439 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
442 remote_uin = Chars_2_DW(pak.data);
443 new_status = Chars_2_DW(&pak.data[17]);
444 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
445 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
446 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
447 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
448 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
449 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
452 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
453 icq_AckSrv(Chars_2_Word(pak.head.seq));
456 void icq_Status_Update(srv_net_icq_pak pak)
458 unsigned long remote_uin, new_status;
461 remote_uin = Chars_2_DW(pak.data);
462 new_status = Chars_2_DW(&pak.data[4]);
463 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
464 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
465 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
467 if (icq_UserStatusUpdate)
468 (*icq_UserStatusUpdate) (remote_uin, new_status);
469 icq_AckSrv(Chars_2_Word(pak.head.seq));
472 /************************************
473 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
474 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
475 It does NOT wait for any kind of a response.
476 *************************************/
477 void icq_Login(DWORD status)
484 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
485 Word_2_Chars(pak.head.ver, ICQ_VER);
486 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
487 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
488 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
490 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
491 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
493 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
494 DW_2_Chars(s2.status, status);
495 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
497 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
498 s2.X2[0] = LOGIN_X2_DEF;
499 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
500 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
501 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
503 memcpy(pak.data, &s1, sizeof(s1));
505 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
506 size += Chars_2_Word(s1.len);
507 memcpy(&pak.data[size], &s2, sizeof(s2));
509 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
512 /*******************************
513 This routine sends the aknowlegement cmd to the
514 server it appears that this must be done after
515 everything the server sends us
516 *******************************/
517 void icq_AckSrv(int seq)
522 Word_2_Chars(pak.head.ver, ICQ_VER);
523 Word_2_Chars(pak.head.cmd, CMD_ACK);
524 Word_2_Chars(pak.head.seq, seq);
525 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
526 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
527 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
528 for (i = 0; i < 6; i++)
529 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
532 void icq_HandleInfoReply(srv_net_icq_pak pak)
534 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
538 seq = Chars_2_Word(pak.data);
539 uin = Chars_2_DW(&pak.data[2]);
540 len = Chars_2_Word(&pak.data[6]);
542 icq_RusConv("wk", ptr1);
543 tmp = &pak.data[8 + len];
544 len = Chars_2_Word(tmp);
546 icq_RusConv("wk", ptr2);
548 len = Chars_2_Word(tmp);
550 icq_RusConv("wk", ptr3);
552 len = Chars_2_Word(tmp);
554 icq_RusConv("wk", ptr4);
556 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
557 sprintf(buf, "Info reply for %lu\n", uin);
558 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
561 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
562 icq_AckSrv(Chars_2_Word(pak.head.seq));
565 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
567 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
571 char cnt_stat, gender, buf[256];
572 uin = Chars_2_DW(&pak.data[2]);
573 len = Chars_2_Word(&pak.data[6]);
575 icq_RusConv("wk", ptr1);
576 cnt_code = Chars_2_Word(&pak.data[8 + len]);
577 cnt_stat = pak.data[len + 10];
578 tmp = &pak.data[11 + len];
579 len = Chars_2_Word(tmp);
580 icq_RusConv("wk", tmp + 2);
582 age = Chars_2_Word(tmp + 2 + len);
583 gender = *(tmp + len + 4);
585 len = Chars_2_Word(tmp);
586 icq_RusConv("wk", tmp + 2);
589 len = Chars_2_Word(tmp);
590 icq_RusConv("wk", tmp + 2);
593 len = Chars_2_Word(tmp);
594 icq_RusConv("wk", tmp + 2);
596 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
597 sprintf(buf, "Extended info reply for %lu\n", uin);
598 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
600 if (icq_ExtInfoReply)
601 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
602 icq_AckSrv(Chars_2_Word(pak.head.seq));
605 void icq_HandleSearchReply(srv_net_icq_pak pak)
607 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
611 uin = Chars_2_DW(&pak.data[2]);
612 len = Chars_2_Word(&pak.data[6]);
614 icq_RusConv("wk", ptr1);
615 tmp = &pak.data[8 + len];
616 len = Chars_2_Word(tmp);
618 icq_RusConv("wk", ptr2);
620 len = Chars_2_Word(tmp);
622 icq_RusConv("wk", ptr3);
624 len = Chars_2_Word(tmp);
626 icq_RusConv("wk", ptr4);
628 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
629 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");
630 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
633 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
634 icq_AckSrv(Chars_2_Word(pak.head.seq));
637 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
640 char *ptr1, *ptr2, *ptr3, *ptr4;
644 case USER_ADDED_MESS:
645 tmp = strchr(data, '\xFE');
647 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
648 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
655 tmp = strchr(tmp, '\xFE');
657 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
658 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
662 icq_RusConv("wk", data);
666 tmp = strchr(tmp, '\xFE');
668 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
669 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
673 icq_RusConv("wk", data);
677 tmp = strchr(tmp, '\xFE');
679 icq_RusConv("wk", data);
680 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
681 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
682 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
683 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
686 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
689 tmp = strchr(data, '\xFE');
694 tmp = strchr(tmp, '\xFE');
696 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
697 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
701 icq_RusConv("wk", data);
705 tmp = strchr(tmp, '\xFE');
707 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
708 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
712 icq_RusConv("wk", data);
716 tmp = strchr(tmp, '\xFE');
718 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
719 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
723 icq_RusConv("wk", data);
727 tmp = strchr(tmp, '\xFE');
729 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
730 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
736 tmp = strchr(tmp, '\x00');
738 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
739 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
743 icq_RusConv("wk", data);
744 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
745 sprintf(buf, "%lu has requested your authorization to be added to "
746 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
747 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
748 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
751 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
754 tmp = strchr(data, '\xFE');
756 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
757 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
761 icq_RusConv("wk", data);
765 icq_RusConv("wk", data);
766 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
767 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
768 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
771 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
774 icq_RusConv("wk", data);
775 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
776 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
777 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
780 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
784 /**********************************
785 Connects to hostname on port port
786 hostname can be DNS or nnn.nnn.nnn.nnn
787 write out messages to the FD aux
788 ***********************************/
789 int icq_Connect(const char *hostname, int port)
793 int conct, length, res;
794 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
795 struct hostent *host_struct; /* used in DNS llokup */
797 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
798 if ((ThisICQ->icq_Sok) == -1) {
799 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
800 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
803 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
804 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
805 sin.sin_addr.s_addr = INADDR_ANY;
806 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
808 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
809 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
810 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
813 length = sizeof(sin);
814 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
815 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
816 if ((ThisICQ->icq_UseProxy)) {
817 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
818 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
819 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
820 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
821 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
822 if (host_struct == 0L) {
823 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
824 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
825 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
829 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
831 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
832 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
833 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
834 if ((ThisICQ->icq_ProxySok) == -1) {
835 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
836 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
839 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
840 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
841 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
842 if (conct == -1) { /* did we connect ? */
843 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
844 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
847 buf[0] = 5; /* protocol version */
848 buf[1] = 1; /* number of methods */
849 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
850 buf[2] = 0; /* no authorization required */
852 buf[2] = 2; /* method username/password */
853 write((ThisICQ->icq_ProxySok), buf, 3);
854 res = read((ThisICQ->icq_ProxySok), buf, 2);
855 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
856 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
857 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
858 close((ThisICQ->icq_ProxySok));
861 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
862 buf[0] = 1; /* version of subnegotiation */
863 buf[1] = strlen((ThisICQ->icq_ProxyName));
864 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
865 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
866 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
867 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
868 res = read((ThisICQ->icq_ProxySok), buf, 2);
869 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
870 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
871 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
872 close((ThisICQ->icq_ProxySok));
876 buf[0] = 5; /* protocol version */
877 buf[1] = 3; /* command UDP associate */
878 buf[2] = 0; /* reserved */
879 buf[3] = 1; /* address type IP v4 */
884 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
885 write((ThisICQ->icq_ProxySok), buf, 10);
886 res = read((ThisICQ->icq_ProxySok), buf, 10);
887 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
890 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
891 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
894 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
895 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
898 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
899 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
902 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
903 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
906 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
907 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
910 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
911 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
914 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
915 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
918 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
919 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
922 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
923 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
926 close((ThisICQ->icq_ProxySok));
930 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
931 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
932 host_struct = gethostbyname(hostname);
933 if (host_struct == 0L) {
934 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
935 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
936 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
938 if ((ThisICQ->icq_UseProxy))
939 close((ThisICQ->icq_ProxySok));
942 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
944 if ((ThisICQ->icq_UseProxy)) {
945 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
946 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
948 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
949 sin.sin_port = htons(port); /* port */
950 if ((ThisICQ->icq_UseProxy)) {
951 (ThisICQ->icq_ProxyDestPort) = htons(port);
952 memcpy(&sin.sin_port, &buf[8], 2);
954 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
955 if (conct == -1) { /* did we connect ? */
956 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
957 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
958 if ((ThisICQ->icq_UseProxy))
959 close((ThisICQ->icq_ProxySok));
962 length = sizeof(sin);
963 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
964 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
965 (ThisICQ->icq_OurPort) = sin.sin_port;
966 return (ThisICQ->icq_Sok);
969 void icq_HandleProxyResponse()
973 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
975 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
976 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
977 if (icq_Disconnected)
978 (*icq_Disconnected) ();
979 SOCKCLOSE((ThisICQ->icq_Sok));
980 SOCKCLOSE((ThisICQ->icq_ProxySok));
984 /******************************************
985 Handles packets that the server sends to us.
986 *******************************************/
987 void icq_HandleServerResponse()
990 SIMPLE_MESSAGE *s_mesg;
991 RECV_MESSAGE *r_mesg;
997 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
999 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1000 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1001 if (icq_Disconnected)
1002 (*icq_Disconnected) ();
1003 SOCKCLOSE((ThisICQ->icq_Sok));
1005 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)) {
1006 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1007 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1008 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1009 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1011 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1015 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1016 icq_SetServMess(Chars_2_Word(pak.head.seq));
1017 switch (Chars_2_Word(pak.head.cmd)) {
1019 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1020 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1022 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1025 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1026 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1027 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1030 case SRV_LOGIN_REPLY:
1031 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1032 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1033 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]);
1034 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1036 icq_AckSrv(Chars_2_Word(pak.head.seq));
1038 icq_SendContactList();
1039 icq_SendVisibleList();
1043 case SRV_RECV_MESSAGE:
1044 r_mesg = (RECV_MESSAGE *) pak.data;
1045 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));
1046 icq_AckSrv(Chars_2_Word(pak.head.seq));
1048 case SRV_X1: /* unknown message sent after login */
1049 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1050 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1051 icq_AckSrv(Chars_2_Word(pak.head.seq));
1053 case SRV_X2: /* unknown message sent after login */
1054 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1055 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1056 icq_AckSrv(Chars_2_Word(pak.head.seq));
1057 icq_SendGotMessages();
1059 case SRV_INFO_REPLY:
1060 icq_HandleInfoReply(pak);
1062 case SRV_EXT_INFO_REPLY:
1063 icq_HandleExtInfoReply(pak);
1065 case SRV_USER_ONLINE:
1066 icq_HandleUserOnline(pak);
1068 case SRV_USER_OFFLINE:
1069 icq_HandleUserOffline(pak);
1072 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1073 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1074 icq_Login((ThisICQ->icq_Status));
1076 case SRV_STATUS_UPDATE:
1077 icq_Status_Update(pak);
1080 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1081 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1082 if (icq_Disconnected)
1083 (*icq_Disconnected) ();
1085 case SRV_END_OF_SEARCH:
1086 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1087 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1089 (*icq_SearchDone) ();
1090 icq_AckSrv(Chars_2_Word(pak.head.seq));
1092 case SRV_USER_FOUND:
1093 icq_HandleSearchReply(pak);
1095 case SRV_SYS_DELIVERED_MESS:
1096 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1097 cur_time = time(0L);
1098 tm_str = localtime(&cur_time);
1099 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);
1100 icq_AckSrv(Chars_2_Word(pak.head.seq));
1102 default: /* commands we dont handle yet */
1103 len = s - (sizeof(pak.head));
1104 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1105 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1106 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1107 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1109 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1114 void icq_Init(DWORD uin, const char *password)
1116 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1117 (ThisICQ->icq_Uin) = uin;
1118 if ((ThisICQ->icq_Password))
1119 phree((ThisICQ->icq_Password));
1120 (ThisICQ->icq_Password) = strdoop(password);
1125 if ((ThisICQ->icq_Password))
1126 phree((ThisICQ->icq_Password));
1129 /******************************
1130 Main function connects gets (ThisICQ->icq_Uin)
1131 and (ThisICQ->icq_Password) and logins in and sits
1132 in a loop waiting for server responses.
1133 *******************************/
1145 FD_SET((ThisICQ->icq_Sok), &readfds);
1146 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1147 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1148 icq_HandleServerResponse();
1152 } while (did_something);
1155 /********************************************************
1156 Russian language ICQ fix.
1157 Usual Windows ICQ users do use Windows 1251 encoding but
1158 unix users do use koi8 encoding, so we need to convert it.
1159 This function will convert string from windows 1251 to koi8
1160 or from koi8 to windows 1251.
1161 Andrew Frolov dron@ilm.net
1162 *********************************************************/
1163 void icq_RusConv(const char to[4], char *t_in)
1168 /* 6-17-1998 by Linux_Dude
1169 * Moved initialization of table out front of 'if' block to prevent compiler
1170 * warning. Improved error message, and now return without performing string
1171 * conversion to prevent addressing memory out of range (table pointer would
1172 * previously have remained uninitialized (= bad)).
1176 if (strcmp(to, "kw") == 0)
1178 else if (strcmp(to, "wk") != 0) {
1179 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1180 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1183 /* End Linux_Dude's changes ;) */
1185 if ((ThisICQ->icq_Russian)) {
1186 for (i = 0; t_in[i] != 0; i++) {
1189 t_in[i] = table[t_in[i] & 0177];
1194 /**************************************************
1195 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1197 ***************************************************/
1198 WORD icq_SendMessage(DWORD uin, const char *text)
1203 char buf[512]; /* message may be only 450 bytes long */
1205 strncpy(buf, text, 512);
1206 icq_RusConv("kw", buf);
1208 Word_2_Chars(pak.head.ver, ICQ_VER);
1209 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1210 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1211 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1212 DW_2_Chars(msg.uin, uin);
1213 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1214 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1215 memcpy(&pak.data, &msg, sizeof(msg));
1216 memcpy(&pak.data[8], buf, len + 1);
1217 size = sizeof(msg) + len + 1;
1218 for (i = 0; i < 6; i++)
1219 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1220 return (ThisICQ->icq_SeqNum) - 1;
1223 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1227 int size, len1, len2;
1228 char buf1[512], buf2[512];
1230 strncpy(buf1, descr, 512);
1231 strncpy(buf2, url, 512);
1232 /* Do we need to convert URL? */
1233 icq_RusConv("kw", buf2);
1234 len1 = strlen(buf1);
1235 len2 = strlen(buf2);
1236 Word_2_Chars(pak.head.ver, ICQ_VER);
1237 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1238 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1239 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1240 DW_2_Chars(msg.uin, uin);
1241 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1242 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1243 memcpy(&pak.data, &msg, sizeof(msg));
1244 memcpy(&pak.data[8], buf1, len1);
1245 pak.data[8 + len1] = 0xFE;
1246 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1247 size = sizeof(msg) + len1 + len2 + 2;
1248 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1249 return (ThisICQ->icq_SeqNum) - 1;
1252 /**************************************************
1253 Sends a authorization to the server so the Mirabilis
1254 client can add the user.
1255 ***************************************************/
1256 void icq_SendAuthMsg(DWORD uin)
1262 Word_2_Chars(pak.head.ver, ICQ_VER);
1263 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1264 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1265 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1266 DW_2_Chars(msg.uin, uin);
1267 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1268 Word_2_Chars(msg.len, 2);
1269 memcpy(&pak.data, &msg, sizeof(msg));
1270 pak.data[sizeof(msg)] = 0x03;
1271 pak.data[sizeof(msg) + 1] = 0x00;
1272 size = sizeof(msg) + 2;
1273 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1276 /**************************************************
1277 Changes the users status on the server
1278 ***************************************************/
1279 void icq_ChangeStatus(DWORD status)
1284 Word_2_Chars(pak.head.ver, ICQ_VER);
1285 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1286 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1287 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1288 DW_2_Chars(pak.data, status);
1289 (ThisICQ->icq_Status) = status;
1291 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1294 /**********************
1296 ***********************/
1302 Word_2_Chars(pak.head.ver, ICQ_VER);
1303 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1304 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1305 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1306 len = strlen("B_USER_DISCONNECTED") + 1;
1307 *(short *) pak.data = len;
1309 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1310 pak.data[2 + len] = 05;
1311 pak.data[3 + len] = 00;
1312 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1315 void icq_Disconnect()
1317 SOCKCLOSE((ThisICQ->icq_Sok));
1318 SOCKCLOSE((ThisICQ->icq_Sok));
1319 if ((ThisICQ->icq_UseProxy))
1320 SOCKCLOSE((ThisICQ->icq_ProxySok));
1323 /********************************************************
1324 Sends a request to the server for info on a specific user
1325 *********************************************************/
1326 WORD icq_SendInfoReq(DWORD uin)
1331 Word_2_Chars(pak.head.ver, ICQ_VER);
1332 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1333 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1334 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1335 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1336 DW_2_Chars(&pak.data[2], uin);
1338 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1339 return (ThisICQ->icq_SeqNum) - 1;
1342 /********************************************************
1343 Sends a request to the server for info on a specific user
1344 *********************************************************/
1345 WORD icq_SendExtInfoReq(DWORD uin)
1350 Word_2_Chars(pak.head.ver, ICQ_VER);
1351 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1352 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1353 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1354 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1355 DW_2_Chars(&pak.data[2], uin);
1357 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1358 return (ThisICQ->icq_SeqNum) - 1;
1361 /**************************************************************
1362 Initializes a server search for the information specified
1363 ***************************************************************/
1364 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1369 Word_2_Chars(pak.head.ver, ICQ_VER);
1370 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1371 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1372 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1373 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1375 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1377 strcpy(pak.data + size, nick);
1378 size += strlen(nick) + 1;
1379 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1381 strcpy(pak.data + size, first);
1382 size += strlen(first) + 1;
1383 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1385 strcpy(pak.data + size, last);
1386 size += strlen(last) + 1;
1387 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1389 strcpy(pak.data + size, email);
1390 size += strlen(email) + 1;
1391 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1394 /**************************************************************
1395 Initializes a server search for the information specified
1396 ***************************************************************/
1397 void icq_SendSearchUINReq(DWORD uin)
1402 Word_2_Chars(pak.head.ver, ICQ_VER);
1403 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1404 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1405 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1406 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1408 DW_2_Chars(&pak.data[size], uin);
1410 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1413 /**************************************************
1414 Registers a new uin in the ICQ network
1415 ***************************************************/
1416 void icq_RegNewUser(const char *pass)
1418 srv_net_icq_pak pak;
1423 Word_2_Chars(pak.head.ver, ICQ_VER);
1424 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1425 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1426 Word_2_Chars(len_buf, len);
1427 memcpy(&pak.data, "\x02\x00", 2);
1428 memcpy(&pak.data[2], &len_buf, 2);
1429 memcpy(&pak.data[4], pass, len + 1);
1430 DW_2_Chars(&pak.data[4 + len], 0x0072);
1431 DW_2_Chars(&pak.data[8 + len], 0x0000);
1433 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1436 void icq_UpdateUserInfo(USER_INFO * user)
1441 Word_2_Chars(pak.head.ver, ICQ_VER);
1442 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1443 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1444 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1445 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1447 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1449 strcpy(pak.data + size, user->nick);
1450 size += strlen(user->nick) + 1;
1451 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1453 strcpy(pak.data + size, user->first);
1454 size += strlen(user->first) + 1;
1455 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1457 strcpy(pak.data + size, user->last);
1458 size += strlen(user->last) + 1;
1459 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1461 strcpy(pak.data + size, user->email);
1462 size += strlen(user->email) + 1;
1463 pak.data[size] = user->auth;
1465 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1468 const char *icq_GetCountryName(int code)
1472 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1473 if (Country_Codes[i].code == code) {
1474 return Country_Codes[i].name;
1477 if (Country_Codes[i].code == code) {
1478 return Country_Codes[i].name;
1483 /********************************************
1484 returns a string describing the status or
1485 a "Error" if no such string exists
1486 *********************************************/
1487 const char *icq_ConvertStatus2Str(int status)
1489 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1492 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1493 switch (status & 0x01FF) {
1498 return "Do not disturb";
1503 case STATUS_OCCUPIED:
1507 return "Not available";
1509 case STATUS_INVISIBLE:
1512 case STATUS_INVISIBLE_2:
1513 return "Invisible mode 2";
1515 case STATUS_FREE_CHAT:
1516 return "Free for chat";
1524 void icq_InitNewUser(const char *hostname, DWORD port)
1526 srv_net_icq_pak pak;
1531 icq_Connect(hostname, port);
1532 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1533 printf("Couldn't establish connection\n");
1536 icq_RegNewUser((ThisICQ->icq_Password));
1539 tv.tv_usec = 500000;
1542 FD_SET((ThisICQ->icq_Sok), &readfds);
1544 /* don't care about writefds and exceptfds: */
1545 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1547 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1548 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1549 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1550 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1557 /********************************************
1558 Converts an intel endian character sequence to
1560 *********************************************/
1561 DWORD Chars_2_DW(unsigned char *buf)
1576 /********************************************
1577 Converts an intel endian character sequence to
1579 *********************************************/
1580 WORD Chars_2_Word(unsigned char *buf)
1591 /********************************************
1593 an intel endian character sequence
1594 *********************************************/
1595 void DW_2_Chars(unsigned char *buf, DWORD num)
1597 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1598 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1599 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1600 buf[0] = (unsigned char) (num) & 0x000000FF;
1603 /********************************************
1605 an intel endian character sequence
1606 *********************************************/
1607 void Word_2_Chars(unsigned char *buf, WORD num)
1609 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1610 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1613 /***************************
1614 ContactList functions
1615 ***************************/
1616 void icq_ContAddUser(DWORD cuin)
1618 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1619 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1622 p->vis_list = FALSE;
1628 (ThisICQ->icq_ContFirst) = p;
1631 void icq_ContDelUser(DWORD cuin)
1633 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1636 if (ptr->uin == cuin) {
1637 (ThisICQ->icq_ContFirst) = ptr->next;
1639 ptr = (ThisICQ->icq_ContFirst);
1642 if (ptr->next->uin == cuin) {
1643 ptr->next = ptr->next->next;
1650 void icq_ContClear()
1652 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1657 (ThisICQ->icq_ContFirst) = ptr;
1661 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1663 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1667 if (ptr->uin == cuin)
1674 icq_ContactItem *icq_ContGetFirst()
1676 return (ThisICQ->icq_ContFirst);
1679 void icq_ContSetVis(DWORD cuin)
1681 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1685 if (ptr->uin == cuin)
1686 ptr->vis_list = TRUE;
1691 /************************
1692 (ThisICQ->icq_ServMess) functions
1693 *************************/
1694 BOOL icq_GetServMess(WORD num)
1696 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1699 void icq_SetServMess(WORD num)
1701 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1706 return (ThisICQ->icq_Sok);
1709 int icq_GetProxySok()
1711 return (ThisICQ->icq_ProxySok);
1714 /*******************
1715 SOCKS5 Proxy support
1716 ********************/
1717 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1719 if ((ThisICQ->icq_ProxyHost))
1720 phree((ThisICQ->icq_ProxyHost));
1721 if ((ThisICQ->icq_ProxyName))
1722 phree((ThisICQ->icq_ProxyName));
1723 if ((ThisICQ->icq_ProxyPass))
1724 phree((ThisICQ->icq_ProxyPass));
1725 if (strlen(pname) > 255) {
1726 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1727 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1728 ThisICQ->icq_UseProxy = 0;
1731 if (strlen(ppass) > 255) {
1732 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1733 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1734 (ThisICQ->icq_UseProxy) = 0;
1737 (ThisICQ->icq_UseProxy) = 1;
1738 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1739 (ThisICQ->icq_ProxyPort) = pport;
1740 (ThisICQ->icq_ProxyAuth) = pauth;
1741 (ThisICQ->icq_ProxyName) = strdoop(pname);
1742 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1745 void icq_UnsetProxy()
1747 ThisICQ->icq_UseProxy = 0;
1752 /***********************************************************************/
1753 /* icqlib stuff ends here, Citadel module stuff begins */
1754 /***********************************************************************/
1758 * Callback function for CtdlICQ_Read_Config()
1760 void CtdlICQ_Read_Config_Backend(long msgnum) {
1761 struct CtdlMessage *msg;
1765 lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
1766 msg = CtdlFetchMessage(msgnum);
1768 ptr = msg->cm_fields['M'];
1769 pos = pattern2(ptr, "\n\n");
1777 safestrncpy(ThisICQ->icq_config, ptr, 256);
1779 CtdlFreeMessage(msg);
1781 lprintf(9, "...it ain't there?\n");
1787 * If this user has an ICQ configuration on disk, read it into memory.
1789 void CtdlICQ_Read_Config(void) {
1790 char hold_rm[ROOMNAMELEN];
1791 char icq_rm[ROOMNAMELEN];
1793 strcpy(hold_rm, CC->quickroom.QRname);
1794 MailboxName(icq_rm, &CC->usersupp, CONFIGROOM);
1795 strcpy(ThisICQ->icq_config, "");
1797 if (getroom(&CC->quickroom, icq_rm) != 0) {
1798 getroom(&CC->quickroom, hold_rm);
1802 /* We want the last (and probably only) config in this room */
1803 lprintf(9, "We're in <%s> looking for config\n",
1804 CC->quickroom.QRname);
1805 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1806 getroom(&CC->quickroom, hold_rm);
1813 * Write our config to disk
1815 void CtdlICQ_Write_Config(void) {
1816 char temp[PATH_MAX];
1819 strcpy(temp, tmpnam(NULL));
1821 fp = fopen(temp, "w");
1822 if (fp == NULL) return;
1823 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1826 /* this handy API function does all the work for us */
1827 CtdlWriteObject(CONFIGROOM, ICQMIME, temp, &CC->usersupp, 0, 1, 0);
1837 * Write our contact list to disk
1839 void CtdlICQ_Write_CL(void) {
1840 char temp[PATH_MAX];
1844 strcpy(temp, tmpnam(NULL));
1846 fp = fopen(temp, "w");
1847 if (fp == NULL) return;
1849 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1850 fprintf(fp, "%ld|%s|\n",
1851 ThisICQ->icq_cl[i].uin,
1852 ThisICQ->icq_cl[i].name);
1856 /* this handy API function does all the work for us */
1857 CtdlWriteObject(CONFIGROOM, ICQCLMIME, temp, &CC->usersupp, 0, 1, 0);
1867 * Callback function for CtdlICQ_Read_CL()
1869 void CtdlICQ_Read_CL_Backend(long msgnum) {
1870 struct CtdlMessage *msg;
1875 msg = CtdlFetchMessage(msgnum);
1877 ptr = msg->cm_fields['M'];
1878 pos = pattern2(ptr, "\n\n");
1886 for (i=0; i<strlen(ptr); ++i)
1887 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1888 if (ThisICQ->icq_numcl) {
1889 ThisICQ->icq_cl = mallok(
1890 (ThisICQ->icq_numcl *
1891 sizeof (struct CtdlICQ_CL)));
1893 while (cont=strtok(ptr, "\n"), cont != NULL) {
1894 ThisICQ->icq_cl[i].uin =
1895 extract_long(cont, 0);
1896 extract(ThisICQ->icq_cl[i].name,
1898 ThisICQ->icq_cl[i].status =
1905 CtdlFreeMessage(msg);
1911 * Read contact list into memory
1913 void CtdlICQ_Read_CL(void) {
1914 char hold_rm[ROOMNAMELEN];
1915 char icq_rm[ROOMNAMELEN];
1917 strcpy(hold_rm, CC->quickroom.QRname);
1918 MailboxName(icq_rm, &CC->usersupp, CONFIGROOM);
1919 strcpy(ThisICQ->icq_config, "");
1921 if (getroom(&CC->quickroom, icq_rm) != 0) {
1922 getroom(&CC->quickroom, hold_rm);
1926 /* Free any contact list already in memory */
1927 if (ThisICQ->icq_numcl) {
1928 phree(ThisICQ->icq_cl);
1929 ThisICQ->icq_numcl = 0;
1932 /* We want the last (and probably only) list in this room */
1933 CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
1934 getroom(&CC->quickroom, hold_rm);
1939 * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
1940 * entry in the table if necessary
1942 struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
1945 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
1946 if (ThisICQ->icq_cl[i].uin == uin)
1947 return (&ThisICQ->icq_cl[i]);
1949 ++ThisICQ->icq_numcl;
1950 ThisICQ->icq_cl = reallok(ThisICQ->icq_cl,
1951 (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
1952 memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
1953 0, sizeof(struct CtdlICQ_CL));
1954 ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
1955 return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
1961 * Refresh the contact list
1963 void CtdlICQ_Refresh_Contact_List(void) {
1966 if (ThisICQ->icq_Sok < 0) return;
1970 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1971 if (ThisICQ->icq_cl[i].uin > 0L) {
1972 icq_ContAddUser(ThisICQ->icq_cl[i].uin);
1973 icq_ContSetVis(ThisICQ->icq_cl[i].uin);
1977 icq_SendContactList();
1985 * Utility routine to logout and disconnect from the ICQ server if we happen
1986 * to already be connected
1988 void CtdlICQ_Logout_If_Connected(void) {
1989 if (ThisICQ->icq_Sok >= 0) {
1992 ThisICQ->icq_Sok = (-1);
1998 * If we have an ICQ uin/password on file for this user, go ahead and try
1999 * to log on to the ICQ server.
2001 void CtdlICQ_Login_If_Possible(void) {
2005 CtdlICQ_Logout_If_Connected();
2006 CtdlICQ_Read_Config();
2008 uin = extract_long(ThisICQ->icq_config, 0);
2009 extract(pass, ThisICQ->icq_config, 1);
2011 if ( (uin > 0L) && (strlen(pass)>0) ) {
2012 icq_Init(uin, pass);
2013 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2014 icq_Login(STATUS_ONLINE);
2021 /* This merely hooks icqlib's log function into Citadel's log function. */
2022 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2024 lprintf(level, "ICQ: %s", str);
2029 * At the start of each Citadel server session, we have to allocate some
2030 * space for the Citadel-ICQ session for this thread. It'll be automatically
2031 * freed by the server when the session ends.
2033 void CtdlICQ_session_startup_hook(void)
2035 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2036 icq_init_handle(ThisICQ);
2042 * End-of-session cleanup
2044 void CtdlICQ_session_stopdown_hook(void) {
2051 * icq_Main() needs to be called as frequently as possible. We'll do it
2052 * following the completion of each Citadel server command.
2055 void CtdlICQ_after_cmd_hook(void)
2057 if (ThisICQ->icq_Sok >= 0) {
2058 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 60 ) {
2060 ThisICQ->icq_LastKeepAlive = time(NULL);
2068 * There are a couple of things we absolutely need to make sure of when we
2069 * kill off the session. One of them is that the sockets are all closed --
2070 * otherwise we could have a nasty file descriptor leak.
2072 void CtdlICQ_session_logout_hook(void)
2074 lprintf(9, "Shutting down ICQ\n");
2075 CtdlICQ_Logout_If_Connected();
2077 /* Free the memory used by the contact list */
2078 if (ThisICQ->icq_numcl) {
2079 phree(ThisICQ->icq_cl);
2080 ThisICQ->icq_numcl = 0;
2087 void CtdlICQ_session_login_hook(void)
2089 /* If this user has an ICQ config on file, start it up. */
2090 CtdlICQ_Login_If_Possible();
2099 * Here's what we have to do when an ICQ message arrives!
2101 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2102 BYTE day, BYTE month, WORD year,
2109 /* Default sender is 'uin@icq' syntax */
2110 sprintf(from, "%ld@icq", uin);
2112 /* Use the sender's name if we have it inthe contact list */
2113 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
2114 if (uin == ThisICQ->icq_cl[i].uin) {
2115 safestrncpy(from, ThisICQ->icq_cl[i].name, 256);
2119 num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2120 lprintf(9, "Delivered to %d users\n", num_delivered);
2125 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2126 const char *first, const char *last,
2127 const char *email, char auth) {
2129 struct CtdlICQ_CL *ptr;
2132 ptr = CtdlICQ_CLent(uin);
2133 safestrncpy(ptr->name, nick, 32);
2134 ptr->status = STATUS_OFFLINE;
2135 lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
2143 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2146 DWORD target_uin = 0L;
2149 /* If this is an incoming ICQ from someone on the contact list,
2150 * change the sender from "uin@icq" to the contact name.
2153 for (i=0; i<strlen(from); ++i)
2154 if (!strcasecmp(&from[i], "@icq")) {
2157 if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
2158 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2159 if (ThisICQ->icq_cl[i].uin == atol(from))
2160 strcpy(from, ThisICQ->icq_cl[i].name);
2165 /* Handle "uin@icq" syntax */
2167 for (i=0; i<strlen(recp); ++i)
2168 if (!strcasecmp(&recp[i], "@icq")) {
2171 if (is_aticq == 1) target_uin = atol(recp);
2173 /* Handle "nick" syntax */
2174 if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
2175 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2176 if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
2177 target_uin = ThisICQ->icq_cl[i].uin;
2183 if (target_uin == 0L) return(0);
2185 if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
2191 void cmd_cicq(char *argbuf) {
2198 if (!(CC->logged_in)) {
2199 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
2202 extract(cmd, argbuf, 0);
2204 /* "CICQ login" tells us how to log in. */
2205 if (!strcasecmp(cmd, "login")) {
2206 uin = extract_long(argbuf, 1);
2207 extract(pass, argbuf, 2);
2208 sprintf(ThisICQ->icq_config, "%ld|%s|", uin, pass);
2210 CtdlICQ_Write_Config();
2211 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2212 CtdlICQ_Login_If_Possible();
2214 cprintf("%d You must supply a UIN.\n", ERROR);
2219 /* "CICQ getcl" returns the contact list */
2220 if (!strcasecmp(cmd, "getcl")) {
2222 cprintf("%d Your ICQ contact list:\n", LISTING_FOLLOWS);
2223 if (ThisICQ->icq_numcl > 0) {
2224 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2225 cprintf("%ld|%s|%s|\n",
2226 ThisICQ->icq_cl[i].uin,
2227 ThisICQ->icq_cl[i].name,
2228 icq_ConvertStatus2Str(
2229 ThisICQ->icq_cl[i].status)
2237 /* "CICQ putcl" accepts a new contact list from the client */
2238 if (!strcasecmp(cmd, "putcl")) {
2239 cprintf("%d Send contact list\n", SEND_LISTING);
2240 ThisICQ->icq_numcl = 0;
2241 while (client_gets(buf), strcmp(buf, "000")) {
2242 uin = extract_long(buf, 0);
2248 CtdlICQ_Refresh_Contact_List();
2252 /* "CICQ status" returns the connected/notconnected status */
2253 if (!strcasecmp(cmd, "status")) {
2254 cprintf("%d %d\n", OK,
2255 ((ThisICQ->icq_Sok >= 0) ? 1 : 0) );
2259 cprintf("%d Invalid subcommand\n", ERROR);
2267 * During an RWHO command, we want to append our ICQ information.
2269 void CtdlICQ_rwho(void) {
2272 if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
2273 if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
2274 cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
2275 0, /* no session ID */
2276 ThisICQ->icq_cl[i].name,
2277 icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
2278 ThisICQ->icq_cl[i].host,
2279 " ", /* no client */
2280 time(NULL), /* now? */
2281 " ", /* no last command */
2287 void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
2288 struct CtdlICQ_CL *ptr;
2290 ptr = CtdlICQ_CLent(uin);
2291 ptr->status = status;
2292 if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
2296 void CtdlICQ_Logged(void) {
2297 CtdlICQ_Refresh_Contact_List();
2301 void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
2302 DWORD port, DWORD realip) {
2306 CtdlICQ_Status_Update(uin, status);
2307 decoded_ip = ntohl(ip);
2308 locate_host(CtdlICQ_CLent(uin)->host, (struct in_addr *)&decoded_ip);
2312 void CtdlICQ_UserOffline(DWORD uin) {
2313 CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
2317 char *Dynamic_Module_Init(void)
2319 /* Make sure we've got a valid ThisICQ for each session */
2320 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2322 /* Tell the Citadel server about our wonderful ICQ hooks */
2323 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2324 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2325 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2326 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2327 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2328 CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
2329 CtdlRegisterProtoHook(cmd_cicq, "CICQ", "Configure Citadel ICQ");
2330 CtdlRegisterXmsgHook(CtdlICQ_Send_Msg, XMSG_PRI_FOREIGN);
2332 /* Tell the code formerly known as icqlib about our callbacks */
2333 icq_Log = CtdlICQlog;
2334 icq_RecvMessage = CtdlICQ_Incoming_Message;
2335 icq_InfoReply = CtdlICQ_InfoReply;
2336 icq_Disconnected = CtdlICQ_Login_If_Possible;
2337 icq_Logged = CtdlICQ_Logged;
2338 icq_UserStatusUpdate = CtdlICQ_Status_Update;
2339 icq_UserOnline = CtdlICQ_UserOnline;
2340 icq_UserOffline = CtdlICQ_UserOffline;