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