* move some more vars from the session context to strbuf (the use of StrBufAppendTemp...
[citadel.git] / webcit / paging.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup PageFunc Functions which implement the chat and paging facilities.
6  * \ingroup ClientPower
7  */
8 /*@{*/
9 #include "webcit.h"
10
11 /**
12  * \brief display the form for paging (x-messaging) another user
13  */
14 void display_page(void)
15 {
16         char recp[SIZ];
17
18         strcpy(recp, bstr("recp"));
19
20         output_headers(1, 1, 2, 0, 0, 0);
21         wprintf("<div id=\"banner\">\n");
22         wprintf("<h1>");
23         wprintf(_("Send instant message"));
24         wprintf("</h1>");
25         wprintf("</div>\n");
26
27         wprintf("<div id=\"content\" class=\"service\">\n");
28
29         wprintf("<div class=\"fix_scrollbar_bug\">"
30                 "<table class=\"paging_background\"><tr><td>\n");
31
32         wprintf(_("Send an instant message to: "));
33         escputs(recp);
34         wprintf("<br>\n");
35
36         wprintf("<FORM METHOD=\"POST\" action=\"page_user\">\n");
37         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
38         wprintf("<input type=\"hidden\" name=\"template\" value=\"who\">\n");
39
40         wprintf("<TABLE border=0 width=100%%><TR><TD>\n");
41
42         wprintf("<INPUT TYPE=\"hidden\" NAME=\"recp\" VALUE=\"");
43         escputs(recp);
44         wprintf("\">\n");
45
46         wprintf(_("Enter message text:"));
47         wprintf("<br />");
48
49         wprintf("<TEXTAREA NAME=\"msgtext\" wrap=soft ROWS=5 COLS=40 "
50                 "WIDTH=40></TEXTAREA>\n");
51
52         wprintf("</TD></TR></TABLE><br />\n");
53
54         wprintf("<INPUT TYPE=\"submit\" NAME=\"send_button\" VALUE=\"%s\">", _("Send message"));
55         wprintf("<br /><a href=\"javascript:window.close();\"%s</A>\n", _("Cancel"));
56
57         wprintf("</FORM></CENTER>\n");
58         wprintf("</td></tr></table></div>\n");
59         wDumpContent(1);
60 }
61
62 /**
63  * \brief page another user
64  */
65 void page_user(void)
66 {
67         char recp[256];
68         char buf[256];
69
70         safestrncpy(recp, bstr("recp"), sizeof recp);
71
72         if (!havebstr("send_button")) {
73                 safestrncpy(WC->ImportantMessage,
74                         _("Message was not sent."),
75                         sizeof WC->ImportantMessage
76                 );
77         } else {
78                 serv_printf("SEXP %s|-", recp);
79                 serv_getln(buf, sizeof buf);
80
81                 if (buf[0] == '4') {
82                         text_to_server(bstr("msgtext"));
83                         serv_puts("000");
84                         stresc(buf, 256, recp, 0, 0);
85                         snprintf(WC->ImportantMessage,
86                                 sizeof WC->ImportantMessage,
87                                 "%s%s.",
88                                 _("Message has been sent to "),
89                                 buf
90                         );
91                 }
92                 else {
93                         safestrncpy(WC->ImportantMessage, &buf[4], sizeof WC->ImportantMessage);
94                 }
95         }
96
97         url_do_template();
98 }
99
100
101
102 /**
103  * \brief multiuser chat
104  */
105 void do_chat(void)
106 {
107         char buf[SIZ];
108
109         /** First, check to make sure we're still allowed in this room. */
110         serv_printf("GOTO %s", ChrPtr(WC->wc_roomname));
111         serv_getln(buf, sizeof buf);
112         if (buf[0] != '2') {
113                 StrBuf *Buf;
114                 Buf = NewStrBufPlain(HKEY("_BASEROOM_"));
115                 smart_goto(Buf);
116                 FreeStrBuf(&Buf);
117                 return;
118         }
119
120         /**
121          * If the chat socket is still open from a previous chat,
122          * close it -- because it might be stale or in the wrong room.
123          */
124         if (WC->chat_sock < 0) {
125                 close(WC->chat_sock);
126                 WC->chat_sock = (-1);
127         }
128
129         /**
130          * WebCit Chat works by having transmit, receive, and refresh
131          * frames.  Load the frameset.  (This isn't AJAX but the headers
132          * output by begin_ajax_response() happen to be the ones we need.)
133          */
134         begin_ajax_response();
135         do_template("chatframeset", NULL);
136         end_ajax_response();
137         return;
138 }
139
140
141 /**
142  * \brief display page popup
143  * If there are instant messages waiting, and we notice that we haven't checked them in
144  * a while, it probably means that we need to open the instant messenger window.
145  */
146 void page_popup(void)
147 {
148         int len;
149         char buf[SIZ];
150
151         /** JavaScript function to alert the user that popups are probably blocked */
152         wprintf("<script type=\"text/javascript\">      "
153                 "function PopUpFailed() {       "
154                 " alert(\"%s\");        "
155                 "}      "
156                 "</script>\n",
157                 _("You have one or more instant messages waiting, but the Citadel Instant Messenger "
158                   "window failed to open.  This is probably because you have a popup blocker "
159                   "installed.  Please configure your popup blocker to allow popups from this site "
160                   "if you wish to receive instant messages.")
161         );
162
163         /** First, do the check as part of our page load. */
164         serv_puts("NOOP");
165         len = serv_getln(buf, sizeof buf);
166         if ((len >= 3) && (buf[3] == '*')) {
167                 if ((time(NULL) - WC->last_pager_check) > 60) {
168                         wprintf("<script type=\"text/javascript\">"
169                                 " var oWin = window.open('static/instant_messenger.html', "
170                                 " 'CTDL_MESSENGER', 'width=700,height=400');    "
171                                 " if (oWin==null || typeof(oWin)==\"undefined\") {      "
172                                 "  PopUpFailed();       "
173                                 " }     "
174                                 "</script>"
175                         );      
176                 }
177         }
178
179         /** Then schedule it to happen again a minute from now if the user is idle. */
180         wprintf("<script type=\"text/javascript\">      "
181                 " function HandleSslp(sslg_xmlresponse) {       "
182                 "  sslg_response = sslg_xmlresponse.responseText.substr(0, 1);  "
183                 "  if (sslg_response == 'Y') {  "
184                 "   var oWin = window.open('static/instant_messenger.html', 'CTDL_MESSENGER',   "
185                 "    'width=700,height=400');   "
186                 "   if (oWin==null || typeof(oWin)==\"undefined\") {    "
187                 "    PopUpFailed();     "
188                 "   }   "
189                 "  }    "
190                 " }     "
191                 " function CheckPager() {       "
192                 "  new Ajax.Request('sslg', { method: 'get', parameters: CtdlRandomString(),    "
193                 "   onSuccess: HandleSslp } );  "
194                 " }     "
195                 " new PeriodicalExecuter(CheckPager, 30);       "
196                 "</script>      "
197         );
198 }
199
200
201
202 /**
203  * \brief Support function for chat
204  * make sure the chat socket is connected
205  * and in chat mode.
206  */
207 int setup_chat_socket(void) {
208         char buf[SIZ];
209         int i;
210         int good_chatmode = 0;
211
212         if (WC->chat_sock < 0) {
213
214                 if (!strcasecmp(ctdlhost, "uds")) {
215                         /** unix domain socket */
216                         sprintf(buf, "%s/citadel.socket", ctdlport);
217                         WC->chat_sock = uds_connectsock(buf);
218                 }
219                 else {
220                         /** tcp socket */
221                         WC->chat_sock = tcp_connectsock(ctdlhost, ctdlport);
222                 }
223
224                 if (WC->chat_sock < 0) {
225                         return(errno);
226                 }
227
228                 /** Temporarily swap the serv and chat sockets during chat talk */
229                 i = WC->serv_sock;
230                 WC->serv_sock = WC->chat_sock;
231                 WC->chat_sock = i;
232
233                 serv_getln(buf, sizeof buf);
234                 if (buf[0] == '2') {
235                         serv_printf("USER %s", ChrPtr(WC->wc_username));
236                         serv_getln(buf, sizeof buf);
237                         if (buf[0] == '3') {
238                                 serv_printf("PASS %s", ChrPtr(WC->wc_password));
239                                 serv_getln(buf, sizeof buf);
240                                 if (buf[0] == '2') {
241                                         serv_printf("GOTO %s", ChrPtr(WC->wc_roomname));
242                                         serv_getln(buf, sizeof buf);
243                                         if (buf[0] == '2') {
244                                                 serv_puts("CHAT");
245                                                 serv_getln(buf, sizeof buf);
246                                                 if (buf[0] == '8') {
247                                                         good_chatmode = 1;
248                                                 }
249                                         }
250                                 }
251                         }
252                 }
253
254                 /** Unswap the sockets. */
255                 i = WC->serv_sock;
256                 WC->serv_sock = WC->chat_sock;
257                 WC->chat_sock = i;
258
259                 if (!good_chatmode) close(WC->serv_sock);
260
261         }
262         return(0);
263 }
264
265
266
267 /**
268  * \brief Receiving side of the chat window.  
269  * This is implemented in a
270  * tiny hidden IFRAME that just does JavaScript writes to
271  * other frames whenever it refreshes and finds new data.
272  */
273 void chat_recv(void) {
274         int i;
275         struct pollfd pf;
276         int got_data = 0;
277         int end_chat_now = 0;
278         char buf[SIZ];
279         char cl_user[SIZ];
280         char cl_text[SIZ];
281         char *output_data = NULL;
282
283         output_headers(0, 0, 0, 0, 0, 0);
284
285         hprintf("Content-type: text/html; charset=utf-8\r\n");
286         wprintf("<html>\n"
287                 "<head>\n"
288                 "<meta http-equiv=\"refresh\" content=\"3\" />\n"
289                 "</head>\n"
290
291                 "<body bgcolor=\"#FFFFFF\">\n"
292         );
293
294         if (setup_chat_socket() != 0) {
295                 wprintf(_("An error occurred while setting up the chat socket."));
296                 wprintf("</BODY></HTML>\n");
297                 wDumpContent(0);
298                 return;
299         }
300
301         /**
302          * See if there is any chat data waiting.
303          */
304         output_data = strdup("");
305         do {
306                 got_data = 0;
307                 pf.fd = WC->chat_sock;
308                 pf.events = POLLIN;
309                 pf.revents = 0;
310                 if ((poll(&pf, 1, 1) > 0) && (pf.revents & POLLIN)) {
311                         ++got_data;
312
313                         /** Temporarily swap the serv and chat sockets during chat talk */
314                         i = WC->serv_sock;
315                         WC->serv_sock = WC->chat_sock;
316                         WC->chat_sock = i;
317         
318                         serv_getln(buf, sizeof buf);
319
320                         if (!strcmp(buf, "000")) {
321                                 strcpy(buf, ":|");
322                                 strcat(buf, _("Now exiting chat mode."));
323                                 end_chat_now = 1;
324                         }
325                         
326                         /** Unswap the sockets. */
327                         i = WC->serv_sock;
328                         WC->serv_sock = WC->chat_sock;
329                         WC->chat_sock = i;
330
331                         /** Append our output data */
332                         output_data = realloc(output_data, strlen(output_data) + strlen(buf) + 4);
333                         strcat(output_data, buf);
334                         strcat(output_data, "\n");
335                 }
336
337         } while ( (got_data) && (!end_chat_now) );
338
339         if (end_chat_now) {
340                 close(WC->chat_sock);
341                 WC->chat_sock = (-1);
342                 wprintf("<img src=\"static/blank.gif\" onLoad=\"parent.window.close();\">\n");
343         }
344
345         if (!IsEmptyStr(output_data)) {
346                 int len;
347                 len = strlen(output_data);
348                 if (output_data[len-1] == '\n') {
349                         output_data[len-1] = 0;
350                 }
351
352                 /** Output our fun to the other frame. */
353                 wprintf("<img src=\"static/blank.gif\" WIDTH=1 HEIGHT=1\n"
354                         "onLoad=\" \n"
355                 );
356
357                 for (i=0; i<num_tokens(output_data, '\n'); ++i) {
358                         extract_token(buf, output_data, i, '\n', sizeof buf);
359                         extract_token(cl_user, buf, 0, '|', sizeof cl_user);
360                         extract_token(cl_text, buf, 1, '|', sizeof cl_text);
361
362                         if (strcasecmp(cl_text, "NOOP")) {
363
364                                 wprintf("parent.chat_transcript.document.write('");
365         
366                                 if (strcasecmp(cl_user, WC->last_chat_user)) {
367                                         wprintf("<TABLE border=0 WIDTH=100%% "
368                                                 "CELLSPACING=1 CELLPADDING=0 "
369                                                 "BGCOLOR=&quot;#FFFFFF&quot;>"
370                                                 "<TR><TD></TR></TD></TABLE>"
371                                         );
372         
373                                 }
374
375                                 wprintf("<TABLE border=0 WIDTH=100%% "
376                                         "CELLSPACING=0 CELLPADDING=0 "
377                                         "BGCOLOR=&quot;#EEEEEE&quot;>");
378         
379                                 wprintf("<TR><TD>");
380         
381                                 if (!strcasecmp(cl_user, ":")) {
382                                         wprintf("<I>");
383                                 }
384
385                                 if (strcasecmp(cl_user, WC->last_chat_user)) {
386                                         wprintf("<B>");
387         
388                                         if (!strcasecmp(cl_user, ChrPtr(WC->wc_fullname))) {
389                                                 wprintf("<FONT COLOR=&quot;#FF0000&quot;>");
390                                         }
391                                         else {
392                                                 wprintf("<FONT COLOR=&quot;#0000FF&quot;>");
393                                         }
394                                         jsescputs(cl_user);
395         
396                                         wprintf("</FONT>: </B>");
397                                 }
398                                 else {
399                                         wprintf("&nbsp;&nbsp;&nbsp;");
400                                 }
401                                 jsescputs(cl_text);
402                                 if (!strcasecmp(cl_user, ":")) {
403                                         wprintf("</I>");
404                                 }
405
406                                 wprintf("</TD></TR></TABLE>");
407                                 wprintf("'); \n");
408
409                                 strcpy(WC->last_chat_user, cl_user);
410                         }
411                 }
412
413                 wprintf("parent.chat_transcript.scrollTo(999999,999999);\">\n");
414         }
415
416         free(output_data);
417
418         wprintf("</BODY></HTML>\n");
419         wDumpContent(0);
420 }
421
422
423 /**
424  * \brief sending side of the chat window
425  */
426 void chat_send(void) {
427         int i;
428         char send_this[SIZ];
429         char buf[SIZ];
430
431         output_headers(0, 0, 0, 0, 0, 0);
432         hprintf("Content-type: text/html; charset=utf-8\r\n");
433         wprintf("<HTML>"
434                 "<BODY onLoad=\"document.chatsendform.send_this.focus();\" >"
435         );
436
437         if (havebstr("send_this")) {
438                 strcpy(send_this, bstr("send_this"));
439         }
440         else {
441                 strcpy(send_this, "");
442         }
443
444         if (havebstr("help_button")) {
445                 strcpy(send_this, "/help");
446         }
447
448         if (havebstr("list_button")) {
449                 strcpy(send_this, "/who");
450         }
451
452         if (havebstr("exit_button")) {
453                 strcpy(send_this, "/quit");
454         }
455
456         if (setup_chat_socket() != 0) {
457                 wprintf(_("An error occurred while setting up the chat socket."));
458                 wprintf("</BODY></HTML>\n");
459                 wDumpContent(0);
460                 return;
461         }
462
463         /** Temporarily swap the serv and chat sockets during chat talk */
464         i = WC->serv_sock;
465         WC->serv_sock = WC->chat_sock;
466         WC->chat_sock = i;
467
468         while (!IsEmptyStr(send_this)) {
469                 if (strlen(send_this) < 67) {
470                         serv_puts(send_this);
471                         strcpy(send_this, "");
472                 }
473                 else {
474                         for (i=55; i<67; ++i) {
475                                 if (send_this[i] == ' ') break;
476                         }
477                         strncpy(buf, send_this, i);
478                         buf[i] = 0;
479                         strcpy(send_this, &send_this[i]);
480                         serv_puts(buf);
481                 }
482         }
483
484         /** Unswap the sockets. */
485         i = WC->serv_sock;
486         WC->serv_sock = WC->chat_sock;
487         WC->chat_sock = i;
488
489         wprintf("<FORM METHOD=\"POST\" action=\"chat_send\" NAME=\"chatsendform\">\n");
490         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
491         wprintf("<INPUT TYPE=\"text\" SIZE=\"80\" MAXLENGTH=\"%d\" "
492                 "NAME=\"send_this\">\n", SIZ-10);
493         wprintf("<br />");
494         wprintf("<INPUT TYPE=\"submit\" NAME=\"send_button\" VALUE=\"%s\">\n", _("Send"));
495         wprintf("<INPUT TYPE=\"submit\" NAME=\"help_button\" VALUE=\"%s\">\n", _("Help"));
496         wprintf("<INPUT TYPE=\"submit\" NAME=\"list_button\" VALUE=\"%s\">\n", _("List users"));
497         wprintf("<INPUT TYPE=\"submit\" NAME=\"exit_button\" VALUE=\"%s\">\n", _("Exit"));
498         wprintf("</FORM>\n");
499
500         wprintf("</BODY></HTML>\n");
501         wDumpContent(0);
502 }
503
504 void 
505 InitModule_PAGING
506 (void)
507 {
508         WebcitAddUrlHandler(HKEY("display_page"), display_page, 0);
509         WebcitAddUrlHandler(HKEY("page_user"), page_user, 0);
510         WebcitAddUrlHandler(HKEY("chat"), do_chat, 0);
511         WebcitAddUrlHandler(HKEY("chat_recv"), chat_recv, 0);
512         WebcitAddUrlHandler(HKEY("chat_send"), chat_send, 0);
513 }
514
515 /*@}*/