63feceb51e82246158ab433acddf087e3a39ed2c
[citadel.git] / citadel / modules / roomchat / serv_roomchat.c
1 /*
2  * $Id$
3  *
4  * This module handles instant messaging between users.
5  * 
6  * Copyright (c) 2010 by the citadel.org team
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "sysdep.h"
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <signal.h>
29 #include <pwd.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <assert.h>
33
34 #if TIME_WITH_SYS_TIME
35 # include <sys/time.h>
36 # include <time.h>
37 #else
38 # if HAVE_SYS_TIME_H
39 #  include <sys/time.h>
40 # else
41 #  include <time.h>
42 # endif
43 #endif
44
45 #include <sys/wait.h>
46 #include <string.h>
47 #include <limits.h>
48 #include <libcitadel.h>
49 #include "citadel.h"
50 #include "server.h"
51 #include "citserver.h"
52 #include "support.h"
53 #include "config.h"
54 #include "msgbase.h"
55 #include "user_ops.h"
56
57 #ifndef HAVE_SNPRINTF
58 #include "snprintf.h"
59 #endif
60
61 #include "ctdl_module.h"
62
63
64 struct chatmsg {
65         struct chatmsg *next;
66         time_t timestamp;
67         int seq;
68         long roomnum;
69         char *sender;
70         char *msgtext;
71 };
72
73 struct chatmsg *first_chat_msg = NULL;
74 struct chatmsg *last_chat_msg = NULL;
75
76
77 /* 
78  * Periodically called for housekeeping.  Expire old chat messages so they don't take up memory forever.
79  */
80 void roomchat_timer(void) {
81         struct chatmsg *ptr;
82
83         begin_critical_section(S_CHATQUEUE);
84
85         while ((first_chat_msg != NULL) && ((time(NULL) - first_chat_msg->timestamp) > 300)) {
86                 ptr = first_chat_msg->next;
87                 free(first_chat_msg->sender);
88                 free(first_chat_msg->msgtext);
89                 free(first_chat_msg);
90                 first_chat_msg = ptr;
91                 if (first_chat_msg == NULL) {
92                         last_chat_msg = NULL;
93                 }
94         }
95
96         end_critical_section(S_CHATQUEUE);
97 }
98
99
100 /*
101  * Perform shutdown-related activities...
102  */
103 void roomchat_shutdown(void) {
104         /* if we ever start logging chats, we have to flush them to disk here .*/
105 }
106
107
108 /*
109  * Add a message into the chat queue
110  */
111 void add_to_chat_queue(char *msg) {
112         static int seq = 0;
113
114         struct chatmsg *m = malloc(sizeof(struct chatmsg));
115         if (!m) return;
116
117         m->next = NULL;
118         m->timestamp = time(NULL);
119         m->roomnum = CC->room.QRnumber;
120         m->sender = strdup(CC->user.fullname);
121         m->msgtext = strdup(msg);
122
123         if ((m->sender == NULL) || (m->msgtext == NULL)) {
124                 free(m->sender);
125                 free(m->msgtext);
126                 free(m);
127                 return;
128         }
129
130         begin_critical_section(S_CHATQUEUE);
131         m->seq = ++seq;
132
133         if (first_chat_msg == NULL) {
134                 assert(last_chat_msg == NULL);
135                 first_chat_msg = m;
136                 last_chat_msg = m;
137         }
138         else {
139                 assert(last_chat_msg != NULL);
140                 assert(last_chat_msg->next == NULL);
141                 last_chat_msg->next = m;
142                 last_chat_msg = m;
143         }
144
145         end_critical_section(S_CHATQUEUE);
146 }
147
148
149 /*
150  * Transmit a message into a room chat
151  */
152 void roomchat_send(char *argbuf) {
153         char buf[1024];
154
155         if ((CC->cs_flags & CS_CHAT) == 0) {
156                 cprintf("%d Session is not in chat mode.\n", ERROR);
157                 return;
158         }
159
160         cprintf("%d send now\n", SEND_LISTING);
161         while (client_getln(buf, sizeof buf) >= 0 && strcmp(buf, "000")) {
162                 add_to_chat_queue(buf);
163         }
164 }
165
166
167 /*
168  * Poll room for incoming chat messages
169  */
170 void roomchat_poll(char *argbuf) {
171         int newer_than = 0;
172         struct chatmsg *found = NULL;
173         struct chatmsg *ptr = NULL;
174
175         newer_than = extract_int(argbuf, 1);
176
177         if ((CC->cs_flags & CS_CHAT) == 0) {
178                 cprintf("%d Session is not in chat mode.\n", ERROR);
179                 return;
180         }
181
182         begin_critical_section(S_CHATQUEUE);
183         for (ptr = first_chat_msg; ((ptr != NULL) && (found == NULL)); ptr = ptr->next) {
184                 if ((ptr->seq > newer_than) && (ptr->roomnum == CC->room.QRnumber)) {
185                         found = ptr;
186                 }
187         }
188         end_critical_section(S_CHATQUEUE);
189
190         if (found == NULL) {
191                 cprintf("%d no messages\n", ERROR + MESSAGE_NOT_FOUND);
192                 return;
193         }
194
195         cprintf("%d %d|%ld|%s\n", LISTING_FOLLOWS, found->seq, found->timestamp, found->sender);
196         cprintf("%s\n", found->msgtext);
197         cprintf("000\n");
198 }
199
200
201
202 /*
203  * list users in chat in this room
204  */
205 void roomchat_rwho(char *argbuf) {
206         struct CitContext *nptr;
207         int nContexts, i;
208
209         if ((CC->cs_flags & CS_CHAT) == 0) {
210                 cprintf("%d Session is not in chat mode.\n", ERROR);
211                 return;
212         }
213
214         cprintf("%d%c \n", LISTING_FOLLOWS, CtdlCheckExpress() );
215         
216         nptr = CtdlGetContextArray(&nContexts) ;                // grab a copy of the wholist
217         if (nptr) {
218                 for (i=0; i<nContexts; i++)  {                  // list the users
219                         if ( (nptr[i].room.QRnumber == CC->room.QRnumber) 
220                            && (nptr[i].cs_flags & CS_CHAT)
221                         ) {
222                                 cprintf("%s\n", nptr[i].user.fullname);
223                         }
224                 }
225                 free(nptr);                                     // free our copy
226         }
227
228         cprintf("000\n");
229 }
230
231
232
233 /*
234  * Participate in real time chat in a room
235  */
236 void cmd_rcht(char *argbuf)
237 {
238         char subcmd[16];
239
240         if (CtdlAccessCheck(ac_logged_in)) return;
241
242         extract_token(subcmd, argbuf, 0, '|', sizeof subcmd);
243
244         if (!strcasecmp(subcmd, "enter")) {
245                 CC->cs_flags |= CS_CHAT;
246                 cprintf("%d Entering chat mode.\n", CIT_OK);
247         }
248         else if (!strcasecmp(subcmd, "exit")) {
249                 CC->cs_flags &= ~CS_CHAT;
250                 cprintf("%d Exiting chat mode.\n", CIT_OK);
251         }
252         else if (!strcasecmp(subcmd, "send")) {
253                 roomchat_send(argbuf);
254         }
255         else if (!strcasecmp(subcmd, "poll")) {
256                 roomchat_poll(argbuf);
257         }
258         else if (!strcasecmp(subcmd, "rwho")) {
259                 roomchat_rwho(argbuf);
260         }
261         else {
262                 cprintf("%d Invalid subcommand\n", ERROR + CMD_NOT_SUPPORTED);
263         }
264 }
265
266
267 CTDL_MODULE_INIT(roomchat)
268 {
269         if (!threading)
270         {
271                 CtdlRegisterProtoHook(cmd_rcht, "RCHT", "Participate in real time chat in a room");
272                 CtdlRegisterSessionHook(roomchat_timer, EVT_TIMER);
273                 CtdlRegisterSessionHook(roomchat_shutdown, EVT_SHUTDOWN);
274         }
275         
276         /* return our Subversion id for the Log */
277         return "$Id$";
278 }