Back to index

citadel  8.12
serv_roomchat.c
Go to the documentation of this file.
00001 /*
00002  * This module handles instant messaging between users.
00003  * 
00004  * Copyright (c) 2012 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License version 3.
00008  * 
00009  * 
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * 
00017  * 
00018  * 
00019  *
00020  */
00021 #include "sysdep.h"
00022 #include <stdlib.h>
00023 #include <unistd.h>
00024 #include <stdio.h>
00025 #include <fcntl.h>
00026 #include <signal.h>
00027 #include <pwd.h>
00028 #include <errno.h>
00029 #include <sys/types.h>
00030 #include <assert.h>
00031 
00032 #if TIME_WITH_SYS_TIME
00033 # include <sys/time.h>
00034 # include <time.h>
00035 #else
00036 # if HAVE_SYS_TIME_H
00037 #  include <sys/time.h>
00038 # else
00039 #  include <time.h>
00040 # endif
00041 #endif
00042 
00043 #include <sys/wait.h>
00044 #include <string.h>
00045 #include <limits.h>
00046 #include <libcitadel.h>
00047 #include "citadel.h"
00048 #include "server.h"
00049 #include "citserver.h"
00050 #include "support.h"
00051 #include "config.h"
00052 #include "msgbase.h"
00053 #include "user_ops.h"
00054 
00055 #ifndef HAVE_SNPRINTF
00056 #include "snprintf.h"
00057 #endif
00058 
00059 #include "ctdl_module.h"
00060 
00061 
00062 struct chatmsg {
00063        struct chatmsg *next;
00064        time_t timestamp;
00065        int seq;
00066        long roomnum;
00067        char *sender;
00068        char *msgtext;
00069 };
00070 
00071 struct chatmsg *first_chat_msg = NULL;
00072 struct chatmsg *last_chat_msg = NULL;
00073 
00074 
00075 /* 
00076  * Periodically called for housekeeping.  Expire old chat messages so they don't take up memory forever.
00077  */
00078 void roomchat_timer(void) {
00079        struct chatmsg *ptr;
00080 
00081        begin_critical_section(S_CHATQUEUE);
00082 
00083        while ((first_chat_msg != NULL) && ((time(NULL) - first_chat_msg->timestamp) > 300)) {
00084               ptr = first_chat_msg->next;
00085               free(first_chat_msg->sender);
00086               free(first_chat_msg->msgtext);
00087               free(first_chat_msg);
00088               first_chat_msg = ptr;
00089               if (first_chat_msg == NULL) {
00090                      last_chat_msg = NULL;
00091               }
00092        }
00093 
00094        end_critical_section(S_CHATQUEUE);
00095 }
00096 
00097 
00098 /*
00099  * Perform shutdown-related activities...
00100  */
00101 void roomchat_shutdown(void) {
00102        /* if we ever start logging chats, we have to flush them to disk here .*/
00103 }
00104 
00105 
00106 /*
00107  * Add a message into the chat queue
00108  */
00109 void add_to_chat_queue(char *msg) {
00110        static int seq = 0;
00111 
00112        struct chatmsg *m = malloc(sizeof(struct chatmsg));
00113        if (!m) return;
00114 
00115        m->next = NULL;
00116        m->timestamp = time(NULL);
00117        m->roomnum = CC->room.QRnumber;
00118        m->sender = strdup(CC->user.fullname);
00119        m->msgtext = strdup(msg);
00120 
00121        if ((m->sender == NULL) || (m->msgtext == NULL)) {
00122               free(m->sender);
00123               free(m->msgtext);
00124               free(m);
00125               return;
00126        }
00127 
00128        begin_critical_section(S_CHATQUEUE);
00129        m->seq = ++seq;
00130 
00131        if (first_chat_msg == NULL) {
00132               assert(last_chat_msg == NULL);
00133               first_chat_msg = m;
00134               last_chat_msg = m;
00135        }
00136        else {
00137               assert(last_chat_msg != NULL);
00138               assert(last_chat_msg->next == NULL);
00139               last_chat_msg->next = m;
00140               last_chat_msg = m;
00141        }
00142 
00143        end_critical_section(S_CHATQUEUE);
00144 }
00145 
00146 
00147 /*
00148  * Transmit a message into a room chat
00149  */
00150 void roomchat_send(char *argbuf) {
00151        char buf[1024];
00152 
00153        if ((CC->cs_flags & CS_CHAT) == 0) {
00154               cprintf("%d Session is not in chat mode.\n", ERROR);
00155               return;
00156        }
00157 
00158        cprintf("%d send now\n", SEND_LISTING);
00159        while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) {
00160               add_to_chat_queue(buf);
00161        }
00162 }
00163 
00164 
00165 /*
00166  * Poll room for incoming chat messages
00167  */
00168 void roomchat_poll(char *argbuf) {
00169        int newer_than = 0;
00170        struct chatmsg *found = NULL;
00171        struct chatmsg *ptr = NULL;
00172 
00173        newer_than = extract_int(argbuf, 1);
00174 
00175        if ((CC->cs_flags & CS_CHAT) == 0) {
00176               cprintf("%d Session is not in chat mode.\n", ERROR);
00177               return;
00178        }
00179 
00180        begin_critical_section(S_CHATQUEUE);
00181        for (ptr = first_chat_msg; ((ptr != NULL) && (found == NULL)); ptr = ptr->next) {
00182               if ((ptr->seq > newer_than) && (ptr->roomnum == CC->room.QRnumber)) {
00183                      found = ptr;
00184               }
00185        }
00186        end_critical_section(S_CHATQUEUE);
00187 
00188        if (found == NULL) {
00189               cprintf("%d no messages\n", ERROR + MESSAGE_NOT_FOUND);
00190               return;
00191        }
00192 
00193        cprintf("%d %d|%ld|%s\n", LISTING_FOLLOWS, found->seq, found->timestamp, found->sender);
00194        cprintf("%s\n", found->msgtext);
00195        cprintf("000\n");
00196 }
00197 
00198 
00199 
00200 /*
00201  * list users in chat in this room
00202  */
00203 void roomchat_rwho(char *argbuf) {
00204        struct CitContext *nptr;
00205        int nContexts, i;
00206 
00207        if ((CC->cs_flags & CS_CHAT) == 0) {
00208               cprintf("%d Session is not in chat mode.\n", ERROR);
00209               return;
00210        }
00211 
00212        cprintf("%d%c \n", LISTING_FOLLOWS, CtdlCheckExpress() );
00213        
00214        nptr = CtdlGetContextArray(&nContexts) ;         // grab a copy of the wholist
00215        if (nptr) {
00216               for (i=0; i<nContexts; i++)  {                   // list the users
00217                       if ( (nptr[i].room.QRnumber == CC->room.QRnumber) 
00218                         && (nptr[i].cs_flags & CS_CHAT)
00219                      ) {
00220                             cprintf("%s\n", nptr[i].user.fullname);
00221                      }
00222               }
00223               free(nptr);                               // free our copy
00224        }
00225 
00226        cprintf("000\n");
00227 }
00228 
00229 
00230 
00231 /*
00232  * Participate in real time chat in a room
00233  */
00234 void cmd_rcht(char *argbuf)
00235 {
00236        char subcmd[16];
00237 
00238        if (CtdlAccessCheck(ac_logged_in)) return;
00239 
00240        extract_token(subcmd, argbuf, 0, '|', sizeof subcmd);
00241 
00242        if (!strcasecmp(subcmd, "enter")) {
00243               CC->cs_flags |= CS_CHAT;
00244               cprintf("%d Entering chat mode.\n", CIT_OK);
00245        }
00246        else if (!strcasecmp(subcmd, "exit")) {
00247               CC->cs_flags &= ~CS_CHAT;
00248               cprintf("%d Exiting chat mode.\n", CIT_OK);
00249        }
00250        else if (!strcasecmp(subcmd, "send")) {
00251               roomchat_send(argbuf);
00252        }
00253        else if (!strcasecmp(subcmd, "poll")) {
00254               roomchat_poll(argbuf);
00255        }
00256        else if (!strcasecmp(subcmd, "rwho")) {
00257               roomchat_rwho(argbuf);
00258        }
00259        else {
00260               cprintf("%d Invalid subcommand\n", ERROR + CMD_NOT_SUPPORTED);
00261        }
00262 }
00263 
00264 
00265 CTDL_MODULE_INIT(roomchat)
00266 {
00267        if (!threading)
00268        {
00269               CtdlRegisterProtoHook(cmd_rcht, "RCHT", "Participate in real time chat in a room");
00270               CtdlRegisterSessionHook(roomchat_timer, EVT_TIMER, PRIO_CLEANUP + 400);
00271               CtdlRegisterSessionHook(roomchat_shutdown, EVT_SHUTDOWN, PRIO_SHUTDOWN + 55);
00272        }
00273        
00274        /* return our module name for the log */
00275        return "roomchat";
00276 }