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.
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
32 #include "dynloader.h"
34 #include "citserver.h"
36 struct ctdl_icq_handle {
39 BYTE icq_ServMess[8192];
44 icq_ContactItem *icq_ContFirst;
55 DWORD icq_ProxyDestHost;
56 WORD icq_ProxyDestPort;
57 WORD icq_ProxyOurPort;
58 FILE *icq_MyConfigFile; /* ig */
59 time_t icq_LastKeepAlive; /* ig */
64 /* ICQROOM is the name of the room in which each user's ICQ configuration
65 * and contact lists will be stored. (It's a personal room.)
67 #define ICQROOM "My ICQ Config"
69 /* MIME type to use for storing a user's ICQ uin, password, etc. */
70 #define ICQMIME "application/x-citadel-icq"
72 /* Citadel server TSD symbol for use by serv_icq */
73 unsigned long SYM_CTDL_ICQ;
74 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
76 extern struct CitContext *ContextList;
79 void (*icq_Logged) (void);
80 void (*icq_Disconnected) (void);
81 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
82 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
83 void (*icq_RecvAdded) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email);
84 void (*icq_RecvAuthReq) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *nick, const char *first, const char *last, const char *email, const char *reason);
85 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
86 void (*icq_SearchDone) (void);
87 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
88 void (*icq_UserOffline) (DWORD uin);
89 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
90 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
91 void (*icq_ExtInfoReply) (DWORD uin, const char *city, WORD country_code, char country_stat, const char *state, WORD age, char gender, const char *phone, const char *hp, const char *about);
92 void (*icq_Log) (time_t time, unsigned char level, const char *str);
93 void (*icq_SrvAck) (WORD seq);
96 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
97 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
98 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
99 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
100 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
101 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
102 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
103 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
106 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
107 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
108 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
109 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
110 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
111 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
112 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
113 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
115 static COUNTRY_CODE Country_Codes[] =
130 {"American Samoa", 684},
145 {"Czech Republic", 42},
148 {"El Salvador", 503},
152 {"French Antilles", 596},
153 {"French Polynesia", 689},
160 {"Guantanomo Bay", 53},
174 {"Ivory Coast", 225},
182 {"Liechtenstein", 41},
194 {"Netherlands Antilles", 599},
195 {"New Caledonia", 687},
202 {"Papua New Guinea", 675},
212 {"Saudia Arabia", 966},
217 {"South Africa", 27},
228 {"United Arab Emirates", 971},
230 {"Vatican City", 39},
237 {"Not entered", 0xffff}};
239 void icq_init_handle(struct ctdl_icq_handle *i)
241 memset(i, 0, sizeof(struct ctdl_icq_handle));
242 i->icq_Russian = TRUE;
244 i->icq_OurIp = 0x0100007f;
245 i->icq_Status = STATUS_OFFLINE;
248 i->icq_MyConfigFile = NULL;
251 int icq_SockWrite(int sok, const void *buf, size_t count)
254 if (!(ThisICQ->icq_UseProxy))
255 return write(sok, buf, count);
257 tmpbuf[0] = 0; /* reserved */
258 tmpbuf[1] = 0; /* reserved */
259 tmpbuf[2] = 0; /* standalone packet */
260 tmpbuf[3] = 1; /* address type IP v4 */
261 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
262 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
263 memcpy(&tmpbuf[10], buf, count);
264 return write(sok, tmpbuf, count + 10) - 10;
268 int icq_SockRead(int sok, void *buf, size_t count)
272 if (!(ThisICQ->icq_UseProxy))
273 return read(sok, buf, count);
275 res = read(sok, tmpbuf, count + 10);
276 memcpy(buf, &tmpbuf[10], res - 10);
281 /****************************************
282 This must be called every 2 min.
283 so the server knows we're still alive.
284 JAVA client sends two different commands
286 *****************************************/
291 Word_2_Chars(pak.head.ver, ICQ_VER);
292 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
293 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
294 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
295 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
297 Word_2_Chars(pak.head.ver, ICQ_VER);
298 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
299 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
300 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
301 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
303 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
304 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
307 /**********************************
308 This must be called to remove
309 messages from the server
310 ***********************************/
311 void icq_SendGotMessages()
315 Word_2_Chars(pak.head.ver, ICQ_VER);
316 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
317 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
318 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
320 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
323 /*************************************
324 this sends over the contact list
325 *************************************/
326 void icq_SendContactList()
332 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
334 Word_2_Chars(pak.head.ver, ICQ_VER);
335 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
336 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
337 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
343 DW_2_Chars(tmp, ptr->uin);
348 pak.data[0] = num_used;
349 size = ((int) tmp - (int) pak.data);
350 size += sizeof(pak.head);
351 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
354 void icq_SendNewUser(unsigned long uin)
359 Word_2_Chars(pak.head.ver, ICQ_VER);
360 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
361 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
362 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
363 DW_2_Chars(pak.data, uin);
364 size = sizeof(pak.head) + 4;
365 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
368 /*************************************
369 this sends over the visible list
370 that allows certain users to see you
372 *************************************/
373 void icq_SendVisibleList()
379 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
381 Word_2_Chars(pak.head.ver, ICQ_VER);
382 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
383 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
384 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
391 DW_2_Chars(tmp, ptr->uin);
398 pak.data[0] = num_used;
399 size = ((int) tmp - (int) pak.data);
400 size += sizeof(pak.head);
401 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
405 /**************************************
406 This sends the second login command
407 this is necessary to finish logging in.
408 ***************************************/
409 void icq_SendLogin1()
413 Word_2_Chars(pak.head.ver, ICQ_VER);
414 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
415 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
416 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
418 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
421 /************************************************
422 This is called when a user goes offline
423 *************************************************/
424 void icq_HandleUserOffline(srv_net_icq_pak pak)
429 remote_uin = Chars_2_DW(pak.data);
430 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
431 sprintf(buf, "User %lu logged off\n", remote_uin);
432 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
435 (*icq_UserOffline) (remote_uin);
436 icq_AckSrv(Chars_2_Word(pak.head.seq));
439 void icq_HandleUserOnline(srv_net_icq_pak pak)
441 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
442 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
443 icq_ContactItem *ptr;
446 remote_uin = Chars_2_DW(pak.data);
447 new_status = Chars_2_DW(&pak.data[17]);
448 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
449 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
450 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
451 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
452 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
453 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
456 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
457 icq_AckSrv(Chars_2_Word(pak.head.seq));
460 void icq_Status_Update(srv_net_icq_pak pak)
462 unsigned long remote_uin, new_status;
465 remote_uin = Chars_2_DW(pak.data);
466 new_status = Chars_2_DW(&pak.data[4]);
467 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
468 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
469 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
471 if (icq_UserStatusUpdate)
472 (*icq_UserStatusUpdate) (remote_uin, new_status);
473 icq_AckSrv(Chars_2_Word(pak.head.seq));
476 /************************************
477 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
478 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
479 It does NOT wait for any kind of a response.
480 *************************************/
481 void icq_Login(DWORD status)
488 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
489 Word_2_Chars(pak.head.ver, ICQ_VER);
490 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
491 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
492 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
494 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
495 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
497 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
498 DW_2_Chars(s2.status, status);
499 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
501 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
502 s2.X2[0] = LOGIN_X2_DEF;
503 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
504 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
505 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
507 memcpy(pak.data, &s1, sizeof(s1));
509 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
510 size += Chars_2_Word(s1.len);
511 memcpy(&pak.data[size], &s2, sizeof(s2));
513 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
516 /*******************************
517 This routine sends the aknowlegement cmd to the
518 server it appears that this must be done after
519 everything the server sends us
520 *******************************/
521 void icq_AckSrv(int seq)
526 Word_2_Chars(pak.head.ver, ICQ_VER);
527 Word_2_Chars(pak.head.cmd, CMD_ACK);
528 Word_2_Chars(pak.head.seq, seq);
529 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
530 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
531 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
532 for (i = 0; i < 6; i++)
533 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
536 void icq_HandleInfoReply(srv_net_icq_pak pak)
538 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
542 seq = Chars_2_Word(pak.data);
543 uin = Chars_2_DW(&pak.data[2]);
544 len = Chars_2_Word(&pak.data[6]);
546 icq_RusConv("wk", ptr1);
547 tmp = &pak.data[8 + len];
548 len = Chars_2_Word(tmp);
550 icq_RusConv("wk", ptr2);
552 len = Chars_2_Word(tmp);
554 icq_RusConv("wk", ptr3);
556 len = Chars_2_Word(tmp);
558 icq_RusConv("wk", ptr4);
560 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
561 sprintf(buf, "Info reply for %lu\n", uin);
562 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
565 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
566 icq_AckSrv(Chars_2_Word(pak.head.seq));
569 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
571 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
575 char cnt_stat, gender, buf[256];
576 uin = Chars_2_DW(&pak.data[2]);
577 len = Chars_2_Word(&pak.data[6]);
579 icq_RusConv("wk", ptr1);
580 cnt_code = Chars_2_Word(&pak.data[8 + len]);
581 cnt_stat = pak.data[len + 10];
582 tmp = &pak.data[11 + len];
583 len = Chars_2_Word(tmp);
584 icq_RusConv("wk", tmp + 2);
586 age = Chars_2_Word(tmp + 2 + len);
587 gender = *(tmp + len + 4);
589 len = Chars_2_Word(tmp);
590 icq_RusConv("wk", tmp + 2);
593 len = Chars_2_Word(tmp);
594 icq_RusConv("wk", tmp + 2);
597 len = Chars_2_Word(tmp);
598 icq_RusConv("wk", tmp + 2);
600 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
601 sprintf(buf, "Extended info reply for %lu\n", uin);
602 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
604 if (icq_ExtInfoReply)
605 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
606 icq_AckSrv(Chars_2_Word(pak.head.seq));
609 void icq_HandleSearchReply(srv_net_icq_pak pak)
611 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
615 uin = Chars_2_DW(&pak.data[2]);
616 len = Chars_2_Word(&pak.data[6]);
618 icq_RusConv("wk", ptr1);
619 tmp = &pak.data[8 + len];
620 len = Chars_2_Word(tmp);
622 icq_RusConv("wk", ptr2);
624 len = Chars_2_Word(tmp);
626 icq_RusConv("wk", ptr3);
628 len = Chars_2_Word(tmp);
630 icq_RusConv("wk", ptr4);
632 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
633 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");
634 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
637 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
638 icq_AckSrv(Chars_2_Word(pak.head.seq));
641 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
644 char *ptr1, *ptr2, *ptr3, *ptr4;
648 case USER_ADDED_MESS:
649 tmp = strchr(data, '\xFE');
651 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
652 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
659 tmp = strchr(tmp, '\xFE');
661 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
662 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
666 icq_RusConv("wk", data);
670 tmp = strchr(tmp, '\xFE');
672 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
673 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
677 icq_RusConv("wk", data);
681 tmp = strchr(tmp, '\xFE');
683 icq_RusConv("wk", data);
684 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
685 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
686 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
687 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
690 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
693 tmp = strchr(data, '\xFE');
698 tmp = strchr(tmp, '\xFE');
700 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
701 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
705 icq_RusConv("wk", data);
709 tmp = strchr(tmp, '\xFE');
711 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
712 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
716 icq_RusConv("wk", data);
720 tmp = strchr(tmp, '\xFE');
722 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
723 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
727 icq_RusConv("wk", data);
731 tmp = strchr(tmp, '\xFE');
733 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
734 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
740 tmp = strchr(tmp, '\x00');
742 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
743 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
747 icq_RusConv("wk", data);
748 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
749 sprintf(buf, "%lu has requested your authorization to be added to "
750 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
751 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
752 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
755 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
758 tmp = strchr(data, '\xFE');
760 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
761 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
765 icq_RusConv("wk", data);
769 icq_RusConv("wk", data);
770 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
771 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
772 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
775 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
778 icq_RusConv("wk", data);
779 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
780 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
781 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
784 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
788 /**********************************
789 Connects to hostname on port port
790 hostname can be DNS or nnn.nnn.nnn.nnn
791 write out messages to the FD aux
792 ***********************************/
793 int icq_Connect(const char *hostname, int port)
795 char buf[1024], un = 1;
796 char our_host[256], tmpbuf[256];
797 int conct, length, res;
798 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
799 struct hostent *host_struct; /* used in DNS llokup */
801 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
802 if ((ThisICQ->icq_Sok) == -1) {
803 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
804 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
807 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
808 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
809 sin.sin_addr.s_addr = INADDR_ANY;
810 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
812 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
813 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
814 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
817 length = sizeof(sin);
818 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
819 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
820 if ((ThisICQ->icq_UseProxy)) {
821 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
822 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
823 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
824 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
825 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
826 if (host_struct == 0L) {
827 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
828 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
829 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
833 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
835 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
836 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
837 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
838 if ((ThisICQ->icq_ProxySok) == -1) {
839 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
840 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
843 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
844 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
845 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
846 if (conct == -1) { /* did we connect ? */
847 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
848 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
851 buf[0] = 5; /* protocol version */
852 buf[1] = 1; /* number of methods */
853 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
854 buf[2] = 0; /* no authorization required */
856 buf[2] = 2; /* method username/password */
857 write((ThisICQ->icq_ProxySok), buf, 3);
858 res = read((ThisICQ->icq_ProxySok), buf, 2);
859 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
860 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
861 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
862 close((ThisICQ->icq_ProxySok));
865 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
866 buf[0] = 1; /* version of subnegotiation */
867 buf[1] = strlen((ThisICQ->icq_ProxyName));
868 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
869 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
870 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
871 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
872 res = read((ThisICQ->icq_ProxySok), buf, 2);
873 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
874 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
875 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
876 close((ThisICQ->icq_ProxySok));
880 buf[0] = 5; /* protocol version */
881 buf[1] = 3; /* command UDP associate */
882 buf[2] = 0; /* reserved */
883 buf[3] = 1; /* address type IP v4 */
888 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
889 write((ThisICQ->icq_ProxySok), buf, 10);
890 res = read((ThisICQ->icq_ProxySok), buf, 10);
891 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
894 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
895 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
898 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
899 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
902 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
903 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
906 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
907 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
910 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
911 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
914 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
915 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
918 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
919 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
922 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
923 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
926 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
927 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
930 close((ThisICQ->icq_ProxySok));
934 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
935 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
936 host_struct = gethostbyname(hostname);
937 if (host_struct == 0L) {
938 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
939 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
940 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
942 if ((ThisICQ->icq_UseProxy))
943 close((ThisICQ->icq_ProxySok));
946 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
948 if ((ThisICQ->icq_UseProxy)) {
949 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
950 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
952 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
953 sin.sin_port = htons(port); /* port */
954 if ((ThisICQ->icq_UseProxy)) {
955 (ThisICQ->icq_ProxyDestPort) = htons(port);
956 memcpy(&sin.sin_port, &buf[8], 2);
958 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
959 if (conct == -1) { /* did we connect ? */
960 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
961 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
962 if ((ThisICQ->icq_UseProxy))
963 close((ThisICQ->icq_ProxySok));
966 length = sizeof(sin);
967 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
968 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
969 (ThisICQ->icq_OurPort) = sin.sin_port;
970 return (ThisICQ->icq_Sok);
973 void icq_HandleProxyResponse()
977 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
979 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
980 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
981 if (icq_Disconnected)
982 (*icq_Disconnected) ();
983 SOCKCLOSE((ThisICQ->icq_Sok));
984 SOCKCLOSE((ThisICQ->icq_ProxySok));
988 /******************************************
989 Handles packets that the server sends to us.
990 *******************************************/
991 void icq_HandleServerResponse()
994 SIMPLE_MESSAGE *s_mesg;
995 RECV_MESSAGE *r_mesg;
1002 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1004 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1005 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1006 if (icq_Disconnected)
1007 (*icq_Disconnected) ();
1008 SOCKCLOSE((ThisICQ->icq_Sok));
1010 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)) {
1011 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1012 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1013 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1014 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1016 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1020 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1021 icq_SetServMess(Chars_2_Word(pak.head.seq));
1022 switch (Chars_2_Word(pak.head.cmd)) {
1024 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1025 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1027 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1030 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1031 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1032 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1035 case SRV_LOGIN_REPLY:
1036 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1037 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1038 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]);
1039 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1041 icq_AckSrv(Chars_2_Word(pak.head.seq));
1043 icq_SendContactList();
1044 icq_SendVisibleList();
1048 case SRV_RECV_MESSAGE:
1049 r_mesg = (RECV_MESSAGE *) pak.data;
1050 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));
1051 icq_AckSrv(Chars_2_Word(pak.head.seq));
1053 case SRV_X1: /* unknown message sent after login */
1054 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1055 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1056 icq_AckSrv(Chars_2_Word(pak.head.seq));
1058 case SRV_X2: /* unknown message sent after login */
1059 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1060 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1061 icq_AckSrv(Chars_2_Word(pak.head.seq));
1062 icq_SendGotMessages();
1064 case SRV_INFO_REPLY:
1065 icq_HandleInfoReply(pak);
1067 case SRV_EXT_INFO_REPLY:
1068 icq_HandleExtInfoReply(pak);
1070 case SRV_USER_ONLINE:
1071 icq_HandleUserOnline(pak);
1073 case SRV_USER_OFFLINE:
1074 icq_HandleUserOffline(pak);
1077 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1078 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1079 icq_Login((ThisICQ->icq_Status));
1081 case SRV_STATUS_UPDATE:
1082 icq_Status_Update(pak);
1085 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1086 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1087 if (icq_Disconnected)
1088 (*icq_Disconnected) ();
1090 case SRV_END_OF_SEARCH:
1091 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1092 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1094 (*icq_SearchDone) ();
1095 icq_AckSrv(Chars_2_Word(pak.head.seq));
1097 case SRV_USER_FOUND:
1098 icq_HandleSearchReply(pak);
1100 case SRV_SYS_DELIVERED_MESS:
1101 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1102 cur_time = time(0L);
1103 tm_str = localtime(&cur_time);
1104 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);
1105 icq_AckSrv(Chars_2_Word(pak.head.seq));
1107 default: /* commands we dont handle yet */
1108 len = s - (sizeof(pak.head));
1109 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1110 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1111 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1112 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1114 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1119 void icq_Init(DWORD uin, const char *password)
1121 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1122 (ThisICQ->icq_Uin) = uin;
1123 if ((ThisICQ->icq_Password))
1124 free((ThisICQ->icq_Password));
1125 (ThisICQ->icq_Password) = strdup(password);
1130 if ((ThisICQ->icq_Password))
1131 free((ThisICQ->icq_Password));
1134 /******************************
1135 Main function connects gets (ThisICQ->icq_Uin)
1136 and (ThisICQ->icq_Password) and logins in and sits
1137 in a loop waiting for server responses.
1138 *******************************/
1150 FD_SET((ThisICQ->icq_Sok), &readfds);
1151 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1152 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1153 icq_HandleServerResponse();
1157 } while (did_something);
1160 /********************************************************
1161 Russian language ICQ fix.
1162 Usual Windows ICQ users do use Windows 1251 encoding but
1163 unix users do use koi8 encoding, so we need to convert it.
1164 This function will convert string from windows 1251 to koi8
1165 or from koi8 to windows 1251.
1166 Andrew Frolov dron@ilm.net
1167 *********************************************************/
1168 void icq_RusConv(const char to[4], char *t_in)
1173 /* 6-17-1998 by Linux_Dude
1174 * Moved initialization of table out front of 'if' block to prevent compiler
1175 * warning. Improved error message, and now return without performing string
1176 * conversion to prevent addressing memory out of range (table pointer would
1177 * previously have remained uninitialized (= bad)).
1181 if (strcmp(to, "kw") == 0)
1183 else if (strcmp(to, "wk") != 0) {
1184 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1185 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1188 /* End Linux_Dude's changes ;) */
1190 if ((ThisICQ->icq_Russian)) {
1191 for (i = 0; t_in[i] != 0; i++) {
1194 t_in[i] = table[t_in[i] & 0177];
1199 /**************************************************
1200 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1202 ***************************************************/
1203 WORD icq_SendMessage(DWORD uin, const char *text)
1208 char buf[512]; /* message may be only 450 bytes long */
1210 strncpy(buf, text, 512);
1211 icq_RusConv("kw", buf);
1213 Word_2_Chars(pak.head.ver, ICQ_VER);
1214 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1215 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1216 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1217 DW_2_Chars(msg.uin, uin);
1218 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1219 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1220 memcpy(&pak.data, &msg, sizeof(msg));
1221 memcpy(&pak.data[8], buf, len + 1);
1222 size = sizeof(msg) + len + 1;
1223 for (i = 0; i < 6; i++)
1224 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1225 return (ThisICQ->icq_SeqNum) - 1;
1228 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1232 int size, len1, len2;
1233 char buf1[512], buf2[512];
1235 strncpy(buf1, descr, 512);
1236 strncpy(buf2, url, 512);
1237 /* Do we need to convert URL? */
1238 icq_RusConv("kw", buf2);
1239 len1 = strlen(buf1);
1240 len2 = strlen(buf2);
1241 Word_2_Chars(pak.head.ver, ICQ_VER);
1242 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1243 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1244 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1245 DW_2_Chars(msg.uin, uin);
1246 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1247 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1248 memcpy(&pak.data, &msg, sizeof(msg));
1249 memcpy(&pak.data[8], buf1, len1);
1250 pak.data[8 + len1] = 0xFE;
1251 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1252 size = sizeof(msg) + len1 + len2 + 2;
1253 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1254 return (ThisICQ->icq_SeqNum) - 1;
1257 /**************************************************
1258 Sends a authorization to the server so the Mirabilis
1259 client can add the user.
1260 ***************************************************/
1261 void icq_SendAuthMsg(DWORD uin)
1267 Word_2_Chars(pak.head.ver, ICQ_VER);
1268 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1269 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1270 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1271 DW_2_Chars(msg.uin, uin);
1272 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1273 Word_2_Chars(msg.len, 2);
1274 memcpy(&pak.data, &msg, sizeof(msg));
1275 pak.data[sizeof(msg)] = 0x03;
1276 pak.data[sizeof(msg) + 1] = 0x00;
1277 size = sizeof(msg) + 2;
1278 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1281 /**************************************************
1282 Changes the users status on the server
1283 ***************************************************/
1284 void icq_ChangeStatus(DWORD status)
1289 Word_2_Chars(pak.head.ver, ICQ_VER);
1290 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1291 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1292 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1293 DW_2_Chars(pak.data, status);
1294 (ThisICQ->icq_Status) = status;
1296 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1299 /**********************
1301 ***********************/
1307 Word_2_Chars(pak.head.ver, ICQ_VER);
1308 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1309 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1310 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1311 len = strlen("B_USER_DISCONNECTED") + 1;
1312 *(short *) pak.data = len;
1314 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1315 pak.data[2 + len] = 05;
1316 pak.data[3 + len] = 00;
1317 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1320 void icq_Disconnect()
1322 SOCKCLOSE((ThisICQ->icq_Sok));
1323 SOCKCLOSE((ThisICQ->icq_Sok));
1324 if ((ThisICQ->icq_UseProxy))
1325 SOCKCLOSE((ThisICQ->icq_ProxySok));
1328 /********************************************************
1329 Sends a request to the server for info on a specific user
1330 *********************************************************/
1331 WORD icq_SendInfoReq(DWORD uin)
1336 Word_2_Chars(pak.head.ver, ICQ_VER);
1337 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1338 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1339 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1340 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1341 DW_2_Chars(&pak.data[2], uin);
1343 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1344 return (ThisICQ->icq_SeqNum) - 1;
1347 /********************************************************
1348 Sends a request to the server for info on a specific user
1349 *********************************************************/
1350 WORD icq_SendExtInfoReq(DWORD uin)
1355 Word_2_Chars(pak.head.ver, ICQ_VER);
1356 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1357 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1358 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1359 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1360 DW_2_Chars(&pak.data[2], uin);
1362 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1363 return (ThisICQ->icq_SeqNum) - 1;
1366 /**************************************************************
1367 Initializes a server search for the information specified
1368 ***************************************************************/
1369 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1374 Word_2_Chars(pak.head.ver, ICQ_VER);
1375 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1376 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1377 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1378 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1380 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1382 strcpy(pak.data + size, nick);
1383 size += strlen(nick) + 1;
1384 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1386 strcpy(pak.data + size, first);
1387 size += strlen(first) + 1;
1388 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1390 strcpy(pak.data + size, last);
1391 size += strlen(last) + 1;
1392 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1394 strcpy(pak.data + size, email);
1395 size += strlen(email) + 1;
1396 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1399 /**************************************************************
1400 Initializes a server search for the information specified
1401 ***************************************************************/
1402 void icq_SendSearchUINReq(DWORD uin)
1407 Word_2_Chars(pak.head.ver, ICQ_VER);
1408 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1409 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1410 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1411 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1413 DW_2_Chars(&pak.data[size], uin);
1415 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1418 /**************************************************
1419 Registers a new uin in the ICQ network
1420 ***************************************************/
1421 void icq_RegNewUser(const char *pass)
1423 srv_net_icq_pak pak;
1428 Word_2_Chars(pak.head.ver, ICQ_VER);
1429 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1430 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1431 Word_2_Chars(len_buf, len);
1432 memcpy(&pak.data, "\x02\x00", 2);
1433 memcpy(&pak.data[2], &len_buf, 2);
1434 memcpy(&pak.data[4], pass, len + 1);
1435 DW_2_Chars(&pak.data[4 + len], 0x0072);
1436 DW_2_Chars(&pak.data[8 + len], 0x0000);
1438 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1441 void icq_UpdateUserInfo(USER_INFO * user)
1446 Word_2_Chars(pak.head.ver, ICQ_VER);
1447 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1448 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1449 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1450 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1452 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1454 strcpy(pak.data + size, user->nick);
1455 size += strlen(user->nick) + 1;
1456 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1458 strcpy(pak.data + size, user->first);
1459 size += strlen(user->first) + 1;
1460 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1462 strcpy(pak.data + size, user->last);
1463 size += strlen(user->last) + 1;
1464 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1466 strcpy(pak.data + size, user->email);
1467 size += strlen(user->email) + 1;
1468 pak.data[size] = user->auth;
1470 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1473 const char *icq_GetCountryName(int code)
1477 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1478 if (Country_Codes[i].code == code) {
1479 return Country_Codes[i].name;
1482 if (Country_Codes[i].code == code) {
1483 return Country_Codes[i].name;
1488 /********************************************
1489 returns a string describing the status or
1490 a "Error" if no such string exists
1491 *********************************************/
1492 const char *icq_ConvertStatus2Str(int status)
1494 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1497 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1498 switch (status & 0x01FF) {
1503 return "Do not disturb";
1508 case STATUS_OCCUPIED:
1512 return "Not available";
1514 case STATUS_INVISIBLE:
1517 case STATUS_INVISIBLE_2:
1518 return "Invisible mode 2";
1520 case STATUS_FREE_CHAT:
1521 return "Free for chat";
1529 void icq_InitNewUser(const char *hostname, DWORD port)
1531 srv_net_icq_pak pak;
1536 icq_Connect(hostname, port);
1537 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1538 printf("Couldn't establish connection\n");
1541 icq_RegNewUser((ThisICQ->icq_Password));
1544 tv.tv_usec = 500000;
1547 FD_SET((ThisICQ->icq_Sok), &readfds);
1549 /* don't care about writefds and exceptfds: */
1550 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1552 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1553 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1554 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1555 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1562 /********************************************
1563 Converts an intel endian character sequence to
1565 *********************************************/
1566 DWORD Chars_2_DW(unsigned char *buf)
1581 /********************************************
1582 Converts an intel endian character sequence to
1584 *********************************************/
1585 WORD Chars_2_Word(unsigned char *buf)
1596 /********************************************
1598 an intel endian character sequence
1599 *********************************************/
1600 void DW_2_Chars(unsigned char *buf, DWORD num)
1602 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1603 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1604 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1605 buf[0] = (unsigned char) (num) & 0x000000FF;
1608 /********************************************
1610 an intel endian character sequence
1611 *********************************************/
1612 void Word_2_Chars(unsigned char *buf, WORD num)
1614 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1615 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1618 /***************************
1619 ContactList functions
1620 ***************************/
1621 void icq_ContAddUser(DWORD cuin)
1623 icq_ContactItem *p = malloc(sizeof(icq_ContactItem));
1624 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1627 p->vis_list = FALSE;
1633 (ThisICQ->icq_ContFirst) = p;
1636 void icq_ContDelUser(DWORD cuin)
1638 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1641 if (ptr->uin == cuin) {
1642 (ThisICQ->icq_ContFirst) = ptr->next;
1644 ptr = (ThisICQ->icq_ContFirst);
1647 if (ptr->next->uin == cuin) {
1648 ptr->next = ptr->next->next;
1655 void icq_ContClear()
1657 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1662 (ThisICQ->icq_ContFirst) = ptr;
1666 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1668 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1672 if (ptr->uin == cuin)
1679 icq_ContactItem *icq_ContGetFirst()
1681 return (ThisICQ->icq_ContFirst);
1684 void icq_ContSetVis(DWORD cuin)
1686 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1690 if (ptr->uin == cuin)
1691 ptr->vis_list = TRUE;
1696 /************************
1697 (ThisICQ->icq_ServMess) functions
1698 *************************/
1699 BOOL icq_GetServMess(WORD num)
1701 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1704 void icq_SetServMess(WORD num)
1706 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1711 return (ThisICQ->icq_Sok);
1714 int icq_GetProxySok()
1716 return (ThisICQ->icq_ProxySok);
1719 /*******************
1720 SOCKS5 Proxy support
1721 ********************/
1722 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1724 if ((ThisICQ->icq_ProxyHost))
1725 free((ThisICQ->icq_ProxyHost));
1726 if ((ThisICQ->icq_ProxyName))
1727 free((ThisICQ->icq_ProxyName));
1728 if ((ThisICQ->icq_ProxyPass))
1729 free((ThisICQ->icq_ProxyPass));
1730 if (strlen(pname) > 255) {
1731 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1732 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1733 ThisICQ->icq_UseProxy = 0;
1736 if (strlen(ppass) > 255) {
1737 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1738 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1739 (ThisICQ->icq_UseProxy) = 0;
1742 (ThisICQ->icq_UseProxy) = 1;
1743 (ThisICQ->icq_ProxyHost) = strdup(phost);
1744 (ThisICQ->icq_ProxyPort) = pport;
1745 (ThisICQ->icq_ProxyAuth) = pauth;
1746 (ThisICQ->icq_ProxyName) = strdup(pname);
1747 (ThisICQ->icq_ProxyPass) = strdup(ppass);
1750 void icq_UnsetProxy()
1752 ThisICQ->icq_UseProxy = 0;
1757 /***********************************************************************/
1758 /* icqlib stuff ends here, Citadel module stuff begins */
1759 /***********************************************************************/
1762 /* config file manipulation routines ... probably temporary until we can
1763 * get something more robust written
1767 void CtdlICQ_Config_Delete(char *key) {
1768 long readpos, writepos;
1769 char buf[256], keyplusspace[256];
1772 if (ThisICQ->icq_MyConfigFile == NULL) return;
1775 sprintf(keyplusspace, "%s ", key);
1778 fseek(ThisICQ->icq_MyConfigFile, readpos, 0);
1779 ptr = fgets(buf, 256, ThisICQ->icq_MyConfigFile);
1780 readpos = ftell(ThisICQ->icq_MyConfigFile);
1782 fflush(ThisICQ->icq_MyConfigFile);
1783 ftruncate(fileno(ThisICQ->icq_MyConfigFile), writepos);
1786 if (strncasecmp(buf, keyplusspace,
1787 strlen(keyplusspace))) {
1788 fseek(ThisICQ->icq_MyConfigFile, writepos, 0);
1789 fprintf(ThisICQ->icq_MyConfigFile, "%s", buf);
1790 writepos = ftell(ThisICQ->icq_MyConfigFile);
1798 void CtdlICQ_Config_Write(char *key, char *contents) {
1800 char buf[256]; /* FIX */
1802 CtdlICQ_Config_Delete(key);
1803 fseek(ThisICQ->icq_MyConfigFile, 0L, SEEK_END);
1804 fprintf(ThisICQ->icq_MyConfigFile, "%s %s\n", key, contents);
1806 /****** FIX ****** TEMPORARY HACK TO SEE STUFF ***********/
1808 fflush(ThisICQ->icq_MyConfigFile);
1809 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1810 CtdlWriteObject(ICQROOM, ICQMIME, buf, 1, 0, 1);
1815 void CtdlICQ_Config_Read(char *contents, char *key) {
1818 strcpy(contents, "");
1819 rewind(ThisICQ->icq_MyConfigFile);
1820 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1821 buf[strlen(buf)-1]=0;
1822 if ( (!strncasecmp(buf, key, strlen(key)))
1823 && (isspace(buf[strlen(key)])) ) {
1824 strcpy(contents, &buf[strlen(key)+1]);
1831 * Refresh the contact list
1833 void CtdlICQ_Refresh_Contact_List(void) {
1837 if (ThisICQ->icq_Sok < 0) return;
1840 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1841 buf[strlen(buf)-1]=0;
1843 contact_uin = atol(buf);
1844 if (contact_uin > 0L) {
1845 icq_ContAddUser(contact_uin);
1846 icq_ContSetVis(contact_uin);
1849 icq_SendContactList();
1856 * Utility routine to logout and disconnect from the ICQ server if we happen
1857 * to already be connected
1859 void CtdlICQ_Logout_If_Connected(void) {
1860 if (ThisICQ->icq_Sok >= 0) {
1863 ThisICQ->icq_Sok = (-1);
1869 * If we have an ICQ uin/password on file for this user, go ahead and try
1870 * to log on to the ICQ server.
1872 void CtdlICQ_Login_If_Possible(void) {
1877 CtdlICQ_Logout_If_Connected();
1879 CtdlICQ_Config_Read(buf, "uin");
1881 CtdlICQ_Config_Read(pass, "pass");
1883 if ( (uin > 0L) && (strlen(pass)>0) ) {
1884 icq_Init(uin, pass);
1885 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
1886 icq_Login(STATUS_ONLINE);
1887 CtdlICQ_Refresh_Contact_List();
1894 /* This merely hooks icqlib's log function into Citadel's log function. */
1895 void CtdlICQlog(time_t time, unsigned char level, const char *str)
1897 lprintf(level, "ICQ: %s", str);
1902 * At the start of each Citadel server session, we have to allocate some
1903 * space for the Citadel-ICQ session for this thread. It'll be automatically
1904 * freed by the server when the session ends.
1906 void CtdlICQ_session_startup_hook(void)
1908 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
1909 icq_init_handle(ThisICQ);
1914 * icq_Main() needs to be called as frequently as possible. We'll do it
1915 * following the completion of each Citadel server command.
1918 void CtdlICQ_after_cmd_hook(void)
1920 if (ThisICQ->icq_Sok >= 0) {
1921 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
1923 ThisICQ->icq_LastKeepAlive = time(NULL);
1931 * There are a couple of things we absolutely need to make sure of when we
1932 * kill off the session. One of them is that the sockets are all closed --
1933 * otherwise we could have a nasty file descriptor leak.
1935 void CtdlICQ_session_logout_hook(void)
1937 CtdlICQ_Logout_If_Connected();
1938 if (ThisICQ->icq_MyConfigFile != NULL)
1939 fclose(ThisICQ->icq_MyConfigFile);
1945 void CtdlICQ_session_login_hook(void)
1948 if (ThisICQ->icq_MyConfigFile != NULL)
1949 fclose(ThisICQ->icq_MyConfigFile);
1951 /* Open this user's ICQ config file; create it if it's not there */
1952 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1953 ThisICQ->icq_MyConfigFile = fopen(buf, "r+");
1954 if (ThisICQ->icq_MyConfigFile == NULL)
1955 ThisICQ->icq_MyConfigFile = fopen(buf, "w+");
1956 if (ThisICQ->icq_MyConfigFile == NULL)
1957 lprintf(2, "Cannot create %s: %s\n", buf, strerror(errno));
1959 /* Login to the ICQ server if we already have info on file. */
1960 CtdlICQ_Login_If_Possible();
1964 void cmd_icqa(char *argbuf) {
1967 extract(cuin, argbuf, 0);
1968 if (atol(cuin) > 0L) {
1969 CtdlICQ_Config_Write(cuin, "");
1970 icq_SendInfoReq(atol(cuin));
1971 cprintf("%d %s added to your ICQ contact list.\n", OK, cuin);
1973 cprintf("%d You must specify a numeric ICQ uin.\n", ERROR);
1976 CtdlICQ_Refresh_Contact_List();
1982 void cmd_icql(char *argbuf)
1987 extract(uin, argbuf, 0);
1988 extract(password, argbuf, 1);
1990 CtdlICQ_Config_Write("uin", uin);
1991 CtdlICQ_Config_Write("pass", password);
1993 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
1994 CtdlICQ_Login_If_Possible();
1999 * When deleting a user from the server, be sure to delete
2000 * his/her/its ICQ config file as well.
2002 void CtdlICQ_DeleteUserConfigFile(char *uname, long unum) {
2005 sprintf(buf, "icq/%ld", unum);
2012 * Here's what we have to do when an ICQ message arrives!
2014 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2015 BYTE day, BYTE month, WORD year,
2021 sprintf(from, "%ld", uin);
2022 CtdlICQ_Config_Read(nick, from);
2023 if (strlen(nick) == 0) {
2024 icq_SendInfoReq(atol(from));
2027 sprintf(from, "%ld@icq (%s)", uin, nick);
2028 if (CtdlSendExpressMessageFunc) {
2029 CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
2034 lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
2040 CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2041 const char *first, const char *last,
2042 const char *email, char auth) {
2046 sprintf(str_uin, "%ld", uin);
2047 CtdlICQ_Config_Write(str_uin, nick);
2052 char *Dynamic_Module_Init(void)
2054 /* Create a directory to store ICQ stuff in.
2055 * It's ok if the directory is already there.
2057 if (mkdir("icq", 0700) != 0) if (errno != EEXIST) {
2058 lprintf(2, "Can't create icq subdirectory: %s\n",
2062 /* Make sure we've got a valid ThisICQ for each session */
2063 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2065 /* Tell the Citadel server about our wonderful ICQ hooks */
2066 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2067 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2068 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2069 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2070 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2071 CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add an ICQ contact");
2072 CtdlRegisterUserHook(CtdlICQ_DeleteUserConfigFile, EVT_PURGEUSER);
2074 /* Tell the code formerly known as icqlib about our callbacks */
2075 icq_Log = CtdlICQlog;
2076 icq_RecvMessage = CtdlICQ_Incoming_Message;
2077 icq_InfoReply = CtdlICQ_InfoReply;