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