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>
38 #include "dynloader.h"
40 #include "citserver.h"
41 #include "locate_host.h"
43 #include "sysdep_decls.h"
49 * Contact list in memory
61 /* MIME types to use for storing ICQ stuff */
62 #define ICQMIME "application/x-citadel-icq" /* configuration */
63 #define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
65 /* Citadel server TSD symbol for use by serv_icq */
66 unsigned long SYM_CTDL_ICQ;
67 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
69 extern struct CitContext *ContextList;
74 void (*icq_Logged) (void);
75 void (*icq_Disconnected) (void);
76 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
77 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
78 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);
79 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);
80 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
81 void (*icq_SearchDone) (void);
82 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
83 void (*icq_UserOffline) (DWORD uin);
84 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
85 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
86 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);
87 void (*icq_Log) (time_t time, unsigned char level, const char *str);
88 void (*icq_SrvAck) (WORD seq);
91 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
92 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
93 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
94 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
95 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
96 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
97 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
98 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
101 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
102 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
103 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
104 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
105 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
106 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
107 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
108 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
110 static COUNTRY_CODE Country_Codes[] =
125 {"American Samoa", 684},
140 {"Czech Republic", 42},
143 {"El Salvador", 503},
147 {"French Antilles", 596},
148 {"French Polynesia", 689},
155 {"Guantanomo Bay", 53},
169 {"Ivory Coast", 225},
177 {"Liechtenstein", 41},
189 {"Netherlands Antilles", 599},
190 {"New Caledonia", 687},
197 {"Papua New Guinea", 675},
207 {"Saudia Arabia", 966},
212 {"South Africa", 27},
223 {"United Arab Emirates", 971},
225 {"Vatican City", 39},
232 {"Not entered", 0xffff}};
234 void icq_init_handle(struct ctdl_icq_handle *i)
236 memset(i, 0, sizeof(struct ctdl_icq_handle));
237 i->icq_Russian = TRUE;
239 i->icq_OurIp = 0x0100007f;
240 i->icq_Status = STATUS_OFFLINE;
245 int icq_SockWrite(int sok, const void *buf, size_t count)
248 if (!(ThisICQ->icq_UseProxy))
249 return write(sok, buf, count);
251 tmpbuf[0] = 0; /* reserved */
252 tmpbuf[1] = 0; /* reserved */
253 tmpbuf[2] = 0; /* standalone packet */
254 tmpbuf[3] = 1; /* address type IP v4 */
255 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
256 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
257 memcpy(&tmpbuf[10], buf, count);
258 return write(sok, tmpbuf, count + 10) - 10;
262 int icq_SockRead(int sok, void *buf, size_t count)
266 if (!(ThisICQ->icq_UseProxy))
267 return read(sok, buf, count);
269 res = read(sok, tmpbuf, count + 10);
270 memcpy(buf, &tmpbuf[10], res - 10);
275 /****************************************
276 This must be called every 2 min.
277 so the server knows we're still alive.
278 JAVA client sends two different commands
280 *****************************************/
285 Word_2_Chars(pak.head.ver, ICQ_VER);
286 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
287 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
288 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
289 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
291 Word_2_Chars(pak.head.ver, ICQ_VER);
292 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
293 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
294 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
295 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
297 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
298 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
301 /**********************************
302 This must be called to remove
303 messages from the server
304 ***********************************/
305 void icq_SendGotMessages()
309 Word_2_Chars(pak.head.ver, ICQ_VER);
310 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
311 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
312 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
314 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
317 /*************************************
318 this sends over the contact list
319 *************************************/
320 void icq_SendContactList()
326 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
328 Word_2_Chars(pak.head.ver, ICQ_VER);
329 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
330 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
331 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
337 DW_2_Chars(tmp, ptr->uin);
342 pak.data[0] = num_used;
343 size = ((int) tmp - (int) pak.data);
344 size += sizeof(pak.head);
345 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
348 void icq_SendNewUser(unsigned long uin)
353 Word_2_Chars(pak.head.ver, ICQ_VER);
354 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
355 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
356 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
357 DW_2_Chars(pak.data, uin);
358 size = sizeof(pak.head) + 4;
359 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
362 /*************************************
363 this sends over the visible list
364 that allows certain users to see you
366 *************************************/
367 void icq_SendVisibleList()
373 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
375 Word_2_Chars(pak.head.ver, ICQ_VER);
376 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
377 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
378 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
385 DW_2_Chars(tmp, ptr->uin);
392 pak.data[0] = num_used;
393 size = ((int) tmp - (int) pak.data);
394 size += sizeof(pak.head);
395 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
399 /**************************************
400 This sends the second login command
401 this is necessary to finish logging in.
402 ***************************************/
403 void icq_SendLogin1()
407 Word_2_Chars(pak.head.ver, ICQ_VER);
408 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
409 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
410 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
412 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
415 /************************************************
416 This is called when a user goes offline
417 *************************************************/
418 void icq_HandleUserOffline(srv_net_icq_pak pak)
423 remote_uin = Chars_2_DW(pak.data);
424 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
425 sprintf(buf, "User %lu logged off\n", remote_uin);
426 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
429 (*icq_UserOffline) (remote_uin);
430 icq_AckSrv(Chars_2_Word(pak.head.seq));
433 void icq_HandleUserOnline(srv_net_icq_pak pak)
435 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
436 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
439 remote_uin = Chars_2_DW(pak.data);
440 new_status = Chars_2_DW(&pak.data[17]);
441 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
442 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
443 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
444 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
445 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
446 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
449 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
450 icq_AckSrv(Chars_2_Word(pak.head.seq));
453 void icq_Status_Update(srv_net_icq_pak pak)
455 unsigned long remote_uin, new_status;
458 remote_uin = Chars_2_DW(pak.data);
459 new_status = Chars_2_DW(&pak.data[4]);
460 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
461 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
462 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
464 if (icq_UserStatusUpdate)
465 (*icq_UserStatusUpdate) (remote_uin, new_status);
466 icq_AckSrv(Chars_2_Word(pak.head.seq));
469 /************************************
470 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
471 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
472 It does NOT wait for any kind of a response.
473 *************************************/
474 void icq_Login(DWORD status)
481 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
482 Word_2_Chars(pak.head.ver, ICQ_VER);
483 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
484 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
485 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
487 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
488 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
490 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
491 DW_2_Chars(s2.status, status);
492 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
494 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
495 s2.X2[0] = LOGIN_X2_DEF;
496 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
497 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
498 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
500 memcpy(pak.data, &s1, sizeof(s1));
502 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
503 size += Chars_2_Word(s1.len);
504 memcpy(&pak.data[size], &s2, sizeof(s2));
506 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
509 /*******************************
510 This routine sends the aknowlegement cmd to the
511 server it appears that this must be done after
512 everything the server sends us
513 *******************************/
514 void icq_AckSrv(int seq)
519 Word_2_Chars(pak.head.ver, ICQ_VER);
520 Word_2_Chars(pak.head.cmd, CMD_ACK);
521 Word_2_Chars(pak.head.seq, seq);
522 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
523 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
524 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
525 for (i = 0; i < 6; i++)
526 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
529 void icq_HandleInfoReply(srv_net_icq_pak pak)
531 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
535 seq = Chars_2_Word(pak.data);
536 uin = Chars_2_DW(&pak.data[2]);
537 len = Chars_2_Word(&pak.data[6]);
539 icq_RusConv("wk", ptr1);
540 tmp = &pak.data[8 + len];
541 len = Chars_2_Word(tmp);
543 icq_RusConv("wk", ptr2);
545 len = Chars_2_Word(tmp);
547 icq_RusConv("wk", ptr3);
549 len = Chars_2_Word(tmp);
551 icq_RusConv("wk", ptr4);
553 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
554 sprintf(buf, "Info reply for %lu\n", uin);
555 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
558 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
559 icq_AckSrv(Chars_2_Word(pak.head.seq));
562 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
564 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
568 char cnt_stat, gender, buf[256];
569 uin = Chars_2_DW(&pak.data[2]);
570 len = Chars_2_Word(&pak.data[6]);
572 icq_RusConv("wk", ptr1);
573 cnt_code = Chars_2_Word(&pak.data[8 + len]);
574 cnt_stat = pak.data[len + 10];
575 tmp = &pak.data[11 + len];
576 len = Chars_2_Word(tmp);
577 icq_RusConv("wk", tmp + 2);
579 age = Chars_2_Word(tmp + 2 + len);
580 gender = *(tmp + len + 4);
582 len = Chars_2_Word(tmp);
583 icq_RusConv("wk", tmp + 2);
586 len = Chars_2_Word(tmp);
587 icq_RusConv("wk", tmp + 2);
590 len = Chars_2_Word(tmp);
591 icq_RusConv("wk", tmp + 2);
593 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
594 sprintf(buf, "Extended info reply for %lu\n", uin);
595 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
597 if (icq_ExtInfoReply)
598 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
599 icq_AckSrv(Chars_2_Word(pak.head.seq));
602 void icq_HandleSearchReply(srv_net_icq_pak pak)
604 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
608 uin = Chars_2_DW(&pak.data[2]);
609 len = Chars_2_Word(&pak.data[6]);
611 icq_RusConv("wk", ptr1);
612 tmp = &pak.data[8 + len];
613 len = Chars_2_Word(tmp);
615 icq_RusConv("wk", ptr2);
617 len = Chars_2_Word(tmp);
619 icq_RusConv("wk", ptr3);
621 len = Chars_2_Word(tmp);
623 icq_RusConv("wk", ptr4);
625 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
626 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");
627 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
630 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
631 icq_AckSrv(Chars_2_Word(pak.head.seq));
634 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
637 char *ptr1, *ptr2, *ptr3, *ptr4;
641 case USER_ADDED_MESS:
642 tmp = strchr(data, '\xFE');
644 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
645 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
652 tmp = strchr(tmp, '\xFE');
654 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
655 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
659 icq_RusConv("wk", data);
663 tmp = strchr(tmp, '\xFE');
665 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
666 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
670 icq_RusConv("wk", data);
674 tmp = strchr(tmp, '\xFE');
676 icq_RusConv("wk", data);
677 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
678 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
679 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
680 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
683 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
686 tmp = strchr(data, '\xFE');
691 tmp = strchr(tmp, '\xFE');
693 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
694 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
698 icq_RusConv("wk", data);
702 tmp = strchr(tmp, '\xFE');
704 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
705 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
709 icq_RusConv("wk", data);
713 tmp = strchr(tmp, '\xFE');
715 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
716 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
720 icq_RusConv("wk", data);
724 tmp = strchr(tmp, '\xFE');
726 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
727 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
733 tmp = strchr(tmp, '\x00');
735 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
736 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
740 icq_RusConv("wk", data);
741 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
742 sprintf(buf, "%lu has requested your authorization to be added to "
743 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
744 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
745 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
748 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
751 tmp = strchr(data, '\xFE');
753 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
754 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
758 icq_RusConv("wk", data);
762 icq_RusConv("wk", data);
763 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
764 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
765 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
768 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
771 icq_RusConv("wk", data);
772 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
773 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
774 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
777 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
781 /**********************************
782 Connects to hostname on port port
783 hostname can be DNS or nnn.nnn.nnn.nnn
784 write out messages to the FD aux
785 ***********************************/
786 int icq_Connect(const char *hostname, int port)
790 int conct, length, res;
791 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
792 struct hostent *host_struct; /* used in DNS llokup */
794 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
795 if ((ThisICQ->icq_Sok) == -1) {
796 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
797 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
800 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
801 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
802 sin.sin_addr.s_addr = INADDR_ANY;
803 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
805 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
806 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
807 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
810 length = sizeof(sin);
811 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
812 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
813 if ((ThisICQ->icq_UseProxy)) {
814 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
815 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
816 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
817 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
818 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
819 if (host_struct == 0L) {
820 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
821 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
822 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
826 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
828 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
829 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
830 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
831 if ((ThisICQ->icq_ProxySok) == -1) {
832 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
833 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
836 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
837 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
838 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
839 if (conct == -1) { /* did we connect ? */
840 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
841 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
844 buf[0] = 5; /* protocol version */
845 buf[1] = 1; /* number of methods */
846 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
847 buf[2] = 0; /* no authorization required */
849 buf[2] = 2; /* method username/password */
850 write((ThisICQ->icq_ProxySok), buf, 3);
851 res = read((ThisICQ->icq_ProxySok), buf, 2);
852 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
853 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
854 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
855 close((ThisICQ->icq_ProxySok));
858 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
859 buf[0] = 1; /* version of subnegotiation */
860 buf[1] = strlen((ThisICQ->icq_ProxyName));
861 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
862 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
863 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
864 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
865 res = read((ThisICQ->icq_ProxySok), buf, 2);
866 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
867 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
868 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
869 close((ThisICQ->icq_ProxySok));
873 buf[0] = 5; /* protocol version */
874 buf[1] = 3; /* command UDP associate */
875 buf[2] = 0; /* reserved */
876 buf[3] = 1; /* address type IP v4 */
881 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
882 write((ThisICQ->icq_ProxySok), buf, 10);
883 res = read((ThisICQ->icq_ProxySok), buf, 10);
884 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
887 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
888 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
891 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
892 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
895 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
896 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
899 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
900 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
903 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
904 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
907 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
908 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
911 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
912 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
915 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
916 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
919 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
920 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
923 close((ThisICQ->icq_ProxySok));
927 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
928 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
929 host_struct = gethostbyname(hostname);
930 if (host_struct == 0L) {
931 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
932 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
933 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
935 if ((ThisICQ->icq_UseProxy))
936 close((ThisICQ->icq_ProxySok));
939 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
941 if ((ThisICQ->icq_UseProxy)) {
942 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
943 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
945 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
946 sin.sin_port = htons(port); /* port */
947 if ((ThisICQ->icq_UseProxy)) {
948 (ThisICQ->icq_ProxyDestPort) = htons(port);
949 memcpy(&sin.sin_port, &buf[8], 2);
951 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
952 if (conct == -1) { /* did we connect ? */
953 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
954 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
955 if ((ThisICQ->icq_UseProxy))
956 close((ThisICQ->icq_ProxySok));
959 length = sizeof(sin);
960 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
961 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
962 (ThisICQ->icq_OurPort) = sin.sin_port;
963 return (ThisICQ->icq_Sok);
966 void icq_HandleProxyResponse()
970 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
972 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
973 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
974 if (icq_Disconnected)
975 (*icq_Disconnected) ();
976 SOCKCLOSE((ThisICQ->icq_Sok));
977 SOCKCLOSE((ThisICQ->icq_ProxySok));
981 /******************************************
982 Handles packets that the server sends to us.
983 *******************************************/
984 void icq_HandleServerResponse()
987 SIMPLE_MESSAGE *s_mesg;
988 RECV_MESSAGE *r_mesg;
994 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
996 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
997 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
998 if (icq_Disconnected)
999 (*icq_Disconnected) ();
1000 SOCKCLOSE((ThisICQ->icq_Sok));
1002 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)) {
1003 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1004 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1005 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1006 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1008 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1012 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1013 icq_SetServMess(Chars_2_Word(pak.head.seq));
1014 switch (Chars_2_Word(pak.head.cmd)) {
1016 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1017 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1019 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1022 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1023 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1024 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1027 case SRV_LOGIN_REPLY:
1028 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1029 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1030 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]);
1031 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1033 icq_AckSrv(Chars_2_Word(pak.head.seq));
1035 icq_SendContactList();
1036 icq_SendVisibleList();
1040 case SRV_RECV_MESSAGE:
1041 r_mesg = (RECV_MESSAGE *) pak.data;
1042 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));
1043 icq_AckSrv(Chars_2_Word(pak.head.seq));
1045 case SRV_X1: /* unknown message sent after login */
1046 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1047 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1048 icq_AckSrv(Chars_2_Word(pak.head.seq));
1050 case SRV_X2: /* 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_X2 (Done old messages)\n");
1053 icq_AckSrv(Chars_2_Word(pak.head.seq));
1054 icq_SendGotMessages();
1056 case SRV_INFO_REPLY:
1057 icq_HandleInfoReply(pak);
1059 case SRV_EXT_INFO_REPLY:
1060 icq_HandleExtInfoReply(pak);
1062 case SRV_USER_ONLINE:
1063 icq_HandleUserOnline(pak);
1065 case SRV_USER_OFFLINE:
1066 icq_HandleUserOffline(pak);
1069 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1070 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1071 icq_Login((ThisICQ->icq_Status));
1073 case SRV_STATUS_UPDATE:
1074 icq_Status_Update(pak);
1077 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1078 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1079 if (icq_Disconnected)
1080 (*icq_Disconnected) ();
1082 case SRV_END_OF_SEARCH:
1083 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1084 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1086 (*icq_SearchDone) ();
1087 icq_AckSrv(Chars_2_Word(pak.head.seq));
1089 case SRV_USER_FOUND:
1090 icq_HandleSearchReply(pak);
1092 case SRV_SYS_DELIVERED_MESS:
1093 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1094 cur_time = time(0L);
1095 tm_str = localtime(&cur_time);
1096 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);
1097 icq_AckSrv(Chars_2_Word(pak.head.seq));
1099 default: /* commands we dont handle yet */
1100 len = s - (sizeof(pak.head));
1101 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1102 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1103 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1104 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1106 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1111 void icq_Init(DWORD uin, const char *password)
1113 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1114 (ThisICQ->icq_Uin) = uin;
1115 if ((ThisICQ->icq_Password))
1116 phree((ThisICQ->icq_Password));
1117 (ThisICQ->icq_Password) = strdoop(password);
1122 if ((ThisICQ->icq_Password))
1123 phree((ThisICQ->icq_Password));
1126 /******************************
1127 Main function connects gets (ThisICQ->icq_Uin)
1128 and (ThisICQ->icq_Password) and logins in and sits
1129 in a loop waiting for server responses.
1130 *******************************/
1142 FD_SET((ThisICQ->icq_Sok), &readfds);
1143 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1144 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1145 icq_HandleServerResponse();
1149 } while (did_something);
1152 /********************************************************
1153 Russian language ICQ fix.
1154 Usual Windows ICQ users do use Windows 1251 encoding but
1155 unix users do use koi8 encoding, so we need to convert it.
1156 This function will convert string from windows 1251 to koi8
1157 or from koi8 to windows 1251.
1158 Andrew Frolov dron@ilm.net
1159 *********************************************************/
1160 void icq_RusConv(const char to[4], char *t_in)
1165 /* 6-17-1998 by Linux_Dude
1166 * Moved initialization of table out front of 'if' block to prevent compiler
1167 * warning. Improved error message, and now return without performing string
1168 * conversion to prevent addressing memory out of range (table pointer would
1169 * previously have remained uninitialized (= bad)).
1173 if (strcmp(to, "kw") == 0)
1175 else if (strcmp(to, "wk") != 0) {
1176 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1177 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1180 /* End Linux_Dude's changes ;) */
1182 if ((ThisICQ->icq_Russian)) {
1183 for (i = 0; t_in[i] != 0; i++) {
1186 t_in[i] = table[t_in[i] & 0177];
1191 /**************************************************
1192 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1194 ***************************************************/
1195 WORD icq_SendMessage(DWORD uin, const char *text)
1200 char buf[512]; /* message may be only 450 bytes long */
1202 strncpy(buf, text, 512);
1203 icq_RusConv("kw", buf);
1205 Word_2_Chars(pak.head.ver, ICQ_VER);
1206 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1207 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1208 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1209 DW_2_Chars(msg.uin, uin);
1210 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1211 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1212 memcpy(&pak.data, &msg, sizeof(msg));
1213 memcpy(&pak.data[8], buf, len + 1);
1214 size = sizeof(msg) + len + 1;
1215 for (i = 0; i < 6; i++)
1216 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1217 return (ThisICQ->icq_SeqNum) - 1;
1220 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1224 int size, len1, len2;
1225 char buf1[512], buf2[512];
1227 strncpy(buf1, descr, 512);
1228 strncpy(buf2, url, 512);
1229 /* Do we need to convert URL? */
1230 icq_RusConv("kw", buf2);
1231 len1 = strlen(buf1);
1232 len2 = strlen(buf2);
1233 Word_2_Chars(pak.head.ver, ICQ_VER);
1234 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1235 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1236 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1237 DW_2_Chars(msg.uin, uin);
1238 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1239 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1240 memcpy(&pak.data, &msg, sizeof(msg));
1241 memcpy(&pak.data[8], buf1, len1);
1242 pak.data[8 + len1] = 0xFE;
1243 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1244 size = sizeof(msg) + len1 + len2 + 2;
1245 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1246 return (ThisICQ->icq_SeqNum) - 1;
1249 /**************************************************
1250 Sends a authorization to the server so the Mirabilis
1251 client can add the user.
1252 ***************************************************/
1253 void icq_SendAuthMsg(DWORD uin)
1259 Word_2_Chars(pak.head.ver, ICQ_VER);
1260 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1261 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1262 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1263 DW_2_Chars(msg.uin, uin);
1264 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1265 Word_2_Chars(msg.len, 2);
1266 memcpy(&pak.data, &msg, sizeof(msg));
1267 pak.data[sizeof(msg)] = 0x03;
1268 pak.data[sizeof(msg) + 1] = 0x00;
1269 size = sizeof(msg) + 2;
1270 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1273 /**************************************************
1274 Changes the users status on the server
1275 ***************************************************/
1276 void icq_ChangeStatus(DWORD status)
1281 Word_2_Chars(pak.head.ver, ICQ_VER);
1282 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1283 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1284 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1285 DW_2_Chars(pak.data, status);
1286 (ThisICQ->icq_Status) = status;
1288 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1291 /**********************
1293 ***********************/
1299 Word_2_Chars(pak.head.ver, ICQ_VER);
1300 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1301 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1302 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1303 len = strlen("B_USER_DISCONNECTED") + 1;
1304 *(short *) pak.data = len;
1306 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1307 pak.data[2 + len] = 05;
1308 pak.data[3 + len] = 00;
1309 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1312 void icq_Disconnect()
1314 SOCKCLOSE((ThisICQ->icq_Sok));
1315 SOCKCLOSE((ThisICQ->icq_Sok));
1316 if ((ThisICQ->icq_UseProxy))
1317 SOCKCLOSE((ThisICQ->icq_ProxySok));
1320 /********************************************************
1321 Sends a request to the server for info on a specific user
1322 *********************************************************/
1323 WORD icq_SendInfoReq(DWORD uin)
1328 Word_2_Chars(pak.head.ver, ICQ_VER);
1329 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1330 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1331 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1332 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1333 DW_2_Chars(&pak.data[2], uin);
1335 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1336 return (ThisICQ->icq_SeqNum) - 1;
1339 /********************************************************
1340 Sends a request to the server for info on a specific user
1341 *********************************************************/
1342 WORD icq_SendExtInfoReq(DWORD uin)
1347 Word_2_Chars(pak.head.ver, ICQ_VER);
1348 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1349 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1350 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1351 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1352 DW_2_Chars(&pak.data[2], uin);
1354 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1355 return (ThisICQ->icq_SeqNum) - 1;
1358 /**************************************************************
1359 Initializes a server search for the information specified
1360 ***************************************************************/
1361 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1366 Word_2_Chars(pak.head.ver, ICQ_VER);
1367 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1368 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1369 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1370 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1372 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1374 strcpy(pak.data + size, nick);
1375 size += strlen(nick) + 1;
1376 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1378 strcpy(pak.data + size, first);
1379 size += strlen(first) + 1;
1380 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1382 strcpy(pak.data + size, last);
1383 size += strlen(last) + 1;
1384 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1386 strcpy(pak.data + size, email);
1387 size += strlen(email) + 1;
1388 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1391 /**************************************************************
1392 Initializes a server search for the information specified
1393 ***************************************************************/
1394 void icq_SendSearchUINReq(DWORD uin)
1399 Word_2_Chars(pak.head.ver, ICQ_VER);
1400 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1401 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1402 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1403 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1405 DW_2_Chars(&pak.data[size], uin);
1407 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1410 /**************************************************
1411 Registers a new uin in the ICQ network
1412 ***************************************************/
1413 void icq_RegNewUser(const char *pass)
1415 srv_net_icq_pak pak;
1420 Word_2_Chars(pak.head.ver, ICQ_VER);
1421 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1422 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1423 Word_2_Chars(len_buf, len);
1424 memcpy(&pak.data, "\x02\x00", 2);
1425 memcpy(&pak.data[2], &len_buf, 2);
1426 memcpy(&pak.data[4], pass, len + 1);
1427 DW_2_Chars(&pak.data[4 + len], 0x0072);
1428 DW_2_Chars(&pak.data[8 + len], 0x0000);
1430 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1433 void icq_UpdateUserInfo(USER_INFO * user)
1438 Word_2_Chars(pak.head.ver, ICQ_VER);
1439 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1440 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1441 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1442 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1444 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1446 strcpy(pak.data + size, user->nick);
1447 size += strlen(user->nick) + 1;
1448 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1450 strcpy(pak.data + size, user->first);
1451 size += strlen(user->first) + 1;
1452 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1454 strcpy(pak.data + size, user->last);
1455 size += strlen(user->last) + 1;
1456 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1458 strcpy(pak.data + size, user->email);
1459 size += strlen(user->email) + 1;
1460 pak.data[size] = user->auth;
1462 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1465 const char *icq_GetCountryName(int code)
1469 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1470 if (Country_Codes[i].code == code) {
1471 return Country_Codes[i].name;
1474 if (Country_Codes[i].code == code) {
1475 return Country_Codes[i].name;
1480 /********************************************
1481 returns a string describing the status or
1482 a "Error" if no such string exists
1483 *********************************************/
1484 const char *icq_ConvertStatus2Str(int status)
1486 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1489 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1490 switch (status & 0x01FF) {
1495 return "Do not disturb";
1500 case STATUS_OCCUPIED:
1504 return "Not available";
1506 case STATUS_INVISIBLE:
1509 case STATUS_INVISIBLE_2:
1510 return "Invisible mode 2";
1512 case STATUS_FREE_CHAT:
1513 return "Free for chat";
1521 void icq_InitNewUser(const char *hostname, DWORD port)
1523 srv_net_icq_pak pak;
1528 icq_Connect(hostname, port);
1529 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1530 printf("Couldn't establish connection\n");
1533 icq_RegNewUser((ThisICQ->icq_Password));
1536 tv.tv_usec = 500000;
1539 FD_SET((ThisICQ->icq_Sok), &readfds);
1541 /* don't care about writefds and exceptfds: */
1542 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1544 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1545 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1546 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1547 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1554 /********************************************
1555 Converts an intel endian character sequence to
1557 *********************************************/
1558 DWORD Chars_2_DW(unsigned char *buf)
1573 /********************************************
1574 Converts an intel endian character sequence to
1576 *********************************************/
1577 WORD Chars_2_Word(unsigned char *buf)
1588 /********************************************
1590 an intel endian character sequence
1591 *********************************************/
1592 void DW_2_Chars(unsigned char *buf, DWORD num)
1594 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1595 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1596 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1597 buf[0] = (unsigned char) (num) & 0x000000FF;
1600 /********************************************
1602 an intel endian character sequence
1603 *********************************************/
1604 void Word_2_Chars(unsigned char *buf, WORD num)
1606 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1607 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1610 /***************************
1611 ContactList functions
1612 ***************************/
1613 void icq_ContAddUser(DWORD cuin)
1615 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1616 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1619 p->vis_list = FALSE;
1625 (ThisICQ->icq_ContFirst) = p;
1628 void icq_ContDelUser(DWORD cuin)
1630 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1633 if (ptr->uin == cuin) {
1634 (ThisICQ->icq_ContFirst) = ptr->next;
1636 ptr = (ThisICQ->icq_ContFirst);
1639 if (ptr->next->uin == cuin) {
1640 ptr->next = ptr->next->next;
1647 void icq_ContClear()
1649 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1654 (ThisICQ->icq_ContFirst) = ptr;
1658 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1660 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1664 if (ptr->uin == cuin)
1671 icq_ContactItem *icq_ContGetFirst()
1673 return (ThisICQ->icq_ContFirst);
1676 void icq_ContSetVis(DWORD cuin)
1678 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1682 if (ptr->uin == cuin)
1683 ptr->vis_list = TRUE;
1688 /************************
1689 (ThisICQ->icq_ServMess) functions
1690 *************************/
1691 BOOL icq_GetServMess(WORD num)
1693 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1696 void icq_SetServMess(WORD num)
1698 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1703 return (ThisICQ->icq_Sok);
1706 int icq_GetProxySok()
1708 return (ThisICQ->icq_ProxySok);
1711 /*******************
1712 SOCKS5 Proxy support
1713 ********************/
1714 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1716 if ((ThisICQ->icq_ProxyHost))
1717 phree((ThisICQ->icq_ProxyHost));
1718 if ((ThisICQ->icq_ProxyName))
1719 phree((ThisICQ->icq_ProxyName));
1720 if ((ThisICQ->icq_ProxyPass))
1721 phree((ThisICQ->icq_ProxyPass));
1722 if (strlen(pname) > 255) {
1723 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1724 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1725 ThisICQ->icq_UseProxy = 0;
1728 if (strlen(ppass) > 255) {
1729 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1730 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1731 (ThisICQ->icq_UseProxy) = 0;
1734 (ThisICQ->icq_UseProxy) = 1;
1735 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1736 (ThisICQ->icq_ProxyPort) = pport;
1737 (ThisICQ->icq_ProxyAuth) = pauth;
1738 (ThisICQ->icq_ProxyName) = strdoop(pname);
1739 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1742 void icq_UnsetProxy()
1744 ThisICQ->icq_UseProxy = 0;
1749 /***********************************************************************/
1750 /* icqlib stuff ends here, Citadel module stuff begins */
1751 /***********************************************************************/
1755 * Callback function for CtdlICQ_Read_Config()
1757 void CtdlICQ_Read_Config_Backend(long msgnum) {
1758 struct CtdlMessage *msg;
1762 lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
1763 msg = CtdlFetchMessage(msgnum);
1765 ptr = msg->cm_fields['M'];
1766 pos = pattern2(ptr, "\n\n");
1774 safestrncpy(ThisICQ->icq_config, ptr, 256);
1776 CtdlFreeMessage(msg);
1778 lprintf(9, "...it ain't there?\n");
1784 * If this user has an ICQ configuration on disk, read it into memory.
1786 void CtdlICQ_Read_Config(void) {
1787 char hold_rm[ROOMNAMELEN];
1788 char icq_rm[ROOMNAMELEN];
1790 strcpy(hold_rm, CC->quickroom.QRname);
1791 MailboxName(icq_rm, &CC->usersupp, USERCONFIGROOM);
1792 strcpy(ThisICQ->icq_config, "");
1794 if (getroom(&CC->quickroom, icq_rm) != 0) {
1795 getroom(&CC->quickroom, hold_rm);
1799 /* We want the last (and probably only) config in this room */
1800 lprintf(9, "We're in <%s> looking for config\n",
1801 CC->quickroom.QRname);
1802 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, NULL,
1803 CtdlICQ_Read_Config_Backend);
1804 getroom(&CC->quickroom, hold_rm);
1811 * Write our config to disk
1813 void CtdlICQ_Write_Config(void) {
1814 char temp[PATH_MAX];
1817 strcpy(temp, tmpnam(NULL));
1819 fp = fopen(temp, "w");
1820 if (fp == NULL) return;
1821 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1824 /* this handy API function does all the work for us */
1825 CtdlWriteObject(USERCONFIGROOM, ICQMIME, temp, &CC->usersupp, 0, 1, 0);
1835 * Write our contact list to disk
1837 void CtdlICQ_Write_CL(void) {
1838 char temp[PATH_MAX];
1842 strcpy(temp, tmpnam(NULL));
1844 fp = fopen(temp, "w");
1845 if (fp == NULL) return;
1847 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1848 fprintf(fp, "%ld|%s|\n",
1849 ThisICQ->icq_cl[i].uin,
1850 ThisICQ->icq_cl[i].name);
1854 /* this handy API function does all the work for us */
1855 CtdlWriteObject(USERCONFIGROOM, ICQCLMIME, temp, &CC->usersupp, 0, 1, 0);
1865 * Callback function for CtdlICQ_Read_CL()
1867 void CtdlICQ_Read_CL_Backend(long msgnum) {
1868 struct CtdlMessage *msg;
1873 msg = CtdlFetchMessage(msgnum);
1875 ptr = msg->cm_fields['M'];
1876 pos = pattern2(ptr, "\n\n");
1884 for (i=0; i<strlen(ptr); ++i)
1885 if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
1886 if (ThisICQ->icq_numcl) {
1887 ThisICQ->icq_cl = mallok(
1888 (ThisICQ->icq_numcl *
1889 sizeof (struct CtdlICQ_CL)));
1891 while (cont=strtok(ptr, "\n"), cont != NULL) {
1892 ThisICQ->icq_cl[i].uin =
1893 extract_long(cont, 0);
1894 extract(ThisICQ->icq_cl[i].name,
1896 ThisICQ->icq_cl[i].status =
1903 CtdlFreeMessage(msg);
1909 * Read contact list into memory
1911 void CtdlICQ_Read_CL(void) {
1912 char hold_rm[ROOMNAMELEN];
1913 char icq_rm[ROOMNAMELEN];
1915 strcpy(hold_rm, CC->quickroom.QRname);
1916 MailboxName(icq_rm, &CC->usersupp, USERCONFIGROOM);
1917 strcpy(ThisICQ->icq_config, "");
1919 if (getroom(&CC->quickroom, icq_rm) != 0) {
1920 getroom(&CC->quickroom, hold_rm);
1924 /* Free any contact list already in memory */
1925 if (ThisICQ->icq_numcl) {
1926 phree(ThisICQ->icq_cl);
1927 ThisICQ->icq_numcl = 0;
1930 /* We want the last (and probably only) list in this room */
1931 CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, NULL,
1932 CtdlICQ_Read_CL_Backend);
1933 getroom(&CC->quickroom, hold_rm);
1938 * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
1939 * entry in the table if necessary
1941 struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
1944 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
1945 if (ThisICQ->icq_cl[i].uin == uin)
1946 return (&ThisICQ->icq_cl[i]);
1948 ++ThisICQ->icq_numcl;
1949 ThisICQ->icq_cl = reallok(ThisICQ->icq_cl,
1950 (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
1951 memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
1952 0, sizeof(struct CtdlICQ_CL));
1953 ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
1954 return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
1960 * Refresh the contact list
1962 void CtdlICQ_Refresh_Contact_List(void) {
1965 if (ThisICQ->icq_Sok < 0) return;
1969 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
1970 if (ThisICQ->icq_cl[i].uin > 0L) {
1971 icq_ContAddUser(ThisICQ->icq_cl[i].uin);
1972 icq_ContSetVis(ThisICQ->icq_cl[i].uin);
1976 icq_SendContactList();
1984 * Utility routine to logout and disconnect from the ICQ server if we happen
1985 * to already be connected
1987 void CtdlICQ_Logout_If_Connected(void) {
1988 if (ThisICQ->icq_Sok >= 0) {
1991 ThisICQ->icq_Sok = (-1);
1997 * If we have an ICQ uin/password on file for this user, go ahead and try
1998 * to log on to the ICQ server.
2000 void CtdlICQ_Login_If_Possible(void) {
2004 CtdlICQ_Logout_If_Connected();
2005 CtdlICQ_Read_Config();
2007 uin = extract_long(ThisICQ->icq_config, 0);
2008 extract(pass, ThisICQ->icq_config, 1);
2010 if ( (uin > 0L) && (strlen(pass)>0) ) {
2011 icq_Init(uin, pass);
2012 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
2013 icq_Login(STATUS_ONLINE);
2020 /* This merely hooks icqlib's log function into Citadel's log function. */
2021 void CtdlICQlog(time_t time, unsigned char level, const char *str)
2023 lprintf(level, "ICQ: %s", str);
2028 * At the start of each Citadel server session, we have to allocate some
2029 * space for the Citadel-ICQ session for this thread. It'll be automatically
2030 * freed by the server when the session ends.
2032 void CtdlICQ_session_startup_hook(void)
2034 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
2035 icq_init_handle(ThisICQ);
2041 * End-of-session cleanup
2043 void CtdlICQ_session_stopdown_hook(void) {
2050 * icq_Main() needs to be called as frequently as possible. We'll do it
2051 * following the completion of each Citadel server command.
2054 void CtdlICQ_after_cmd_hook(void)
2056 if (ThisICQ->icq_Sok >= 0) {
2057 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 60 ) {
2059 ThisICQ->icq_LastKeepAlive = time(NULL);
2067 * There are a couple of things we absolutely need to make sure of when we
2068 * kill off the session. One of them is that the sockets are all closed --
2069 * otherwise we could have a nasty file descriptor leak.
2071 void CtdlICQ_session_logout_hook(void)
2073 lprintf(9, "Shutting down ICQ\n");
2074 CtdlICQ_Logout_If_Connected();
2076 /* Free the memory used by the contact list */
2077 if (ThisICQ->icq_numcl) {
2078 phree(ThisICQ->icq_cl);
2079 ThisICQ->icq_numcl = 0;
2086 void CtdlICQ_session_login_hook(void)
2088 /* If this user has an ICQ config on file, start it up. */
2089 CtdlICQ_Login_If_Possible();
2098 * Here's what we have to do when an ICQ message arrives!
2100 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2101 BYTE day, BYTE month, WORD year,
2108 /* Default sender is 'uin@icq' syntax */
2109 sprintf(from, "%ld@icq", uin);
2111 /* Use the sender's name if we have it inthe contact list */
2112 if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
2113 if (uin == ThisICQ->icq_cl[i].uin) {
2114 safestrncpy(from, ThisICQ->icq_cl[i].name, 256);
2118 num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
2119 lprintf(9, "Delivered to %d users\n", num_delivered);
2124 void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2125 const char *first, const char *last,
2126 const char *email, char auth) {
2128 struct CtdlICQ_CL *ptr;
2131 ptr = CtdlICQ_CLent(uin);
2132 safestrncpy(ptr->name, nick, 32);
2133 ptr->status = STATUS_OFFLINE;
2134 lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
2142 int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
2145 DWORD target_uin = 0L;
2148 /* If this is an incoming ICQ from someone on the contact list,
2149 * change the sender from "uin@icq" to the contact name.
2152 for (i=0; i<strlen(from); ++i)
2153 if (!strcasecmp(&from[i], "@icq")) {
2156 if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
2157 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2158 if (ThisICQ->icq_cl[i].uin == atol(from))
2159 strcpy(from, ThisICQ->icq_cl[i].name);
2164 /* Handle "uin@icq" syntax */
2166 for (i=0; i<strlen(recp); ++i)
2167 if (!strcasecmp(&recp[i], "@icq")) {
2170 if (is_aticq == 1) target_uin = atol(recp);
2172 /* Handle "nick" syntax */
2173 if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
2174 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2175 if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
2176 target_uin = ThisICQ->icq_cl[i].uin;
2182 if (target_uin == 0L) return(0);
2184 if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
2190 void cmd_cicq(char *argbuf) {
2197 if (!(CC->logged_in)) {
2198 cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
2201 extract(cmd, argbuf, 0);
2203 /* "CICQ login" tells us how to log in. */
2204 if (!strcasecmp(cmd, "login")) {
2205 uin = extract_long(argbuf, 1);
2206 extract(pass, argbuf, 2);
2207 sprintf(ThisICQ->icq_config, "%ld|%s|", uin, pass);
2209 CtdlICQ_Write_Config();
2210 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2211 CtdlICQ_Login_If_Possible();
2213 cprintf("%d You must supply a UIN.\n", ERROR);
2218 /* "CICQ getcl" returns the contact list */
2219 if (!strcasecmp(cmd, "getcl")) {
2221 cprintf("%d Your ICQ contact list:\n", LISTING_FOLLOWS);
2222 if (ThisICQ->icq_numcl > 0) {
2223 for (i=0; i<ThisICQ->icq_numcl; ++i) {
2224 cprintf("%ld|%s|%s|\n",
2225 ThisICQ->icq_cl[i].uin,
2226 ThisICQ->icq_cl[i].name,
2227 icq_ConvertStatus2Str(
2228 ThisICQ->icq_cl[i].status)
2236 /* "CICQ putcl" accepts a new contact list from the client */
2237 if (!strcasecmp(cmd, "putcl")) {
2238 cprintf("%d Send contact list\n", SEND_LISTING);
2239 ThisICQ->icq_numcl = 0;
2240 while (client_gets(buf), strcmp(buf, "000")) {
2241 uin = extract_long(buf, 0);
2247 CtdlICQ_Refresh_Contact_List();
2251 /* "CICQ status" returns the connected/notconnected status */
2252 if (!strcasecmp(cmd, "status")) {
2253 cprintf("%d %d\n", OK,
2254 ((ThisICQ->icq_Sok >= 0) ? 1 : 0) );
2258 cprintf("%d Invalid subcommand\n", ERROR);
2266 * During an RWHO command, we want to append our ICQ information.
2268 void CtdlICQ_rwho(void) {
2271 if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
2272 if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
2273 cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
2274 0, /* no session ID */
2275 ThisICQ->icq_cl[i].name,
2276 icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
2277 ThisICQ->icq_cl[i].host,
2278 " ", /* no client */
2279 time(NULL), /* now? */
2280 " ", /* no last command */
2286 void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
2287 struct CtdlICQ_CL *ptr;
2289 ptr = CtdlICQ_CLent(uin);
2290 ptr->status = status;
2291 if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
2295 void CtdlICQ_Logged(void) {
2296 CtdlICQ_Refresh_Contact_List();
2300 void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
2301 DWORD port, DWORD realip) {
2305 CtdlICQ_Status_Update(uin, status);
2306 decoded_ip = ntohl(ip);
2307 locate_host(CtdlICQ_CLent(uin)->host, (struct in_addr *)&decoded_ip);
2311 void CtdlICQ_UserOffline(DWORD uin) {
2312 CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
2316 char *Dynamic_Module_Init(void)
2318 /* Make sure we've got a valid ThisICQ for each session */
2319 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2321 /* Tell the Citadel server about our wonderful ICQ hooks */
2322 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2323 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2324 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2325 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2326 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2327 CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
2328 CtdlRegisterProtoHook(cmd_cicq, "CICQ", "Configure Citadel ICQ");
2329 CtdlRegisterXmsgHook(CtdlICQ_Send_Msg, XMSG_PRI_FOREIGN);
2331 /* Tell the code formerly known as icqlib about our callbacks */
2332 icq_Log = CtdlICQlog;
2333 icq_RecvMessage = CtdlICQ_Incoming_Message;
2334 icq_InfoReply = CtdlICQ_InfoReply;
2335 icq_Disconnected = CtdlICQ_Login_If_Possible;
2336 icq_Logged = CtdlICQ_Logged;
2337 icq_UserStatusUpdate = CtdlICQ_Status_Update;
2338 icq_UserOnline = CtdlICQ_UserOnline;
2339 icq_UserOffline = CtdlICQ_UserOffline;