X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Finstmsg%2Fserv_instmsg.c;fp=citadel%2Fmodules%2Finstmsg%2Fserv_instmsg.c;h=0000000000000000000000000000000000000000;hb=f6fcf350671e3661f8f22696eb35133014ab6a14;hp=b54276cfd2be36cfa0d3eb648e35d1e16850e17c;hpb=2e4e67a1f7f65568abace99d13a71024ad06ebde;p=citadel.git diff --git a/citadel/modules/instmsg/serv_instmsg.c b/citadel/modules/instmsg/serv_instmsg.c deleted file mode 100644 index b54276cfd..000000000 --- a/citadel/modules/instmsg/serv_instmsg.c +++ /dev/null @@ -1,586 +0,0 @@ -/* - * This module handles instant messaging between users. - * - * Copyright (c) 1987-2020 by the citadel.org team - * - * This program is open source software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "sysdep.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "citadel.h" -#include "server.h" -#include "serv_instmsg.h" -#include "citserver.h" -#include "support.h" -#include "config.h" -#include "msgbase.h" -#include "user_ops.h" -#include "ctdl_module.h" - -struct imlog { - struct imlog *next; - long usernums[2]; - char usernames[2][128]; - time_t lastmsg; - int last_serial; - StrBuf *conversation; -}; - -struct imlog *imlist = NULL; - -/* - * This function handles the logging of instant messages to disk. - */ -void log_instant_message(struct CitContext *me, struct CitContext *them, char *msgtext, int serial_number) -{ - long usernums[2]; - long t; - struct imlog *iptr = NULL; - struct imlog *this_im = NULL; - - memset(usernums, 0, sizeof usernums); - usernums[0] = me->user.usernum; - usernums[1] = them->user.usernum; - - /* Always put the lower user number first, so we can use the array as a hash value which - * represents a pair of users. For a broadcast message one of the users will be 0. - */ - if (usernums[0] > usernums[1]) { - t = usernums[0]; - usernums[0] = usernums[1]; - usernums[1] = t; - } - - begin_critical_section(S_IM_LOGS); - - /* Look for an existing conversation in the hash table. - * If not found, create a new one. - */ - - this_im = NULL; - for (iptr = imlist; iptr != NULL; iptr = iptr->next) { - if ((iptr->usernums[0] == usernums[0]) && (iptr->usernums[1] == usernums[1])) { - /* Existing conversation */ - this_im = iptr; - } - } - if (this_im == NULL) { - /* New conversation */ - this_im = malloc(sizeof(struct imlog)); - memset(this_im, 0, sizeof (struct imlog)); - this_im->usernums[0] = usernums[0]; - this_im->usernums[1] = usernums[1]; - /* usernames[] and usernums[] might not be in the same order. This is not an error. */ - if (me) { - safestrncpy(this_im->usernames[0], me->user.fullname, sizeof this_im->usernames[0]); - } - if (them) { - safestrncpy(this_im->usernames[1], them->user.fullname, sizeof this_im->usernames[1]); - } - this_im->conversation = NewStrBuf(); - this_im->next = imlist; - imlist = this_im; - StrBufAppendBufPlain(this_im->conversation, HKEY("\r\n"), 0); - } - - /* Since it's possible for this function to get called more than once if a user is logged - * in on multiple sessions, we use the message's serial number to keep track of whether - * we've already logged it. - */ - if (this_im->last_serial != serial_number) - { - this_im->lastmsg = time(NULL); /* Touch the timestamp so we know when to flush */ - this_im->last_serial = serial_number; - StrBufAppendBufPlain(this_im->conversation, HKEY("

"), 0); - StrBufAppendBufPlain(this_im->conversation, me->user.fullname, -1, 0); - StrBufAppendBufPlain(this_im->conversation, HKEY(": "), 0); - StrEscAppend(this_im->conversation, NULL, msgtext, 0, 0); - StrBufAppendBufPlain(this_im->conversation, HKEY("

\r\n"), 0); - } - end_critical_section(S_IM_LOGS); -} - - -/* - * Delete any remaining instant messages - */ -void delete_instant_messages(void) { - struct ExpressMessage *ptr; - - begin_critical_section(S_SESSION_TABLE); - while (CC->FirstExpressMessage != NULL) { - ptr = CC->FirstExpressMessage->next; - if (CC->FirstExpressMessage->text != NULL) - free(CC->FirstExpressMessage->text); - free(CC->FirstExpressMessage); - CC->FirstExpressMessage = ptr; - } - end_critical_section(S_SESSION_TABLE); -} - - -/* - * Retrieve instant messages - */ -void cmd_gexp(char *argbuf) { - struct ExpressMessage *ptr; - - if (CC->FirstExpressMessage == NULL) { - cprintf("%d No instant messages waiting.\n", ERROR + MESSAGE_NOT_FOUND); - return; - } - - begin_critical_section(S_SESSION_TABLE); - ptr = CC->FirstExpressMessage; - CC->FirstExpressMessage = CC->FirstExpressMessage->next; - end_critical_section(S_SESSION_TABLE); - - cprintf("%d %d|%ld|%d|%s|%s|%s\n", - LISTING_FOLLOWS, - ((ptr->next != NULL) ? 1 : 0), /* more msgs? */ - (long)ptr->timestamp, /* time sent */ - ptr->flags, /* flags */ - ptr->sender, /* sender of msg */ - CtdlGetConfigStr("c_nodename"), /* static for now (and possibly deprecated) */ - ptr->sender_email /* email or jid of sender */ - ); - - if (ptr->text != NULL) { - memfmout(ptr->text, "\n"); - free(ptr->text); - } - - cprintf("000\n"); - free(ptr); -} - - -/* - * Asynchronously deliver instant messages - */ -void cmd_gexp_async(void) { - - /* Only do this if the session can handle asynchronous protocol */ - if (CC->is_async == 0) return; - - /* And don't do it if there's nothing to send. */ - if (CC->FirstExpressMessage == NULL) return; - - cprintf("%d instant msg\n", ASYNC_MSG + ASYNC_GEXP); -} - - -/* - * Back end support function for send_instant_message() and company - */ -void add_xmsg_to_context(struct CitContext *ccptr, struct ExpressMessage *newmsg) -{ - struct ExpressMessage *findend; - - if (ccptr->FirstExpressMessage == NULL) { - ccptr->FirstExpressMessage = newmsg; - } - else { - findend = ccptr->FirstExpressMessage; - while (findend->next != NULL) { - findend = findend->next; - } - findend->next = newmsg; - } - - /* If the target context is a session which can handle asynchronous - * messages, go ahead and set the flag for that. - */ - set_async_waiting(ccptr); -} - - -/* - * This is the back end to the instant message sending function. - * Returns the number of users to which the message was sent. - * Sending a zero-length message tests for recipients without sending messages. - */ -int send_instant_message(char *lun, char *lem, char *x_user, char *x_msg) -{ - int message_sent = 0; /* number of successful sends */ - struct CitContext *ccptr; - struct ExpressMessage *newmsg = NULL; - int do_send = 0; /* 1 = send message; 0 = only check for valid recipient */ - static int serial_number = 0; /* this keeps messages from getting logged twice */ - - if (!IsEmptyStr(x_msg)) { - do_send = 1; - } - - /* find the target user's context and append the message */ - begin_critical_section(S_SESSION_TABLE); - ++serial_number; - for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) { - - if ( ((!strcasecmp(ccptr->user.fullname, x_user)) - || (!strcasecmp(x_user, "broadcast"))) - && (ccptr->can_receive_im) - && ((ccptr->disable_exp == 0) - || (CC->user.axlevel >= AxAideU)) ) { - if (do_send) { - newmsg = (struct ExpressMessage *) malloc(sizeof (struct ExpressMessage)); - memset(newmsg, 0, sizeof (struct ExpressMessage)); - time(&(newmsg->timestamp)); - safestrncpy(newmsg->sender, lun, sizeof newmsg->sender); - safestrncpy(newmsg->sender_email, lem, sizeof newmsg->sender_email); - if (!strcasecmp(x_user, "broadcast")) { - newmsg->flags |= EM_BROADCAST; - } - newmsg->text = strdup(x_msg); - - add_xmsg_to_context(ccptr, newmsg); - - /* and log it ... */ - if (ccptr != CC) { - log_instant_message(CC, ccptr, newmsg->text, serial_number); - } - } - ++message_sent; - } - } - end_critical_section(S_SESSION_TABLE); - return (message_sent); -} - - -/* - * send instant messages - */ -void cmd_sexp(char *argbuf) -{ - int message_sent = 0; - char x_user[USERNAME_SIZE]; - char x_msg[1024]; - char *lem; - char *x_big_msgbuf = NULL; - - if ((!(CC->logged_in)) && (!(CC->internal_pgm))) { - cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN); - return; - } - - lem = CC->cs_principal_id; - - extract_token(x_user, argbuf, 0, '|', sizeof x_user); - extract_token(x_msg, argbuf, 1, '|', sizeof x_msg); - - if (!x_user[0]) { - cprintf("%d You were not previously paged.\n", ERROR + NO_SUCH_USER); - return; - } - if ((!strcasecmp(x_user, "broadcast")) && (CC->user.axlevel < AxAideU)) { - cprintf("%d Higher access required to send a broadcast.\n", - ERROR + HIGHER_ACCESS_REQUIRED); - return; - } - /* This loop handles text-transfer pages */ - if (!strcmp(x_msg, "-")) { - message_sent = PerformXmsgHooks(CC->user.fullname, lem, x_user, ""); - if (message_sent == 0) { - if (CtdlGetUser(NULL, x_user)) - cprintf("%d '%s' does not exist.\n", - ERROR + NO_SUCH_USER, x_user); - else - cprintf("%d '%s' is not logged in " - "or is not accepting pages.\n", - ERROR + RESOURCE_NOT_OPEN, x_user); - return; - } - unbuffer_output(); - cprintf("%d Transmit message (will deliver to %d users)\n", - SEND_LISTING, message_sent); - x_big_msgbuf = malloc(SIZ); - memset(x_big_msgbuf, 0, SIZ); - while (client_getln(x_msg, sizeof x_msg) >= 0 && strcmp(x_msg, "000")) { - x_big_msgbuf = realloc(x_big_msgbuf, - strlen(x_big_msgbuf) + strlen(x_msg) + 4); - if (!IsEmptyStr(x_big_msgbuf)) - if (x_big_msgbuf[strlen(x_big_msgbuf)] != '\n') - strcat(x_big_msgbuf, "\n"); - strcat(x_big_msgbuf, x_msg); - } - PerformXmsgHooks(CC->user.fullname, lem, x_user, x_big_msgbuf); - free(x_big_msgbuf); - - /* This loop handles inline pages */ - } else { - message_sent = PerformXmsgHooks(CC->user.fullname, lem, x_user, x_msg); - - if (message_sent > 0) { - if (!IsEmptyStr(x_msg)) { - cprintf("%d Message sent", CIT_OK); - } - else { - cprintf("%d Ok to send message", CIT_OK); - } - if (message_sent > 1) { - cprintf(" to %d users", message_sent); - } - cprintf(".\n"); - } else { - if (CtdlGetUser(NULL, x_user)) { - cprintf("%d '%s' does not exist.\n", ERROR + NO_SUCH_USER, x_user); - } - else { - cprintf("%d '%s' is not logged in or is not accepting instant messages.\n", - ERROR + RESOURCE_NOT_OPEN, x_user); - } - } - - - } -} - - -/* - * Enter or exit paging-disabled mode - */ -void cmd_dexp(char *argbuf) -{ - int new_state; - - if (CtdlAccessCheck(ac_logged_in)) return; - - new_state = extract_int(argbuf, 0); - if ((new_state == 0) || (new_state == 1)) { - CC->disable_exp = new_state; - } - - cprintf("%d %d\n", CIT_OK, CC->disable_exp); -} - - -/* - * Request client termination - */ -void cmd_reqt(char *argbuf) { - struct CitContext *ccptr; - int sessions = 0; - int which_session; - struct ExpressMessage *newmsg; - - if (CtdlAccessCheck(ac_aide)) return; - which_session = extract_int(argbuf, 0); - - begin_critical_section(S_SESSION_TABLE); - for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) { - if ((ccptr->cs_pid == which_session) || (which_session == 0)) { - - newmsg = (struct ExpressMessage *) - malloc(sizeof (struct ExpressMessage)); - memset(newmsg, 0, - sizeof (struct ExpressMessage)); - time(&(newmsg->timestamp)); - safestrncpy(newmsg->sender, CC->user.fullname, - sizeof newmsg->sender); - newmsg->flags |= EM_GO_AWAY; - newmsg->text = strdup("Automatic logoff requested."); - - add_xmsg_to_context(ccptr, newmsg); - ++sessions; - - } - } - end_critical_section(S_SESSION_TABLE); - cprintf("%d Sent termination request to %d sessions.\n", CIT_OK, sessions); -} - - -/* - * This is the back end for flush_conversations_to_disk() - * At this point we've isolated a single conversation (struct imlog) - * and are ready to write it to disk. - */ -void flush_individual_conversation(struct imlog *im) { - struct CtdlMessage *msg; - long msgnum = 0; - char roomname[ROOMNAMELEN]; - StrBuf *MsgBuf, *FullMsgBuf; - - StrBufAppendBufPlain(im->conversation, HKEY( - "\r\n" - "\r\n" - ), 0 - ); - - MsgBuf = StrBufRFC2047encodeMessage(im->conversation); - FlushStrBuf(im->conversation); - FullMsgBuf = NewStrBufPlain(NULL, StrLength(im->conversation) + 100); - - StrBufAppendBufPlain(FullMsgBuf, HKEY( - "Content-type: text/html; charset=UTF-8\r\n" - "Content-Transfer-Encoding: quoted-printable\r\n" - "\r\n" - ), 0 - ); - StrBufAppendBuf (FullMsgBuf, MsgBuf, 0); - FreeStrBuf(&MsgBuf); - - msg = malloc(sizeof(struct CtdlMessage)); - memset(msg, 0, sizeof(struct CtdlMessage)); - msg->cm_magic = CTDLMESSAGE_MAGIC; - msg->cm_anon_type = MES_NORMAL; - msg->cm_format_type = FMT_RFC822; - if (!IsEmptyStr(im->usernames[0])) { - CM_SetField(msg, eAuthor, im->usernames[0], strlen(im->usernames[0])); - } else { - CM_SetField(msg, eAuthor, HKEY("Citadel")); - } - if (!IsEmptyStr(im->usernames[1])) { - CM_SetField(msg, eRecipient, im->usernames[1], strlen(im->usernames[1])); - } - - CM_SetField(msg, eOriginalRoom, HKEY(PAGELOGROOM)); - CM_SetAsFieldSB(msg, eMesageText, &FullMsgBuf); /* we own this memory now */ - - /* Start with usernums[1] because it's guaranteed to be higher than usernums[0], - * so if there's only one party, usernums[0] will be zero but usernums[1] won't. - * Create the room if necessary. Note that we create as a type 5 room rather - * than 4, which indicates that it's a personal room but we've already supplied - * the namespace prefix. - * - * In the unlikely event that usernums[1] is zero, a room with an invalid namespace - * prefix will be created. That's ok because the auto-purger will clean it up later. - */ - snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[1], PAGELOGROOM); - CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS); - msgnum = CtdlSubmitMsg(msg, NULL, roomname); - CM_Free(msg); - - /* If there is a valid user number in usernums[0], save a copy for them too. */ - if (im->usernums[0] > 0) { - snprintf(roomname, sizeof roomname, "%010ld.%s", im->usernums[0], PAGELOGROOM); - CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS); - CtdlSaveMsgPointerInRoom(roomname, msgnum, 0, NULL); - } - - /* Finally, if we're logging instant messages globally, do that now. */ - if (!IsEmptyStr(CtdlGetConfigStr("c_logpages"))) { - CtdlCreateRoom(CtdlGetConfigStr("c_logpages"), 3, "", 0, 1, 1, VIEW_BBS); - CtdlSaveMsgPointerInRoom(CtdlGetConfigStr("c_logpages"), msgnum, 0, NULL); - } - -} - -/* - * Locate instant message conversations which have gone idle - * (or, if the server is shutting down, locate *all* conversations) - * and flush them to disk (in the participants' log rooms, etc.) - */ -void flush_conversations_to_disk(time_t if_older_than) { - - struct imlog *flush_these = NULL; - struct imlog *dont_flush_these = NULL; - struct imlog *imptr = NULL; - struct CitContext *nptr; - int nContexts, i; - - nptr = CtdlGetContextArray(&nContexts) ; /* Make a copy of the current wholist */ - - begin_critical_section(S_IM_LOGS); - while (imlist) - { - imptr = imlist; - imlist = imlist->next; - - /* For a two party conversation, if one party has logged out, force flush. */ - if (nptr) { - int user0_is_still_online = 0; - int user1_is_still_online = 0; - for (i=0; iusernums[0]) ++user0_is_still_online; - if (nptr[i].user.usernum == imptr->usernums[1]) ++user1_is_still_online; - } - if (imptr->usernums[0] != imptr->usernums[1]) { /* two party conversation */ - if ((!user0_is_still_online) || (!user1_is_still_online)) { - imptr->lastmsg = 0L; /* force flush */ - } - } - else { /* one party conversation (yes, people do IM themselves) */ - if (!user0_is_still_online) { - imptr->lastmsg = 0L; /* force flush */ - } - } - } - - /* Now test this conversation to see if it qualifies for flushing. */ - if ((time(NULL) - imptr->lastmsg) > if_older_than) - { - /* This conversation qualifies. Move it to the list of ones to flush. */ - imptr->next = flush_these; - flush_these = imptr; - } - else { - /* Move it to the list of ones not to flush. */ - imptr->next = dont_flush_these; - dont_flush_these = imptr; - } - } - imlist = dont_flush_these; - end_critical_section(S_IM_LOGS); - free(nptr); - - /* We are now outside of the critical section, and we are the only thread holding a - * pointer to a linked list of conversations to be flushed to disk. - */ - while (flush_these) { - - flush_individual_conversation(flush_these); /* This will free the string buffer */ - imptr = flush_these; - flush_these = flush_these->next; - free(imptr); - } -} - - - -void instmsg_timer(void) { - flush_conversations_to_disk(300); /* Anything that hasn't peeped in more than 5 minutes */ -} - -void instmsg_shutdown(void) { - flush_conversations_to_disk(0); /* Get it ALL onto disk NOW. */ -} - -CTDL_MODULE_INIT(instmsg) -{ - if (!threading) - { - CtdlRegisterProtoHook(cmd_gexp, "GEXP", "Get instant messages"); - CtdlRegisterProtoHook(cmd_sexp, "SEXP", "Send an instant message"); - CtdlRegisterProtoHook(cmd_dexp, "DEXP", "Disable instant messages"); - CtdlRegisterProtoHook(cmd_reqt, "REQT", "Request client termination"); - CtdlRegisterSessionHook(cmd_gexp_async, EVT_ASYNC, PRIO_ASYNC + 1); - CtdlRegisterSessionHook(delete_instant_messages, EVT_STOP, PRIO_STOP + 1); - CtdlRegisterXmsgHook(send_instant_message, XMSG_PRI_LOCAL); - CtdlRegisterSessionHook(instmsg_timer, EVT_TIMER, PRIO_CLEANUP + 400); - CtdlRegisterSessionHook(instmsg_shutdown, EVT_SHUTDOWN, PRIO_SHUTDOWN + 10); - } - - /* return our module name for the log */ - return "instmsg"; -}