* $Id$
*
* This module handles all "real time" communication between users. The
- * modes of communication currently supported are Chat and Paging.
+ * modes of communication currently supported are Chat and Instant Messages.
+ *
+ * Copyright (c) 1987-2010 by the citadel.org team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "sysdep.h"
#include "config.h"
#include "msgbase.h"
#include "user_ops.h"
-#include "room_ops.h"
#ifndef HAVE_SNPRINTF
#include "snprintf.h"
long usernums[2];
char usernames[2][128];
time_t lastmsg;
+ int last_serial;
StrBuf *conversation;
};
struct imlog *imlist = NULL;
+
+
+
+/*
+ * FIXME: OMG this module is realy horrible to the rest of the system when accessing contexts.
+ * It pays no regard at all to how long it may have the context list locked for.
+ * It carries out IO whilst the context list is locked.
+ * I'd recomend disabling this module altogether for the moment.
+ */
+
/*
* This function handles the logging of instant messages to disk.
*/
-void log_instant_message(struct CitContext *me, struct CitContext *them, char *msgtext)
+void log_instant_message(struct CitContext *me, struct CitContext *them, char *msgtext, int serial_number)
{
long usernums[2];
long t;
this_im->conversation = NewStrBuf();
this_im->next = imlist;
imlist = this_im;
- StrBufAppendBufPlain(this_im->conversation,
+ StrBufAppendBufPlain(this_im->conversation, HKEY(
"Content-type: text/html\r\n"
- "Content-transfer-encoding: 7bit\r\n\r\n"
- "<html><head><title>instant message transcript</title></head>\r\n"
- "<body>\r\n",
- -1, 0);
- }
-
- this_im->lastmsg = time(NULL); /* Touch the timestamp so we know when to flush */
- StrBufAppendBufPlain(this_im->conversation, "<p><b>", -1, 0);
- StrBufAppendBufPlain(this_im->conversation, me->user.fullname, -1, 0);
- StrBufAppendBufPlain(this_im->conversation, ":</b> ", -1, 0);
- StrEscAppend(this_im->conversation, NULL, msgtext, 0, 0);
- StrBufAppendBufPlain(this_im->conversation, "</p>\r\n", -1, 0);
+ "Content-transfer-encoding: 7bit\r\n"
+ "\r\n"
+ "<html><body>\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("<p><b>"), 0);
+ StrBufAppendBufPlain(this_im->conversation, me->user.fullname, -1, 0);
+ StrBufAppendBufPlain(this_im->conversation, HKEY(":</b> "), 0);
+ StrEscAppend(this_im->conversation, NULL, msgtext, 0, 0);
+ StrBufAppendBufPlain(this_im->conversation, HKEY("</p>\r\n"), 0);
+ }
end_critical_section(S_IM_LOGS);
}
}
-t_context *find_context(char **unstr)
+CitContext *find_context(char **unstr)
{
- t_context *t_cc, *found_cc = NULL;
+ CitContext *t_cc, *found_cc = NULL;
char *name, *tptr;
if ((!*unstr) || (!unstr))
}
GenerateRoomDisplay(roomname, ccptr, CC);
- if ((CC->user.axlevel < 6)
- && (!IsEmptyStr(ccptr->fake_roomname))) {
+ if ((CC->user.axlevel < AxAideU) && (!IsEmptyStr(ccptr->fake_roomname))) {
strcpy(roomname, ccptr->fake_roomname);
}
- if ((ccptr->cs_flags & CS_CHAT)
- && ((ccptr->cs_flags & CS_STEALTH) == 0)) {
+ if ((ccptr->cs_flags & CS_CHAT) && ((ccptr->cs_flags & CS_STEALTH) == 0)) {
if ((allflag == 0) || (allflag == 1)) {
cprintf(":| %-25s <%s>:\n",
(ccptr->fake_username[0]) ? ccptr->fake_username : ccptr->curr_user,
for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
GenerateRoomDisplay(roomname, ccptr, CC);
- if ((CC->user.axlevel < 6)
+ if ((CC->user.axlevel < AxAideU)
&& (!IsEmptyStr(ccptr->fake_roomname))) {
strcpy(roomname, ccptr->fake_roomname);
}
void cmd_chat(char *argbuf)
{
+ /* FIXME chat has been broken by the underlying buffered I/O layer */
+ cprintf("%d Chat is currently disabled at this site.\n", ERROR);
+ return;
+
+#if 0
char cmdbuf[SIZ];
char *un;
char *strptr1;
struct ChatLine *clptr;
struct CitContext *t_context;
int retval;
+ CitContext *CCC = CC;
- if (!(CC->logged_in)) {
+ if (!(CCC->logged_in)) {
cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN);
return;
}
- CC->cs_flags = CC->cs_flags | CS_CHAT;
+ CCC->cs_flags = CCC->cs_flags | CS_CHAT;
cprintf("%d Entering chat mode (type '/help' for available commands)\n",
START_CHAT_MODE);
unbuffer_output();
MyLastMsg = ChatLastMsg;
- if ((CC->cs_flags & CS_STEALTH) == 0) {
+ if ((CCC->cs_flags & CS_STEALTH) == 0) {
allwrite("<entering chat>", 0, NULL);
}
strcpy(cmdbuf, "");
retval = client_read_to(&cmdbuf[linelen], 1, 2);
- if (retval < 0 || CC->kill_me) { /* socket broken? */
- if ((CC->cs_flags & CS_STEALTH) == 0) {
+ if (retval < 0 || CCC->kill_me) { /* socket broken? */
+ if ((CCC->cs_flags & CS_STEALTH) == 0) {
allwrite("<disconnected>", 0, NULL);
}
return;
if (!IsEmptyStr(cmdbuf))
if (cmdbuf[strlen(cmdbuf) - 1] == 10) {
cmdbuf[strlen(cmdbuf) - 1] = 0;
- time(&CC->lastcmd);
- time(&CC->lastidle);
+ time(&CCC->lastcmd);
+ time(&CCC->lastidle);
if ((!strcasecmp(cmdbuf, "exit"))
|| (!strcasecmp(cmdbuf, "/exit"))
strcpy(cmdbuf, "000");
if (!strcmp(cmdbuf, "000")) {
- if ((CC->cs_flags & CS_STEALTH) == 0) {
+ if ((CCC->cs_flags & CS_STEALTH) == 0) {
allwrite("<exiting chat>", 0, NULL);
}
sleep(1);
cprintf("000\n");
- CC->cs_flags = CC->cs_flags - CS_CHAT;
+ CCC->cs_flags = CCC->cs_flags - CS_CHAT;
return;
}
if ((!strcasecmp(cmdbuf, "/help"))
ok_cmd = 1;
strptr1 = &cmdbuf[5];
if ((t_context = find_context(&strptr1))) {
- allwrite(strptr1, 2, CC->curr_user);
- if (strcasecmp(CC->curr_user, t_context->curr_user))
+ allwrite(strptr1, 2, CCC->curr_user);
+ if (strcasecmp(CCC->curr_user, t_context->curr_user))
allwrite(strptr1, 2, t_context->curr_user);
} else
cprintf(":|User not found.\n");
strptr1 = &cmdbuf[6];
strcat(strptr1, " ");
if ((t_context = find_context(&strptr1))) {
- if (strcasecmp(CC->curr_user, t_context->curr_user))
+ if (strcasecmp(CCC->curr_user, t_context->curr_user))
allwrite(strptr1, 3, t_context->curr_user);
} else
cprintf(":|User not found.\n");
}
/* now check the queue for new incoming stuff */
- if (CC->fake_username[0])
- un = CC->fake_username;
+ if (CCC->fake_username[0])
+ un = CCC->fake_username;
else
- un = CC->curr_user;
+ un = CCC->curr_user;
if (ChatLastMsg > MyLastMsg) {
ThisLastMsg = ChatLastMsg;
for (clptr = ChatQueue; clptr != NULL; clptr = clptr->next) {
if ((clptr->chat_seq > MyLastMsg) && ((!clptr->chat_username[0]) || (!strncasecmp(un, clptr->chat_username, 32)))) {
- if ((!clptr->chat_room[0]) || (!strncasecmp(CC->room.QRname, clptr->chat_room, ROOMNAMELEN))) {
+ if ((!clptr->chat_room[0]) || (!strncasecmp(CCC->room.QRname, clptr->chat_room, ROOMNAMELEN))) {
/* Output new chat data */
cprintf("%s\n", clptr->chat_text);
if (!strcmp(&clptr->chat_text[2], KICKEDMSG)) {
allwrite("<kicked out of this room>", 0, NULL);
cprintf("000\n");
- CC->cs_flags = CC->cs_flags - CS_CHAT;
+ CCC->cs_flags = CCC->cs_flags - CS_CHAT;
/* Kick user out of room */
- CtdlInvtKick(CC->user.fullname, 0);
+ CtdlInvtKick(CCC->user.fullname, 0);
/* And return to the Lobby */
- usergoto(config.c_baseroom, 0, 0, NULL, NULL);
+ CtdlUserGoto(config.c_baseroom, 0, 0, NULL, NULL);
return;
}
}
MyLastMsg = ThisLastMsg;
}
}
+#endif
}
-
-/*
- * Poll for instant messages (OLD METHOD -- ***DEPRECATED ***)
- */
-void cmd_pexp(char *argbuf)
-{
- struct ExpressMessage *ptr, *holdptr;
-
- 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 = NULL;
- end_critical_section(S_SESSION_TABLE);
-
- cprintf("%d Express msgs:\n", LISTING_FOLLOWS);
- while (ptr != NULL) {
- if (ptr->flags && EM_BROADCAST)
- cprintf("Broadcast message ");
- else if (ptr->flags && EM_CHAT)
- cprintf("Chat request ");
- else if (ptr->flags && EM_GO_AWAY)
- cprintf("Please logoff now, as requested ");
- else
- cprintf("Message ");
- cprintf("from %s:\n", ptr->sender);
- if (ptr->text != NULL)
- memfmout(ptr->text, 0, "\n");
-
- holdptr = ptr->next;
- if (ptr->text != NULL) free(ptr->text);
- free(ptr);
- ptr = holdptr;
- }
- cprintf("000\n");
-}
-
-
/*
- * Get instant messages (new method)
+ * Retrieve instant messages
*/
void cmd_gexp(char *argbuf) {
struct ExpressMessage *ptr;
);
if (ptr->text != NULL) {
- memfmout(ptr->text, 0, "\n");
- if (ptr->text[strlen(ptr->text)-1] != '\n') cprintf("\n");
+ memfmout(ptr->text, "\n");
free(ptr->text);
}
/* If the target context is a session which can handle asynchronous
* messages, go ahead and set the flag for that.
*/
- if (ccptr->is_async) {
- ccptr->async_waiting = 1;
- if (ccptr->state == CON_IDLE) {
- ccptr->state = CON_READY;
- }
- }
+ set_async_waiting(ccptr);
}
struct CitContext *ccptr;
struct ExpressMessage *newmsg = NULL;
char *un;
- size_t msglen = 0;
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 (strlen(x_msg) > 0) {
- msglen = strlen(x_msg) + 4;
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 (ccptr->fake_username[0]) {
|| (!strcasecmp(x_user, "broadcast")))
&& (ccptr->can_receive_im)
&& ((ccptr->disable_exp == 0)
- || (CC->user.axlevel >= 6)) ) {
+ || (CC->user.axlevel >= AxAideU)) ) {
if (do_send) {
- newmsg = (struct ExpressMessage *)
- malloc(sizeof (struct ExpressMessage));
- memset(newmsg, 0,
- sizeof (struct ExpressMessage));
+ 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);
/* and log it ... */
if (ccptr != CC) {
- log_instant_message(CC, ccptr, newmsg->text);
+ log_instant_message(CC, ccptr, newmsg->text, serial_number);
}
}
++message_sent;
cprintf("%d You were not previously paged.\n", ERROR + NO_SUCH_USER);
return;
}
- if ((!strcasecmp(x_user, "broadcast")) && (CC->user.axlevel < 6)) {
+ if ((!strcasecmp(x_user, "broadcast")) && (CC->user.axlevel < AxAideU)) {
cprintf("%d Higher access required to send a broadcast.\n",
ERROR + HIGHER_ACCESS_REQUIRED);
return;
if (!strcmp(x_msg, "-")) {
message_sent = PerformXmsgHooks(lun, lem, x_user, "");
if (message_sent == 0) {
- if (getuser(NULL, x_user))
+ if (CtdlGetUser(NULL, x_user))
cprintf("%d '%s' does not exist.\n",
ERROR + NO_SUCH_USER, x_user);
else
cprintf(" to %d users", message_sent);
cprintf(".\n");
} else {
- if (getuser(NULL, x_user))
+ if (CtdlGetUser(NULL, x_user))
cprintf("%d '%s' does not exist.\n",
ERROR + NO_SUCH_USER, x_user);
else
long msgnum = 0;
char roomname[ROOMNAMELEN];
- StrBufAppendBufPlain(im->conversation,
+ StrBufAppendBufPlain(im->conversation, HKEY(
"</body>\r\n"
- "</html>\r\n",
- -1, 0
+ "</html>\r\n"
+ ), 0
);
msg = malloc(sizeof(struct CtdlMessage));
}
msg->cm_fields['O'] = strdup(PAGELOGROOM);
msg->cm_fields['N'] = strdup(NODENAME);
- msg->cm_fields['M'] = strdup(ChrPtr(im->conversation));
+ msg->cm_fields['M'] = SmashStrBuf(&im->conversation); /* 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.
* 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);
- create_room(roomname, 5, "", 0, 1, 1, VIEW_BBS);
+ CtdlCreateRoom(roomname, 5, "", 0, 1, 1, VIEW_BBS);
msgnum = CtdlSubmitMsg(msg, NULL, roomname, 0);
CtdlFreeMessage(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);
- create_room(roomname, 5, "", 0, 1, 1, VIEW_BBS);
+ 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(config.c_logpages)) {
- create_room(config.c_logpages, 3, "", 0, 1, 1, VIEW_BBS);
+ CtdlCreateRoom(config.c_logpages, 3, "", 0, 1, 1, VIEW_BBS);
CtdlSaveMsgPointerInRoom(config.c_logpages, msgnum, 0, NULL);
}
*/
while (flush_these) {
- flush_individual_conversation(flush_these);
+ flush_individual_conversation(flush_these); /* This will free the string buffer */
imptr = flush_these;
flush_these = flush_these->next;
- FreeStrBuf(&imptr->conversation);
free(imptr);
}
}
if (!threading)
{
CtdlRegisterProtoHook(cmd_chat, "CHAT", "Begin real-time chat");
- CtdlRegisterProtoHook(cmd_pexp, "PEXP", "Poll for instant messages");
CtdlRegisterProtoHook(cmd_gexp, "GEXP", "Get instant messages");
CtdlRegisterProtoHook(cmd_sexp, "SEXP", "Send an instant message");
CtdlRegisterProtoHook(cmd_dexp, "DEXP", "Disable instant messages");