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