]> code.citadel.org Git - citadel.git/blob - citadel/serv_chat.c
* Replaced all occurances of malloc(), realloc(), and free() in the
[citadel.git] / citadel / serv_chat.c
1 /* $Id$ */
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <fcntl.h>
6 #include <signal.h>
7 #include <pwd.h>
8 #include <errno.h>
9 #include <sys/types.h>
10 #include <sys/time.h>
11 #include <sys/wait.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <pthread.h>
15 #include "citadel.h"
16 #include "server.h"
17 #include <syslog.h>
18 #ifdef HAVE_SYS_SELECT_H
19 #include <sys/select.h>
20 #endif
21 #include "serv_chat.h"
22 #include "sysdep_decls.h"
23 #include "citserver.h"
24 #include "support.h"
25 #include "config.h"
26 #include "dynloader.h"
27 #include "tools.h"
28
29 struct ChatLine *ChatQueue = NULL;
30 int ChatLastMsg = 0;
31
32 extern struct CitContext *ContextList;
33
34 #define MODULE_NAME     "Chat module"
35 #define MODULE_AUTHOR   "Art Cancro"
36 #define MODULE_EMAIL    "ajc@uncnsrd.mt-kisco.ny.us"
37 #define MAJOR_VERSION   0
38 #define MINOR_VERSION   2
39
40 static struct DLModule_Info info =
41 {
42   MODULE_NAME,
43   MODULE_AUTHOR,
44   MODULE_EMAIL,
45   MAJOR_VERSION,
46   MINOR_VERSION
47 };
48
49 struct DLModule_Info *Dynamic_Module_Init(void)
50 {
51    CtdlRegisterProtoHook(cmd_chat, "CHAT",
52                          "Initiates a real-time chat session");
53    CtdlRegisterProtoHook(cmd_pexp, "PEXP", "Poll for express messages");
54    CtdlRegisterProtoHook(cmd_sexp, "SEXP", "Send an express message");
55    return &info;
56 }
57
58 void allwrite(char *cmdbuf, int flag, char *roomname, char *username)
59 {       
60         FILE *fp;
61         char bcast[256];
62         char *un;
63         struct ChatLine *clptr, *clnew;
64         time_t now;
65
66         if (CC->fake_username[0])
67            un = CC->fake_username;
68         else
69            un = CC->usersupp.fullname;
70         if (flag == 1) 
71         {
72                 sprintf(bcast,":|<%s %s>",un,cmdbuf);
73         }
74         else
75         if (flag == 0)
76         {
77                 sprintf(bcast,"%s|%s",un,cmdbuf);
78         }
79         else
80         if (flag == 2)
81         {
82                 sprintf(bcast,":|<%s whispers %s>", un, cmdbuf);
83         }
84         if ((strcasecmp(cmdbuf,"NOOP")) && (flag !=2)) {
85                 fp = fopen(CHATLOG,"a");
86                 fprintf(fp,"%s\n",bcast);
87                 fclose(fp);
88                 }
89
90         clnew = (struct ChatLine *) mallok(sizeof(struct ChatLine));
91         memset(clnew, 0, sizeof(struct ChatLine));
92         if (clnew == NULL) {
93                 fprintf(stderr, "citserver: cannot alloc chat line: %s\n",
94                         strerror(errno));
95                 return;
96                 }
97
98         time(&now);
99         clnew->next = NULL;
100         clnew->chat_time = now;
101         strncpy(clnew->chat_room, roomname, sizeof clnew->chat_room);
102         clnew->chat_room[sizeof clnew->chat_room - 1] = 0;
103         if (username)
104           {
105             strncpy(clnew->chat_username, username,
106                     sizeof clnew->chat_username);
107             clnew->chat_username[sizeof clnew->chat_username - 1] = 0;
108           }
109         else
110            clnew->chat_username[0] = '\0';
111         strcpy(clnew->chat_text, bcast);
112
113         /* Here's the critical section.
114          * First, add the new message to the queue...
115          */
116         begin_critical_section(S_CHATQUEUE);
117         ++ChatLastMsg;
118         clnew->chat_seq = ChatLastMsg;
119         if (ChatQueue == NULL) {
120                 ChatQueue = clnew;
121                 }
122         else {
123                 for (clptr=ChatQueue; clptr->next != NULL; clptr=clptr->next) ;;
124                 clptr->next = clnew;
125                 }
126
127         /* Then, before releasing the lock, free the expired messages */
128         while(1) {
129                 if (ChatQueue == NULL) goto DONE_FREEING;
130                 if ( (now - ChatQueue->chat_time) < 120L ) goto DONE_FREEING;
131                 clptr = ChatQueue;
132                 ChatQueue = ChatQueue->next;
133                 phree(clptr);
134                 }
135 DONE_FREEING:   end_critical_section(S_CHATQUEUE);
136         }
137
138
139 t_context *find_context(char **unstr)
140 {
141    t_context *t_cc, *found_cc = NULL;
142    char *name, *tptr;
143    
144    if ((!*unstr) || (!unstr))
145       return(NULL);
146       
147    begin_critical_section(S_SESSION_TABLE);
148    for (t_cc = ContextList; ((t_cc) && (!found_cc)); t_cc = t_cc->next)
149    {
150       if (t_cc->fake_username[0])
151          name = t_cc->fake_username;
152       else
153          name = t_cc->curr_user;
154       tptr = *unstr;
155       if ((!strncasecmp(name, tptr, strlen(name))) && (tptr[strlen(name)] == ' '))
156       {
157          found_cc = t_cc;
158          *unstr = &(tptr[strlen(name)+1]);
159       }
160    }
161    end_critical_section(S_SESSION_TABLE);
162
163    return(found_cc);
164 }
165
166 /*
167  * List users in chat.  Setting allflag to 1 also lists users elsewhere.
168  */
169
170 void do_chat_listing(int allflag)
171 {
172         struct CitContext *ccptr;
173
174         cprintf(":|\n:| Users currently in chat:\n");
175         begin_critical_section(S_SESSION_TABLE);
176         for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
177                 if ( (!strcasecmp(ccptr->cs_room, "<chat>"))
178                    && ((ccptr->cs_flags & CS_STEALTH) == 0)) {
179                         cprintf(":| %-25s <%s>\n", (ccptr->fake_username[0]) ? ccptr->fake_username : ccptr->curr_user, ccptr->chat_room);
180                         }
181                 }
182
183         if (allflag == 1) 
184         {
185                 cprintf(":|\n:| Users not in chat:\n");
186                 for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) 
187                 {
188                         if ( (strcasecmp(ccptr->cs_room, "<chat>"))
189                            && ((ccptr->cs_flags & CS_STEALTH) == 0)) 
190                         {
191                                 cprintf(":| %-25s <%s>:\n", (ccptr->fake_username[0]) ? ccptr->fake_username : ccptr->curr_user, (ccptr->fake_roomname[0]) ? ccptr->fake_roomname : ccptr->cs_room);
192                         }
193                 }
194         }
195         
196         end_critical_section(S_SESSION_TABLE);
197         cprintf(":|\n");
198         }
199
200
201 void cmd_chat(char *argbuf)
202 {
203         char cmdbuf[256];
204         char *un;
205         char *strptr1;
206         char hold_cs_room[ROOMNAMELEN];
207         int MyLastMsg, ThisLastMsg;
208         struct ChatLine *clptr;
209         struct CitContext *t_context;
210         int retval;
211
212         if (!(CC->logged_in)) {
213                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
214                 return;
215                 }
216
217         strcpy(CC->chat_room, "Main room");
218
219         strcpy(hold_cs_room,CC->cs_room);
220         CC->cs_flags = CC->cs_flags | CS_CHAT;
221         set_wtmpsupp("<chat>");
222         cprintf("%d Entering chat mode (type '/help' for available commands)\n",
223                 START_CHAT_MODE);
224
225         MyLastMsg = ChatLastMsg;
226
227         if ((CC->cs_flags & CS_STEALTH) == 0) {
228                 allwrite("<entering chat>",0, CC->chat_room, NULL);
229                 }
230
231         strcpy(cmdbuf, "");
232
233         while(1) {
234                 int ok_cmd;
235                 
236                 ok_cmd = 0;
237                 cmdbuf[strlen(cmdbuf) + 1] = 0;
238                 retval = client_read_to(&cmdbuf[strlen(cmdbuf)], 1, 2);
239
240                 /* if we have a complete line, do send processing */
241                 if (strlen(cmdbuf) > 0) if (cmdbuf[strlen(cmdbuf)-1] == 10) {
242                         cmdbuf[strlen(cmdbuf) - 1] = 0;
243                         time(&CC->lastcmd);
244                         time(&CC->lastidle);
245
246                         if ( (!strcasecmp(cmdbuf,"exit"))
247                         ||(!strcasecmp(cmdbuf,"/exit"))
248                         ||(!strcasecmp(cmdbuf,"quit"))
249                         ||(!strcasecmp(cmdbuf,"logout"))
250                         ||(!strcasecmp(cmdbuf,"logoff"))
251                         ||(!strcasecmp(cmdbuf,"/q"))
252                         ||(!strcasecmp(cmdbuf,".q"))
253                         ||(!strcasecmp(cmdbuf,"/quit"))
254                                 ) strcpy(cmdbuf,"000");
255         
256                         if (!strcmp(cmdbuf,"000")) {
257                                 if ((CC->cs_flags & CS_STEALTH) == 0) {
258                                         allwrite("<exiting chat>",0, CC->chat_room, NULL);
259                                         }
260                                 sleep(1);
261                                 cprintf("000\n");
262                                 CC->cs_flags = CC->cs_flags - CS_CHAT;
263                                 set_wtmpsupp(hold_cs_room);
264                                 return;
265                                 }
266         
267                         if ((!strcasecmp(cmdbuf,"/help"))
268                         ||(!strcasecmp(cmdbuf,"help"))
269                         ||(!strcasecmp(cmdbuf,"/?"))
270                         ||(!strcasecmp(cmdbuf,"?"))) {
271                                 cprintf(":|\n");
272                                 cprintf(":|Available commands: \n");
273                                 cprintf(":|/help   (prints this message) \n");
274                                 cprintf(":|/who    (list users currently in chat) \n");
275                                 cprintf(":|/whobbs (list users in chat -and- elsewhere) \n");
276                                 cprintf(":|/me     ('action' line, ala irc) \n");
277                                 cprintf(":|/msg    (send private message, ala irc) \n");
278                                 cprintf(":|/join   (join new room) \n"); 
279                                 cprintf(":|/quit   (return to the BBS) \n");
280                                 cprintf(":|\n");
281                                 ok_cmd = 1;
282                                 }
283                         if (!strcasecmp(cmdbuf,"/who")) {
284                                 do_chat_listing(0);
285                                 ok_cmd = 1;
286                                 }
287                         if (!strcasecmp(cmdbuf,"/whobbs")) {
288                                 do_chat_listing(1);
289                                 ok_cmd = 1;
290                                 }
291                         if (!strncasecmp(cmdbuf,"/me ",4)) {
292                                 allwrite(&cmdbuf[4],1, CC->chat_room, NULL);
293                                 ok_cmd = 1;
294                                 }
295                                 
296                         if (!strncasecmp(cmdbuf,"/msg ", 5))
297                         {
298                            ok_cmd =1;
299                            strptr1 = &cmdbuf[5];
300                            if ((t_context = find_context(&strptr1)))
301                            {
302                               allwrite(strptr1, 2, "", CC->curr_user);
303                               if (strcasecmp(CC->curr_user, t_context->curr_user))
304                                  allwrite(strptr1, 2, "", t_context->curr_user);
305                            }
306                            else
307                               cprintf(":|User not found.\n", cmdbuf);
308                         cprintf("\n");
309                         }
310
311                         if (!strncasecmp(cmdbuf,"/join ", 6))
312                         {
313                            ok_cmd = 1;
314                            allwrite("<changing rooms>",0, CC->chat_room, NULL);
315                            if (!cmdbuf[6])
316                               strcpy(CC->chat_room, "Main room");
317                            else
318                            {
319                               strncpy(CC->chat_room, &cmdbuf[6],
320                                       sizeof CC->chat_room);
321                               CC->chat_room[sizeof CC->chat_room - 1] = 0;
322                            }
323                            allwrite("<joining room>",0, CC->chat_room, NULL);
324                            cprintf("\n");
325                         }
326                         if ((cmdbuf[0]!='/')&&(strlen(cmdbuf)>0)) {
327                                 ok_cmd = 1;
328                                 allwrite(cmdbuf,0, CC->chat_room, NULL);
329                                 }
330
331                         if ((!ok_cmd) && (cmdbuf[0]) && (cmdbuf[0] != '\n'))
332                            cprintf(":|Command %s is not understood.\n", cmdbuf);
333                            
334                         strcpy(cmdbuf, "");
335
336                         }
337         
338                 /* now check the queue for new incoming stuff */
339                 
340                 if (CC->fake_username[0])
341                    un = CC->fake_username;
342                 else
343                    un = CC->curr_user;
344                 if (ChatLastMsg > MyLastMsg) {
345                         ThisLastMsg = ChatLastMsg;
346                         for (clptr=ChatQueue; clptr!=NULL; clptr=clptr->next) 
347                         {
348                            if ((clptr->chat_seq > MyLastMsg) && ((!clptr->chat_username[0]) || (!strncasecmp(un, clptr->chat_username, 32))))
349                            {
350                               if ((!clptr->chat_room[0]) || (!strncasecmp(CC->chat_room, clptr->chat_room, ROOMNAMELEN)))
351                               {
352                                  cprintf("%s\n", clptr->chat_text);
353                               }
354                            }
355                         }
356                         MyLastMsg = ThisLastMsg;
357                         }
358
359                 }
360         }
361
362
363 /*
364  * poll for express messages
365  */
366 void cmd_pexp(char *argbuf) /* arg unused */ {
367         struct ExpressMessage *emptr;
368
369         if (CC->FirstExpressMessage == NULL) {
370                 cprintf("%d No express messages waiting.\n",ERROR);
371                 return;
372                 }
373
374         cprintf("%d Express msgs:\n",LISTING_FOLLOWS);
375
376         while (CC->FirstExpressMessage != NULL) {
377                 cprintf("%s", CC->FirstExpressMessage->em_text);
378                 begin_critical_section(S_SESSION_TABLE);
379                 emptr = CC->FirstExpressMessage;
380                 CC->FirstExpressMessage = CC->FirstExpressMessage->next;
381                 phree(emptr);
382                 end_critical_section(S_SESSION_TABLE);
383                 }
384         cprintf("000\n");
385         }
386
387
388 /*
389  * send express messages  <bc>
390  */
391 void cmd_sexp(char *argbuf)
392 {
393         char x_user[256];
394         char x_msg[256];
395         int message_sent = 0;
396         struct CitContext *ccptr;
397         struct ExpressMessage *emptr, *emnew;
398         char *lun;              /* <bc> */
399
400         if ((!(CC->logged_in))&&(!(CC->internal_pgm))) {
401                 cprintf("%d Not logged in.\n",ERROR+NOT_LOGGED_IN);
402                 return;
403                 }
404
405         if (num_parms(argbuf)!=2) {
406                 cprintf("%d usage error\n",ERROR);
407                 return; 
408                 }
409
410         if (CC->fake_username[0])
411            lun = CC->fake_username;
412         else
413            lun = CC->usersupp.fullname;
414
415         extract(x_user,argbuf,0);
416
417         if (!strcmp(x_user, "."))
418         {
419            strcpy(x_user, CC->last_pager);
420         }
421         extract(x_msg,argbuf,1);
422         
423         if (!x_user[0])
424         {
425            cprintf("%d You were not previously paged.\n", ERROR);
426            return;
427         }
428
429         if ( (!strcasecmp(x_user, "broadcast")) && (CC->usersupp.axlevel < 6) ) {
430                 cprintf("%d Higher access required to send a broadcast.\n",
431                         ERROR+HIGHER_ACCESS_REQUIRED);
432                 return;
433                 }
434
435         /* find the target user's context and append the message */
436         begin_critical_section(S_SESSION_TABLE);
437         for (ccptr = ContextList; ccptr != NULL; ccptr = ccptr->next) {
438                 char *un;
439                 
440                 if (ccptr->fake_username[0])            /* <bc> */
441                    un = ccptr->fake_username;
442                 else
443                    un = ccptr->usersupp.fullname;
444                    
445                 if ( (!strcasecmp(un, x_user))
446                    || (!strcasecmp(x_user, "broadcast")) ) {
447                         strcpy(ccptr->last_pager, CC->curr_user);
448                         emnew = (struct ExpressMessage *)
449                                 mallok(sizeof(struct ExpressMessage));
450                         emnew->next = NULL;
451                         sprintf(emnew->em_text, "%s from %s:\n %s\n",
452                                 ( (!strcasecmp(x_user, "broadcast")) ? "Broadcast message" : "Message" ),
453                                 lun, x_msg);
454
455                         if (ccptr->FirstExpressMessage == NULL) {
456                                 ccptr->FirstExpressMessage = emnew;
457                                 }
458                         else {
459                                 emptr = ccptr->FirstExpressMessage;
460                                 while (emptr->next != NULL) {
461                                         emptr = emptr->next;
462                                         }
463                                 emptr->next = emnew;
464                                 }
465
466                         ++message_sent;
467                         }
468                 }
469         end_critical_section(S_SESSION_TABLE);
470
471         if (message_sent > 0) {
472                 cprintf("%d Message sent.\n",OK);
473                 }
474         else {
475                 cprintf("%d No user '%s' logged in.\n",ERROR,x_user);
476                 }
477         }