/*
* serv_icq.c
*
- * This is a modified version of Denis' ICQLIB, a very cleanly
+ * This is a modified version of Denis V. Dmitrienko's ICQLIB, a very cleanly
* written implementation of the Mirabilis ICQ client protocol. The library
* has been modified rather than merely utilized because we need it to be
* threadsafe in order to tie it into the Citadel server.
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <sys/stat.h>
#include <limits.h>
#include "sysdep.h"
-#ifdef HAVE_PTHREAD_H
-#include <pthread.h>
-#endif
#include "citadel.h"
#include "server.h"
#include "dynloader.h"
#include "tools.h"
#include "citserver.h"
+#include "locate_host.h"
#include "msgbase.h"
+#include "sysdep_decls.h"
+#include "support.h"
+#include "room_ops.h"
+#include "user_ops.h"
/*
* Contact list in memory
DWORD uin;
char name[32];
DWORD status;
+ char host[26];
};
-struct ctdl_icq_handle {
- int icq_Sok;
- BOOL icq_Russian;
- BYTE icq_ServMess[8192];
- WORD icq_SeqNum;
- DWORD icq_OurIp;
- DWORD icq_OurPort;
- DWORD icq_Uin;
- icq_ContactItem *icq_ContFirst;
- DWORD icq_Status;
- char *icq_Password;
- BYTE icq_LogLevel;
- BYTE icq_UseProxy;
- char *icq_ProxyHost;
- WORD icq_ProxyPort;
- int icq_ProxyAuth;
- char *icq_ProxyName;
- char *icq_ProxyPass;
- int icq_ProxySok;
- DWORD icq_ProxyDestHost;
- WORD icq_ProxyDestPort;
- WORD icq_ProxyOurPort;
- time_t icq_LastKeepAlive; /* ig */
- char icq_config[256]; /* ig */
- struct CtdlICQ_CL *icq_cl; /* ig */
- int icq_numcl; /* ig */
-};
-
/* <ig> */
-/* ICQROOM is the name of the room in which each user's ICQ configuration
- * and contact lists will be stored. (It's a personal room.)
- */
-#define ICQROOM "My ICQ Config"
-
/* MIME types to use for storing ICQ stuff */
#define ICQMIME "application/x-citadel-icq" /* configuration */
#define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
{
DWORD remote_uin, new_status, remote_ip, remote_real_ip;
DWORD remote_port; /* Why Mirabilis used 4 bytes for port? */
- icq_ContactItem *ptr;
char buf[256];
remote_uin = Chars_2_DW(pak.data);
***********************************/
int icq_Connect(const char *hostname, int port)
{
- char buf[1024], un = 1;
- char our_host[256], tmpbuf[256];
+ char buf[1024];
+ char tmpbuf[256];
int conct, length, res;
struct sockaddr_in sin, prsin; /* used to store inet addr stuff */
struct hostent *host_struct; /* used in DNS llokup */
struct tm *tm_str;
int s, len;
char buf[1024];
- cback acback;
s = icq_SockRead((ThisICQ->icq_Sok), &pak.head, sizeof(pak));
if (s <= 0) {
char icq_rm[ROOMNAMELEN];
strcpy(hold_rm, CC->quickroom.QRname);
- MailboxName(icq_rm, &CC->usersupp, ICQROOM);
+ MailboxName(icq_rm, &CC->usersupp, CONFIGROOM);
strcpy(ThisICQ->icq_config, "");
if (getroom(&CC->quickroom, icq_rm) != 0) {
/* We want the last (and probably only) config in this room */
lprintf(9, "We're in <%s> looking for config\n",
CC->quickroom.QRname);
- CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, CtdlICQ_Read_Config_Backend);
+ CtdlForEachMessage(MSGS_LAST, 1, ICQMIME, NULL,
+ CtdlICQ_Read_Config_Backend);
getroom(&CC->quickroom, hold_rm);
return;
}
fclose(fp);
/* this handy API function does all the work for us */
- CtdlWriteObject(ICQROOM, ICQMIME, temp, 1, 0, 1);
+ CtdlWriteObject(CONFIGROOM, ICQMIME, temp, &CC->usersupp, 0, 1, 0);
unlink(temp);
}
fclose(fp);
/* this handy API function does all the work for us */
- CtdlWriteObject(ICQROOM, ICQCLMIME, temp, 1, 0, 1);
+ CtdlWriteObject(CONFIGROOM, ICQCLMIME, temp, &CC->usersupp, 0, 1, 0);
unlink(temp);
}
char icq_rm[ROOMNAMELEN];
strcpy(hold_rm, CC->quickroom.QRname);
- MailboxName(icq_rm, &CC->usersupp, ICQROOM);
+ MailboxName(icq_rm, &CC->usersupp, CONFIGROOM);
strcpy(ThisICQ->icq_config, "");
if (getroom(&CC->quickroom, icq_rm) != 0) {
}
/* We want the last (and probably only) list in this room */
- CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, CtdlICQ_Read_CL_Backend);
+ CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, NULL,
+ CtdlICQ_Read_CL_Backend);
getroom(&CC->quickroom, hold_rm);
}
if (ThisICQ->icq_Sok < 0) return;
icq_ContClear();
+ CtdlICQ_Read_CL();
if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
if (ThisICQ->icq_cl[i].uin > 0L) {
icq_ContAddUser(ThisICQ->icq_cl[i].uin);
}
icq_SendContactList();
+
}
* to log on to the ICQ server.
*/
void CtdlICQ_Login_If_Possible(void) {
- char buf[256];
long uin;
char pass[256];
uin = extract_long(ThisICQ->icq_config, 0);
extract(pass, ThisICQ->icq_config, 1);
- lprintf(9, "Here's my config: %s\n", ThisICQ->icq_config);
-
if ( (uin > 0L) && (strlen(pass)>0) ) {
icq_Init(uin, pass);
if (icq_Connect("icq1.mirabilis.com", 4000) >= 0) {
icq_Login(STATUS_ONLINE);
- CtdlICQ_Refresh_Contact_List();
}
}
}
void CtdlICQ_after_cmd_hook(void)
{
if (ThisICQ->icq_Sok >= 0) {
- if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 90 ) {
+ if ( (time(NULL) - ThisICQ->icq_LastKeepAlive) > 60 ) {
icq_KeepAlive();
ThisICQ->icq_LastKeepAlive = time(NULL);
}
-void cmd_icql(char *argbuf)
-{
- safestrncpy(ThisICQ->icq_config, argbuf, 256);
- CtdlICQ_Write_Config();
-
- cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
- CtdlICQ_Login_If_Possible();
-}
-
-
/*
char from[256];
int num_delivered;
+ int i;
+ /* Default sender is 'uin@icq' syntax */
sprintf(from, "%ld@icq", uin);
+
+ /* Use the sender's name if we have it inthe contact list */
+ if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
+ if (uin == ThisICQ->icq_cl[i].uin) {
+ safestrncpy(from, ThisICQ->icq_cl[i].name, 256);
+ }
+ }
+
num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
lprintf(9, "Delivered to %d users\n", num_delivered);
}
struct CtdlICQ_CL *ptr;
+ CtdlICQ_Read_CL();
ptr = CtdlICQ_CLent(uin);
safestrncpy(ptr->name, nick, 32);
ptr->status = STATUS_OFFLINE;
-void cmd_icqa(char *argbuf) {
+void cmd_cicq(char *argbuf) {
+ char cmd[256];
long uin;
+ char pass[256];
+ char buf[256];
+ int i;
- uin = extract_long(argbuf, 0);
- if (uin <= 0L) {
- cprintf("%d You must supply a uin.\n", ERROR);
+ if (!(CC->logged_in)) {
+ cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
+ return;
+ }
+ extract(cmd, argbuf, 0);
+
+ /* "CICQ login" tells us how to log in. */
+ if (!strcasecmp(cmd, "login")) {
+ uin = extract_long(argbuf, 1);
+ extract(pass, argbuf, 2);
+ sprintf(ThisICQ->icq_config, "%ld|%s|", uin, pass);
+ if (uin > 0L) {
+ CtdlICQ_Write_Config();
+ cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
+ CtdlICQ_Login_If_Possible();
+ } else {
+ cprintf("%d You must supply a UIN.\n", ERROR);
+ }
return;
}
- CtdlICQ_Read_CL();
-
- /* This function is normally used to obtain a relevant pointer, but
- * it also creates an entry in the contact list if one isn't there.
- */
- CtdlICQ_CLent(uin);
+ /* "CICQ getcl" returns the contact list */
+ if (!strcasecmp(cmd, "getcl")) {
+ CtdlICQ_Read_CL();
+ cprintf("%d Your ICQ contact list:\n", LISTING_FOLLOWS);
+ if (ThisICQ->icq_numcl > 0) {
+ for (i=0; i<ThisICQ->icq_numcl; ++i) {
+ cprintf("%ld|%s|%s|\n",
+ ThisICQ->icq_cl[i].uin,
+ ThisICQ->icq_cl[i].name,
+ icq_ConvertStatus2Str(
+ ThisICQ->icq_cl[i].status)
+ );
+ }
+ }
+ cprintf("000\n");
+ return;
+ }
- /* Save the new contact list and tell the ICQ server about it */
- CtdlICQ_Write_CL();
- CtdlICQ_Refresh_Contact_List();
+ /* "CICQ putcl" accepts a new contact list from the client */
+ if (!strcasecmp(cmd, "putcl")) {
+ cprintf("%d Send contact list\n", SEND_LISTING);
+ ThisICQ->icq_numcl = 0;
+ while (client_gets(buf), strcmp(buf, "000")) {
+ uin = extract_long(buf, 0);
+ if (uin > 0L) {
+ CtdlICQ_CLent(uin);
+ }
+ }
+ CtdlICQ_Write_CL();
+ CtdlICQ_Refresh_Contact_List();
+ return;
+ }
- /* Leave the user clueless as to what happened, because we really
- * don't know (and this is why I hate asynchronous protocols).
- */
- cprintf("%d Ok (maybe)\n", OK);
+ /* "CICQ status" returns the connected/notconnected status */
+ if (!strcasecmp(cmd, "status")) {
+ cprintf("%d %d\n", OK,
+ ((ThisICQ->icq_Sok >= 0) ? 1 : 0) );
+ return;
+ }
- /* Request more info on this user from the server, which will arrive
- * at some time in the future. Maybe.
- */
- icq_SendInfoReq(uin);
+ cprintf("%d Invalid subcommand\n", ERROR);
}
+
+
+
/*
* During an RWHO command, we want to append our ICQ information.
*/
0, /* no session ID */
ThisICQ->icq_cl[i].name,
icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
- " ", /* FIX add host */
+ ThisICQ->icq_cl[i].host,
" ", /* no client */
time(NULL), /* now? */
" ", /* no last command */
void CtdlICQ_Logged(void) {
- CtdlICQ_Read_CL();
CtdlICQ_Refresh_Contact_List();
}
void CtdlICQ_UserOnline(DWORD uin, DWORD status, DWORD ip,
DWORD port, DWORD realip) {
+ DWORD decoded_ip;
+
CtdlICQ_Status_Update(uin, status);
+ decoded_ip = ntohl(ip);
+ locate_host(CtdlICQ_CLent(uin)->host, (struct in_addr *)&decoded_ip);
}
CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
- CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
- CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add ICQ contact");
- CtdlRegisterXmsgHook(CtdlICQ_Send_Msg);
+ CtdlRegisterProtoHook(cmd_cicq, "CICQ", "Configure Citadel ICQ");
+ CtdlRegisterXmsgHook(CtdlICQ_Send_Msg, XMSG_PRI_FOREIGN);
/* Tell the code formerly known as icqlib about our callbacks */
icq_Log = CtdlICQlog;