--- /dev/null
+/*
+ * $Id$
+ *
+ * This module handles instant messaging between users.
+ *
+ * Copyright (c) 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 <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#include <sys/wait.h>
+#include <string.h>
+#include <limits.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "server.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "msgbase.h"
+#include "user_ops.h"
+
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+
+#include "ctdl_module.h"
+
+
+struct chatmsg {
+ struct chatmsg *next;
+ time_t timestamp;
+ int seq;
+ long roomnum;
+ char *sender;
+ char *msgtext;
+};
+
+struct chatmsg *first_chat_msg = NULL;
+struct chatmsg *last_chat_msg = NULL;
+
+
+/*
+ * Periodically called for housekeeping. Expire old chat messages so they don't take up memory forever.
+ */
+void roomchat_timer(void) {
+ struct chatmsg *ptr;
+
+ begin_critical_section(S_CHATQUEUE);
+
+ while ((first_chat_msg != NULL) && ((time(NULL) - first_chat_msg->timestamp) > 300)) {
+ ptr = first_chat_msg->next;
+ free(first_chat_msg->sender);
+ free(first_chat_msg->msgtext);
+ free(first_chat_msg);
+ first_chat_msg = ptr;
+ if (first_chat_msg == NULL) {
+ last_chat_msg = NULL;
+ }
+ }
+
+ end_critical_section(S_CHATQUEUE);
+}
+
+
+/*
+ * Perform shutdown-related activities...
+ */
+void roomchat_shutdown(void) {
+ /* if we ever start logging chats, we have to flush them to disk here .*/
+}
+
+
+/*
+ * Add a message into the chat queue
+ */
+void add_to_chat_queue(char *msg) {
+ static int seq = 0;
+
+ struct chatmsg *m = malloc(sizeof(struct chatmsg));
+ if (!m) return;
+
+ m->next = NULL;
+ m->timestamp = time(NULL);
+ m->roomnum = CC->room.QRnumber;
+ m->sender = strdup(CC->user.fullname);
+ m->msgtext = strdup(msg);
+
+ if ((m->sender == NULL) || (m->msgtext == NULL)) {
+ free(m->sender);
+ free(m->msgtext);
+ free(m);
+ return;
+ }
+
+ begin_critical_section(S_CHATQUEUE);
+ m->seq = ++seq;
+
+ if (first_chat_msg == NULL) {
+ assert(last_chat_msg == NULL);
+ first_chat_msg = m;
+ last_chat_msg = m;
+ }
+ else {
+ assert(last_chat_msg != NULL);
+ assert(last_chat_msg->next == NULL);
+ last_chat_msg->next = m;
+ last_chat_msg = m;
+ }
+
+ end_critical_section(S_CHATQUEUE);
+}
+
+
+/*
+ * Transmit a message into a room chat
+ */
+void roomchat_send(char *argbuf) {
+ char buf[1024];
+
+ if ((CC->cs_flags & CS_CHAT) == 0) {
+ cprintf("%d Session is not in chat mode.\n", ERROR);
+ return;
+ }
+
+ cprintf("%d send now\n", SEND_LISTING);
+ while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) {
+ add_to_chat_queue(buf);
+ }
+}
+
+
+/*
+ * Poll room for incoming chat messages
+ */
+void roomchat_poll(char *argbuf) {
+ int newer_than = 0;
+ struct chatmsg *found = NULL;
+ struct chatmsg *ptr = NULL;
+
+ newer_than = extract_int(argbuf, 1);
+
+ if (!CC->cs_flags & CS_CHAT) {
+ cprintf("%d Session is not in chat mode.\n", ERROR);
+ return;
+ }
+
+ begin_critical_section(S_CHATQUEUE);
+ for (ptr = first_chat_msg; ((ptr != NULL) && (found == NULL)); ptr = ptr->next) {
+ if ((ptr->seq > newer_than) && (ptr->roomnum == CC->room.QRnumber)) {
+ found = ptr;
+ }
+ }
+ end_critical_section(S_CHATQUEUE);
+
+ if (found == NULL) {
+ cprintf("%d no messages\n", ERROR + MESSAGE_NOT_FOUND);
+ return;
+ }
+
+ cprintf("%d %d|%ld|%s\n", LISTING_FOLLOWS, found->seq, found->timestamp, found->sender);
+ cprintf("%s\n", found->msgtext);
+ cprintf("000\n");
+}
+
+
+/*
+ * Participate in real time chat in a room
+ */
+void cmd_rcht(char *argbuf)
+{
+ char subcmd[16];
+
+ if (CtdlAccessCheck(ac_logged_in)) return;
+
+ extract_token(subcmd, argbuf, 0, '|', sizeof subcmd);
+
+ if (!strcasecmp(subcmd, "enter")) {
+ CC->cs_flags |= CS_CHAT;
+ cprintf("%d Entering chat mode.\n", CIT_OK);
+ }
+ else if (!strcasecmp(subcmd, "exit")) {
+ CC->cs_flags &= ~CS_CHAT;
+ cprintf("%d Exiting chat mode.\n", CIT_OK);
+ }
+ else if (!strcasecmp(subcmd, "send")) {
+ roomchat_send(argbuf);
+ }
+ else if (!strcasecmp(subcmd, "poll")) {
+ roomchat_poll(argbuf);
+ }
+ else {
+ cprintf("%d Invalid subcommand\n", ERROR + CMD_NOT_SUPPORTED);
+ }
+}
+
+
+CTDL_MODULE_INIT(roomchat)
+{
+ if (!threading)
+ {
+ CtdlRegisterProtoHook(cmd_rcht, "RCHT", "Participate in real time chat in a room");
+ CtdlRegisterSessionHook(roomchat_timer, EVT_TIMER);
+ CtdlRegisterSessionHook(roomchat_shutdown, EVT_SHUTDOWN);
+ }
+
+ /* return our Subversion id for the Log */
+ return "$Id$";
+}