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