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