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