4 * This is a modified version of Denis' 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>
39 #include "dynloader.h"
41 #include "citserver.h"
44 struct ctdl_icq_handle {
47 BYTE icq_ServMess[8192];
52 icq_ContactItem *icq_ContFirst;
63 DWORD icq_ProxyDestHost;
64 WORD icq_ProxyDestPort;
65 WORD icq_ProxyOurPort;
66 time_t icq_LastKeepAlive; /* ig */
67 char icq_config[256]; /* ig */
72 /* ICQROOM is the name of the room in which each user's ICQ configuration
73 * and contact lists will be stored. (It's a personal room.)
75 #define ICQROOM "My ICQ Config"
77 /* MIME type to use for storing a user's ICQ uin, password, etc. */
78 #define ICQMIME "application/x-citadel-icq"
80 /* Citadel server TSD symbol for use by serv_icq */
81 unsigned long SYM_CTDL_ICQ;
82 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
84 extern struct CitContext *ContextList;
87 void (*icq_Logged) (void);
88 void (*icq_Disconnected) (void);
89 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
90 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
91 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);
92 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);
93 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
94 void (*icq_SearchDone) (void);
95 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
96 void (*icq_UserOffline) (DWORD uin);
97 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
98 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
99 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);
100 void (*icq_Log) (time_t time, unsigned char level, const char *str);
101 void (*icq_SrvAck) (WORD seq);
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 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
109 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
110 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
111 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
114 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
115 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
116 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
117 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
118 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
119 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
120 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
121 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
123 static COUNTRY_CODE Country_Codes[] =
138 {"American Samoa", 684},
153 {"Czech Republic", 42},
156 {"El Salvador", 503},
160 {"French Antilles", 596},
161 {"French Polynesia", 689},
168 {"Guantanomo Bay", 53},
182 {"Ivory Coast", 225},
190 {"Liechtenstein", 41},
202 {"Netherlands Antilles", 599},
203 {"New Caledonia", 687},
210 {"Papua New Guinea", 675},
220 {"Saudia Arabia", 966},
225 {"South Africa", 27},
236 {"United Arab Emirates", 971},
238 {"Vatican City", 39},
245 {"Not entered", 0xffff}};
247 void icq_init_handle(struct ctdl_icq_handle *i)
249 memset(i, 0, sizeof(struct ctdl_icq_handle));
250 i->icq_Russian = TRUE;
252 i->icq_OurIp = 0x0100007f;
253 i->icq_Status = STATUS_OFFLINE;
258 int icq_SockWrite(int sok, const void *buf, size_t count)
261 if (!(ThisICQ->icq_UseProxy))
262 return write(sok, buf, count);
264 tmpbuf[0] = 0; /* reserved */
265 tmpbuf[1] = 0; /* reserved */
266 tmpbuf[2] = 0; /* standalone packet */
267 tmpbuf[3] = 1; /* address type IP v4 */
268 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
269 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
270 memcpy(&tmpbuf[10], buf, count);
271 return write(sok, tmpbuf, count + 10) - 10;
275 int icq_SockRead(int sok, void *buf, size_t count)
279 if (!(ThisICQ->icq_UseProxy))
280 return read(sok, buf, count);
282 res = read(sok, tmpbuf, count + 10);
283 memcpy(buf, &tmpbuf[10], res - 10);
288 /****************************************
289 This must be called every 2 min.
290 so the server knows we're still alive.
291 JAVA client sends two different commands
293 *****************************************/
298 Word_2_Chars(pak.head.ver, ICQ_VER);
299 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
300 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
301 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
302 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
304 Word_2_Chars(pak.head.ver, ICQ_VER);
305 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
306 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
307 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
308 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
310 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
311 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
314 /**********************************
315 This must be called to remove
316 messages from the server
317 ***********************************/
318 void icq_SendGotMessages()
322 Word_2_Chars(pak.head.ver, ICQ_VER);
323 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
324 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
325 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
327 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
330 /*************************************
331 this sends over the contact list
332 *************************************/
333 void icq_SendContactList()
339 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
341 Word_2_Chars(pak.head.ver, ICQ_VER);
342 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
343 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
344 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
350 DW_2_Chars(tmp, ptr->uin);
355 pak.data[0] = num_used;
356 size = ((int) tmp - (int) pak.data);
357 size += sizeof(pak.head);
358 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
361 void icq_SendNewUser(unsigned long uin)
366 Word_2_Chars(pak.head.ver, ICQ_VER);
367 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
368 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
369 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
370 DW_2_Chars(pak.data, uin);
371 size = sizeof(pak.head) + 4;
372 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
375 /*************************************
376 this sends over the visible list
377 that allows certain users to see you
379 *************************************/
380 void icq_SendVisibleList()
386 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
388 Word_2_Chars(pak.head.ver, ICQ_VER);
389 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
390 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
391 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
398 DW_2_Chars(tmp, ptr->uin);
405 pak.data[0] = num_used;
406 size = ((int) tmp - (int) pak.data);
407 size += sizeof(pak.head);
408 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
412 /**************************************
413 This sends the second login command
414 this is necessary to finish logging in.
415 ***************************************/
416 void icq_SendLogin1()
420 Word_2_Chars(pak.head.ver, ICQ_VER);
421 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
422 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
423 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
425 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
428 /************************************************
429 This is called when a user goes offline
430 *************************************************/
431 void icq_HandleUserOffline(srv_net_icq_pak pak)
436 remote_uin = Chars_2_DW(pak.data);
437 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
438 sprintf(buf, "User %lu logged off\n", remote_uin);
439 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
442 (*icq_UserOffline) (remote_uin);
443 icq_AckSrv(Chars_2_Word(pak.head.seq));
446 void icq_HandleUserOnline(srv_net_icq_pak pak)
448 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
449 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
450 icq_ContactItem *ptr;
453 remote_uin = Chars_2_DW(pak.data);
454 new_status = Chars_2_DW(&pak.data[17]);
455 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
456 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
457 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
458 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
459 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
460 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
463 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
464 icq_AckSrv(Chars_2_Word(pak.head.seq));
467 void icq_Status_Update(srv_net_icq_pak pak)
469 unsigned long remote_uin, new_status;
472 remote_uin = Chars_2_DW(pak.data);
473 new_status = Chars_2_DW(&pak.data[4]);
474 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
475 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
476 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
478 if (icq_UserStatusUpdate)
479 (*icq_UserStatusUpdate) (remote_uin, new_status);
480 icq_AckSrv(Chars_2_Word(pak.head.seq));
483 /************************************
484 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
485 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
486 It does NOT wait for any kind of a response.
487 *************************************/
488 void icq_Login(DWORD status)
495 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
496 Word_2_Chars(pak.head.ver, ICQ_VER);
497 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
498 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
499 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
501 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
502 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
504 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
505 DW_2_Chars(s2.status, status);
506 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
508 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
509 s2.X2[0] = LOGIN_X2_DEF;
510 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
511 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
512 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
514 memcpy(pak.data, &s1, sizeof(s1));
516 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
517 size += Chars_2_Word(s1.len);
518 memcpy(&pak.data[size], &s2, sizeof(s2));
520 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
523 /*******************************
524 This routine sends the aknowlegement cmd to the
525 server it appears that this must be done after
526 everything the server sends us
527 *******************************/
528 void icq_AckSrv(int seq)
533 Word_2_Chars(pak.head.ver, ICQ_VER);
534 Word_2_Chars(pak.head.cmd, CMD_ACK);
535 Word_2_Chars(pak.head.seq, seq);
536 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
537 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
538 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
539 for (i = 0; i < 6; i++)
540 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
543 void icq_HandleInfoReply(srv_net_icq_pak pak)
545 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
549 seq = Chars_2_Word(pak.data);
550 uin = Chars_2_DW(&pak.data[2]);
551 len = Chars_2_Word(&pak.data[6]);
553 icq_RusConv("wk", ptr1);
554 tmp = &pak.data[8 + len];
555 len = Chars_2_Word(tmp);
557 icq_RusConv("wk", ptr2);
559 len = Chars_2_Word(tmp);
561 icq_RusConv("wk", ptr3);
563 len = Chars_2_Word(tmp);
565 icq_RusConv("wk", ptr4);
567 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
568 sprintf(buf, "Info reply for %lu\n", uin);
569 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
572 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
573 icq_AckSrv(Chars_2_Word(pak.head.seq));
576 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
578 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
582 char cnt_stat, gender, buf[256];
583 uin = Chars_2_DW(&pak.data[2]);
584 len = Chars_2_Word(&pak.data[6]);
586 icq_RusConv("wk", ptr1);
587 cnt_code = Chars_2_Word(&pak.data[8 + len]);
588 cnt_stat = pak.data[len + 10];
589 tmp = &pak.data[11 + len];
590 len = Chars_2_Word(tmp);
591 icq_RusConv("wk", tmp + 2);
593 age = Chars_2_Word(tmp + 2 + len);
594 gender = *(tmp + len + 4);
596 len = Chars_2_Word(tmp);
597 icq_RusConv("wk", tmp + 2);
600 len = Chars_2_Word(tmp);
601 icq_RusConv("wk", tmp + 2);
604 len = Chars_2_Word(tmp);
605 icq_RusConv("wk", tmp + 2);
607 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
608 sprintf(buf, "Extended info reply for %lu\n", uin);
609 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
611 if (icq_ExtInfoReply)
612 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
613 icq_AckSrv(Chars_2_Word(pak.head.seq));
616 void icq_HandleSearchReply(srv_net_icq_pak pak)
618 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
622 uin = Chars_2_DW(&pak.data[2]);
623 len = Chars_2_Word(&pak.data[6]);
625 icq_RusConv("wk", ptr1);
626 tmp = &pak.data[8 + len];
627 len = Chars_2_Word(tmp);
629 icq_RusConv("wk", ptr2);
631 len = Chars_2_Word(tmp);
633 icq_RusConv("wk", ptr3);
635 len = Chars_2_Word(tmp);
637 icq_RusConv("wk", ptr4);
639 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
640 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");
641 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
644 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
645 icq_AckSrv(Chars_2_Word(pak.head.seq));
648 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
651 char *ptr1, *ptr2, *ptr3, *ptr4;
655 case USER_ADDED_MESS:
656 tmp = strchr(data, '\xFE');
658 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
659 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
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 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
680 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
684 icq_RusConv("wk", data);
688 tmp = strchr(tmp, '\xFE');
690 icq_RusConv("wk", data);
691 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
692 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
693 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
694 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
697 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
700 tmp = strchr(data, '\xFE');
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");
734 icq_RusConv("wk", data);
738 tmp = strchr(tmp, '\xFE');
740 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
741 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
747 tmp = strchr(tmp, '\x00');
749 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
750 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
754 icq_RusConv("wk", data);
755 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
756 sprintf(buf, "%lu has requested your authorization to be added to "
757 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
758 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
759 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
762 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
765 tmp = strchr(data, '\xFE');
767 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
768 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
772 icq_RusConv("wk", data);
776 icq_RusConv("wk", data);
777 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
778 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
779 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
782 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
785 icq_RusConv("wk", data);
786 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
787 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
788 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
791 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
795 /**********************************
796 Connects to hostname on port port
797 hostname can be DNS or nnn.nnn.nnn.nnn
798 write out messages to the FD aux
799 ***********************************/
800 int icq_Connect(const char *hostname, int port)
802 char buf[1024], un = 1;
803 char our_host[256], tmpbuf[256];
804 int conct, length, res;
805 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
806 struct hostent *host_struct; /* used in DNS llokup */
808 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
809 if ((ThisICQ->icq_Sok) == -1) {
810 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
811 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
814 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
815 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
816 sin.sin_addr.s_addr = INADDR_ANY;
817 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
819 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
820 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
821 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
824 length = sizeof(sin);
825 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
826 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
827 if ((ThisICQ->icq_UseProxy)) {
828 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
829 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
830 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
831 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
832 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
833 if (host_struct == 0L) {
834 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
835 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
836 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
840 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
842 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
843 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
844 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
845 if ((ThisICQ->icq_ProxySok) == -1) {
846 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
847 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
850 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
851 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
852 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
853 if (conct == -1) { /* did we connect ? */
854 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
855 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
858 buf[0] = 5; /* protocol version */
859 buf[1] = 1; /* number of methods */
860 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
861 buf[2] = 0; /* no authorization required */
863 buf[2] = 2; /* method username/password */
864 write((ThisICQ->icq_ProxySok), buf, 3);
865 res = read((ThisICQ->icq_ProxySok), buf, 2);
866 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
867 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
868 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
869 close((ThisICQ->icq_ProxySok));
872 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
873 buf[0] = 1; /* version of subnegotiation */
874 buf[1] = strlen((ThisICQ->icq_ProxyName));
875 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
876 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
877 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
878 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
879 res = read((ThisICQ->icq_ProxySok), buf, 2);
880 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
881 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
882 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
883 close((ThisICQ->icq_ProxySok));
887 buf[0] = 5; /* protocol version */
888 buf[1] = 3; /* command UDP associate */
889 buf[2] = 0; /* reserved */
890 buf[3] = 1; /* address type IP v4 */
895 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
896 write((ThisICQ->icq_ProxySok), buf, 10);
897 res = read((ThisICQ->icq_ProxySok), buf, 10);
898 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
901 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
902 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
905 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
906 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
909 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
910 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
913 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
914 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
917 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
918 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
921 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
922 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
925 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
926 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
929 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
930 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
933 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
934 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
937 close((ThisICQ->icq_ProxySok));
941 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
942 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
943 host_struct = gethostbyname(hostname);
944 if (host_struct == 0L) {
945 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
946 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
947 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
949 if ((ThisICQ->icq_UseProxy))
950 close((ThisICQ->icq_ProxySok));
953 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
955 if ((ThisICQ->icq_UseProxy)) {
956 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
957 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
959 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
960 sin.sin_port = htons(port); /* port */
961 if ((ThisICQ->icq_UseProxy)) {
962 (ThisICQ->icq_ProxyDestPort) = htons(port);
963 memcpy(&sin.sin_port, &buf[8], 2);
965 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
966 if (conct == -1) { /* did we connect ? */
967 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
968 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
969 if ((ThisICQ->icq_UseProxy))
970 close((ThisICQ->icq_ProxySok));
973 length = sizeof(sin);
974 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
975 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
976 (ThisICQ->icq_OurPort) = sin.sin_port;
977 return (ThisICQ->icq_Sok);
980 void icq_HandleProxyResponse()
984 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
986 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
987 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
988 if (icq_Disconnected)
989 (*icq_Disconnected) ();
990 SOCKCLOSE((ThisICQ->icq_Sok));
991 SOCKCLOSE((ThisICQ->icq_ProxySok));
995 /******************************************
996 Handles packets that the server sends to us.
997 *******************************************/
998 void icq_HandleServerResponse()
1000 srv_net_icq_pak pak;
1001 SIMPLE_MESSAGE *s_mesg;
1002 RECV_MESSAGE *r_mesg;
1009 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1011 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1012 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1013 if (icq_Disconnected)
1014 (*icq_Disconnected) ();
1015 SOCKCLOSE((ThisICQ->icq_Sok));
1017 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)) {
1018 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1019 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1020 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1021 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1023 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1027 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1028 icq_SetServMess(Chars_2_Word(pak.head.seq));
1029 switch (Chars_2_Word(pak.head.cmd)) {
1031 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1032 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1034 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1037 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1038 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1039 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1042 case SRV_LOGIN_REPLY:
1043 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1044 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1045 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]);
1046 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1048 icq_AckSrv(Chars_2_Word(pak.head.seq));
1050 icq_SendContactList();
1051 icq_SendVisibleList();
1055 case SRV_RECV_MESSAGE:
1056 r_mesg = (RECV_MESSAGE *) pak.data;
1057 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));
1058 icq_AckSrv(Chars_2_Word(pak.head.seq));
1060 case SRV_X1: /* unknown message sent after login */
1061 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1062 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1063 icq_AckSrv(Chars_2_Word(pak.head.seq));
1065 case SRV_X2: /* unknown message sent after login */
1066 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1067 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1068 icq_AckSrv(Chars_2_Word(pak.head.seq));
1069 icq_SendGotMessages();
1071 case SRV_INFO_REPLY:
1072 icq_HandleInfoReply(pak);
1074 case SRV_EXT_INFO_REPLY:
1075 icq_HandleExtInfoReply(pak);
1077 case SRV_USER_ONLINE:
1078 icq_HandleUserOnline(pak);
1080 case SRV_USER_OFFLINE:
1081 icq_HandleUserOffline(pak);
1084 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1085 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1086 icq_Login((ThisICQ->icq_Status));
1088 case SRV_STATUS_UPDATE:
1089 icq_Status_Update(pak);
1092 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1093 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1094 if (icq_Disconnected)
1095 (*icq_Disconnected) ();
1097 case SRV_END_OF_SEARCH:
1098 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1099 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1101 (*icq_SearchDone) ();
1102 icq_AckSrv(Chars_2_Word(pak.head.seq));
1104 case SRV_USER_FOUND:
1105 icq_HandleSearchReply(pak);
1107 case SRV_SYS_DELIVERED_MESS:
1108 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1109 cur_time = time(0L);
1110 tm_str = localtime(&cur_time);
1111 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);
1112 icq_AckSrv(Chars_2_Word(pak.head.seq));
1114 default: /* commands we dont handle yet */
1115 len = s - (sizeof(pak.head));
1116 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1117 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1118 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1119 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1121 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1126 void icq_Init(DWORD uin, const char *password)
1128 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1129 (ThisICQ->icq_Uin) = uin;
1130 if ((ThisICQ->icq_Password))
1131 phree((ThisICQ->icq_Password));
1132 (ThisICQ->icq_Password) = strdoop(password);
1137 if ((ThisICQ->icq_Password))
1138 phree((ThisICQ->icq_Password));
1141 /******************************
1142 Main function connects gets (ThisICQ->icq_Uin)
1143 and (ThisICQ->icq_Password) and logins in and sits
1144 in a loop waiting for server responses.
1145 *******************************/
1157 FD_SET((ThisICQ->icq_Sok), &readfds);
1158 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1159 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1160 icq_HandleServerResponse();
1164 } while (did_something);
1167 /********************************************************
1168 Russian language ICQ fix.
1169 Usual Windows ICQ users do use Windows 1251 encoding but
1170 unix users do use koi8 encoding, so we need to convert it.
1171 This function will convert string from windows 1251 to koi8
1172 or from koi8 to windows 1251.
1173 Andrew Frolov dron@ilm.net
1174 *********************************************************/
1175 void icq_RusConv(const char to[4], char *t_in)
1180 /* 6-17-1998 by Linux_Dude
1181 * Moved initialization of table out front of 'if' block to prevent compiler
1182 * warning. Improved error message, and now return without performing string
1183 * conversion to prevent addressing memory out of range (table pointer would
1184 * previously have remained uninitialized (= bad)).
1188 if (strcmp(to, "kw") == 0)
1190 else if (strcmp(to, "wk") != 0) {
1191 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1192 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1195 /* End Linux_Dude's changes ;) */
1197 if ((ThisICQ->icq_Russian)) {
1198 for (i = 0; t_in[i] != 0; i++) {
1201 t_in[i] = table[t_in[i] & 0177];
1206 /**************************************************
1207 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1209 ***************************************************/
1210 WORD icq_SendMessage(DWORD uin, const char *text)
1215 char buf[512]; /* message may be only 450 bytes long */
1217 strncpy(buf, text, 512);
1218 icq_RusConv("kw", buf);
1220 Word_2_Chars(pak.head.ver, ICQ_VER);
1221 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1222 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1223 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1224 DW_2_Chars(msg.uin, uin);
1225 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1226 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1227 memcpy(&pak.data, &msg, sizeof(msg));
1228 memcpy(&pak.data[8], buf, len + 1);
1229 size = sizeof(msg) + len + 1;
1230 for (i = 0; i < 6; i++)
1231 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1232 return (ThisICQ->icq_SeqNum) - 1;
1235 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1239 int size, len1, len2;
1240 char buf1[512], buf2[512];
1242 strncpy(buf1, descr, 512);
1243 strncpy(buf2, url, 512);
1244 /* Do we need to convert URL? */
1245 icq_RusConv("kw", buf2);
1246 len1 = strlen(buf1);
1247 len2 = strlen(buf2);
1248 Word_2_Chars(pak.head.ver, ICQ_VER);
1249 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1250 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1251 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1252 DW_2_Chars(msg.uin, uin);
1253 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1254 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1255 memcpy(&pak.data, &msg, sizeof(msg));
1256 memcpy(&pak.data[8], buf1, len1);
1257 pak.data[8 + len1] = 0xFE;
1258 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1259 size = sizeof(msg) + len1 + len2 + 2;
1260 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1261 return (ThisICQ->icq_SeqNum) - 1;
1264 /**************************************************
1265 Sends a authorization to the server so the Mirabilis
1266 client can add the user.
1267 ***************************************************/
1268 void icq_SendAuthMsg(DWORD uin)
1274 Word_2_Chars(pak.head.ver, ICQ_VER);
1275 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1276 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1277 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1278 DW_2_Chars(msg.uin, uin);
1279 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1280 Word_2_Chars(msg.len, 2);
1281 memcpy(&pak.data, &msg, sizeof(msg));
1282 pak.data[sizeof(msg)] = 0x03;
1283 pak.data[sizeof(msg) + 1] = 0x00;
1284 size = sizeof(msg) + 2;
1285 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1288 /**************************************************
1289 Changes the users status on the server
1290 ***************************************************/
1291 void icq_ChangeStatus(DWORD status)
1296 Word_2_Chars(pak.head.ver, ICQ_VER);
1297 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1298 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1299 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1300 DW_2_Chars(pak.data, status);
1301 (ThisICQ->icq_Status) = status;
1303 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1306 /**********************
1308 ***********************/
1314 Word_2_Chars(pak.head.ver, ICQ_VER);
1315 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1316 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1317 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1318 len = strlen("B_USER_DISCONNECTED") + 1;
1319 *(short *) pak.data = len;
1321 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1322 pak.data[2 + len] = 05;
1323 pak.data[3 + len] = 00;
1324 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1327 void icq_Disconnect()
1329 SOCKCLOSE((ThisICQ->icq_Sok));
1330 SOCKCLOSE((ThisICQ->icq_Sok));
1331 if ((ThisICQ->icq_UseProxy))
1332 SOCKCLOSE((ThisICQ->icq_ProxySok));
1335 /********************************************************
1336 Sends a request to the server for info on a specific user
1337 *********************************************************/
1338 WORD icq_SendInfoReq(DWORD uin)
1343 Word_2_Chars(pak.head.ver, ICQ_VER);
1344 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1345 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1346 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1347 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1348 DW_2_Chars(&pak.data[2], uin);
1350 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1351 return (ThisICQ->icq_SeqNum) - 1;
1354 /********************************************************
1355 Sends a request to the server for info on a specific user
1356 *********************************************************/
1357 WORD icq_SendExtInfoReq(DWORD uin)
1362 Word_2_Chars(pak.head.ver, ICQ_VER);
1363 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1364 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1365 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1366 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1367 DW_2_Chars(&pak.data[2], uin);
1369 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1370 return (ThisICQ->icq_SeqNum) - 1;
1373 /**************************************************************
1374 Initializes a server search for the information specified
1375 ***************************************************************/
1376 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1381 Word_2_Chars(pak.head.ver, ICQ_VER);
1382 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1383 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1384 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1385 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1387 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1389 strcpy(pak.data + size, nick);
1390 size += strlen(nick) + 1;
1391 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1393 strcpy(pak.data + size, first);
1394 size += strlen(first) + 1;
1395 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1397 strcpy(pak.data + size, last);
1398 size += strlen(last) + 1;
1399 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1401 strcpy(pak.data + size, email);
1402 size += strlen(email) + 1;
1403 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1406 /**************************************************************
1407 Initializes a server search for the information specified
1408 ***************************************************************/
1409 void icq_SendSearchUINReq(DWORD uin)
1414 Word_2_Chars(pak.head.ver, ICQ_VER);
1415 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1416 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1417 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1418 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1420 DW_2_Chars(&pak.data[size], uin);
1422 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1425 /**************************************************
1426 Registers a new uin in the ICQ network
1427 ***************************************************/
1428 void icq_RegNewUser(const char *pass)
1430 srv_net_icq_pak pak;
1435 Word_2_Chars(pak.head.ver, ICQ_VER);
1436 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1437 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1438 Word_2_Chars(len_buf, len);
1439 memcpy(&pak.data, "\x02\x00", 2);
1440 memcpy(&pak.data[2], &len_buf, 2);
1441 memcpy(&pak.data[4], pass, len + 1);
1442 DW_2_Chars(&pak.data[4 + len], 0x0072);
1443 DW_2_Chars(&pak.data[8 + len], 0x0000);
1445 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1448 void icq_UpdateUserInfo(USER_INFO * user)
1453 Word_2_Chars(pak.head.ver, ICQ_VER);
1454 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1455 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1456 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1457 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1459 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1461 strcpy(pak.data + size, user->nick);
1462 size += strlen(user->nick) + 1;
1463 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1465 strcpy(pak.data + size, user->first);
1466 size += strlen(user->first) + 1;
1467 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1469 strcpy(pak.data + size, user->last);
1470 size += strlen(user->last) + 1;
1471 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1473 strcpy(pak.data + size, user->email);
1474 size += strlen(user->email) + 1;
1475 pak.data[size] = user->auth;
1477 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1480 const char *icq_GetCountryName(int code)
1484 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1485 if (Country_Codes[i].code == code) {
1486 return Country_Codes[i].name;
1489 if (Country_Codes[i].code == code) {
1490 return Country_Codes[i].name;
1495 /********************************************
1496 returns a string describing the status or
1497 a "Error" if no such string exists
1498 *********************************************/
1499 const char *icq_ConvertStatus2Str(int status)
1501 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1504 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1505 switch (status & 0x01FF) {
1510 return "Do not disturb";
1515 case STATUS_OCCUPIED:
1519 return "Not available";
1521 case STATUS_INVISIBLE:
1524 case STATUS_INVISIBLE_2:
1525 return "Invisible mode 2";
1527 case STATUS_FREE_CHAT:
1528 return "Free for chat";
1536 void icq_InitNewUser(const char *hostname, DWORD port)
1538 srv_net_icq_pak pak;
1543 icq_Connect(hostname, port);
1544 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1545 printf("Couldn't establish connection\n");
1548 icq_RegNewUser((ThisICQ->icq_Password));
1551 tv.tv_usec = 500000;
1554 FD_SET((ThisICQ->icq_Sok), &readfds);
1556 /* don't care about writefds and exceptfds: */
1557 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1559 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1560 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1561 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1562 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1569 /********************************************
1570 Converts an intel endian character sequence to
1572 *********************************************/
1573 DWORD Chars_2_DW(unsigned char *buf)
1588 /********************************************
1589 Converts an intel endian character sequence to
1591 *********************************************/
1592 WORD Chars_2_Word(unsigned char *buf)
1603 /********************************************
1605 an intel endian character sequence
1606 *********************************************/
1607 void DW_2_Chars(unsigned char *buf, DWORD num)
1609 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1610 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1611 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1612 buf[0] = (unsigned char) (num) & 0x000000FF;
1615 /********************************************
1617 an intel endian character sequence
1618 *********************************************/
1619 void Word_2_Chars(unsigned char *buf, WORD num)
1621 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1622 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1625 /***************************
1626 ContactList functions
1627 ***************************/
1628 void icq_ContAddUser(DWORD cuin)
1630 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1631 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1634 p->vis_list = FALSE;
1640 (ThisICQ->icq_ContFirst) = p;
1643 void icq_ContDelUser(DWORD cuin)
1645 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1648 if (ptr->uin == cuin) {
1649 (ThisICQ->icq_ContFirst) = ptr->next;
1651 ptr = (ThisICQ->icq_ContFirst);
1654 if (ptr->next->uin == cuin) {
1655 ptr->next = ptr->next->next;
1662 void icq_ContClear()
1664 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1669 (ThisICQ->icq_ContFirst) = ptr;
1673 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1675 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1679 if (ptr->uin == cuin)
1686 icq_ContactItem *icq_ContGetFirst()
1688 return (ThisICQ->icq_ContFirst);
1691 void icq_ContSetVis(DWORD cuin)
1693 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1697 if (ptr->uin == cuin)
1698 ptr->vis_list = TRUE;
1703 /************************
1704 (ThisICQ->icq_ServMess) functions
1705 *************************/
1706 BOOL icq_GetServMess(WORD num)
1708 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1711 void icq_SetServMess(WORD num)
1713 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1718 return (ThisICQ->icq_Sok);
1721 int icq_GetProxySok()
1723 return (ThisICQ->icq_ProxySok);
1726 /*******************
1727 SOCKS5 Proxy support
1728 ********************/
1729 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1731 if ((ThisICQ->icq_ProxyHost))
1732 phree((ThisICQ->icq_ProxyHost));
1733 if ((ThisICQ->icq_ProxyName))
1734 phree((ThisICQ->icq_ProxyName));
1735 if ((ThisICQ->icq_ProxyPass))
1736 phree((ThisICQ->icq_ProxyPass));
1737 if (strlen(pname) > 255) {
1738 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1739 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1740 ThisICQ->icq_UseProxy = 0;
1743 if (strlen(ppass) > 255) {
1744 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1745 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1746 (ThisICQ->icq_UseProxy) = 0;
1749 (ThisICQ->icq_UseProxy) = 1;
1750 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1751 (ThisICQ->icq_ProxyPort) = pport;
1752 (ThisICQ->icq_ProxyAuth) = pauth;
1753 (ThisICQ->icq_ProxyName) = strdoop(pname);
1754 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1757 void icq_UnsetProxy()
1759 ThisICQ->icq_UseProxy = 0;
1764 /***********************************************************************/
1765 /* icqlib stuff ends here, Citadel module stuff begins */
1766 /***********************************************************************/
1770 * Callback function for CtdlICQ_Read_Config()
1772 void CtdlICQ_Read_Config_Backend(long msgnum) {
1773 struct CtdlMessage *msg;
1777 msg = CtdlFetchMessage(msgnum);
1779 ptr = msg->cm_fields['M'];
1780 pos = pattern2(ptr, "\n\n");
1788 safestrncpy(ThisICQ->icq_config, ptr, 256);
1790 CtdlFreeMessage(msg);
1796 * If this user has an ICQ configuration on disk, read it into memory.
1798 void CtdlICQ_Read_Config(void) {
1799 char hold_rm[ROOMNAMELEN];
1800 char icq_rm[ROOMNAMELEN];
1802 strcpy(hold_rm, CC->quickroom.QRname);
1803 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1804 strcpy(ThisICQ->icq_config, "");
1806 if (getroom(&CC->quickroom, icq_rm) != 0) {
1807 getroom(&CC->quickroom, hold_rm);
1811 /* We want the last (and probably only) config in this room */
1812 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1813 getroom(&CC->quickroom, hold_rm);
1820 * Write our config to disk
1822 void CtdlICQ_Write_Config(void) {
1823 char temp[PATH_MAX];
1826 strcpy(temp, tmpnam(NULL));
1828 fp = fopen(temp, "w");
1829 if (fp == NULL) return;
1830 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1833 /* this handy API function does all the work for us */
1834 CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
1842 * Refresh the contact list
1844 void CtdlICQ_Refresh_Contact_List(void) {
1848 if (ThisICQ->icq_Sok < 0) return;
1853 if (contact_uin > 0L) {
1854 icq_ContAddUser(contact_uin);
1855 icq_ContSetVis(contact_uin);
1861 icq_SendContactList();
1868 * Utility routine to logout and disconnect from the ICQ server if we happen
1869 * to already be connected
1871 void CtdlICQ_Logout_If_Connected(void) {
1872 if (ThisICQ->icq_Sok >= 0) {
1875 ThisICQ->icq_Sok = (-1);
1881 * If we have an ICQ uin/password on file for this user, go ahead and try
1882 * to log on to the ICQ server.
1884 void CtdlICQ_Login_If_Possible(void) {
1889 CtdlICQ_Logout_If_Connected();
1890 CtdlICQ_Read_Config();
1892 uin = extract_long(ThisICQ->icq_config, 0);
1893 extract(pass, ThisICQ->icq_config, 1);
1895 lprintf(9, "Here's my config: %s\n", ThisICQ->icq_config);
1897 if ( (uin > 0L) && (strlen(pass)>0) ) {
1898 icq_Init(uin, pass);
1899 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
1900 icq_Login(STATUS_ONLINE);
1901 CtdlICQ_Refresh_Contact_List();
1908 /* This merely hooks icqlib's log function into Citadel's log function. */
1909 void CtdlICQlog(time_t time, unsigned char level, const char *str)
1911 lprintf(level, "ICQ: %s", str);
1916 * At the start of each Citadel server session, we have to allocate some
1917 * space for the Citadel-ICQ session for this thread. It'll be automatically
1918 * freed by the server when the session ends.
1920 void CtdlICQ_session_startup_hook(void)
1922 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
1923 icq_init_handle(ThisICQ);
1928 * icq_Main() needs to be called as frequently as possible. We'll do it
1929 * following the completion of each Citadel server command.
1932 void CtdlICQ_after_cmd_hook(void)
1934 if (ThisICQ->icq_Sok >= 0) {
1935 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
1937 ThisICQ->icq_LastKeepAlive = time(NULL);
1945 * There are a couple of things we absolutely need to make sure of when we
1946 * kill off the session. One of them is that the sockets are all closed --
1947 * otherwise we could have a nasty file descriptor leak.
1949 void CtdlICQ_session_logout_hook(void)
1951 CtdlICQ_Logout_If_Connected();
1958 void CtdlICQ_session_login_hook(void)
1960 /* If this user has an ICQ config on file, start it up. */
1961 CtdlICQ_Login_If_Possible();
1967 void cmd_icql(char *argbuf)
1969 safestrncpy(ThisICQ->icq_config, argbuf, 256);
1970 CtdlICQ_Write_Config();
1972 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
1973 CtdlICQ_Login_If_Possible();
1980 * Here's what we have to do when an ICQ message arrives!
1982 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
1983 BYTE day, BYTE month, WORD year,
1989 sprintf(from, "%ld@icq", uin);
1990 if (CtdlSendExpressMessageFunc) {
1991 CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
1993 lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
1999 CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2000 const char *first, const char *last,
2001 const char *email, char auth) {
2003 /* FIX do something with this info!! */
2009 char *Dynamic_Module_Init(void)
2011 /* Make sure we've got a valid ThisICQ for each session */
2012 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2014 /* Tell the Citadel server about our wonderful ICQ hooks */
2015 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2016 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2017 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2018 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2019 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2021 /* Tell the code formerly known as icqlib about our callbacks */
2022 icq_Log = CtdlICQlog;
2023 icq_RecvMessage = CtdlICQ_Incoming_Message;
2024 icq_InfoReply = CtdlICQ_InfoReply;