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>
40 #include "dynloader.h"
42 #include "citserver.h"
45 struct ctdl_icq_handle {
48 BYTE icq_ServMess[8192];
53 icq_ContactItem *icq_ContFirst;
64 DWORD icq_ProxyDestHost;
65 WORD icq_ProxyDestPort;
66 WORD icq_ProxyOurPort;
67 time_t icq_LastKeepAlive; /* ig */
68 char icq_config[256]; /* ig */
73 /* ICQROOM is the name of the room in which each user's ICQ configuration
74 * and contact lists will be stored. (It's a personal room.)
76 #define ICQROOM "My ICQ Config"
78 /* MIME type to use for storing a user's ICQ uin, password, etc. */
79 #define ICQMIME "application/x-citadel-icq"
81 /* Citadel server TSD symbol for use by serv_icq */
82 unsigned long SYM_CTDL_ICQ;
83 #define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
85 extern struct CitContext *ContextList;
88 void (*icq_Logged) (void);
89 void (*icq_Disconnected) (void);
90 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
91 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
92 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);
93 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);
94 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
95 void (*icq_SearchDone) (void);
96 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
97 void (*icq_UserOffline) (DWORD uin);
98 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
99 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
100 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);
101 void (*icq_Log) (time_t time, unsigned char level, const char *str);
102 void (*icq_SrvAck) (WORD seq);
105 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
106 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
107 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
108 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
109 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
110 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
111 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
112 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
115 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
116 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
117 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
118 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
119 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
120 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
121 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
122 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
124 static COUNTRY_CODE Country_Codes[] =
139 {"American Samoa", 684},
154 {"Czech Republic", 42},
157 {"El Salvador", 503},
161 {"French Antilles", 596},
162 {"French Polynesia", 689},
169 {"Guantanomo Bay", 53},
183 {"Ivory Coast", 225},
191 {"Liechtenstein", 41},
203 {"Netherlands Antilles", 599},
204 {"New Caledonia", 687},
211 {"Papua New Guinea", 675},
221 {"Saudia Arabia", 966},
226 {"South Africa", 27},
237 {"United Arab Emirates", 971},
239 {"Vatican City", 39},
246 {"Not entered", 0xffff}};
248 void icq_init_handle(struct ctdl_icq_handle *i)
250 memset(i, 0, sizeof(struct ctdl_icq_handle));
251 i->icq_Russian = TRUE;
253 i->icq_OurIp = 0x0100007f;
254 i->icq_Status = STATUS_OFFLINE;
259 int icq_SockWrite(int sok, const void *buf, size_t count)
262 if (!(ThisICQ->icq_UseProxy))
263 return write(sok, buf, count);
265 tmpbuf[0] = 0; /* reserved */
266 tmpbuf[1] = 0; /* reserved */
267 tmpbuf[2] = 0; /* standalone packet */
268 tmpbuf[3] = 1; /* address type IP v4 */
269 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
270 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
271 memcpy(&tmpbuf[10], buf, count);
272 return write(sok, tmpbuf, count + 10) - 10;
276 int icq_SockRead(int sok, void *buf, size_t count)
280 if (!(ThisICQ->icq_UseProxy))
281 return read(sok, buf, count);
283 res = read(sok, tmpbuf, count + 10);
284 memcpy(buf, &tmpbuf[10], res - 10);
289 /****************************************
290 This must be called every 2 min.
291 so the server knows we're still alive.
292 JAVA client sends two different commands
294 *****************************************/
299 Word_2_Chars(pak.head.ver, ICQ_VER);
300 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
301 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
302 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
303 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
305 Word_2_Chars(pak.head.ver, ICQ_VER);
306 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
307 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
308 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
309 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
311 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
312 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
315 /**********************************
316 This must be called to remove
317 messages from the server
318 ***********************************/
319 void icq_SendGotMessages()
323 Word_2_Chars(pak.head.ver, ICQ_VER);
324 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
325 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
326 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
328 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
331 /*************************************
332 this sends over the contact list
333 *************************************/
334 void icq_SendContactList()
340 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
342 Word_2_Chars(pak.head.ver, ICQ_VER);
343 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
344 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
345 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
351 DW_2_Chars(tmp, ptr->uin);
356 pak.data[0] = num_used;
357 size = ((int) tmp - (int) pak.data);
358 size += sizeof(pak.head);
359 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
362 void icq_SendNewUser(unsigned long uin)
367 Word_2_Chars(pak.head.ver, ICQ_VER);
368 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
369 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
370 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
371 DW_2_Chars(pak.data, uin);
372 size = sizeof(pak.head) + 4;
373 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
376 /*************************************
377 this sends over the visible list
378 that allows certain users to see you
380 *************************************/
381 void icq_SendVisibleList()
387 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
389 Word_2_Chars(pak.head.ver, ICQ_VER);
390 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
391 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
392 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
399 DW_2_Chars(tmp, ptr->uin);
406 pak.data[0] = num_used;
407 size = ((int) tmp - (int) pak.data);
408 size += sizeof(pak.head);
409 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
413 /**************************************
414 This sends the second login command
415 this is necessary to finish logging in.
416 ***************************************/
417 void icq_SendLogin1()
421 Word_2_Chars(pak.head.ver, ICQ_VER);
422 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
423 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
424 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
426 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
429 /************************************************
430 This is called when a user goes offline
431 *************************************************/
432 void icq_HandleUserOffline(srv_net_icq_pak pak)
437 remote_uin = Chars_2_DW(pak.data);
438 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
439 sprintf(buf, "User %lu logged off\n", remote_uin);
440 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
443 (*icq_UserOffline) (remote_uin);
444 icq_AckSrv(Chars_2_Word(pak.head.seq));
447 void icq_HandleUserOnline(srv_net_icq_pak pak)
449 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
450 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
451 icq_ContactItem *ptr;
454 remote_uin = Chars_2_DW(pak.data);
455 new_status = Chars_2_DW(&pak.data[17]);
456 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
457 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
458 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
459 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
460 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
461 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
464 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
465 icq_AckSrv(Chars_2_Word(pak.head.seq));
468 void icq_Status_Update(srv_net_icq_pak pak)
470 unsigned long remote_uin, new_status;
473 remote_uin = Chars_2_DW(pak.data);
474 new_status = Chars_2_DW(&pak.data[4]);
475 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
476 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
477 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
479 if (icq_UserStatusUpdate)
480 (*icq_UserStatusUpdate) (remote_uin, new_status);
481 icq_AckSrv(Chars_2_Word(pak.head.seq));
484 /************************************
485 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
486 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
487 It does NOT wait for any kind of a response.
488 *************************************/
489 void icq_Login(DWORD status)
496 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
497 Word_2_Chars(pak.head.ver, ICQ_VER);
498 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
499 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
500 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
502 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
503 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
505 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
506 DW_2_Chars(s2.status, status);
507 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
509 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
510 s2.X2[0] = LOGIN_X2_DEF;
511 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
512 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
513 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
515 memcpy(pak.data, &s1, sizeof(s1));
517 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
518 size += Chars_2_Word(s1.len);
519 memcpy(&pak.data[size], &s2, sizeof(s2));
521 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
524 /*******************************
525 This routine sends the aknowlegement cmd to the
526 server it appears that this must be done after
527 everything the server sends us
528 *******************************/
529 void icq_AckSrv(int seq)
534 Word_2_Chars(pak.head.ver, ICQ_VER);
535 Word_2_Chars(pak.head.cmd, CMD_ACK);
536 Word_2_Chars(pak.head.seq, seq);
537 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
538 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
539 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
540 for (i = 0; i < 6; i++)
541 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
544 void icq_HandleInfoReply(srv_net_icq_pak pak)
546 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
550 seq = Chars_2_Word(pak.data);
551 uin = Chars_2_DW(&pak.data[2]);
552 len = Chars_2_Word(&pak.data[6]);
554 icq_RusConv("wk", ptr1);
555 tmp = &pak.data[8 + len];
556 len = Chars_2_Word(tmp);
558 icq_RusConv("wk", ptr2);
560 len = Chars_2_Word(tmp);
562 icq_RusConv("wk", ptr3);
564 len = Chars_2_Word(tmp);
566 icq_RusConv("wk", ptr4);
568 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
569 sprintf(buf, "Info reply for %lu\n", uin);
570 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
573 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
574 icq_AckSrv(Chars_2_Word(pak.head.seq));
577 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
579 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
583 char cnt_stat, gender, buf[256];
584 uin = Chars_2_DW(&pak.data[2]);
585 len = Chars_2_Word(&pak.data[6]);
587 icq_RusConv("wk", ptr1);
588 cnt_code = Chars_2_Word(&pak.data[8 + len]);
589 cnt_stat = pak.data[len + 10];
590 tmp = &pak.data[11 + len];
591 len = Chars_2_Word(tmp);
592 icq_RusConv("wk", tmp + 2);
594 age = Chars_2_Word(tmp + 2 + len);
595 gender = *(tmp + len + 4);
597 len = Chars_2_Word(tmp);
598 icq_RusConv("wk", tmp + 2);
601 len = Chars_2_Word(tmp);
602 icq_RusConv("wk", tmp + 2);
605 len = Chars_2_Word(tmp);
606 icq_RusConv("wk", tmp + 2);
608 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
609 sprintf(buf, "Extended info reply for %lu\n", uin);
610 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
612 if (icq_ExtInfoReply)
613 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
614 icq_AckSrv(Chars_2_Word(pak.head.seq));
617 void icq_HandleSearchReply(srv_net_icq_pak pak)
619 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
623 uin = Chars_2_DW(&pak.data[2]);
624 len = Chars_2_Word(&pak.data[6]);
626 icq_RusConv("wk", ptr1);
627 tmp = &pak.data[8 + len];
628 len = Chars_2_Word(tmp);
630 icq_RusConv("wk", ptr2);
632 len = Chars_2_Word(tmp);
634 icq_RusConv("wk", ptr3);
636 len = Chars_2_Word(tmp);
638 icq_RusConv("wk", ptr4);
640 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
641 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");
642 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
645 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
646 icq_AckSrv(Chars_2_Word(pak.head.seq));
649 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
652 char *ptr1, *ptr2, *ptr3, *ptr4;
656 case USER_ADDED_MESS:
657 tmp = strchr(data, '\xFE');
659 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
660 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
667 tmp = strchr(tmp, '\xFE');
669 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
670 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
674 icq_RusConv("wk", data);
678 tmp = strchr(tmp, '\xFE');
680 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
681 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
685 icq_RusConv("wk", data);
689 tmp = strchr(tmp, '\xFE');
691 icq_RusConv("wk", data);
692 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
693 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
694 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
695 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
698 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
701 tmp = strchr(data, '\xFE');
706 tmp = strchr(tmp, '\xFE');
708 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
709 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
713 icq_RusConv("wk", data);
717 tmp = strchr(tmp, '\xFE');
719 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
720 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
724 icq_RusConv("wk", data);
728 tmp = strchr(tmp, '\xFE');
730 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
731 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
735 icq_RusConv("wk", data);
739 tmp = strchr(tmp, '\xFE');
741 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
742 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
748 tmp = strchr(tmp, '\x00');
750 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
751 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
755 icq_RusConv("wk", data);
756 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
757 sprintf(buf, "%lu has requested your authorization to be added to "
758 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
759 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
760 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
763 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
766 tmp = strchr(data, '\xFE');
768 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
769 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
773 icq_RusConv("wk", data);
777 icq_RusConv("wk", data);
778 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
779 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
780 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
783 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
786 icq_RusConv("wk", data);
787 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
788 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
789 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
792 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
796 /**********************************
797 Connects to hostname on port port
798 hostname can be DNS or nnn.nnn.nnn.nnn
799 write out messages to the FD aux
800 ***********************************/
801 int icq_Connect(const char *hostname, int port)
803 char buf[1024], un = 1;
804 char our_host[256], tmpbuf[256];
805 int conct, length, res;
806 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
807 struct hostent *host_struct; /* used in DNS llokup */
809 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
810 if ((ThisICQ->icq_Sok) == -1) {
811 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
812 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
815 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
816 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
817 sin.sin_addr.s_addr = INADDR_ANY;
818 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
820 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
821 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
822 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
825 length = sizeof(sin);
826 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
827 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
828 if ((ThisICQ->icq_UseProxy)) {
829 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
830 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
831 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
832 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
833 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
834 if (host_struct == 0L) {
835 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
836 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
837 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
841 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
843 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
844 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
845 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
846 if ((ThisICQ->icq_ProxySok) == -1) {
847 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
848 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
851 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
852 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
853 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
854 if (conct == -1) { /* did we connect ? */
855 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
856 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
859 buf[0] = 5; /* protocol version */
860 buf[1] = 1; /* number of methods */
861 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
862 buf[2] = 0; /* no authorization required */
864 buf[2] = 2; /* method username/password */
865 write((ThisICQ->icq_ProxySok), buf, 3);
866 res = read((ThisICQ->icq_ProxySok), buf, 2);
867 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
868 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
869 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
870 close((ThisICQ->icq_ProxySok));
873 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
874 buf[0] = 1; /* version of subnegotiation */
875 buf[1] = strlen((ThisICQ->icq_ProxyName));
876 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
877 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
878 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
879 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
880 res = read((ThisICQ->icq_ProxySok), buf, 2);
881 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
882 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
883 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
884 close((ThisICQ->icq_ProxySok));
888 buf[0] = 5; /* protocol version */
889 buf[1] = 3; /* command UDP associate */
890 buf[2] = 0; /* reserved */
891 buf[3] = 1; /* address type IP v4 */
896 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
897 write((ThisICQ->icq_ProxySok), buf, 10);
898 res = read((ThisICQ->icq_ProxySok), buf, 10);
899 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
902 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
903 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
906 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
907 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
910 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
911 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
914 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
915 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
918 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
919 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
922 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
923 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
926 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
927 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
930 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
931 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
934 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
935 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
938 close((ThisICQ->icq_ProxySok));
942 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
943 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
944 host_struct = gethostbyname(hostname);
945 if (host_struct == 0L) {
946 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
947 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
948 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
950 if ((ThisICQ->icq_UseProxy))
951 close((ThisICQ->icq_ProxySok));
954 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
956 if ((ThisICQ->icq_UseProxy)) {
957 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
958 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
960 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
961 sin.sin_port = htons(port); /* port */
962 if ((ThisICQ->icq_UseProxy)) {
963 (ThisICQ->icq_ProxyDestPort) = htons(port);
964 memcpy(&sin.sin_port, &buf[8], 2);
966 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
967 if (conct == -1) { /* did we connect ? */
968 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
969 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
970 if ((ThisICQ->icq_UseProxy))
971 close((ThisICQ->icq_ProxySok));
974 length = sizeof(sin);
975 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
976 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
977 (ThisICQ->icq_OurPort) = sin.sin_port;
978 return (ThisICQ->icq_Sok);
981 void icq_HandleProxyResponse()
985 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
987 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
988 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
989 if (icq_Disconnected)
990 (*icq_Disconnected) ();
991 SOCKCLOSE((ThisICQ->icq_Sok));
992 SOCKCLOSE((ThisICQ->icq_ProxySok));
996 /******************************************
997 Handles packets that the server sends to us.
998 *******************************************/
999 void icq_HandleServerResponse()
1001 srv_net_icq_pak pak;
1002 SIMPLE_MESSAGE *s_mesg;
1003 RECV_MESSAGE *r_mesg;
1010 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1012 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1013 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1014 if (icq_Disconnected)
1015 (*icq_Disconnected) ();
1016 SOCKCLOSE((ThisICQ->icq_Sok));
1018 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)) {
1019 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1020 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1021 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1022 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1024 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1028 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1029 icq_SetServMess(Chars_2_Word(pak.head.seq));
1030 switch (Chars_2_Word(pak.head.cmd)) {
1032 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1033 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1035 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1038 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1039 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1040 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1043 case SRV_LOGIN_REPLY:
1044 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1045 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1046 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]);
1047 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1049 icq_AckSrv(Chars_2_Word(pak.head.seq));
1051 icq_SendContactList();
1052 icq_SendVisibleList();
1056 case SRV_RECV_MESSAGE:
1057 r_mesg = (RECV_MESSAGE *) pak.data;
1058 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));
1059 icq_AckSrv(Chars_2_Word(pak.head.seq));
1061 case SRV_X1: /* unknown message sent after login */
1062 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1063 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1064 icq_AckSrv(Chars_2_Word(pak.head.seq));
1066 case SRV_X2: /* unknown message sent after login */
1067 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1068 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1069 icq_AckSrv(Chars_2_Word(pak.head.seq));
1070 icq_SendGotMessages();
1072 case SRV_INFO_REPLY:
1073 icq_HandleInfoReply(pak);
1075 case SRV_EXT_INFO_REPLY:
1076 icq_HandleExtInfoReply(pak);
1078 case SRV_USER_ONLINE:
1079 icq_HandleUserOnline(pak);
1081 case SRV_USER_OFFLINE:
1082 icq_HandleUserOffline(pak);
1085 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1086 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1087 icq_Login((ThisICQ->icq_Status));
1089 case SRV_STATUS_UPDATE:
1090 icq_Status_Update(pak);
1093 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1094 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1095 if (icq_Disconnected)
1096 (*icq_Disconnected) ();
1098 case SRV_END_OF_SEARCH:
1099 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1100 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1102 (*icq_SearchDone) ();
1103 icq_AckSrv(Chars_2_Word(pak.head.seq));
1105 case SRV_USER_FOUND:
1106 icq_HandleSearchReply(pak);
1108 case SRV_SYS_DELIVERED_MESS:
1109 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1110 cur_time = time(0L);
1111 tm_str = localtime(&cur_time);
1112 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);
1113 icq_AckSrv(Chars_2_Word(pak.head.seq));
1115 default: /* commands we dont handle yet */
1116 len = s - (sizeof(pak.head));
1117 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1118 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1119 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1120 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1122 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1127 void icq_Init(DWORD uin, const char *password)
1129 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1130 (ThisICQ->icq_Uin) = uin;
1131 if ((ThisICQ->icq_Password))
1132 phree((ThisICQ->icq_Password));
1133 (ThisICQ->icq_Password) = strdoop(password);
1138 if ((ThisICQ->icq_Password))
1139 phree((ThisICQ->icq_Password));
1142 /******************************
1143 Main function connects gets (ThisICQ->icq_Uin)
1144 and (ThisICQ->icq_Password) and logins in and sits
1145 in a loop waiting for server responses.
1146 *******************************/
1158 FD_SET((ThisICQ->icq_Sok), &readfds);
1159 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1160 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1161 icq_HandleServerResponse();
1165 } while (did_something);
1168 /********************************************************
1169 Russian language ICQ fix.
1170 Usual Windows ICQ users do use Windows 1251 encoding but
1171 unix users do use koi8 encoding, so we need to convert it.
1172 This function will convert string from windows 1251 to koi8
1173 or from koi8 to windows 1251.
1174 Andrew Frolov dron@ilm.net
1175 *********************************************************/
1176 void icq_RusConv(const char to[4], char *t_in)
1181 /* 6-17-1998 by Linux_Dude
1182 * Moved initialization of table out front of 'if' block to prevent compiler
1183 * warning. Improved error message, and now return without performing string
1184 * conversion to prevent addressing memory out of range (table pointer would
1185 * previously have remained uninitialized (= bad)).
1189 if (strcmp(to, "kw") == 0)
1191 else if (strcmp(to, "wk") != 0) {
1192 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1193 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1196 /* End Linux_Dude's changes ;) */
1198 if ((ThisICQ->icq_Russian)) {
1199 for (i = 0; t_in[i] != 0; i++) {
1202 t_in[i] = table[t_in[i] & 0177];
1207 /**************************************************
1208 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1210 ***************************************************/
1211 WORD icq_SendMessage(DWORD uin, const char *text)
1216 char buf[512]; /* message may be only 450 bytes long */
1218 strncpy(buf, text, 512);
1219 icq_RusConv("kw", buf);
1221 Word_2_Chars(pak.head.ver, ICQ_VER);
1222 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1223 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1224 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1225 DW_2_Chars(msg.uin, uin);
1226 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1227 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1228 memcpy(&pak.data, &msg, sizeof(msg));
1229 memcpy(&pak.data[8], buf, len + 1);
1230 size = sizeof(msg) + len + 1;
1231 for (i = 0; i < 6; i++)
1232 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1233 return (ThisICQ->icq_SeqNum) - 1;
1236 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1240 int size, len1, len2;
1241 char buf1[512], buf2[512];
1243 strncpy(buf1, descr, 512);
1244 strncpy(buf2, url, 512);
1245 /* Do we need to convert URL? */
1246 icq_RusConv("kw", buf2);
1247 len1 = strlen(buf1);
1248 len2 = strlen(buf2);
1249 Word_2_Chars(pak.head.ver, ICQ_VER);
1250 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1251 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1252 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1253 DW_2_Chars(msg.uin, uin);
1254 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1255 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1256 memcpy(&pak.data, &msg, sizeof(msg));
1257 memcpy(&pak.data[8], buf1, len1);
1258 pak.data[8 + len1] = 0xFE;
1259 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1260 size = sizeof(msg) + len1 + len2 + 2;
1261 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1262 return (ThisICQ->icq_SeqNum) - 1;
1265 /**************************************************
1266 Sends a authorization to the server so the Mirabilis
1267 client can add the user.
1268 ***************************************************/
1269 void icq_SendAuthMsg(DWORD uin)
1275 Word_2_Chars(pak.head.ver, ICQ_VER);
1276 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1277 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1278 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1279 DW_2_Chars(msg.uin, uin);
1280 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1281 Word_2_Chars(msg.len, 2);
1282 memcpy(&pak.data, &msg, sizeof(msg));
1283 pak.data[sizeof(msg)] = 0x03;
1284 pak.data[sizeof(msg) + 1] = 0x00;
1285 size = sizeof(msg) + 2;
1286 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1289 /**************************************************
1290 Changes the users status on the server
1291 ***************************************************/
1292 void icq_ChangeStatus(DWORD status)
1297 Word_2_Chars(pak.head.ver, ICQ_VER);
1298 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1299 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1300 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1301 DW_2_Chars(pak.data, status);
1302 (ThisICQ->icq_Status) = status;
1304 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1307 /**********************
1309 ***********************/
1315 Word_2_Chars(pak.head.ver, ICQ_VER);
1316 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1317 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1318 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1319 len = strlen("B_USER_DISCONNECTED") + 1;
1320 *(short *) pak.data = len;
1322 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1323 pak.data[2 + len] = 05;
1324 pak.data[3 + len] = 00;
1325 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1328 void icq_Disconnect()
1330 SOCKCLOSE((ThisICQ->icq_Sok));
1331 SOCKCLOSE((ThisICQ->icq_Sok));
1332 if ((ThisICQ->icq_UseProxy))
1333 SOCKCLOSE((ThisICQ->icq_ProxySok));
1336 /********************************************************
1337 Sends a request to the server for info on a specific user
1338 *********************************************************/
1339 WORD icq_SendInfoReq(DWORD uin)
1344 Word_2_Chars(pak.head.ver, ICQ_VER);
1345 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1346 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1347 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1348 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1349 DW_2_Chars(&pak.data[2], uin);
1351 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1352 return (ThisICQ->icq_SeqNum) - 1;
1355 /********************************************************
1356 Sends a request to the server for info on a specific user
1357 *********************************************************/
1358 WORD icq_SendExtInfoReq(DWORD uin)
1363 Word_2_Chars(pak.head.ver, ICQ_VER);
1364 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1365 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1366 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1367 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1368 DW_2_Chars(&pak.data[2], uin);
1370 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1371 return (ThisICQ->icq_SeqNum) - 1;
1374 /**************************************************************
1375 Initializes a server search for the information specified
1376 ***************************************************************/
1377 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1382 Word_2_Chars(pak.head.ver, ICQ_VER);
1383 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1384 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1385 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1386 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1388 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1390 strcpy(pak.data + size, nick);
1391 size += strlen(nick) + 1;
1392 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1394 strcpy(pak.data + size, first);
1395 size += strlen(first) + 1;
1396 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1398 strcpy(pak.data + size, last);
1399 size += strlen(last) + 1;
1400 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1402 strcpy(pak.data + size, email);
1403 size += strlen(email) + 1;
1404 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1407 /**************************************************************
1408 Initializes a server search for the information specified
1409 ***************************************************************/
1410 void icq_SendSearchUINReq(DWORD uin)
1415 Word_2_Chars(pak.head.ver, ICQ_VER);
1416 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1417 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1418 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1419 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1421 DW_2_Chars(&pak.data[size], uin);
1423 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1426 /**************************************************
1427 Registers a new uin in the ICQ network
1428 ***************************************************/
1429 void icq_RegNewUser(const char *pass)
1431 srv_net_icq_pak pak;
1436 Word_2_Chars(pak.head.ver, ICQ_VER);
1437 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1438 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1439 Word_2_Chars(len_buf, len);
1440 memcpy(&pak.data, "\x02\x00", 2);
1441 memcpy(&pak.data[2], &len_buf, 2);
1442 memcpy(&pak.data[4], pass, len + 1);
1443 DW_2_Chars(&pak.data[4 + len], 0x0072);
1444 DW_2_Chars(&pak.data[8 + len], 0x0000);
1446 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1449 void icq_UpdateUserInfo(USER_INFO * user)
1454 Word_2_Chars(pak.head.ver, ICQ_VER);
1455 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1456 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1457 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1458 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1460 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1462 strcpy(pak.data + size, user->nick);
1463 size += strlen(user->nick) + 1;
1464 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1466 strcpy(pak.data + size, user->first);
1467 size += strlen(user->first) + 1;
1468 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1470 strcpy(pak.data + size, user->last);
1471 size += strlen(user->last) + 1;
1472 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1474 strcpy(pak.data + size, user->email);
1475 size += strlen(user->email) + 1;
1476 pak.data[size] = user->auth;
1478 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1481 const char *icq_GetCountryName(int code)
1485 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1486 if (Country_Codes[i].code == code) {
1487 return Country_Codes[i].name;
1490 if (Country_Codes[i].code == code) {
1491 return Country_Codes[i].name;
1496 /********************************************
1497 returns a string describing the status or
1498 a "Error" if no such string exists
1499 *********************************************/
1500 const char *icq_ConvertStatus2Str(int status)
1502 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1505 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1506 switch (status & 0x01FF) {
1511 return "Do not disturb";
1516 case STATUS_OCCUPIED:
1520 return "Not available";
1522 case STATUS_INVISIBLE:
1525 case STATUS_INVISIBLE_2:
1526 return "Invisible mode 2";
1528 case STATUS_FREE_CHAT:
1529 return "Free for chat";
1537 void icq_InitNewUser(const char *hostname, DWORD port)
1539 srv_net_icq_pak pak;
1544 icq_Connect(hostname, port);
1545 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1546 printf("Couldn't establish connection\n");
1549 icq_RegNewUser((ThisICQ->icq_Password));
1552 tv.tv_usec = 500000;
1555 FD_SET((ThisICQ->icq_Sok), &readfds);
1557 /* don't care about writefds and exceptfds: */
1558 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1560 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1561 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1562 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1563 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1570 /********************************************
1571 Converts an intel endian character sequence to
1573 *********************************************/
1574 DWORD Chars_2_DW(unsigned char *buf)
1589 /********************************************
1590 Converts an intel endian character sequence to
1592 *********************************************/
1593 WORD Chars_2_Word(unsigned char *buf)
1604 /********************************************
1606 an intel endian character sequence
1607 *********************************************/
1608 void DW_2_Chars(unsigned char *buf, DWORD num)
1610 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1611 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1612 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1613 buf[0] = (unsigned char) (num) & 0x000000FF;
1616 /********************************************
1618 an intel endian character sequence
1619 *********************************************/
1620 void Word_2_Chars(unsigned char *buf, WORD num)
1622 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1623 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1626 /***************************
1627 ContactList functions
1628 ***************************/
1629 void icq_ContAddUser(DWORD cuin)
1631 icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
1632 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1635 p->vis_list = FALSE;
1641 (ThisICQ->icq_ContFirst) = p;
1644 void icq_ContDelUser(DWORD cuin)
1646 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1649 if (ptr->uin == cuin) {
1650 (ThisICQ->icq_ContFirst) = ptr->next;
1652 ptr = (ThisICQ->icq_ContFirst);
1655 if (ptr->next->uin == cuin) {
1656 ptr->next = ptr->next->next;
1663 void icq_ContClear()
1665 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1670 (ThisICQ->icq_ContFirst) = ptr;
1674 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1676 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1680 if (ptr->uin == cuin)
1687 icq_ContactItem *icq_ContGetFirst()
1689 return (ThisICQ->icq_ContFirst);
1692 void icq_ContSetVis(DWORD cuin)
1694 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1698 if (ptr->uin == cuin)
1699 ptr->vis_list = TRUE;
1704 /************************
1705 (ThisICQ->icq_ServMess) functions
1706 *************************/
1707 BOOL icq_GetServMess(WORD num)
1709 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1712 void icq_SetServMess(WORD num)
1714 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1719 return (ThisICQ->icq_Sok);
1722 int icq_GetProxySok()
1724 return (ThisICQ->icq_ProxySok);
1727 /*******************
1728 SOCKS5 Proxy support
1729 ********************/
1730 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1732 if ((ThisICQ->icq_ProxyHost))
1733 phree((ThisICQ->icq_ProxyHost));
1734 if ((ThisICQ->icq_ProxyName))
1735 phree((ThisICQ->icq_ProxyName));
1736 if ((ThisICQ->icq_ProxyPass))
1737 phree((ThisICQ->icq_ProxyPass));
1738 if (strlen(pname) > 255) {
1739 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1740 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1741 ThisICQ->icq_UseProxy = 0;
1744 if (strlen(ppass) > 255) {
1745 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1746 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1747 (ThisICQ->icq_UseProxy) = 0;
1750 (ThisICQ->icq_UseProxy) = 1;
1751 (ThisICQ->icq_ProxyHost) = strdoop(phost);
1752 (ThisICQ->icq_ProxyPort) = pport;
1753 (ThisICQ->icq_ProxyAuth) = pauth;
1754 (ThisICQ->icq_ProxyName) = strdoop(pname);
1755 (ThisICQ->icq_ProxyPass) = strdoop(ppass);
1758 void icq_UnsetProxy()
1760 ThisICQ->icq_UseProxy = 0;
1765 /***********************************************************************/
1766 /* icqlib stuff ends here, Citadel module stuff begins */
1767 /***********************************************************************/
1771 * Callback function for CtdlICQ_Read_Config()
1773 void CtdlICQ_Read_Config_Backend(long msgnum) {
1774 struct CtdlMessage *msg;
1778 msg = CtdlFetchMessage(msgnum);
1780 ptr = msg->cm_fields['M'];
1781 pos = pattern2(ptr, "\n\n");
1789 safestrncpy(ThisICQ->icq_config, ptr, 256);
1791 CtdlFreeMessage(msg);
1797 * If this user has an ICQ configuration on disk, read it into memory.
1799 void CtdlICQ_Read_Config(void) {
1800 char hold_rm[ROOMNAMELEN];
1801 char icq_rm[ROOMNAMELEN];
1803 strcpy(hold_rm, CC->quickroom.QRname);
1804 MailboxName(icq_rm, &CC->usersupp, ICQROOM);
1805 strcpy(ThisICQ->icq_config, "");
1807 if (getroom(&CC->quickroom, icq_rm) != 0) {
1808 getroom(&CC->quickroom, hold_rm);
1812 /* We want the last (and probably only) config in this room */
1813 CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
1814 getroom(&CC->quickroom, hold_rm);
1821 * Write our config to disk
1823 void CtdlICQ_Write_Config(void) {
1824 char temp[PATH_MAX];
1827 strcpy(temp, tmpnam(NULL));
1829 fp = fopen(temp, "w");
1830 if (fp == NULL) return;
1831 fprintf(fp, "%s|\n", ThisICQ->icq_config);
1834 /* this handy API function does all the work for us */
1835 CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
1843 * Refresh the contact list
1845 void CtdlICQ_Refresh_Contact_List(void) {
1849 if (ThisICQ->icq_Sok < 0) return;
1854 if (contact_uin > 0L) {
1855 icq_ContAddUser(contact_uin);
1856 icq_ContSetVis(contact_uin);
1862 icq_SendContactList();
1869 * Utility routine to logout and disconnect from the ICQ server if we happen
1870 * to already be connected
1872 void CtdlICQ_Logout_If_Connected(void) {
1873 if (ThisICQ->icq_Sok >= 0) {
1876 ThisICQ->icq_Sok = (-1);
1882 * If we have an ICQ uin/password on file for this user, go ahead and try
1883 * to log on to the ICQ server.
1885 void CtdlICQ_Login_If_Possible(void) {
1890 CtdlICQ_Logout_If_Connected();
1891 CtdlICQ_Read_Config();
1893 uin = extract_long(ThisICQ->icq_config, 0);
1894 extract(pass, ThisICQ->icq_config, 1);
1896 lprintf(9, "Here's my config: %s\n", ThisICQ->icq_config);
1898 if ( (uin > 0L) && (strlen(pass)>0) ) {
1899 icq_Init(uin, pass);
1900 if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
1901 icq_Login(STATUS_ONLINE);
1902 CtdlICQ_Refresh_Contact_List();
1909 /* This merely hooks icqlib's log function into Citadel's log function. */
1910 void CtdlICQlog(time_t time, unsigned char level, const char *str)
1912 lprintf(level, "ICQ: %s", str);
1917 * At the start of each Citadel server session, we have to allocate some
1918 * space for the Citadel-ICQ session for this thread. It'll be automatically
1919 * freed by the server when the session ends.
1921 void CtdlICQ_session_startup_hook(void)
1923 CtdlAllocUserData(SYM_CTDL_ICQ, sizeof(struct ctdl_icq_handle));
1924 icq_init_handle(ThisICQ);
1930 * End-of-session cleanup
1932 void CtdlICQ_session_stopdown_hook(void) {
1939 * icq_Main() needs to be called as frequently as possible. We'll do it
1940 * following the completion of each Citadel server command.
1943 void CtdlICQ_after_cmd_hook(void)
1945 if (ThisICQ->icq_Sok >= 0) {
1946 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
1948 ThisICQ->icq_LastKeepAlive = time(NULL);
1956 * There are a couple of things we absolutely need to make sure of when we
1957 * kill off the session. One of them is that the sockets are all closed --
1958 * otherwise we could have a nasty file descriptor leak.
1960 void CtdlICQ_session_logout_hook(void)
1962 lprintf(9, "Shutting down ICQ\n");
1963 CtdlICQ_Logout_If_Connected();
1969 void CtdlICQ_session_login_hook(void)
1971 /* If this user has an ICQ config on file, start it up. */
1972 CtdlICQ_Login_If_Possible();
1978 void cmd_icql(char *argbuf)
1980 safestrncpy(ThisICQ->icq_config, argbuf, 256);
1981 CtdlICQ_Write_Config();
1983 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
1984 CtdlICQ_Login_If_Possible();
1991 * Here's what we have to do when an ICQ message arrives!
1993 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
1994 BYTE day, BYTE month, WORD year,
2000 sprintf(from, "%ld@icq", uin);
2001 if (CtdlSendExpressMessageFunc) {
2002 CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
2003 lprintf(9, "Converted incoming message.\n");
2005 lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
2011 CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2012 const char *first, const char *last,
2013 const char *email, char auth) {
2015 /* FIX do something with this info!! */
2021 char *Dynamic_Module_Init(void)
2023 /* Make sure we've got a valid ThisICQ for each session */
2024 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2026 /* Tell the Citadel server about our wonderful ICQ hooks */
2027 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2028 CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
2029 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2030 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2031 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2032 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2034 /* Tell the code formerly known as icqlib about our callbacks */
2035 icq_Log = CtdlICQlog;
2036 icq_RecvMessage = CtdlICQ_Incoming_Message;
2037 icq_InfoReply = CtdlICQ_InfoReply;