/*
* 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.
-
- $Id$
- $Log$
- Revision 1.2 1999/07/23 04:27:45 ajc
- Added CtdlWriteObject() to store generic data in the msgbase
-
- Revision 1.1 1999/07/19 04:12:49 ajc
- * serv_icq.c, serv_icq.mk: added (separate makefile is temporary)
-
- Revision 1.16 1998/12/08 16:00:59 denis
- Cleaned up a little before releasing
-
- Revision 1.15 1998/11/25 19:18:16 denis
- Added close icq_ProxySok in icq_Disconnect
-
- Revision 1.14 1998/11/25 09:48:49 denis
- icq_GetProxySok and icq_HandleProxyResponse methods added
- Connection terminated support added
-
- Revision 1.13 1998/11/19 12:22:48 denis
- SOCKS support cleaned a little
- icq_RecvUrl renamed to icq_RecvURL
- icq_ProxyAuth added for Username/Password Authentication
- URL/Description order inverted
- icq_Quit splitted to icq_Logout and icq_Disconnect
- icq_ProxyName and icq_ProxyPass range checking added
-
- Revision 1.12 1998/11/18 16:21:29 denis
- Fixed SOCKS5 proxy support
+ *
+ * Incomplete list of changes I made:
+ * * All globals placed into struct ctdl_icq_handle so we can do it per-thread
+ * * References to globals changed to ThisICQ->globalname
+ * * malloc->mallok, free->phree, strdup->strdoop, for memory leak checking
+ * * Added a bunch of #include's needed by Citadel
+ * * Most of the Citadel-specific code is appended to the end
+ *
+ * $Id$
*/
#include "serv_icq.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.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"
-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;
- FILE *icq_MyConfigFile; /* ig */
- time_t icq_LastKeepAlive; /* ig */
+/*
+ * Contact list in memory
+ */
+struct CtdlICQ_CL {
+ DWORD uin;
+ char name[32];
+ DWORD status;
+ char host[26];
};
+
/* <ig> */
-#define ICQROOM "My ICQ Config"
-#define ICQMIME "application/x-citadel-icq"
+
+/* MIME types to use for storing ICQ stuff */
+#define ICQMIME "application/x-citadel-icq" /* configuration */
+#define ICQCLMIME "application/x-citadel-icq-cl" /* contact list */
+
+/* Citadel server TSD symbol for use by serv_icq */
unsigned long SYM_CTDL_ICQ;
#define ThisICQ ((struct ctdl_icq_handle *)CtdlGetUserData(SYM_CTDL_ICQ))
+
extern struct CitContext *ContextList;
-#define MODULE_NAME "ICQ metaclient"
-#define MODULE_AUTHOR "Art Cancro"
-#define MODULE_EMAIL "ajc@uncnsrd.mt-kisco.ny.us"
-#define MAJOR_VERSION 0
-#define MINOR_VERSION 1
-static struct DLModule_Info info =
-{
- MODULE_NAME,
- MODULE_AUTHOR,
- MODULE_EMAIL,
- MAJOR_VERSION,
- MINOR_VERSION
-};
/* </ig> */
i->icq_Status = STATUS_OFFLINE;
i->icq_Sok = (-1);
i->icq_LogLevel = 9;
- i->icq_MyConfigFile = NULL;
}
int icq_SockWrite(int sok, const void *buf, size_t count)
{
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) {
memset((ThisICQ->icq_ServMess), FALSE, sizeof((ThisICQ->icq_ServMess)));
(ThisICQ->icq_Uin) = uin;
if ((ThisICQ->icq_Password))
- free((ThisICQ->icq_Password));
- (ThisICQ->icq_Password) = strdup(password);
+ phree((ThisICQ->icq_Password));
+ (ThisICQ->icq_Password) = strdoop(password);
}
void icq_Done(void)
{
if ((ThisICQ->icq_Password))
- free((ThisICQ->icq_Password));
+ phree((ThisICQ->icq_Password));
}
/******************************
if (FD_ISSET((ThisICQ->icq_Sok), &readfds)) {
icq_HandleServerResponse();
did_something = 1;
- sleep(1);
+ /* sleep(1); */
}
} while (did_something);
}
***************************/
void icq_ContAddUser(DWORD cuin)
{
- icq_ContactItem *p = malloc(sizeof(icq_ContactItem));
+ icq_ContactItem *p = mallok(sizeof(icq_ContactItem));
icq_ContactItem *ptr = (ThisICQ->icq_ContFirst);
p->uin = cuin;
p->next = 0L;
return;
if (ptr->uin == cuin) {
(ThisICQ->icq_ContFirst) = ptr->next;
- free(ptr);
+ phree(ptr);
ptr = (ThisICQ->icq_ContFirst);
}
while (ptr->next) {
if (ptr->next->uin == cuin) {
ptr->next = ptr->next->next;
- free(ptr->next);
+ phree(ptr->next);
}
ptr = ptr->next;
}
icq_ContactItem *tmp, *ptr = (ThisICQ->icq_ContFirst);
while (ptr) {
tmp = ptr->next;
- free(ptr);
+ phree(ptr);
ptr = tmp;
(ThisICQ->icq_ContFirst) = ptr;
}
void icq_SetProxy(const char *phost, unsigned short pport, int pauth, const char *pname, const char *ppass)
{
if ((ThisICQ->icq_ProxyHost))
- free((ThisICQ->icq_ProxyHost));
+ phree((ThisICQ->icq_ProxyHost));
if ((ThisICQ->icq_ProxyName))
- free((ThisICQ->icq_ProxyName));
+ phree((ThisICQ->icq_ProxyName));
if ((ThisICQ->icq_ProxyPass))
- free((ThisICQ->icq_ProxyPass));
+ phree((ThisICQ->icq_ProxyPass));
if (strlen(pname) > 255) {
if (icq_Log && (ThisICQ->icq_LogLevel) >= ICQ_LOG_ERROR)
(*icq_Log) (time(0L), ICQ_LOG_ERROR, "[SOCKS] User name greater than 255 chars\n");
return;
}
(ThisICQ->icq_UseProxy) = 1;
- (ThisICQ->icq_ProxyHost) = strdup(phost);
+ (ThisICQ->icq_ProxyHost) = strdoop(phost);
(ThisICQ->icq_ProxyPort) = pport;
(ThisICQ->icq_ProxyAuth) = pauth;
- (ThisICQ->icq_ProxyName) = strdup(pname);
- (ThisICQ->icq_ProxyPass) = strdup(ppass);
+ (ThisICQ->icq_ProxyName) = strdoop(pname);
+ (ThisICQ->icq_ProxyPass) = strdoop(ppass);
}
void icq_UnsetProxy()
/***********************************************************************/
-/* config file manipulation routines ... probably temporary until we can
- * get something more robust written
+/*
+ * Callback function for CtdlICQ_Read_Config()
*/
-
-/* Delete a key */
-void CtdlICQ_Config_Delete(char *key) {
- long readpos, writepos;
- char buf[256], keyplusspace[256];
+void CtdlICQ_Read_Config_Backend(long msgnum) {
+ struct CtdlMessage *msg;
+ int pos;
char *ptr;
- if (ThisICQ->icq_MyConfigFile == NULL) return;
- readpos = 0L;
- writepos = 0L;
- sprintf(keyplusspace, "%s ", key);
-
- while(1) {
- fseek(ThisICQ->icq_MyConfigFile, readpos, 0);
- ptr = fgets(buf, 256, ThisICQ->icq_MyConfigFile);
- readpos = ftell(ThisICQ->icq_MyConfigFile);
- if (ptr == NULL) {
- fflush(ThisICQ->icq_MyConfigFile);
- ftruncate(fileno(ThisICQ->icq_MyConfigFile), writepos);
- return;
- } else {
- if (strncasecmp(buf, keyplusspace,
- strlen(keyplusspace))) {
- fseek(ThisICQ->icq_MyConfigFile, writepos, 0);
- fprintf(ThisICQ->icq_MyConfigFile, "%s", buf);
- writepos = ftell(ThisICQ->icq_MyConfigFile);
+ lprintf(9, "Fetching my ICQ configuration (msg %ld)\n", msgnum);
+ msg = CtdlFetchMessage(msgnum);
+ if (msg != NULL) {
+ ptr = msg->cm_fields['M'];
+ pos = pattern2(ptr, "\n\n");
+ if (pos >= 0) {
+ while (pos > 0) {
+ ++ptr;
+ --pos;
}
+ ++ptr;
+ ++ptr;
+ safestrncpy(ThisICQ->icq_config, ptr, 256);
}
+ CtdlFreeMessage(msg);
+ } else {
+ lprintf(9, "...it ain't there?\n");
}
}
-/* Write a key */
-void CtdlICQ_Config_Write(char *key, char *contents) {
+/*
+ * If this user has an ICQ configuration on disk, read it into memory.
+ */
+void CtdlICQ_Read_Config(void) {
+ char hold_rm[ROOMNAMELEN];
+ char icq_rm[ROOMNAMELEN];
+
+ strcpy(hold_rm, CC->quickroom.QRname);
+ MailboxName(icq_rm, &CC->usersupp, USERCONFIGROOM);
+ strcpy(ThisICQ->icq_config, "");
+
+ if (getroom(&CC->quickroom, icq_rm) != 0) {
+ getroom(&CC->quickroom, hold_rm);
+ return;
+ }
- char buf[256]; /* FIX */
+ /* 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, NULL,
+ CtdlICQ_Read_Config_Backend);
+ getroom(&CC->quickroom, hold_rm);
+ return;
+}
+
+
+
+/*
+ * Write our config to disk
+ */
+void CtdlICQ_Write_Config(void) {
+ char temp[PATH_MAX];
+ FILE *fp;
- CtdlICQ_Config_Delete(key);
- fseek(ThisICQ->icq_MyConfigFile, 0L, SEEK_END);
- fprintf(ThisICQ->icq_MyConfigFile, "%s %s\n", key, contents);
+ strcpy(temp, tmpnam(NULL));
- /****** FIX ****** TEMPORARY HACK TO SEE STUFF ***********/
+ fp = fopen(temp, "w");
+ if (fp == NULL) return;
+ fprintf(fp, "%s|\n", ThisICQ->icq_config);
+ fclose(fp);
- fflush(ThisICQ->icq_MyConfigFile);
- sprintf(buf, "icq/%ld", CC->usersupp.usernum);
- CtdlWriteObject(ICQROOM, ICQMIME, buf, 1, 0, 1);
+ /* this handy API function does all the work for us */
+ CtdlWriteObject(USERCONFIGROOM, ICQMIME, temp, &CC->usersupp, 0, 1, 0);
+
+ unlink(temp);
}
-/* Read a key */
-void CtdlICQ_Config_Read(char *contents, char *key) {
- char buf[256];
- strcpy(contents, "");
- rewind(ThisICQ->icq_MyConfigFile);
- while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
- buf[strlen(buf)-1]=0;
- if ( (!strncasecmp(buf, key, strlen(key)))
- && (isspace(buf[strlen(key)])) ) {
- strcpy(contents, &buf[strlen(key)+1]);
+
+
+/*
+ * Write our contact list to disk
+ */
+void CtdlICQ_Write_CL(void) {
+ char temp[PATH_MAX];
+ FILE *fp;
+ int i;
+
+ strcpy(temp, tmpnam(NULL));
+
+ fp = fopen(temp, "w");
+ if (fp == NULL) return;
+
+ if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i) {
+ fprintf(fp, "%ld|%s|\n",
+ ThisICQ->icq_cl[i].uin,
+ ThisICQ->icq_cl[i].name);
+ }
+ fclose(fp);
+
+ /* this handy API function does all the work for us */
+ CtdlWriteObject(USERCONFIGROOM, ICQCLMIME, temp, &CC->usersupp, 0, 1, 0);
+
+ unlink(temp);
+}
+
+
+
+
+
+/*
+ * Callback function for CtdlICQ_Read_CL()
+ */
+void CtdlICQ_Read_CL_Backend(long msgnum) {
+ struct CtdlMessage *msg;
+ int pos;
+ char *ptr, *cont;
+ int i;
+
+ msg = CtdlFetchMessage(msgnum);
+ if (msg != NULL) {
+ ptr = msg->cm_fields['M'];
+ pos = pattern2(ptr, "\n\n");
+ if (pos >= 0) {
+ while (pos > 0) {
+ ++ptr;
+ --pos;
+ }
+ ++ptr;
+ ++ptr;
+ for (i=0; i<strlen(ptr); ++i)
+ if (ptr[i]=='\n') ++ThisICQ->icq_numcl;
+ if (ThisICQ->icq_numcl) {
+ ThisICQ->icq_cl = mallok(
+ (ThisICQ->icq_numcl *
+ sizeof (struct CtdlICQ_CL)));
+ i=0;
+ while (cont=strtok(ptr, "\n"), cont != NULL) {
+ ThisICQ->icq_cl[i].uin =
+ extract_long(cont, 0);
+ extract(ThisICQ->icq_cl[i].name,
+ cont, 1);
+ ThisICQ->icq_cl[i].status =
+ STATUS_OFFLINE;
+ ++i;
+ ptr = NULL;
+ }
+ }
}
+ CtdlFreeMessage(msg);
}
}
+/*
+ * Read contact list into memory
+ */
+void CtdlICQ_Read_CL(void) {
+ char hold_rm[ROOMNAMELEN];
+ char icq_rm[ROOMNAMELEN];
+
+ strcpy(hold_rm, CC->quickroom.QRname);
+ MailboxName(icq_rm, &CC->usersupp, USERCONFIGROOM);
+ strcpy(ThisICQ->icq_config, "");
+
+ if (getroom(&CC->quickroom, icq_rm) != 0) {
+ getroom(&CC->quickroom, hold_rm);
+ return;
+ }
+
+ /* Free any contact list already in memory */
+ if (ThisICQ->icq_numcl) {
+ phree(ThisICQ->icq_cl);
+ ThisICQ->icq_numcl = 0;
+ }
+
+ /* We want the last (and probably only) list in this room */
+ CtdlForEachMessage(MSGS_LAST, 1, ICQCLMIME, NULL,
+ CtdlICQ_Read_CL_Backend);
+ getroom(&CC->quickroom, hold_rm);
+}
+
+
+/*
+ * Returns a pointer to a CtdlICQ_CL struct for a given uin, creating an
+ * entry in the table if necessary
+ */
+struct CtdlICQ_CL *CtdlICQ_CLent(DWORD uin) {
+ int i;
+
+ if (ThisICQ->icq_numcl) for (i=0; i<ThisICQ->icq_numcl; ++i)
+ if (ThisICQ->icq_cl[i].uin == uin)
+ return (&ThisICQ->icq_cl[i]);
+
+ ++ThisICQ->icq_numcl;
+ ThisICQ->icq_cl = reallok(ThisICQ->icq_cl,
+ (ThisICQ->icq_numcl * sizeof(struct CtdlICQ_CL)) );
+ memset(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1],
+ 0, sizeof(struct CtdlICQ_CL));
+ ThisICQ->icq_cl[ThisICQ->icq_numcl - 1].uin = uin;
+ return(&ThisICQ->icq_cl[ThisICQ->icq_numcl - 1]);
+}
+
+
+
/*
* Refresh the contact list
*/
void CtdlICQ_Refresh_Contact_List(void) {
- char buf[256];
- long contact_uin;
+ int i;
if (ThisICQ->icq_Sok < 0) return;
-
icq_ContClear();
- while (fgets(buf, 256, ThisICQ->icq_MyConfigFile) != NULL) {
- buf[strlen(buf)-1]=0;
- contact_uin = atol(buf);
- if (contact_uin > 0L) {
- icq_ContAddUser(contact_uin);
- icq_ContSetVis(contact_uin);
+ 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_ContSetVis(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];
CtdlICQ_Logout_If_Connected();
+ CtdlICQ_Read_Config();
- CtdlICQ_Config_Read(buf, "uin");
- uin = atol(buf);
- CtdlICQ_Config_Read(pass, "pass");
+ uin = extract_long(ThisICQ->icq_config, 0);
+ extract(pass, ThisICQ->icq_config, 1);
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();
}
}
}
}
+
+/*
+ * End-of-session cleanup
+ */
+void CtdlICQ_session_stopdown_hook(void) {
+ icq_Done();
+}
+
+
+
/*
* icq_Main() needs to be called as frequently as possible. We'll do it
* following the completion of each Citadel server command.
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 CtdlICQ_session_logout_hook(void)
{
+ lprintf(9, "Shutting down ICQ\n");
CtdlICQ_Logout_If_Connected();
- if (ThisICQ->icq_MyConfigFile != NULL)
- fclose(ThisICQ->icq_MyConfigFile);
+
+ /* Free the memory used by the contact list */
+ if (ThisICQ->icq_numcl) {
+ phree(ThisICQ->icq_cl);
+ ThisICQ->icq_numcl = 0;
+ }
}
*/
void CtdlICQ_session_login_hook(void)
{
- char buf[256];
- if (ThisICQ->icq_MyConfigFile != NULL)
- fclose(ThisICQ->icq_MyConfigFile);
-
- /* Open this user's ICQ config file; create it if it's not there */
- sprintf(buf, "icq/%ld", CC->usersupp.usernum);
- ThisICQ->icq_MyConfigFile = fopen(buf, "r+");
- if (ThisICQ->icq_MyConfigFile == NULL)
- ThisICQ->icq_MyConfigFile = fopen(buf, "w+");
- if (ThisICQ->icq_MyConfigFile == NULL)
- lprintf(2, "Cannot create %s: %s\n", buf, strerror(errno));
-
- /* Login to the ICQ server if we already have info on file. */
+ /* If this user has an ICQ config on file, start it up. */
CtdlICQ_Login_If_Possible();
}
-void cmd_icqa(char *argbuf) {
- char cuin[256];
- extract(cuin, argbuf, 0);
- if (atol(cuin) > 0L) {
- CtdlICQ_Config_Write(cuin, "");
- icq_SendInfoReq(atol(cuin));
- cprintf("%d %s added to your ICQ contact list.\n", OK, cuin);
- } else {
- cprintf("%d You must specify a numeric ICQ uin.\n", ERROR);
+
+
+
+/*
+ * Here's what we have to do when an ICQ message arrives!
+ */
+void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
+ BYTE day, BYTE month, WORD year,
+ const char *msg) {
+
+ 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);
+ }
}
- CtdlICQ_Refresh_Contact_List();
+ num_delivered = PerformXmsgHooks(from, CC->curr_user, (char *)msg);
+ lprintf(9, "Delivered to %d users\n", num_delivered);
}
+void CtdlICQ_InfoReply(unsigned long uin, const char *nick,
+ const char *first, const char *last,
+ const char *email, char auth) {
-void cmd_icql(char *argbuf)
-{
- char uin[256];
- char password[256];
+ struct CtdlICQ_CL *ptr;
+
+ CtdlICQ_Read_CL();
+ ptr = CtdlICQ_CLent(uin);
+ safestrncpy(ptr->name, nick, 32);
+ ptr->status = STATUS_OFFLINE;
+ lprintf(9, "Today we learned that %ld is %s\n", uin, nick);
+ CtdlICQ_Write_CL();
+}
- extract(uin, argbuf, 0);
- extract(password, argbuf, 1);
- CtdlICQ_Config_Write("uin", uin);
- CtdlICQ_Config_Write("pass", password);
- cprintf("%d Ok ... will try to log on to ICQ.\n", OK);
- CtdlICQ_Login_If_Possible();
-}
+/* send an icq */
+int CtdlICQ_Send_Msg(char *from, char *recp, char *msg) {
+ int is_aticq = 0;
+ int i;
+ DWORD target_uin = 0L;
-/*
- * When deleting a user from the server, be sure to delete
- * his/her/its ICQ config file as well.
- */
-void CtdlICQ_DeleteUserConfigFile(char *uname, long unum) {
- char buf[256];
- sprintf(buf, "icq/%ld", unum);
- unlink(buf);
+ /* If this is an incoming ICQ from someone on the contact list,
+ * change the sender from "uin@icq" to the contact name.
+ */
+ is_aticq = 0;
+ for (i=0; i<strlen(from); ++i)
+ if (!strcasecmp(&from[i], "@icq")) {
+ is_aticq = 1;
+ }
+ if (is_aticq == 1) if (ThisICQ->icq_numcl > 0) {
+ for (i=0; i<ThisICQ->icq_numcl; ++i) {
+ if (ThisICQ->icq_cl[i].uin == atol(from))
+ strcpy(from, ThisICQ->icq_cl[i].name);
+ }
+ }
+
+
+ /* Handle "uin@icq" syntax */
+ is_aticq = 0;
+ for (i=0; i<strlen(recp); ++i)
+ if (!strcasecmp(&recp[i], "@icq")) {
+ is_aticq = 1;
+ }
+ if (is_aticq == 1) target_uin = atol(recp);
+
+ /* Handle "nick" syntax */
+ if (target_uin == 0L) if (ThisICQ->icq_numcl > 0) {
+ for (i=0; i<ThisICQ->icq_numcl; ++i) {
+ if (!strcasecmp(ThisICQ->icq_cl[i].name, recp)) {
+ target_uin = ThisICQ->icq_cl[i].uin;
+ }
+ }
+ }
+
+
+ if (target_uin == 0L) return(0);
+
+ if (strlen(msg) > 0) icq_SendMessage(target_uin, msg);
+ return(1);
}
-/*
- * Here's what we have to do when an ICQ message arrives!
- */
-void CtdlICQ_Incoming_Message(DWORD uin, BYTE hour, BYTE minute,
- BYTE day, BYTE month, WORD year,
- const char *msg) {
-
- char from[256];
- char nick[256];
+void cmd_cicq(char *argbuf) {
+ char cmd[256];
+ long uin;
+ char pass[256];
+ char buf[256];
+ int i;
- sprintf(from, "%ld", uin);
- CtdlICQ_Config_Read(nick, from);
- if (strlen(nick) == 0) {
- icq_SendInfoReq(atol(from));
+ 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;
}
- sprintf(from, "%ld@icq (%s)", uin, nick);
- if (CtdlSendExpressMessageFunc) {
- CtdlSendExpressMessageFunc(from, CC->curr_user, msg);
-
+ /* "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;
+ }
+ /* "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;
+ }
- } else {
- lprintf(7, "Hmm, no CtdlSendExpressMessageFunc defined!\n");
+ /* "CICQ status" returns the connected/notconnected status */
+ if (!strcasecmp(cmd, "status")) {
+ cprintf("%d %d\n", OK,
+ ((ThisICQ->icq_Sok >= 0) ? 1 : 0) );
+ return;
}
+
+ cprintf("%d Invalid subcommand\n", ERROR);
}
-CtdlICQ_InfoReply(unsigned long uin, const char *nick,
- const char *first, const char *last,
- const char *email, char auth) {
- char str_uin[256];
- sprintf(str_uin, "%ld", uin);
- CtdlICQ_Config_Write(str_uin, nick);
+/*
+ * During an RWHO command, we want to append our ICQ information.
+ */
+void CtdlICQ_rwho(void) {
+ int i;
+
+ if (ThisICQ->icq_numcl > 0) for (i=0; i<ThisICQ->icq_numcl; ++i)
+ if (ThisICQ->icq_cl[i].status != STATUS_OFFLINE)
+ cprintf("%d|%s|%s|%s|%s|%ld|%s|%s\n",
+ 0, /* no session ID */
+ ThisICQ->icq_cl[i].name,
+ icq_ConvertStatus2Str(ThisICQ->icq_cl[i].status),
+ ThisICQ->icq_cl[i].host,
+ " ", /* no client */
+ time(NULL), /* now? */
+ " ", /* no last command */
+ "ICQ" /* flags */
+ );
+}
+
+
+void CtdlICQ_Status_Update(DWORD uin, DWORD status) {
+ struct CtdlICQ_CL *ptr;
+
+ ptr = CtdlICQ_CLent(uin);
+ ptr->status = status;
+ if (strlen(ptr->name) == 0) icq_SendInfoReq(ptr->uin);
}
+void CtdlICQ_Logged(void) {
+ CtdlICQ_Refresh_Contact_List();
+}
-struct DLModule_Info *Dynamic_Module_Init(void)
-{
- /* Create a directory to store ICQ stuff in.
- * It's ok if the directory is already there.
- */
- if (mkdir("icq", 0700) != 0) if (errno != EEXIST) {
- lprintf(2, "Can't create icq subdirectory: %s\n",
- strerror(errno));
- }
+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);
+}
+
+
+void CtdlICQ_UserOffline(DWORD uin) {
+ CtdlICQ_Status_Update(uin, STATUS_OFFLINE);
+}
+
+
+char *Dynamic_Module_Init(void)
+{
/* Make sure we've got a valid ThisICQ for each session */
SYM_CTDL_ICQ = CtdlGetDynamicSymbol();
/* Tell the Citadel server about our wonderful ICQ hooks */
CtdlRegisterSessionHook(CtdlICQ_session_startup_hook, EVT_START);
+ CtdlRegisterSessionHook(CtdlICQ_session_stopdown_hook, EVT_STOP);
CtdlRegisterSessionHook(CtdlICQ_session_logout_hook, EVT_LOGOUT);
CtdlRegisterSessionHook(CtdlICQ_session_login_hook, EVT_LOGIN);
CtdlRegisterSessionHook(CtdlICQ_after_cmd_hook, EVT_CMD);
- CtdlRegisterProtoHook(cmd_icql, "ICQL", "Log on to ICQ");
- CtdlRegisterProtoHook(cmd_icqa, "ICQA", "Add an ICQ contact");
- CtdlRegisterUserHook(CtdlICQ_DeleteUserConfigFile, EVT_PURGEUSER);
+ CtdlRegisterSessionHook(CtdlICQ_rwho, EVT_RWHO);
+ 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;
icq_RecvMessage = CtdlICQ_Incoming_Message;
icq_InfoReply = CtdlICQ_InfoReply;
+ icq_Disconnected = CtdlICQ_Login_If_Possible;
+ icq_Logged = CtdlICQ_Logged;
+ icq_UserStatusUpdate = CtdlICQ_Status_Update;
+ icq_UserOnline = CtdlICQ_UserOnline;
+ icq_UserOffline = CtdlICQ_UserOffline;
- return &info;
+ return "$Id$";
}