* Moved to the new string tokenizer API
[citadel.git] / webcit / paging.c
1 /*
2  * $Id$
3  *
4  * Functions which implement the chat and paging facilities.
5  */
6
7 #include <ctype.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <fcntl.h>
12 #include <signal.h>
13 #include <sys/types.h>
14 #include <sys/wait.h>
15 #include <sys/socket.h>
16 #include <sys/poll.h>
17 #include <sys/time.h>
18 #include <limits.h>
19 #include <netinet/in.h>
20 #include <netdb.h>
21 #include <string.h>
22 #include <pwd.h>
23 #include <errno.h>
24 #include <stdarg.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include "webcit.h"
28
29
30 /*
31  * display the form for paging (x-messaging) another user
32  */
33 void display_page(void)
34 {
35         char recp[SIZ];
36
37         strcpy(recp, bstr("recp"));
38
39         output_headers(1, 1, 2, 0, 0, 0, 0);
40         wprintf("<div id=\"banner\">\n"
41                 "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
42                 "<SPAN CLASS=\"titlebar\">Send instant message</SPAN>"
43                 "</TD></TR></TABLE>\n"
44                 "</div>\n<div id=\"content\">\n"
45         );
46                                                                                                                              
47         wprintf("<div id=\"fix_scrollbar_bug\">"
48                 "<table border=0 width=100%% bgcolor=\"#ffffff\"><tr><td>\n");
49
50         wprintf("Send an instant message to: ");
51         escputs(recp);
52         wprintf("<br>\n");
53
54         wprintf("<FORM METHOD=\"POST\" ACTION=\"/page_user\">\n");
55
56         wprintf("<TABLE border=0 width=100%%><TR><TD>\n");
57
58         wprintf("<INPUT TYPE=\"hidden\" NAME=\"recp\" VALUE=\"");
59         escputs(recp);
60         wprintf("\">\n");
61
62         wprintf("<INPUT TYPE=\"hidden\" NAME=\"closewin\" VALUE=\"");
63         escputs(bstr("closewin"));
64         wprintf("\">\n");
65
66         wprintf("Enter message text:<br />");
67
68         wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft ROWS=5 COLS=40 "
69                 "WIDTH=40></TEXTAREA>\n");
70
71         wprintf("</TD></TR></TABLE><br />\n");
72
73         wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Send message\">");
74         wprintf("<br /><A HREF=\"javascript:window.close();\"Cancel</A>\n");
75
76         wprintf("</FORM></CENTER>\n");
77         wprintf("</td></tr></table></div>\n");
78         wDumpContent(1);
79 }
80
81 /*
82  * page another user
83  */
84 void page_user(void)
85 {
86         char recp[SIZ];
87         char sc[SIZ];
88         char buf[SIZ];
89         char closewin[SIZ];
90
91         output_headers(1, 1, 2, 0, 0, 0, 0);
92         wprintf("<div id=\"banner\">\n"
93                 "<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>"
94                 "<SPAN CLASS=\"titlebar\">Add or edit an event</SPAN>"
95                 "</TD></TR></TABLE>\n"
96                 "</div>\n<div id=\"content\">\n"
97         );
98                                                                                                                              
99         strcpy(recp, bstr("recp"));
100         strcpy(sc, bstr("sc"));
101         strcpy(closewin, bstr("closewin"));
102
103         if (strcmp(sc, "Send message")) {
104                 wprintf("<EM>Message was not sent.</EM><br />\n");
105         } else {
106                 serv_printf("SEXP %s|-", recp);
107                 serv_gets(buf);
108
109                 if (buf[0] == '4') {
110                         text_to_server(bstr("msgtext"), 0);
111                         serv_puts("000");
112                         wprintf("<EM>Message has been sent to ");
113                         escputs(recp);
114                         wprintf(".</EM><br />\n");
115                 }
116                 else {
117                         wprintf("<EM>%s</EM><br />\n", &buf[4]);
118                 }
119         }
120         
121         if (!strcasecmp(closewin, "yes")) {
122                 wprintf("<CENTER><A HREF=\"javascript:window.close();\">"
123                         "[ close window ]</A></CENTER>\n");
124         }
125
126         wDumpContent(1);
127 }
128
129
130
131 /*
132  * multiuser chat
133  */
134 void do_chat(void)
135 {
136         char buf[SIZ];
137
138         /* First, check to make sure we're still allowed in this room. */
139         serv_printf("GOTO %s", WC->wc_roomname);
140         serv_gets(buf);
141         if (buf[0] != '2') {
142                 smart_goto("_BASEROOM_");
143                 return;
144         }
145
146         /* If the chat socket is still open from a previous chat,
147          * close it -- because it might be stale or in the wrong room.
148          */
149         if (WC->chat_sock < 0) {
150                 close(WC->chat_sock);
151                 WC->chat_sock = (-1);
152         }
153
154         /* WebCit Chat works by having transmit, receive, and refresh
155          * frames.  Load the frameset.
156          */
157         do_template("chatframeset");
158         return;
159 }
160
161
162 /*
163  *
164  */
165 void page_popup(void)
166 {
167         char buf[SIZ];
168         char pagefrom[SIZ];
169
170         while (serv_puts("GEXP"), serv_gets(buf), buf[0]=='1') {
171
172                 extract_token(pagefrom, &buf[4], 3, '|', sizeof pagefrom);
173
174                 wprintf("<table border=1 bgcolor=\"#880000\"><tr><td>");
175                 wprintf("<span class=\"titlebar\">Instant message from ");
176                 escputs(pagefrom);
177                 wprintf("</span></td></tr><tr><td><font color=\"#FFFFFF\">");
178                 fmout(NULL, "LEFT");
179                 wprintf("</font></td></tr>"
180                         "<tr><td><div align=center><font color=\"#FFFFFF\">"
181                         "<a href=\"javascript:hide_page_popup()\">[ close window ]</a>"
182                         "</font></div>"
183                         "</td></tr>"
184                         "</table>\n");
185         }
186
187         WC->HaveInstantMessages = 0;
188 }
189
190
191
192 /*
193  * Support function for chat -- make sure the chat socket is connected
194  * and in chat mode.
195  */
196 int setup_chat_socket(void) {
197         char buf[SIZ];
198         int i;
199         int good_chatmode = 0;
200
201         if (WC->chat_sock < 0) {
202
203                 if (!strcasecmp(ctdlhost, "uds")) {
204                         /* unix domain socket */
205                         sprintf(buf, "%s/citadel.socket", ctdlport);
206                         WC->chat_sock = uds_connectsock(buf);
207                 }
208                 else {
209                         /* tcp socket */
210                         WC->chat_sock = tcp_connectsock(ctdlhost, ctdlport);
211                 }
212
213                 if (WC->chat_sock < 0) {
214                         return(errno);
215                 }
216
217                 /* Temporarily swap the serv and chat sockets during chat talk */
218                 i = WC->serv_sock;
219                 WC->serv_sock = WC->chat_sock;
220                 WC->chat_sock = i;
221
222                 serv_gets(buf);
223                 if (buf[0] == '2') {
224                         serv_printf("USER %s", WC->wc_username);
225                         serv_gets(buf);
226                         if (buf[0] == '3') {
227                                 serv_printf("PASS %s", WC->wc_password);
228                                 serv_gets(buf);
229                                 if (buf[0] == '2') {
230                                         serv_printf("GOTO %s", WC->wc_roomname);
231                                         serv_gets(buf);
232                                         if (buf[0] == '2') {
233                                                 serv_puts("CHAT");
234                                                 serv_gets(buf);
235                                                 if (buf[0] == '8') {
236                                                         good_chatmode = 1;
237                                                 }
238                                         }
239                                 }
240                         }
241                 }
242
243                 /* Unswap the sockets. */
244                 i = WC->serv_sock;
245                 WC->serv_sock = WC->chat_sock;
246                 WC->chat_sock = i;
247
248                 if (!good_chatmode) close(WC->serv_sock);
249
250         }
251         return(0);
252 }
253
254
255
256 /*
257  * Receiving side of the chat window.  This is implemented in a
258  * tiny hidden IFRAME that just does JavaScript writes to
259  * other frames whenever it refreshes and finds new data.
260  */
261 void chat_recv(void) {
262         int i;
263         struct pollfd pf;
264         int got_data = 0;
265         int end_chat_now = 0;
266         char buf[SIZ];
267         char cl_user[SIZ];
268         char cl_text[SIZ];
269         char *output_data = NULL;
270
271         output_headers(0, 0, 0, 0, 0, 0, 0);
272
273         wprintf("Content-type: text/html\n");
274         wprintf("\n");
275         wprintf("<html>\n"
276                 "<head>\n"
277                 "<meta http-equiv=\"refresh\" content=\"3\" />\n"
278                 "</head>\n"
279
280                 "<body bgcolor=\"#FFFFFF\">\n"
281         );
282
283         if (setup_chat_socket() != 0) {
284                 wprintf("Error setting up chat socket</BODY></HTML>\n");
285                 wDumpContent(0);
286                 return;
287         }
288
289         /*
290          * See if there is any chat data waiting.
291          */
292         output_data = strdup("");
293         do {
294                 got_data = 0;
295                 pf.fd = WC->chat_sock;
296                 pf.events = POLLIN;
297                 pf.revents = 0;
298                 if (poll(&pf, 1, 1) > 0) if (pf.revents & POLLIN) {
299                         ++got_data;
300
301                         /* Temporarily swap the serv and chat sockets during chat talk */
302                         i = WC->serv_sock;
303                         WC->serv_sock = WC->chat_sock;
304                         WC->chat_sock = i;
305         
306                         serv_gets(buf);
307
308                         if (!strcmp(buf, "000")) {
309                                 strcpy(buf, ":|exiting chat mode");
310                                 end_chat_now = 1;
311                         }
312                         
313                         /* Unswap the sockets. */
314                         i = WC->serv_sock;
315                         WC->serv_sock = WC->chat_sock;
316                         WC->chat_sock = i;
317
318                         /* Append our output data */
319                         output_data = realloc(output_data, strlen(output_data) + strlen(buf) + 4);
320                         strcat(output_data, buf);
321                         strcat(output_data, "\n");
322                 }
323
324         } while ( (got_data) && (!end_chat_now) );
325
326         if (end_chat_now) {
327                 close(WC->chat_sock);
328                 WC->chat_sock = (-1);
329                 wprintf("<IMG SRC=\"/static/blank.gif\" onLoad=\"parent.window.close();\">\n");
330         }
331
332         if (strlen(output_data) > 0) {
333
334                 if (output_data[strlen(output_data)-1] == '\n') {
335                         output_data[strlen(output_data)-1] = 0;
336                 }
337
338                 /* Output our fun to the other frame. */
339                 wprintf("<IMG SRC=\"/static/blank.gif\" WIDTH=1 HEIGHT=1\n"
340                         "onLoad=\" \n"
341                 );
342
343                 for (i=0; i<num_tokens(output_data, '\n'); ++i) {
344                         extract_token(buf, output_data, i, '\n', sizeof buf);
345                         extract_token(cl_user, buf, 0, '|', sizeof cl_user);
346                         extract_token(cl_text, buf, 1, '|', sizeof cl_text);
347
348                         if (strcasecmp(cl_text, "NOOP")) {
349
350                                 wprintf("parent.chat_transcript.document.write('");
351         
352                                 if (strcasecmp(cl_user, WC->last_chat_user)) {
353                                         wprintf("<TABLE border=0 WIDTH=100%% "
354                                                 "CELLSPACING=1 CELLPADDING=0 "
355                                                 "BGCOLOR=&quot;#FFFFFF&quot;>"
356                                                 "<TR><TD></TR></TD></TABLE>"
357                                         );
358         
359                                 }
360
361                                 wprintf("<TABLE border=0 WIDTH=100%% "
362                                         "CELLSPACING=0 CELLPADDING=0 "
363                                         "BGCOLOR=&quot;#EEEEEE&quot;>");
364         
365                                 wprintf("<TR><TD>");
366         
367                                 if (!strcasecmp(cl_user, ":")) {
368                                         wprintf("<I>");
369                                 }
370
371                                 if (strcasecmp(cl_user, WC->last_chat_user)) {
372                                         wprintf("<B>");
373         
374                                         if (!strcasecmp(cl_user, WC->wc_username)) {
375                                                 wprintf("<FONT COLOR=&quot;#FF0000&quot;>");
376                                         }
377                                         else {
378                                                 wprintf("<FONT COLOR=&quot;#0000FF&quot;>");
379                                         }
380                                         jsescputs(cl_user);
381         
382                                         wprintf("</FONT>: </B>");
383                                 }
384                                 else {
385                                         wprintf("&nbsp;&nbsp;&nbsp;");
386                                 }
387                                 jsescputs(cl_text);
388                                 if (!strcasecmp(cl_user, ":")) {
389                                         wprintf("</I>");
390                                 }
391
392                                 wprintf("</TD></TR></TABLE>");
393                                 wprintf("'); \n");
394
395                                 strcpy(WC->last_chat_user, cl_user);
396                         }
397                 }
398
399                 wprintf("parent.chat_transcript.scrollTo(999999,999999);\">\n");
400         }
401
402         free(output_data);
403
404         wprintf("</BODY></HTML>\n");
405         wDumpContent(0);
406 }
407
408
409 /*
410  * sending side of the chat window
411  */
412 void chat_send(void) {
413         int i;
414         char send_this[SIZ];
415         char buf[SIZ];
416
417         output_headers(0, 0, 0, 0, 0, 0, 0);
418         wprintf("Content-type: text/html\n");
419         wprintf("\n");
420         wprintf("<HTML>"
421                 "<BODY onLoad=\"document.chatsendform.send_this.focus();\" >"
422         );
423
424         if (bstr("send_this") != NULL) {
425                 strcpy(send_this, bstr("send_this"));
426         }
427         else {
428                 strcpy(send_this, "");
429         }
430
431         if (bstr("sendbutton") != NULL) {
432
433                 if (!strcasecmp(bstr("sendbutton"), "Help")) {
434                         strcpy(send_this, "/help");
435                 }
436
437                 if (!strcasecmp(bstr("sendbutton"), "List Users")) {
438                         strcpy(send_this, "/who");
439                 }
440
441                 if (!strcasecmp(bstr("sendbutton"), "Exit")) {
442                         strcpy(send_this, "/quit");
443                 }
444
445                 if (setup_chat_socket() != 0) {
446                         wprintf("Error setting up chat socket</BODY></HTML>\n");
447                         wDumpContent(0);
448                         return;
449                 }
450
451                 /* Temporarily swap the serv and chat sockets during chat talk */
452                 i = WC->serv_sock;
453                 WC->serv_sock = WC->chat_sock;
454                 WC->chat_sock = i;
455
456                 while (strlen(send_this) > 0) {
457                         if (strlen(send_this) < 67) {
458                                 serv_puts(send_this);
459                                 strcpy(send_this, "");
460                         }
461                         else {
462                                 for (i=55; i<67; ++i) {
463                                         if (send_this[i] == ' ') break;
464                                 }
465                                 strncpy(buf, send_this, i);
466                                 buf[i] = 0;
467                                 strcpy(send_this, &send_this[i]);
468                                 serv_puts(buf);
469                         }
470                 }
471
472                 /* Unswap the sockets. */
473                 i = WC->serv_sock;
474                 WC->serv_sock = WC->chat_sock;
475                 WC->chat_sock = i;
476
477         }
478
479         wprintf("<FORM METHOD=\"POST\" ACTION=\"/chat_send\" NAME=\"chatsendform\">\n");
480         wprintf("<INPUT TYPE=\"text\" SIZE=\"80\" MAXLENGTH=\"%d\" "
481                 "NAME=\"send_this\">\n", SIZ-10);
482         wprintf("<br />");
483         wprintf("<INPUT TYPE=\"submit\" NAME=\"sendbutton\" VALUE=\"Send\">\n");
484         wprintf("<INPUT TYPE=\"submit\" NAME=\"sendbutton\" VALUE=\"Help\">\n");
485         wprintf("<INPUT TYPE=\"submit\" NAME=\"sendbutton\" VALUE=\"List Users\">\n");
486         wprintf("<INPUT TYPE=\"submit\" NAME=\"sendbutton\" VALUE=\"Exit\">\n");
487         wprintf("</FORM>\n");
488
489         wprintf("</BODY></HTML>\n");
490         wDumpContent(0);
491 }
492
493