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