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;
77 #define MODULE_NAME "ICQ metaclient"
78 #define MODULE_AUTHOR "Art Cancro"
79 #define MODULE_EMAIL "ajc@uncnsrd.mt-kisco.ny.us"
80 #define MAJOR_VERSION 0
81 #define MINOR_VERSION 1
83 static struct DLModule_Info info =
94 void (*icq_Logged) (void);
95 void (*icq_Disconnected) (void);
96 void (*icq_RecvMessage) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *msg);
97 void (*icq_RecvURL) (DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year, const char *url, const char *descr);
98 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);
99 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);
100 void (*icq_UserFound) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
101 void (*icq_SearchDone) (void);
102 void (*icq_UserOnline) (DWORD uin, DWORD status, DWORD ip, DWORD port, DWORD realip);
103 void (*icq_UserOffline) (DWORD uin);
104 void (*icq_UserStatusUpdate) (DWORD uin, DWORD status);
105 void (*icq_InfoReply) (DWORD uin, const char *nick, const char *first, const char *last, const char *email, char auth);
106 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);
107 void (*icq_Log) (time_t time, unsigned char level, const char *str);
108 void (*icq_SrvAck) (WORD seq);
111 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
112 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
113 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
114 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
115 254, 224, 225, 246, 228, 229, 244, 227, 245, 232, 233, 234, 235, 236, 237, 238,
116 239, 255, 240, 241, 242, 243, 230, 226, 252, 251, 231, 248, 253, 249, 247, 250,
117 222, 192, 193, 214, 196, 197, 212, 195, 213, 200, 201, 202, 203, 204, 205, 206,
118 207, 223, 208, 209, 210, 211, 198, 194, 220, 219, 199, 216, 221, 217, 215, 218};
121 {128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
122 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
123 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
124 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
125 225, 226, 247, 231, 228, 229, 246, 250, 233, 234, 235, 236, 237, 238, 239, 240,
126 242, 243, 244, 245, 230, 232, 227, 254, 251, 253, 255, 249, 248, 252, 224, 241,
127 193, 194, 215, 199, 196, 197, 214, 218, 201, 202, 203, 204, 205, 206, 207, 208,
128 210, 211, 212, 213, 198, 200, 195, 222, 219, 221, 223, 217, 216, 220, 192, 209};
130 static COUNTRY_CODE Country_Codes[] =
145 {"American Samoa", 684},
160 {"Czech Republic", 42},
163 {"El Salvador", 503},
167 {"French Antilles", 596},
168 {"French Polynesia", 689},
175 {"Guantanomo Bay", 53},
189 {"Ivory Coast", 225},
197 {"Liechtenstein", 41},
209 {"Netherlands Antilles", 599},
210 {"New Caledonia", 687},
217 {"Papua New Guinea", 675},
227 {"Saudia Arabia", 966},
232 {"South Africa", 27},
243 {"United Arab Emirates", 971},
245 {"Vatican City", 39},
252 {"Not entered", 0xffff}};
254 void icq_init_handle(struct ctdl_icq_handle *i)
256 memset(i, 0, sizeof(struct ctdl_icq_handle));
257 i->icq_Russian = TRUE;
259 i->icq_OurIp = 0x0100007f;
260 i->icq_Status = STATUS_OFFLINE;
263 i->icq_MyConfigFile = NULL;
266 int icq_SockWrite(int sok, const void *buf, size_t count)
269 if (!(ThisICQ->icq_UseProxy))
270 return write(sok, buf, count);
272 tmpbuf[0] = 0; /* reserved */
273 tmpbuf[1] = 0; /* reserved */
274 tmpbuf[2] = 0; /* standalone packet */
275 tmpbuf[3] = 1; /* address type IP v4 */
276 memcpy(&tmpbuf[4], &(ThisICQ->icq_ProxyDestHost), 4);
277 memcpy(&tmpbuf[8], &(ThisICQ->icq_ProxyDestPort), 2);
278 memcpy(&tmpbuf[10], buf, count);
279 return write(sok, tmpbuf, count + 10) - 10;
283 int icq_SockRead(int sok, void *buf, size_t count)
287 if (!(ThisICQ->icq_UseProxy))
288 return read(sok, buf, count);
290 res = read(sok, tmpbuf, count + 10);
291 memcpy(buf, &tmpbuf[10], res - 10);
296 /****************************************
297 This must be called every 2 min.
298 so the server knows we're still alive.
299 JAVA client sends two different commands
301 *****************************************/
306 Word_2_Chars(pak.head.ver, ICQ_VER);
307 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE);
308 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
309 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
310 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
312 Word_2_Chars(pak.head.ver, ICQ_VER);
313 Word_2_Chars(pak.head.cmd, CMD_KEEP_ALIVE2);
314 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
315 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
316 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
318 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
319 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Send Keep Alive packet to the server\n");
322 /**********************************
323 This must be called to remove
324 messages from the server
325 ***********************************/
326 void icq_SendGotMessages()
330 Word_2_Chars(pak.head.ver, ICQ_VER);
331 Word_2_Chars(pak.head.cmd, CMD_ACK_MESSAGES);
332 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
333 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
335 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
338 /*************************************
339 this sends over the contact list
340 *************************************/
341 void icq_SendContactList()
347 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
349 Word_2_Chars(pak.head.ver, ICQ_VER);
350 Word_2_Chars(pak.head.cmd, CMD_CONT_LIST);
351 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
352 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
358 DW_2_Chars(tmp, ptr->uin);
363 pak.data[0] = num_used;
364 size = ((int) tmp - (int) pak.data);
365 size += sizeof(pak.head);
366 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
369 void icq_SendNewUser(unsigned long uin)
374 Word_2_Chars(pak.head.ver, ICQ_VER);
375 Word_2_Chars(pak.head.cmd, CMD_ADD_TO_LIST);
376 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
377 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
378 DW_2_Chars(pak.data, uin);
379 size = sizeof(pak.head) + 4;
380 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
383 /*************************************
384 this sends over the visible list
385 that allows certain users to see you
387 *************************************/
388 void icq_SendVisibleList()
394 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
396 Word_2_Chars(pak.head.ver, ICQ_VER);
397 Word_2_Chars(pak.head.cmd, CMD_INVIS_LIST);
398 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
399 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
406 DW_2_Chars(tmp, ptr->uin);
413 pak.data[0] = num_used;
414 size = ((int) tmp - (int) pak.data);
415 size += sizeof(pak.head);
416 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size);
420 /**************************************
421 This sends the second login command
422 this is necessary to finish logging in.
423 ***************************************/
424 void icq_SendLogin1()
428 Word_2_Chars(pak.head.ver, ICQ_VER);
429 Word_2_Chars(pak.head.cmd, CMD_LOGIN_1);
430 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
431 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
433 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
436 /************************************************
437 This is called when a user goes offline
438 *************************************************/
439 void icq_HandleUserOffline(srv_net_icq_pak pak)
444 remote_uin = Chars_2_DW(pak.data);
445 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
446 sprintf(buf, "User %lu logged off\n", remote_uin);
447 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
450 (*icq_UserOffline) (remote_uin);
451 icq_AckSrv(Chars_2_Word(pak.head.seq));
454 void icq_HandleUserOnline(srv_net_icq_pak pak)
456 DWORD remote_uin, new_status, remote_ip, remote_real_ip;
457 DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
458 icq_ContactItem *ptr;
461 remote_uin = Chars_2_DW(pak.data);
462 new_status = Chars_2_DW(&pak.data[17]);
463 remote_ip = ntohl(Chars_2_DW(&pak.data[4]));
464 remote_port = ntohl(Chars_2_DW(&pak.data[8]));
465 remote_real_ip = ntohl(Chars_2_DW(&pak.data[12]));
466 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
467 sprintf(buf, "User %lu (%s) logged on\n", remote_uin, icq_ConvertStatus2Str(new_status));
468 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
471 (*icq_UserOnline) (remote_uin, new_status, remote_ip, remote_port, remote_real_ip);
472 icq_AckSrv(Chars_2_Word(pak.head.seq));
475 void icq_Status_Update(srv_net_icq_pak pak)
477 unsigned long remote_uin, new_status;
480 remote_uin = Chars_2_DW(pak.data);
481 new_status = Chars_2_DW(&pak.data[4]);
482 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
483 sprintf(buf, "%lu changed status to %s\n", remote_uin, icq_ConvertStatus2Str(new_status));
484 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
486 if (icq_UserStatusUpdate)
487 (*icq_UserStatusUpdate) (remote_uin, new_status);
488 icq_AckSrv(Chars_2_Word(pak.head.seq));
491 /************************************
492 This procedure logins into the server with (ThisICQ->icq_Uin) and pass
493 on the socket (ThisICQ->icq_Sok) and gives our ip and port.
494 It does NOT wait for any kind of a response.
495 *************************************/
496 void icq_Login(DWORD status)
503 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
504 Word_2_Chars(pak.head.ver, ICQ_VER);
505 Word_2_Chars(pak.head.cmd, CMD_LOGIN);
506 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
507 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
509 DW_2_Chars(s1.port, (ThisICQ->icq_OurPort));
510 Word_2_Chars(s1.len, strlen((ThisICQ->icq_Password)) + 1);
512 DW_2_Chars(s2.ip, (ThisICQ->icq_OurIp));
513 DW_2_Chars(s2.status, status);
514 Word_2_Chars(s2.seq, (ThisICQ->icq_SeqNum)++);
516 DW_2_Chars(s2.X1, LOGIN_X1_DEF);
517 s2.X2[0] = LOGIN_X2_DEF;
518 DW_2_Chars(s2.X3, LOGIN_X3_DEF);
519 DW_2_Chars(s2.X4, LOGIN_X4_DEF);
520 DW_2_Chars(s2.X5, LOGIN_X5_DEF);
522 memcpy(pak.data, &s1, sizeof(s1));
524 memcpy(&pak.data[size], (ThisICQ->icq_Password), Chars_2_Word(s1.len));
525 size += Chars_2_Word(s1.len);
526 memcpy(&pak.data[size], &s2, sizeof(s2));
528 ret = icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
531 /*******************************
532 This routine sends the aknowlegement cmd to the
533 server it appears that this must be done after
534 everything the server sends us
535 *******************************/
536 void icq_AckSrv(int seq)
541 Word_2_Chars(pak.head.ver, ICQ_VER);
542 Word_2_Chars(pak.head.cmd, CMD_ACK);
543 Word_2_Chars(pak.head.seq, seq);
544 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
545 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
546 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acking\n");
547 for (i = 0; i < 6; i++)
548 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, sizeof(pak.head));
551 void icq_HandleInfoReply(srv_net_icq_pak pak)
553 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, buf[256];
557 seq = Chars_2_Word(pak.data);
558 uin = Chars_2_DW(&pak.data[2]);
559 len = Chars_2_Word(&pak.data[6]);
561 icq_RusConv("wk", ptr1);
562 tmp = &pak.data[8 + len];
563 len = Chars_2_Word(tmp);
565 icq_RusConv("wk", ptr2);
567 len = Chars_2_Word(tmp);
569 icq_RusConv("wk", ptr3);
571 len = Chars_2_Word(tmp);
573 icq_RusConv("wk", ptr4);
575 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
576 sprintf(buf, "Info reply for %lu\n", uin);
577 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
580 (*icq_InfoReply) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
581 icq_AckSrv(Chars_2_Word(pak.head.seq));
584 void icq_HandleExtInfoReply(srv_net_icq_pak pak)
586 unsigned char *tmp, *ptr1, *ptr2, *ptr3, *ptr4, *ptr5;
590 char cnt_stat, gender, buf[256];
591 uin = Chars_2_DW(&pak.data[2]);
592 len = Chars_2_Word(&pak.data[6]);
594 icq_RusConv("wk", ptr1);
595 cnt_code = Chars_2_Word(&pak.data[8 + len]);
596 cnt_stat = pak.data[len + 10];
597 tmp = &pak.data[11 + len];
598 len = Chars_2_Word(tmp);
599 icq_RusConv("wk", tmp + 2);
601 age = Chars_2_Word(tmp + 2 + len);
602 gender = *(tmp + len + 4);
604 len = Chars_2_Word(tmp);
605 icq_RusConv("wk", tmp + 2);
608 len = Chars_2_Word(tmp);
609 icq_RusConv("wk", tmp + 2);
612 len = Chars_2_Word(tmp);
613 icq_RusConv("wk", tmp + 2);
615 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
616 sprintf(buf, "Extended info reply for %lu\n", uin);
617 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
619 if (icq_ExtInfoReply)
620 (*icq_ExtInfoReply) (uin, ptr1, cnt_code, cnt_stat, ptr2, age, gender, ptr3, ptr4, ptr5);
621 icq_AckSrv(Chars_2_Word(pak.head.seq));
624 void icq_HandleSearchReply(srv_net_icq_pak pak)
626 char *tmp, *ptr1, *ptr2, *ptr3, *ptr4;
630 uin = Chars_2_DW(&pak.data[2]);
631 len = Chars_2_Word(&pak.data[6]);
633 icq_RusConv("wk", ptr1);
634 tmp = &pak.data[8 + len];
635 len = Chars_2_Word(tmp);
637 icq_RusConv("wk", ptr2);
639 len = Chars_2_Word(tmp);
641 icq_RusConv("wk", ptr3);
643 len = Chars_2_Word(tmp);
645 icq_RusConv("wk", ptr4);
647 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
648 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");
649 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
652 (*icq_UserFound) (uin, ptr1, ptr2, ptr3, ptr4, *tmp);
653 icq_AckSrv(Chars_2_Word(pak.head.seq));
656 void icq_DoMsg(DWORD type, WORD len, char *data, DWORD uin, BYTE hour, BYTE minute, BYTE day, BYTE month, WORD year)
659 char *ptr1, *ptr2, *ptr3, *ptr4;
663 case USER_ADDED_MESS:
664 tmp = strchr(data, '\xFE');
666 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
667 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
674 tmp = strchr(tmp, '\xFE');
676 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
677 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
681 icq_RusConv("wk", data);
685 tmp = strchr(tmp, '\xFE');
687 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
688 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
692 icq_RusConv("wk", data);
696 tmp = strchr(tmp, '\xFE');
698 icq_RusConv("wk", data);
699 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
700 sprintf(buf, "%lu has added you to their contact list, Nick: %s, "
701 "First Name: %s, Last Name: %s, EMail: %s\n", uin, ptr1, ptr2, ptr3, data);
702 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
705 (*icq_RecvAdded) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, data);
708 tmp = strchr(data, '\xFE');
713 tmp = strchr(tmp, '\xFE');
715 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
716 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
720 icq_RusConv("wk", data);
724 tmp = strchr(tmp, '\xFE');
726 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
727 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
731 icq_RusConv("wk", data);
735 tmp = strchr(tmp, '\xFE');
737 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
738 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
742 icq_RusConv("wk", data);
746 tmp = strchr(tmp, '\xFE');
748 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
749 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
755 tmp = strchr(tmp, '\x00');
757 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
758 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
762 icq_RusConv("wk", data);
763 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
764 sprintf(buf, "%lu has requested your authorization to be added to "
765 "their contact list, Nick: %s, First Name: %s, Last Name: %s, "
766 "EMail: %s, Reason: %s\n", uin, ptr1, ptr2, ptr3, ptr4, data);
767 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
770 (*icq_RecvAuthReq) (uin, hour, minute, day, month, year, ptr1, ptr2, ptr3, ptr4, data);
773 tmp = strchr(data, '\xFE');
775 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
776 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Bad packet!\n");
780 icq_RusConv("wk", data);
784 icq_RusConv("wk", data);
785 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
786 sprintf(buf, "URL received from %lu, URL: %s, Description: %s", uin, ptr1, data);
787 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
790 (*icq_RecvURL) (uin, hour, minute, day, month, year, data, ptr1);
793 icq_RusConv("wk", data);
794 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
795 sprintf(buf, "Instant message from %lu:\n%s\n", uin, data);
796 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
799 (*icq_RecvMessage) (uin, hour, minute, day, month, year, data);
803 /**********************************
804 Connects to hostname on port port
805 hostname can be DNS or nnn.nnn.nnn.nnn
806 write out messages to the FD aux
807 ***********************************/
808 int icq_Connect(const char *hostname, int port)
810 char buf[1024], un = 1;
811 char our_host[256], tmpbuf[256];
812 int conct, length, res;
813 struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
814 struct hostent *host_struct; /* used in DNS llokup */
816 (ThisICQ->icq_Sok) = socket(AF_INET, SOCK_DGRAM, 0); /* create the unconnected socket */
817 if ((ThisICQ->icq_Sok) == -1) {
818 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
819 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Socket creation failed\n");
822 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
823 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Socket created attempting to connect\n");
824 sin.sin_addr.s_addr = INADDR_ANY;
825 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
827 if (bind((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(struct sockaddr)) < 0) {
828 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
829 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Can't bind socket to free port\n");
832 length = sizeof(sin);
833 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
834 (ThisICQ->icq_ProxyOurPort) = sin.sin_port;
835 if ((ThisICQ->icq_UseProxy)) {
836 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
837 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Trying to use SOCKS5 proxy\n");
838 prsin.sin_addr.s_addr = inet_addr((ThisICQ->icq_ProxyHost));
839 if (prsin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
840 host_struct = gethostbyname((ThisICQ->icq_ProxyHost));
841 if (host_struct == 0L) {
842 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
843 sprintf(tmpbuf, "[SOCKS] Can't find hostname: %s\n", (ThisICQ->icq_ProxyHost));
844 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
848 prsin.sin_addr = *((struct in_addr *) host_struct->h_addr);
850 prsin.sin_family = AF_INET; /* we're using the inet not appletalk */
851 prsin.sin_port = htons((ThisICQ->icq_ProxyPort)); /* port */
852 (ThisICQ->icq_ProxySok) = socket(AF_INET, SOCK_STREAM, 0); /* create the unconnected socket */
853 if ((ThisICQ->icq_ProxySok) == -1) {
854 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
855 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Socket creation failed\n");
858 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
859 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "[SOCKS] Socket created attempting to connect\n");
860 conct = connect((ThisICQ->icq_ProxySok), (struct sockaddr *) &prsin, sizeof(prsin));
861 if (conct == -1) { /* did we connect ? */
862 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
863 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
866 buf[0] = 5; /* protocol version */
867 buf[1] = 1; /* number of methods */
868 if (!strlen((ThisICQ->icq_ProxyName)) || !strlen((ThisICQ->icq_ProxyPass)) || !(ThisICQ->icq_ProxyAuth))
869 buf[2] = 0; /* no authorization required */
871 buf[2] = 2; /* method username/password */
872 write((ThisICQ->icq_ProxySok), buf, 3);
873 res = read((ThisICQ->icq_ProxySok), buf, 2);
874 if (res != 2 || buf[0] != 5 || buf[1] != 2) {
875 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
876 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authentication method incorrect\n");
877 close((ThisICQ->icq_ProxySok));
880 if (strlen((ThisICQ->icq_ProxyName)) && strlen((ThisICQ->icq_ProxyPass)) && (ThisICQ->icq_ProxyAuth)) {
881 buf[0] = 1; /* version of subnegotiation */
882 buf[1] = strlen((ThisICQ->icq_ProxyName));
883 memcpy(&buf[2], (ThisICQ->icq_ProxyName), buf[1]);
884 buf[2 + buf[1]] = strlen((ThisICQ->icq_ProxyPass));
885 memcpy(&buf[3 + buf[1]], (ThisICQ->icq_ProxyPass), buf[2 + buf[1]]);
886 write((ThisICQ->icq_ProxySok), buf, buf[1] + buf[2 + buf[1]] + 3);
887 res = read((ThisICQ->icq_ProxySok), buf, 2);
888 if (res != 2 || buf[0] != 1 || buf[1] != 0) {
889 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
890 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Authorization failure\n");
891 close((ThisICQ->icq_ProxySok));
895 buf[0] = 5; /* protocol version */
896 buf[1] = 3; /* command UDP associate */
897 buf[2] = 0; /* reserved */
898 buf[3] = 1; /* address type IP v4 */
903 memcpy(&buf[8], &(ThisICQ->icq_ProxyOurPort), 2);
904 write((ThisICQ->icq_ProxySok), buf, 10);
905 res = read((ThisICQ->icq_ProxySok), buf, 10);
906 if (res != 10 || buf[0] != 5 || buf[1] != 0) {
909 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
910 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] General SOCKS server failure\n");
913 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
914 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection not allowed by ruleset\n");
917 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
918 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Network unreachable\n");
921 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
922 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Host unreachable\n");
925 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
926 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection refused\n");
929 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
930 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] TTL expired\n");
933 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
934 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Command not supported\n");
937 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
938 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Address type not supported\n");
941 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
942 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Unknown SOCKS server failure\n");
945 close((ThisICQ->icq_ProxySok));
949 sin.sin_addr.s_addr = inet_addr(hostname); /* checks for n.n.n.n notation */
950 if (sin.sin_addr.s_addr == -1) { /* name isn't n.n.n.n so must be DNS */
951 host_struct = gethostbyname(hostname);
952 if (host_struct == 0L) {
953 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL) {
954 sprintf(tmpbuf, "Can't find hostname: %s\n", hostname);
955 (*icq_Log) (time(0L), ICQ_LOG_FATAL, tmpbuf);
957 if ((ThisICQ->icq_UseProxy))
958 close((ThisICQ->icq_ProxySok));
961 sin.sin_addr = *((struct in_addr *) host_struct->h_addr);
963 if ((ThisICQ->icq_UseProxy)) {
964 (ThisICQ->icq_ProxyDestHost) = sin.sin_addr.s_addr;
965 memcpy(&sin.sin_addr.s_addr, &buf[4], 4);
967 sin.sin_family = AF_INET; /* we're using the inet not appletalk */
968 sin.sin_port = htons(port); /* port */
969 if ((ThisICQ->icq_UseProxy)) {
970 (ThisICQ->icq_ProxyDestPort) = htons(port);
971 memcpy(&sin.sin_port, &buf[8], 2);
973 conct = connect((ThisICQ->icq_Sok), (struct sockaddr *) &sin, sizeof(sin));
974 if (conct == -1) { /* did we connect ? */
975 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
976 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection refused\n");
977 if ((ThisICQ->icq_UseProxy))
978 close((ThisICQ->icq_ProxySok));
981 length = sizeof(sin);
982 getsockname((ThisICQ->icq_Sok), (struct sockaddr *) &sin, &length);
983 (ThisICQ->icq_OurIp) = sin.sin_addr.s_addr;
984 (ThisICQ->icq_OurPort) = sin.sin_port;
985 return (ThisICQ->icq_Sok);
988 void icq_HandleProxyResponse()
992 s = read((ThisICQ->icq_ProxySok), &buf, sizeof(buf));
994 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
995 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "[SOCKS] Connection terminated\n");
996 if (icq_Disconnected)
997 (*icq_Disconnected) ();
998 SOCKCLOSE((ThisICQ->icq_Sok));
999 SOCKCLOSE((ThisICQ->icq_ProxySok));
1003 /******************************************
1004 Handles packets that the server sends to us.
1005 *******************************************/
1006 void icq_HandleServerResponse()
1008 srv_net_icq_pak pak;
1009 SIMPLE_MESSAGE *s_mesg;
1010 RECV_MESSAGE *r_mesg;
1017 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1019 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_FATAL)
1020 (*icq_Log) (time(0L), ICQ_LOG_FATAL, "Connection terminated\n");
1021 if (icq_Disconnected)
1022 (*icq_Disconnected) ();
1023 SOCKCLOSE((ThisICQ->icq_Sok));
1025 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)) {
1026 if (Chars_2_Word(pak.head.cmd) != SRV_ACK) { /* ACKs don't matter */
1027 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1028 sprintf(buf, "Ignored a message cmd %04x, seq %04x\n", Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.seq));
1029 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1031 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* LAGGGGG!! */
1035 if (Chars_2_Word(pak.head.cmd) != SRV_ACK)
1036 icq_SetServMess(Chars_2_Word(pak.head.seq));
1037 switch (Chars_2_Word(pak.head.cmd)) {
1039 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1040 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "The server acknowledged the command\n");
1042 (*icq_SrvAck) (Chars_2_Word(pak.head.seq));
1045 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1046 sprintf(buf, "The new uin is %lu\n", Chars_2_DW(&pak.data[2]));
1047 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1050 case SRV_LOGIN_REPLY:
1051 (ThisICQ->icq_OurIp) = Chars_2_DW(&pak.data[4]);
1052 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE) {
1053 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]);
1054 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, buf);
1056 icq_AckSrv(Chars_2_Word(pak.head.seq));
1058 icq_SendContactList();
1059 icq_SendVisibleList();
1063 case SRV_RECV_MESSAGE:
1064 r_mesg = (RECV_MESSAGE *) pak.data;
1065 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));
1066 icq_AckSrv(Chars_2_Word(pak.head.seq));
1068 case SRV_X1: /* unknown message sent after login */
1069 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1070 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X1 (Begin messages)\n");
1071 icq_AckSrv(Chars_2_Word(pak.head.seq));
1073 case SRV_X2: /* unknown message sent after login */
1074 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1075 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Acknowleged SRV_X2 (Done old messages)\n");
1076 icq_AckSrv(Chars_2_Word(pak.head.seq));
1077 icq_SendGotMessages();
1079 case SRV_INFO_REPLY:
1080 icq_HandleInfoReply(pak);
1082 case SRV_EXT_INFO_REPLY:
1083 icq_HandleExtInfoReply(pak);
1085 case SRV_USER_ONLINE:
1086 icq_HandleUserOnline(pak);
1088 case SRV_USER_OFFLINE:
1089 icq_HandleUserOffline(pak);
1092 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING)
1093 (*icq_Log) (time(0L), ICQ_LOG_WARNING, "Server is busy, please try again\n");
1094 icq_Login((ThisICQ->icq_Status));
1096 case SRV_STATUS_UPDATE:
1097 icq_Status_Update(pak);
1100 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1101 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Server has forced us to disconnect\n");
1102 if (icq_Disconnected)
1103 (*icq_Disconnected) ();
1105 case SRV_END_OF_SEARCH:
1106 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_MESSAGE)
1107 (*icq_Log) (time(0L), ICQ_LOG_MESSAGE, "Search done\n");
1109 (*icq_SearchDone) ();
1110 icq_AckSrv(Chars_2_Word(pak.head.seq));
1112 case SRV_USER_FOUND:
1113 icq_HandleSearchReply(pak);
1115 case SRV_SYS_DELIVERED_MESS:
1116 s_mesg = (SIMPLE_MESSAGE *) pak.data;
1117 cur_time = time(0L);
1118 tm_str = localtime(&cur_time);
1119 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);
1120 icq_AckSrv(Chars_2_Word(pak.head.seq));
1122 default: /* commands we dont handle yet */
1123 len = s - (sizeof(pak.head));
1124 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_WARNING) {
1125 sprintf(buf, "Unhandled message %04x, Version: %x, Sequence: %04x, Size: %d\n",
1126 Chars_2_Word(pak.head.cmd), Chars_2_Word(pak.head.ver), Chars_2_Word(pak.head.seq), len);
1127 (*icq_Log) (time(0L), ICQ_LOG_WARNING, buf);
1129 icq_AckSrv(Chars_2_Word(pak.head.seq)); /* fake like we know what we're doing */
1134 void icq_Init(DWORD uin, const char *password)
1136 memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
1137 (ThisICQ->icq_Uin) = uin;
1138 if ((ThisICQ->icq_Password))
1139 free((ThisICQ->icq_Password));
1140 (ThisICQ->icq_Password) = strdup(password);
1145 if ((ThisICQ->icq_Password))
1146 free((ThisICQ->icq_Password));
1149 /******************************
1150 Main function connects gets (ThisICQ->icq_Uin)
1151 and (ThisICQ->icq_Password) and logins in and sits
1152 in a loop waiting for server responses.
1153 *******************************/
1165 FD_SET((ThisICQ->icq_Sok), &readfds);
1166 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1167 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1168 icq_HandleServerResponse();
1172 } while (did_something);
1175 /********************************************************
1176 Russian language ICQ fix.
1177 Usual Windows ICQ users do use Windows 1251 encoding but
1178 unix users do use koi8 encoding, so we need to convert it.
1179 This function will convert string from windows 1251 to koi8
1180 or from koi8 to windows 1251.
1181 Andrew Frolov dron@ilm.net
1182 *********************************************************/
1183 void icq_RusConv(const char to[4], char *t_in)
1188 /* 6-17-1998 by Linux_Dude
1189 * Moved initialization of table out front of 'if' block to prevent compiler
1190 * warning. Improved error message, and now return without performing string
1191 * conversion to prevent addressing memory out of range (table pointer would
1192 * previously have remained uninitialized (= bad)).
1196 if (strcmp(to, "kw") == 0)
1198 else if (strcmp(to, "wk") != 0) {
1199 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1200 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "Unknown option in call to Russian Convert\n");
1203 /* End Linux_Dude's changes ;) */
1205 if ((ThisICQ->icq_Russian)) {
1206 for (i = 0; t_in[i] != 0; i++) {
1209 t_in[i] = table[t_in[i] & 0177];
1214 /**************************************************
1215 Sends a message thru the server to (ThisICQ->icq_Uin). Text is the
1217 ***************************************************/
1218 WORD icq_SendMessage(DWORD uin, const char *text)
1223 char buf[512]; /* message may be only 450 bytes long */
1225 strncpy(buf, text, 512);
1226 icq_RusConv("kw", buf);
1228 Word_2_Chars(pak.head.ver, ICQ_VER);
1229 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1230 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1231 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1232 DW_2_Chars(msg.uin, uin);
1233 DW_2_Chars(msg.type, 0x0001); /* A type 1 msg */
1234 Word_2_Chars(msg.len, len + 1); /* length + the NULL */
1235 memcpy(&pak.data, &msg, sizeof(msg));
1236 memcpy(&pak.data[8], buf, len + 1);
1237 size = sizeof(msg) + len + 1;
1238 for (i = 0; i < 6; i++)
1239 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1240 return (ThisICQ->icq_SeqNum) - 1;
1243 WORD icq_SendURL(DWORD uin, const char *url, const char *descr)
1247 int size, len1, len2;
1248 char buf1[512], buf2[512];
1250 strncpy(buf1, descr, 512);
1251 strncpy(buf2, url, 512);
1252 /* Do we need to convert URL? */
1253 icq_RusConv("kw", buf2);
1254 len1 = strlen(buf1);
1255 len2 = strlen(buf2);
1256 Word_2_Chars(pak.head.ver, ICQ_VER);
1257 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1258 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1259 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1260 DW_2_Chars(msg.uin, uin);
1261 DW_2_Chars(msg.type, 0x0004); /* A type 4 msg */
1262 Word_2_Chars(msg.len, len1 + len2 + 2); /* length + the NULL + 0xFE delimiter */
1263 memcpy(&pak.data, &msg, sizeof(msg));
1264 memcpy(&pak.data[8], buf1, len1);
1265 pak.data[8 + len1] = 0xFE;
1266 memcpy(&pak.data[8 + len1 + 1], buf2, len2 + 1);
1267 size = sizeof(msg) + len1 + len2 + 2;
1268 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1269 return (ThisICQ->icq_SeqNum) - 1;
1272 /**************************************************
1273 Sends a authorization to the server so the Mirabilis
1274 client can add the user.
1275 ***************************************************/
1276 void icq_SendAuthMsg(DWORD uin)
1282 Word_2_Chars(pak.head.ver, ICQ_VER);
1283 Word_2_Chars(pak.head.cmd, CMD_SENDM);
1284 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1285 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1286 DW_2_Chars(msg.uin, uin);
1287 DW_2_Chars(msg.type, AUTH_MESSAGE); /* A type authorization msg */
1288 Word_2_Chars(msg.len, 2);
1289 memcpy(&pak.data, &msg, sizeof(msg));
1290 pak.data[sizeof(msg)] = 0x03;
1291 pak.data[sizeof(msg) + 1] = 0x00;
1292 size = sizeof(msg) + 2;
1293 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1296 /**************************************************
1297 Changes the users status on the server
1298 ***************************************************/
1299 void icq_ChangeStatus(DWORD status)
1304 Word_2_Chars(pak.head.ver, ICQ_VER);
1305 Word_2_Chars(pak.head.cmd, CMD_STATUS_CHANGE);
1306 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1307 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1308 DW_2_Chars(pak.data, status);
1309 (ThisICQ->icq_Status) = status;
1311 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1314 /**********************
1316 ***********************/
1322 Word_2_Chars(pak.head.ver, ICQ_VER);
1323 Word_2_Chars(pak.head.cmd, CMD_SEND_TEXT_CODE);
1324 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1325 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1326 len = strlen("B_USER_DISCONNECTED") + 1;
1327 *(short *) pak.data = len;
1329 memcpy(&pak.data[2], "B_USER_DISCONNECTED", len);
1330 pak.data[2 + len] = 05;
1331 pak.data[3 + len] = 00;
1332 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1335 void icq_Disconnect()
1337 SOCKCLOSE((ThisICQ->icq_Sok));
1338 SOCKCLOSE((ThisICQ->icq_Sok));
1339 if ((ThisICQ->icq_UseProxy))
1340 SOCKCLOSE((ThisICQ->icq_ProxySok));
1343 /********************************************************
1344 Sends a request to the server for info on a specific user
1345 *********************************************************/
1346 WORD icq_SendInfoReq(DWORD uin)
1351 Word_2_Chars(pak.head.ver, ICQ_VER);
1352 Word_2_Chars(pak.head.cmd, CMD_INFO_REQ);
1353 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1354 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1355 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1356 DW_2_Chars(&pak.data[2], uin);
1358 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1359 return (ThisICQ->icq_SeqNum) - 1;
1362 /********************************************************
1363 Sends a request to the server for info on a specific user
1364 *********************************************************/
1365 WORD icq_SendExtInfoReq(DWORD uin)
1370 Word_2_Chars(pak.head.ver, ICQ_VER);
1371 Word_2_Chars(pak.head.cmd, CMD_EXT_INFO_REQ);
1372 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1373 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1374 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1375 DW_2_Chars(&pak.data[2], uin);
1377 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1378 return (ThisICQ->icq_SeqNum) - 1;
1381 /**************************************************************
1382 Initializes a server search for the information specified
1383 ***************************************************************/
1384 void icq_SendSearchReq(const char *email, const char *nick, const char *first, const char *last)
1389 Word_2_Chars(pak.head.ver, ICQ_VER);
1390 Word_2_Chars(pak.head.cmd, CMD_SEARCH_USER);
1391 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1392 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1393 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1395 Word_2_Chars(&pak.data[size], strlen(nick) + 1);
1397 strcpy(pak.data + size, nick);
1398 size += strlen(nick) + 1;
1399 Word_2_Chars(&pak.data[size], strlen(first) + 1);
1401 strcpy(pak.data + size, first);
1402 size += strlen(first) + 1;
1403 Word_2_Chars(&pak.data[size], strlen(last) + 1);
1405 strcpy(pak.data + size, last);
1406 size += strlen(last) + 1;
1407 Word_2_Chars(&pak.data[size], strlen(email) + 1);
1409 strcpy(pak.data + size, email);
1410 size += strlen(email) + 1;
1411 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1414 /**************************************************************
1415 Initializes a server search for the information specified
1416 ***************************************************************/
1417 void icq_SendSearchUINReq(DWORD uin)
1422 Word_2_Chars(pak.head.ver, ICQ_VER);
1423 Word_2_Chars(pak.head.cmd, CMD_SEARCH_UIN);
1424 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1425 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1426 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1428 DW_2_Chars(&pak.data[size], uin);
1430 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1433 /**************************************************
1434 Registers a new uin in the ICQ network
1435 ***************************************************/
1436 void icq_RegNewUser(const char *pass)
1438 srv_net_icq_pak pak;
1443 Word_2_Chars(pak.head.ver, ICQ_VER);
1444 Word_2_Chars(pak.head.cmd, CMD_REG_NEW_USER);
1445 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1446 Word_2_Chars(len_buf, len);
1447 memcpy(&pak.data, "\x02\x00", 2);
1448 memcpy(&pak.data[2], &len_buf, 2);
1449 memcpy(&pak.data[4], pass, len + 1);
1450 DW_2_Chars(&pak.data[4 + len], 0x0072);
1451 DW_2_Chars(&pak.data[8 + len], 0x0000);
1453 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1456 void icq_UpdateUserInfo(USER_INFO * user)
1461 Word_2_Chars(pak.head.ver, ICQ_VER);
1462 Word_2_Chars(pak.head.cmd, CMD_UPDATE_INFO);
1463 Word_2_Chars(pak.head.seq, (ThisICQ->icq_SeqNum)++);
1464 DW_2_Chars(pak.head.uin, (ThisICQ->icq_Uin));
1465 Word_2_Chars(pak.data, (ThisICQ->icq_SeqNum)++);
1467 Word_2_Chars(&pak.data[size], strlen(user->nick) + 1);
1469 strcpy(pak.data + size, user->nick);
1470 size += strlen(user->nick) + 1;
1471 Word_2_Chars(&pak.data[size], strlen(user->first) + 1);
1473 strcpy(pak.data + size, user->first);
1474 size += strlen(user->first) + 1;
1475 Word_2_Chars(&pak.data[size], strlen(user->last) + 1);
1477 strcpy(pak.data + size, user->last);
1478 size += strlen(user->last) + 1;
1479 Word_2_Chars(&pak.data[size], strlen(user->email) + 1);
1481 strcpy(pak.data + size, user->email);
1482 size += strlen(user->email) + 1;
1483 pak.data[size] = user->auth;
1485 icq_SockWrite((ThisICQ->icq_Sok), &pak.head, size + sizeof(pak.head));
1488 const char *icq_GetCountryName(int code)
1492 for (i = 0; Country_Codes[i].code != 0xffff; i++) {
1493 if (Country_Codes[i].code == code) {
1494 return Country_Codes[i].name;
1497 if (Country_Codes[i].code == code) {
1498 return Country_Codes[i].name;
1503 /********************************************
1504 returns a string describing the status or
1505 a "Error" if no such string exists
1506 *********************************************/
1507 const char *icq_ConvertStatus2Str(int status)
1509 if (STATUS_OFFLINE == status) { /* this because -1 & 0x01FF is not -1 */
1512 /* To handle icq99a statuses 0xFFFF changed to 0x01FF */
1513 switch (status & 0x01FF) {
1518 return "Do not disturb";
1523 case STATUS_OCCUPIED:
1527 return "Not available";
1529 case STATUS_INVISIBLE:
1532 case STATUS_INVISIBLE_2:
1533 return "Invisible mode 2";
1535 case STATUS_FREE_CHAT:
1536 return "Free for chat";
1544 void icq_InitNewUser(const char *hostname, DWORD port)
1546 srv_net_icq_pak pak;
1551 icq_Connect(hostname, port);
1552 if (((ThisICQ->icq_Sok) == -1) || ((ThisICQ->icq_Sok) == 0)) {
1553 printf("Couldn't establish connection\n");
1556 icq_RegNewUser((ThisICQ->icq_Password));
1559 tv.tv_usec = 500000;
1562 FD_SET((ThisICQ->icq_Sok), &readfds);
1564 /* don't care about writefds and exceptfds: */
1565 select((ThisICQ->icq_Sok) + 1, &readfds, 0L, 0L, &tv);
1567 if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
1568 s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
1569 if (Chars_2_Word(pak.head.cmd) == SRV_NEW_UIN) {
1570 (ThisICQ->icq_Uin) = Chars_2_DW(&pak.data[2]);
1577 /********************************************
1578 Converts an intel endian character sequence to
1580 *********************************************/
1581 DWORD Chars_2_DW(unsigned char *buf)
1596 /********************************************
1597 Converts an intel endian character sequence to
1599 *********************************************/
1600 WORD Chars_2_Word(unsigned char *buf)
1611 /********************************************
1613 an intel endian character sequence
1614 *********************************************/
1615 void DW_2_Chars(unsigned char *buf, DWORD num)
1617 buf[3] = (unsigned char) ((num) >> 24) & 0x000000FF;
1618 buf[2] = (unsigned char) ((num) >> 16) & 0x000000FF;
1619 buf[1] = (unsigned char) ((num) >> 8) & 0x000000FF;
1620 buf[0] = (unsigned char) (num) & 0x000000FF;
1623 /********************************************
1625 an intel endian character sequence
1626 *********************************************/
1627 void Word_2_Chars(unsigned char *buf, WORD num)
1629 buf[1] = (unsigned char) (((unsigned) num) >> 8) & 0x00FF;
1630 buf[0] = (unsigned char) ((unsigned) num) & 0x00FF;
1633 /***************************
1634 ContactList functions
1635 ***************************/
1636 void icq_ContAddUser(DWORD cuin)
1638 icq_ContactItem *p = malloc(sizeof(icq_ContactItem));
1639 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1642 p->vis_list = FALSE;
1648 (ThisICQ->icq_ContFirst) = p;
1651 void icq_ContDelUser(DWORD cuin)
1653 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1656 if (ptr->uin == cuin) {
1657 (ThisICQ->icq_ContFirst) = ptr->next;
1659 ptr = (ThisICQ->icq_ContFirst);
1662 if (ptr->next->uin == cuin) {
1663 ptr->next = ptr->next->next;
1670 void icq_ContClear()
1672 icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
1677 (ThisICQ->icq_ContFirst) = ptr;
1681 icq_ContactItem *icq_ContFindByUin(DWORD cuin)
1683 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1687 if (ptr->uin == cuin)
1694 icq_ContactItem *icq_ContGetFirst()
1696 return (ThisICQ->icq_ContFirst);
1699 void icq_ContSetVis(DWORD cuin)
1701 icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
1705 if (ptr->uin == cuin)
1706 ptr->vis_list = TRUE;
1711 /************************
1712 (ThisICQ->icq_ServMess) functions
1713 *************************/
1714 BOOL icq_GetServMess(WORD num)
1716 return (((ThisICQ->icq_ServMess)[num / 8] & (1 << (num % 8))) >> (num % 8));
1719 void icq_SetServMess(WORD num)
1721 (ThisICQ->icq_ServMess)[num / 8] |= (1 << (num % 8));
1726 return (ThisICQ->icq_Sok);
1729 int icq_GetProxySok()
1731 return (ThisICQ->icq_ProxySok);
1734 /*******************
1735 SOCKS5 Proxy support
1736 ********************/
1737 void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
1739 if ((ThisICQ->icq_ProxyHost))
1740 free((ThisICQ->icq_ProxyHost));
1741 if ((ThisICQ->icq_ProxyName))
1742 free((ThisICQ->icq_ProxyName));
1743 if ((ThisICQ->icq_ProxyPass))
1744 free((ThisICQ->icq_ProxyPass));
1745 if (strlen(pname) > 255) {
1746 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1747 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
1748 ThisICQ->icq_UseProxy = 0;
1751 if (strlen(ppass) > 255) {
1752 if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
1753 (*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User password greater than 255 chars\n");
1754 (ThisICQ->icq_UseProxy) = 0;
1757 (ThisICQ->icq_UseProxy) = 1;
1758 (ThisICQ->icq_ProxyHost) = strdup(phost);
1759 (ThisICQ->icq_ProxyPort) = pport;
1760 (ThisICQ->icq_ProxyAuth) = pauth;
1761 (ThisICQ->icq_ProxyName) = strdup(pname);
1762 (ThisICQ->icq_ProxyPass) = strdup(ppass);
1765 void icq_UnsetProxy()
1767 ThisICQ->icq_UseProxy = 0;
1772 /***********************************************************************/
1773 /* icqlib stuff ends here, Citadel module stuff begins */
1774 /***********************************************************************/
1777 /* config file manipulation routines ... probably temporary until we can
1778 * get something more robust written
1782 void CtdlICQ_Config_Delete(char *key) {
1783 long readpos, writepos;
1784 char buf[256], keyplusspace[256];
1787 if (ThisICQ->icq_MyConfigFile == NULL) return;
1790 sprintf(keyplusspace, "%s ", key);
1793 fseek(ThisICQ->icq_MyConfigFile, readpos, 0);
1794 ptr = fgets(buf, 256, ThisICQ->icq_MyConfigFile);
1795 readpos = ftell(ThisICQ->icq_MyConfigFile);
1797 fflush(ThisICQ->icq_MyConfigFile);
1798 ftruncate(fileno(ThisICQ->icq_MyConfigFile), writepos);
1801 if (strncasecmp(buf, keyplusspace,
1802 strlen(keyplusspace))) {
1803 fseek(ThisICQ->icq_MyConfigFile, writepos, 0);
1804 fprintf(ThisICQ->icq_MyConfigFile, "%s", buf);
1805 writepos = ftell(ThisICQ->icq_MyConfigFile);
1813 void CtdlICQ_Config_Write(char *key, char *contents) {
1815 char buf[256]; /* FIX */
1817 CtdlICQ_Config_Delete(key);
1818 fseek(ThisICQ->icq_MyConfigFile, 0L, SEEK_END);
1819 fprintf(ThisICQ->icq_MyConfigFile, "%s %s\n", key, contents);
1821 /****** FIX ****** TEMPORARY HACK TO SEE STUFF ***********/
1823 fflush(ThisICQ->icq_MyConfigFile);
1824 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1825 CtdlWriteObject(ICQROOM, ICQMIME, buf, 1, 0, 1);
1830 void CtdlICQ_Config_Read(char *contents, char *key) {
1833 strcpy(contents, "");
1834 rewind(ThisICQ->icq_MyConfigFile);
1835 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1836 buf[strlen(buf)-1]=0;
1837 if ( (!strncasecmp(buf, key, strlen(key)))
1838 && (isspace(buf[strlen(key)])) ) {
1839 strcpy(contents, &buf[strlen(key)+1]);
1846 * Refresh the contact list
1848 void CtdlICQ_Refresh_Contact_List(void) {
1852 if (ThisICQ->icq_Sok < 0) return;
1855 while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
1856 buf[strlen(buf)-1]=0;
1858 contact_uin = atol(buf);
1859 if (contact_uin > 0L) {
1860 icq_ContAddUser(contact_uin);
1861 icq_ContSetVis(contact_uin);
1864 icq_SendContactList();
1871 * Utility routine to logout and disconnect from the ICQ server if we happen
1872 * to already be connected
1874 void CtdlICQ_Logout_If_Connected(void) {
1875 if (ThisICQ->icq_Sok >= 0) {
1878 ThisICQ->icq_Sok = (-1);
1884 * If we have an ICQ uin/password on file for this user, go ahead and try
1885 * to log on to the ICQ server.
1887 void CtdlICQ_Login_If_Possible(void) {
1892 CtdlICQ_Logout_If_Connected();
1894 CtdlICQ_Config_Read(buf, "uin");
1896 CtdlICQ_Config_Read(pass, "pass");
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);
1929 * icq_Main() needs to be called as frequently as possible. We'll do it
1930 * following the completion of each Citadel server command.
1933 void CtdlICQ_after_cmd_hook(void)
1935 if (ThisICQ->icq_Sok >= 0) {
1936 if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
1938 ThisICQ->icq_LastKeepAlive = time(NULL);
1946 * There are a couple of things we absolutely need to make sure of when we
1947 * kill off the session. One of them is that the sockets are all closed --
1948 * otherwise we could have a nasty file descriptor leak.
1950 void CtdlICQ_session_logout_hook(void)
1952 CtdlICQ_Logout_If_Connected();
1953 if (ThisICQ->icq_MyConfigFile != NULL)
1954 fclose(ThisICQ->icq_MyConfigFile);
1960 void CtdlICQ_session_login_hook(void)
1963 if (ThisICQ->icq_MyConfigFile != NULL)
1964 fclose(ThisICQ->icq_MyConfigFile);
1966 /* Open this user's ICQ config file; create it if it's not there */
1967 sprintf(buf, "icq/%ld", CC->usersupp.usernum);
1968 ThisICQ->icq_MyConfigFile = fopen(buf, "r+");
1969 if (ThisICQ->icq_MyConfigFile == NULL)
1970 ThisICQ->icq_MyConfigFile = fopen(buf, "w+");
1971 if (ThisICQ->icq_MyConfigFile == NULL)
1972 lprintf(2, "Cannot create %s: %s\n", buf, strerror(errno));
1974 /* Login to the ICQ server if we already have info on file. */
1975 CtdlICQ_Login_If_Possible();
1979 void cmd_icqa(char *argbuf) {
1982 extract(cuin, argbuf, 0);
1983 if (atol(cuin) > 0L) {
1984 CtdlICQ_Config_Write(cuin, "");
1985 icq_SendInfoReq(atol(cuin));
1986 cprintf("%d %s added to your ICQ contact list.\n", OK, cuin);
1988 cprintf("%d You must specify a numeric ICQ uin.\n", ERROR);
1991 CtdlICQ_Refresh_Contact_List();
1997 void cmd_icql(char *argbuf)
2002 extract(uin, argbuf, 0);
2003 extract(password, argbuf, 1);
2005 CtdlICQ_Config_Write("uin", uin);
2006 CtdlICQ_Config_Write("pass", password);
2008 cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
2009 CtdlICQ_Login_If_Possible();
2014 * When deleting a user from the server, be sure to delete
2015 * his/her/its ICQ config file as well.
2017 void CtdlICQ_DeleteUserConfigFile(char *uname, long unum) {
2020 sprintf(buf, "icq/%ld", unum);
2027 * Here's what we have to do when an ICQ message arrives!
2029 void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
2030 BYTE day, BYTE month, WORD year,
2036 sprintf(from, "%ld", uin);
2037 CtdlICQ_Config_Read(nick, from);
2038 if (strlen(nick) == 0) {
2039 icq_SendInfoReq(atol(from));
2042 sprintf(from, "%ld@icq (%s)", uin, nick);
2043 if (CtdlSendExpressMessageFunc) {
2044 CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
2049 lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
2055 CtdlICQ_InfoReply(unsigned long uin, const char *nick,
2056 const char *first, const char *last,
2057 const char *email, char auth) {
2061 sprintf(str_uin, "%ld", uin);
2062 CtdlICQ_Config_Write(str_uin, nick);
2067 struct DLModule_Info *Dynamic_Module_Init(void)
2069 /* Create a directory to store ICQ stuff in.
2070 * It's ok if the directory is already there.
2072 if (mkdir("icq", 0700) != 0) if (errno != EEXIST) {
2073 lprintf(2, "Can't create icq subdirectory: %s\n",
2077 /* Make sure we've got a valid ThisICQ for each session */
2078 SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
2080 /* Tell the Citadel server about our wonderful ICQ hooks */
2081 CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
2082 CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
2083 CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
2084 CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
2085 CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
2086 CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add an ICQ contact");
2087 CtdlRegisterUserHook(CtdlICQ_DeleteUserConfigFile, EVT_PURGEUSER);
2089 /* Tell the code formerly known as icqlib about our callbacks */
2090 icq_Log = CtdlICQlog;
2091 icq_RecvMessage = CtdlICQ_Incoming_Message;
2092 icq_InfoReply = CtdlICQ_InfoReply;