d27cd782acdec0d8a00ae011fc3f28472449998c
[citadel.git] / webcit / webcit.c
1 /*
2  * $Id$
3  *
4  * This is the main transaction loop of the web service.  It maintains a
5  * persistent session to the Citadel server, handling HTTP WebCit requests as
6  * they arrive and presenting a user interface.
7  */
8 #define SHOW_ME_VAPPEND_PRINTF
9 #include <stdio.h>
10 #include <stdarg.h>
11 #include "webcit.h"
12 #include "groupdav.h"
13 #include "webserver.h"
14
15
16 /*
17  * String to unset the cookie.
18  * Any date "in the past" will work, so I chose my birthday, right down to
19  * the exact minute.  :)
20  */
21 static char *unset = "; expires=28-May-1971 18:10:00 GMT";
22 StrBuf *csslocal = NULL;
23 HashList *HandlerHash = NULL;
24
25
26 void DeleteWebcitHandler(void *vHandler)
27 {
28         WebcitHandler *Handler = (WebcitHandler*) vHandler;
29         FreeStrBuf(&Handler->Name);
30         FreeStrBuf(&Handler->DisplayName);
31         free (Handler);
32 }
33
34 void WebcitAddUrlHandler(const char * UrlString, long UrlSLen, 
35                          const char *DisplayName, long dslen,
36                          WebcitHandlerFunc F, 
37                          long Flags)
38 {
39         WebcitHandler *NewHandler;      
40         NewHandler = (WebcitHandler*) malloc(sizeof(WebcitHandler));
41         NewHandler->F = F;
42         NewHandler->Flags = Flags;
43         NewHandler->Name = NewStrBufPlain(UrlString, UrlSLen);
44         StrBufShrinkToFit(NewHandler->Name, 1);
45         NewHandler->DisplayName = NewStrBufPlain(DisplayName, dslen);
46         StrBufShrinkToFit(NewHandler->DisplayName, 1);
47         Put(HandlerHash, UrlString, UrlSLen, NewHandler, DeleteWebcitHandler);
48 }
49
50
51 /*
52  * web-printing funcion. uses our vsnprintf wrapper
53  */
54 void wc_printf(const char *format,...)
55 {
56         wcsession *WCC = WC;
57         va_list arg_ptr;
58
59         if (WCC->WBuf == NULL)
60                 WCC->WBuf = NewStrBuf();
61
62         va_start(arg_ptr, format);
63         StrBufVAppendPrintf(WCC->WBuf, format, arg_ptr);
64         va_end(arg_ptr);
65 }
66
67 /*
68  * http-header-printing funcion. uses our vsnprintf wrapper
69  */
70 void hprintf(const char *format,...)
71 {
72         wcsession *WCC = WC;
73         va_list arg_ptr;
74
75         va_start(arg_ptr, format);
76         StrBufVAppendPrintf(WCC->HBuf, format, arg_ptr);
77         va_end(arg_ptr);
78 }
79
80
81
82 /*
83  * wrap up an HTTP session, closes tags, etc.
84  *
85  * print_standard_html_footer should be set to:
86  * 0            - to transmit only,
87  * nonzero      - to append the closing tags
88  */
89 void wDumpContent(int print_standard_html_footer)
90 {
91         if (print_standard_html_footer) {
92                 wc_printf("</div> <!-- end of 'content' div -->\n");
93                 do_template("trailing", NULL);
94         }
95
96         /* If we've been saving it all up for one big output burst,
97          * go ahead and do that now.
98          */
99         end_burst();
100 }
101
102
103  
104
105 /*
106  * Output HTTP headers and leading HTML for a page
107  */
108 void output_headers(    int do_httpheaders,     /* 1 = output HTTP headers                          */
109                         int do_htmlhead,        /* 1 = output HTML <head> section and <body> opener */
110
111                         int do_room_banner,     /* 0=no, 1=yes,                                     
112                                                  * 2 = I'm going to embed my own, so don't open the 
113                                                  *     <div id="content"> either.                   
114                                                  */
115
116                         int unset_cookies,      /* 1 = session is terminating, so unset the cookies */
117                         int suppress_check,     /* 1 = suppress check for instant messages          */
118                         int cache               /* 1 = allow browser to cache this page             */
119 ) {
120         wcsession *WCC = WC;
121         char cookie[1024];
122         char httpnow[128];
123
124         hprintf("HTTP/1.1 200 OK\n");
125         http_datestring(httpnow, sizeof httpnow, time(NULL));
126
127         if (do_httpheaders) {
128                 if (WCC->serv_info != NULL)
129                         hprintf("Content-type: text/html; charset=utf-8\r\n"
130                                 "Server: %s / %s\n"
131                                 "Connection: close\r\n",
132                                 PACKAGE_STRING, 
133                                 ChrPtr(WCC->serv_info->serv_software));
134                 else
135                         hprintf("Content-type: text/html; charset=utf-8\r\n"
136                                 "Server: %s / [n/a]\n"
137                                 "Connection: close\r\n",
138                                 PACKAGE_STRING);
139         }
140
141         if (cache > 0) {
142                 char httpTomorow[128];
143
144                 http_datestring(httpTomorow, sizeof httpTomorow, 
145                                 time(NULL) + 60 * 60 * 24 * 2);
146
147                 hprintf("Pragma: public\r\n"
148                         "Cache-Control: max-age=3600, must-revalidate\r\n"
149                         "Last-modified: %s\r\n"
150                         "Expires: %s\r\n",
151                         httpnow,
152                         httpTomorow
153                 );
154         }
155         else {
156                 hprintf("Pragma: no-cache\r\n"
157                         "Cache-Control: no-store\r\n"
158                         "Expires: -1\r\n"
159                 );
160         }
161
162         if (cache < 2) {
163
164                 stuff_to_cookie(cookie, 1024, 
165                                 WCC->wc_session,
166                                 WCC->wc_username,
167                                 WCC->wc_password,
168                                 WCC->wc_roomname,
169                                 get_selected_language()
170                         );
171                 
172                 if (unset_cookies) {
173                         hprintf("Set-cookie: webcit=%s; path=/\r\n", unset);
174                 } else {
175                         if (server_cookie != NULL) {
176                                 hprintf("Set-cookie: webcit=%s; path=/ %s\r\n", 
177                                         cookie, server_cookie);
178                         }
179                         else {
180                                 hprintf("Set-cookie: webcit=%s; path=/\r\n", cookie);
181                         }
182                 }
183         }
184
185         if (do_htmlhead) {
186                 begin_burst();
187                 do_template("head", NULL);
188
189                 /* check for ImportantMessages (these display in a div overlaying the main screen) */
190                 if (!IsEmptyStr(WCC->ImportantMessage)) {
191                         wc_printf("<div id=\"important_message\">\n"
192                                 "<span class=\"imsg\">");
193                         StrEscAppend(WCC->WBuf, NULL, WCC->ImportantMessage, 0, 0);
194                         wc_printf("</span><br />\n"
195                                 "</div>\n"
196                         );
197                         StrBufAppendBufPlain(WCC->trailing_javascript,
198                                              HKEY("setTimeout('hide_imsg_popup()', 5000);       \n"), 
199                                              0
200                         );
201                         WCC->ImportantMessage[0] = 0;
202                 }
203                 else if (StrLength(WCC->ImportantMsg) > 0) {
204                         wc_printf("<div id=\"important_message\">\n"
205                                 "<span class=\"imsg\">");
206                         StrEscAppend(WCC->WBuf, WCC->ImportantMsg, NULL, 0, 0);
207                         wc_printf("</span><br />\n"
208                                 "</div>\n"
209                         );
210                         StrBufAppendBufPlain(WCC->trailing_javascript,
211                                              HKEY("setTimeout('hide_imsg_popup()', 5000);       \n"),
212                                              0
213                         );
214                         FlushStrBuf(WCC->ImportantMsg);
215                 }
216                 if ( (WCC->logged_in) && (!unset_cookies) ) {
217                         DoTemplate(HKEY("paging"), NULL, &NoCtx);
218                 }
219
220                 if (do_room_banner == 1) {
221                         wc_printf("<div id=\"banner\">\n");
222                         embed_room_banner(NULL, navbar_default);
223                         wc_printf("</div>\n");
224                 }
225         }
226
227         if (do_room_banner == 1) {
228                 wc_printf("<div id=\"content\">\n");
229         }
230 }
231
232 void output_custom_content_header(const char *ctype) {
233   hprintf("HTTP/1.1 200 OK\r\n");
234   hprintf("Content-type: %s; charset=utf-8\r\n",ctype);
235   hprintf("Server: %s / %s\r\n", PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software));
236   hprintf("Connection: close\r\n");
237 }
238
239
240 /*
241  * Generic function to do an HTTP redirect.  Easy and fun.
242  */
243 void http_redirect(const char *whichpage) {
244         hprintf("HTTP/1.1 302 Moved Temporarily\n");
245         hprintf("Location: %s\r\n", whichpage);
246         hprintf("URI: %s\r\n", whichpage);
247         hprintf("Content-type: text/html; charset=utf-8\r\n");
248         begin_burst();
249         wc_printf("<html><body>");
250         wc_printf("Go <a href=\"%s\">here</A>.", whichpage);
251         wc_printf("</body></html>\n");
252         end_burst();
253 }
254
255
256
257 /*
258  * Output a piece of content to the web browser using conformant HTTP and MIME semantics
259  */
260 void http_transmit_thing(const char *content_type,
261                          int is_static) {
262
263 #ifndef TECH_PREVIEW
264         lprintf(9, "http_transmit_thing(%s)%s\n",
265                 content_type,
266                 ((is_static > 0) ? " (static)" : "")
267         );
268 #endif
269         output_headers(0, 0, 0, 0, 0, is_static);
270
271         hprintf("Content-type: %s\r\n"
272                 "Server: %s\r\n"
273                 "Connection: close\r\n",
274                 content_type,
275                 PACKAGE_STRING);
276
277         end_burst();
278 }
279
280
281 /*
282  * Convenience functions to display a page containing only a string
283  *
284  * titlebarcolor        color of the titlebar of the frame
285  * titlebarmsg          text to display in the title bar
286  * messagetext          body of the box
287  */
288 void convenience_page(const char *titlebarcolor, const char *titlebarmsg, const char *messagetext)
289 {
290         hprintf("HTTP/1.1 200 OK\n");
291         output_headers(1, 1, 2, 0, 0, 0);
292         wc_printf("<div id=\"banner\">\n");
293         wc_printf("<table width=100%% border=0 bgcolor=\"#%s\"><tr><td>", titlebarcolor);
294         wc_printf("<span class=\"titlebar\">%s</span>\n", titlebarmsg);
295         wc_printf("</td></tr></table>\n");
296         wc_printf("</div>\n<div id=\"content\">\n");
297         escputs(messagetext);
298
299         wc_printf("<hr />\n");
300         wDumpContent(1);
301 }
302
303
304 /*
305  * Display a blank page.
306  */
307 void blank_page(void) {
308         output_headers(1, 1, 0, 0, 0, 0);
309         wDumpContent(2);
310 }
311
312
313 /*
314  * A template has been requested
315  */
316 void url_do_template(void) {
317         const StrBuf *MimeType;
318         const StrBuf *Tmpl = sbstr("template");
319         begin_burst();
320         MimeType = DoTemplate(SKEY(Tmpl), NULL, &NoCtx);
321         http_transmit_thing(ChrPtr(MimeType), 0);
322 }
323
324
325
326 /*
327  * convenience function to indicate success
328  */
329 void display_success(char *successmessage)
330 {
331         convenience_page("007700", "OK", successmessage);
332 }
333
334
335 /*
336  * Authorization required page 
337  * This is probably temporary and should be revisited 
338  */
339 void authorization_required(void)
340 {
341         wcsession *WCC = WC;
342         const char *message = "";
343
344         hprintf("HTTP/1.1 401 Authorization Required\r\n");
345         hprintf(
346                 "Server: %s / %s\r\n"
347                 "Connection: close\r\n",
348                 PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software)
349         );
350         hprintf("WWW-Authenticate: Basic realm=\"%s\"\r\n", ChrPtr(WC->serv_info->serv_humannode));
351         hprintf("Content-Type: text/html\r\n");
352         begin_burst();
353         wc_printf("<h1>");
354         wc_printf(_("Authorization Required"));
355         wc_printf("</h1>\r\n");
356         
357
358         if (WCC->ImportantMsg != NULL)
359                 message = ChrPtr(WCC->ImportantMsg);
360         else if (WCC->ImportantMessage != NULL)
361                 message = WCC->ImportantMessage;
362
363         wc_printf(_("The resource you requested requires a valid username and password. "
364                 "You could not be logged in: %s\n"), message);
365         wDumpContent(0);
366         end_webcit_session();
367 }
368
369 /*
370  * Convenience functions to wrap around asynchronous ajax responses
371  */
372 void begin_ajax_response(void) {
373         wcsession *WCC = WC;
374
375         FlushStrBuf(WCC->HBuf);
376         output_headers(0, 0, 0, 0, 0, 0);
377
378         hprintf("Content-type: text/html; charset=UTF-8\r\n"
379                 "Server: %s\r\n"
380                 "Connection: close\r\n"
381                 ,
382                 PACKAGE_STRING);
383         begin_burst();
384 }
385
386 /*
387  * print ajax response footer 
388  */
389 void end_ajax_response(void) {
390         wDumpContent(0);
391 }
392
393
394
395 /*
396  * Wraps a Citadel server command in an AJAX transaction.
397  */
398 void ajax_servcmd(void)
399 {
400         wcsession *WCC = WC;
401         int Done = 0;
402         StrBuf *Buf;
403         char *junk;
404         size_t len;
405
406         begin_ajax_response();
407         Buf = NewStrBuf();
408         serv_puts(bstr("g_cmd"));
409         StrBuf_ServGetln(Buf);
410         StrBufAppendBuf(WCC->WBuf, Buf, 0);
411         StrBufAppendBufPlain(WCC->WBuf, HKEY("\n"), 0);
412         
413         switch (GetServerStatus(Buf, NULL)) {
414         case 8:
415                 serv_puts("\n\n000");
416                 if ( (StrLength(Buf)==3) && 
417                      !strcmp(ChrPtr(Buf), "000")) {
418                         StrBufAppendBufPlain(WCC->WBuf, HKEY("\000"), 0);
419                         break;
420                 }
421         case 1:
422                 while (!Done) {
423                         StrBuf_ServGetln(Buf);
424                         if ( (StrLength(Buf)==3) && 
425                              !strcmp(ChrPtr(Buf), "000")) {
426                                 Done = 1;
427                         }
428                         StrBufAppendBuf(WCC->WBuf, Buf, 0);
429                         StrBufAppendBufPlain(WCC->WBuf, HKEY("\n"), 0);
430                 }
431                 break;
432         case 4:
433                 text_to_server(bstr("g_input"));
434                 serv_puts("000");
435                 break;
436         case 6:
437                 len = atol(&ChrPtr(Buf)[4]);
438                 StrBuf_ServGetBLOBBuffered(Buf, len);
439                 break;
440         case 7:
441                 len = atol(&ChrPtr(Buf)[4]);
442                 junk = malloc(len);
443                 memset(junk, 0, len);
444                 serv_write(junk, len);
445                 free(junk);
446         }
447         
448         end_ajax_response();
449         
450         /*
451          * This is kind of an ugly hack, but this is the only place it can go.
452          * If the command was GEXP, then the instant messenger window must be
453          * running, so reset the "last_pager_check" watchdog timer so
454          * that page_popup() doesn't try to open it a second time.
455          */
456         if (!strncasecmp(bstr("g_cmd"), "GEXP", 4)) {
457                 WCC->last_pager_check = time(NULL);
458         }
459         FreeStrBuf(&Buf);
460 }
461
462
463 /*
464  * Helper function for the asynchronous check to see if we need
465  * to open the instant messenger window.
466  */
467 void seconds_since_last_gexp(void)
468 {
469         char buf[256];
470
471         if ( (time(NULL) - WC->last_pager_check) < 30) {
472                 wc_printf("NO\n");
473         }
474         else {
475                 memset(buf, 0, 5);
476                 serv_puts("NOOP");
477                 serv_getln(buf, sizeof buf);
478                 if (buf[3] == '*') {
479                         wc_printf("YES");
480                 }
481                 else {
482                         wc_printf("NO");
483                 }
484         }
485 }
486
487
488
489 void ReadPostData(void)
490 {
491         int body_start = 0;
492         wcsession *WCC = WC;
493         StrBuf *content = NULL;
494         
495         content = NewStrBufPlain(NULL, WCC->Hdr->HR.ContentLength + 256);
496
497         StrBufPrintf(content, 
498                      "Content-type: %s\n"
499                      "Content-length: %ld\n\n",
500                      ChrPtr(WCC->Hdr->HR.ContentType), 
501                              WCC->Hdr->HR.ContentLength);
502 /*
503   hprintf("Content-type: %s\n"
504   "Content-length: %d\n\n",
505   ContentType, ContentLength);
506 */
507         body_start = StrLength(content);
508
509         /** Read the entire input data at once. */
510         client_read_to(WCC->Hdr, content, 
511                        WCC->Hdr->HR.ContentLength,
512                        SLEEPING);
513         
514         if (!strncasecmp(ChrPtr(WCC->Hdr->HR.ContentType), "application/x-www-form-urlencoded", 33)) {
515                 StrBufCutLeft(content, body_start);
516                 ParseURLParams(content);
517         } else if (!strncasecmp(ChrPtr(WCC->Hdr->HR.ContentType), "multipart", 9)) {
518                 char *Buf;
519                 char *BufEnd;
520                 long len;
521
522                 len = StrLength(content);
523                 Buf = SmashStrBuf(&content);
524                 BufEnd = Buf + len;
525                 mime_parser(Buf, BufEnd, *upload_handler, NULL, NULL, NULL, 0);
526                 free(Buf);
527         } else if (WCC->Hdr->HR.ContentLength > 0) {
528                 WCC->upload = content;
529                 WCC->upload_length = StrLength(WCC->upload);
530                 content = NULL;
531         }
532         FreeStrBuf(&content);
533 }
534
535
536 void ParseREST_URL(void)
537 {
538         StrBuf *Buf;
539         StrBuf *pFloor = NULL;
540         wcsession *WCC = WC;
541         long i = 0;
542         const char *pCh = NULL;
543         HashList *Floors;
544         void *vFloor;
545
546         WCC->Directory = NewHash(1, Flathash);
547
548         Buf = NewStrBuf();
549         while (StrBufExtract_NextToken(WCC->Hdr->HR.ReqLine, 
550                                        Buf, &pCh,  '/') >= 0)
551         {
552                 Put(WCC->Directory, IKEY(i), Buf, HFreeStrBuf);
553                 if (i==0)
554                         pFloor = Buf;
555                 i++;
556                 Buf = NewStrBuf();
557         }
558         if (i == 0)
559                 FreeStrBuf(&Buf);
560         else if (pFloor != NULL)
561         {
562                 Floors = GetFloorListHash(NULL, NULL);
563                 
564                 if (Floors != NULL)
565                 {
566                         if (GetHash(Floors, SKEY(pFloor), &vFloor))
567                                 WCC->CurrentFloor = (floor*) vFloor;
568                 }
569         }
570 }
571
572
573
574
575 /*
576  * Entry point for WebCit transaction
577  */
578 void session_loop(void)
579 {
580         int Flags = 0;
581         int xhttp;
582         StrBuf *Buf;
583         
584         /*
585          * We stuff these with the values coming from the client cookies,
586          * so we can use them to reconnect a timed out session if we have to.
587          */
588         wcsession *WCC;
589
590         
591         Buf = NewStrBuf();
592
593         WCC= WC;
594
595         WCC->upload_length = 0;
596         WCC->upload = NULL;
597         WCC->is_mobile = 0;
598         WCC->trailing_javascript = NewStrBuf();
599         WCC->Hdr->nWildfireHeaders = 0;
600         if (WCC->Hdr->HR.Handler != NULL)
601                 Flags = WCC->Hdr->HR.Handler->Flags; /* so we can temporarily add our own... */
602
603         if (WCC->Hdr->HR.ContentLength > 0) {
604                 ReadPostData();
605         }
606
607         /* If there are variables in the URL, we must grab them now */
608         if (WCC->Hdr->PlainArgs != NULL)
609                 ParseURLParams(WCC->Hdr->PlainArgs);
610
611         /* If the client sent a nonce that is incorrect, kill the request. */
612         if (havebstr("nonce")) {
613                 lprintf(9, "Comparing supplied nonce %s to session nonce %ld\n", 
614                         bstr("nonce"), WCC->nonce);
615                 if (ibstr("nonce") != WCC->nonce) {
616                         lprintf(9, "Ignoring request with mismatched nonce.\n");
617                         hprintf("HTTP/1.1 404 Security check failed\r\n");
618                         hprintf("Content-Type: text/plain\r\n");
619                         begin_burst();
620                         wc_printf("Security check failed.\r\n");
621                         end_burst();
622                         goto SKIP_ALL_THIS_CRAP;
623                 }
624         }
625
626         /*
627          * If we're not connected to a Citadel server, try to hook up the
628          * connection now.
629          */
630         if (!WCC->connected) {
631                 if (GetConnected ())
632                         goto SKIP_ALL_THIS_CRAP;
633         }
634
635
636         /*
637          * If we're not logged in, but we have authentication data (either from
638          * a cookie or from http-auth), try logging in to Citadel using that.
639          */
640         if ((!WCC->logged_in)
641             && (StrLength(WCC->Hdr->c_username) > 0)
642             && (StrLength(WCC->Hdr->c_password) > 0))
643         {
644                 FlushStrBuf(Buf);
645                 serv_printf("USER %s", ChrPtr(WCC->Hdr->c_username));
646                 StrBuf_ServGetln(Buf);
647                 if (GetServerStatus(Buf, NULL) == 3) {
648                         serv_printf("PASS %s", ChrPtr(WCC->Hdr->c_password));
649                         StrBuf_ServGetln(Buf);
650                         if (GetServerStatus(Buf, NULL) == 2) {
651                                 become_logged_in(WCC->Hdr->c_username,
652                                                  WCC->Hdr->c_password, Buf);
653                         } else {
654                                 /* Should only display when password is wrong */
655                                 WCC->ImportantMsg = NewStrBufPlain(ChrPtr(Buf) + 4, StrLength(Buf) - 4);
656                                 authorization_required();
657                                 FreeStrBuf(&Buf);
658                                 goto SKIP_ALL_THIS_CRAP;
659                         }
660                 }
661         }
662
663         xhttp = (WCC->Hdr->HR.eReqType != eGET) &&
664                 (WCC->Hdr->HR.eReqType != ePOST) &&
665                 (WCC->Hdr->HR.eReqType != eHEAD);
666
667         /*
668          * If a 'gotofirst' parameter has been specified, attempt to goto that room
669          * prior to doing anything else.
670          */
671         if (havebstr("gotofirst")) {
672                 int ret;
673                 ret = gotoroom(sbstr("gotofirst"));     /* do quietly to avoid session output! */
674                 if ((ret/100) != 2) {
675                         lprintf(1, "GOTOFIRST: Unable to change to [%s]; Reason: %d\n",
676                                 bstr("gotofirst"), ret);
677                 }
678         }
679
680         /*
681          * If we aren't in any room yet, but we have cookie data telling us where we're
682          * supposed to be, and 'gotofirst' was not specified, then go there.
683          */
684         else if ( (StrLength(WCC->wc_roomname) == 0) && ( (StrLength(WCC->Hdr->c_roomname) > 0) )) {
685                 int ret;
686
687                 lprintf(9, "We are in '%s' but cookie indicates '%s', going there...\n",
688                         ChrPtr(WCC->wc_roomname),
689                         ChrPtr(WCC->Hdr->c_roomname)
690                 );
691                 ret = gotoroom(WCC->Hdr->c_roomname);   /* do quietly to avoid session output! */
692                 if ((ret/100) != 2) {
693                         lprintf(1, "COOKIEGOTO: Unable to change to [%s]; Reason: %d\n",
694                                 ChrPtr(WCC->Hdr->c_roomname), ret);
695                 }
696         }
697
698         if (WCC->Hdr->HR.Handler != NULL) {
699                 if (!WCC->logged_in && ((WCC->Hdr->HR.Handler->Flags & ANONYMOUS) == 0)) {
700                         display_login(NULL);
701                 }
702                 else {
703 /*
704                         if ((WCC->Hdr->HR.Handler->Flags & PARSE_REST_URL) != 0)
705                                 ParseREST_URL();
706 */
707                         if ((WCC->Hdr->HR.Handler->Flags & AJAX) != 0)
708                                 begin_ajax_response();
709                         WCC->Hdr->HR.Handler->F();
710                         if ((WCC->Hdr->HR.Handler->Flags & AJAX) != 0)
711                                 end_ajax_response();
712                 }
713         }
714         /* When all else fais, display the main menu. */
715         else {
716                 /* 
717                  * ordinary browser users get a nice login screen, DAV etc. requsets
718                  * are given a 401 so they can handle it appropriate.
719                  */
720                 if (!WCC->logged_in)  {
721                         if (xhttp)
722                                 authorization_required();
723                         else 
724                                 display_login(NULL);
725                 }
726                 /*
727                  * Toplevel dav requests? or just a flat browser request? 
728                  */
729                 else {
730                         if (xhttp)
731                                 groupdav_main();
732                         else
733                                 display_main_menu();
734                 }
735         }
736
737 SKIP_ALL_THIS_CRAP:
738         FreeStrBuf(&Buf);
739         fflush(stdout);
740 }
741
742
743 /*
744  * Replacement for sleep() that uses select() in order to avoid SIGALRM
745  */
746 void sleeeeeeeeeep(int seconds)
747 {
748         struct timeval tv;
749
750         tv.tv_sec = seconds;
751         tv.tv_usec = 0;
752         select(0, NULL, NULL, NULL, &tv);
753 }
754
755
756 int ConditionalImportantMesage(StrBuf *Target, WCTemplputParams *TP)
757 {
758         wcsession *WCC = WC;
759         if (WCC != NULL)
760                 return ((!IsEmptyStr(WCC->ImportantMessage)) || 
761                         (StrLength(WCC->ImportantMsg) > 0));
762         else
763                 return 0;
764 }
765
766 void tmplput_importantmessage(StrBuf *Target, WCTemplputParams *TP)
767 {
768         wcsession *WCC = WC;
769         
770         if (WCC != NULL) {
771                 if (!IsEmptyStr(WCC->ImportantMessage)) {
772                         StrEscAppend(Target, NULL, WCC->ImportantMessage, 0, 0);
773                         WCC->ImportantMessage[0] = '\0';
774                 }
775                 else if (StrLength(WCC->ImportantMsg) > 0) {
776                         StrEscAppend(Target, WCC->ImportantMsg, NULL, 0, 0);
777                         FlushStrBuf(WCC->ImportantMsg);
778                 }
779         }
780 }
781
782 void tmplput_trailing_javascript(StrBuf *Target, WCTemplputParams *TP)
783 {
784         wcsession *WCC = WC;
785
786         if (WCC != NULL)
787                 StrBufAppendTemplate(Target, TP, WCC->trailing_javascript, 0);
788 }
789
790 void tmplput_csslocal(StrBuf *Target, WCTemplputParams *TP)
791 {
792         StrBufAppendBuf(Target, 
793                         csslocal, 0);
794 }
795
796 extern char static_local_dir[PATH_MAX];
797
798
799 void 
800 InitModule_WEBCIT
801 (void)
802 {
803         char dir[SIZ];
804         WebcitAddUrlHandler(HKEY("blank"), "", 0, blank_page, ANONYMOUS|COOKIEUNNEEDED|ISSTATIC);
805         WebcitAddUrlHandler(HKEY("do_template"), "", 0, url_do_template, ANONYMOUS);
806         WebcitAddUrlHandler(HKEY("sslg"), "", 0, seconds_since_last_gexp, AJAX|LOGCHATTY);
807         WebcitAddUrlHandler(HKEY("ajax_servcmd"), "", 0, ajax_servcmd, 0);
808         WebcitAddUrlHandler(HKEY("webcit"), "", 0, blank_page, URLNAMESPACE);
809
810         WebcitAddUrlHandler(HKEY("401"), "", 0, authorization_required, ANONYMOUS|COOKIEUNNEEDED);
811         RegisterConditional(HKEY("COND:IMPMSG"), 0, ConditionalImportantMesage, CTX_NONE);
812         RegisterNamespace("CSSLOCAL", 0, 0, tmplput_csslocal, NULL, CTX_NONE);
813         RegisterNamespace("IMPORTANTMESSAGE", 0, 0, tmplput_importantmessage, NULL, CTX_NONE);
814         RegisterNamespace("TRAILING_JAVASCRIPT", 0, 0, tmplput_trailing_javascript, NULL, CTX_NONE);
815
816         snprintf(dir, SIZ, "%s/webcit.css", static_local_dir);
817         if (!access(dir, R_OK)) {
818                 lprintf(9, "Using local Stylesheet [%s]\n", dir);
819                 csslocal = NewStrBufPlain(HKEY("<link href=\"static.local/webcit.css\" rel=\"stylesheet\" type=\"text/css\" />"));
820         }
821         else
822                 lprintf(9, "No Site-local Stylesheet [%s] installed. \n", dir);
823
824 }
825
826 void
827 ServerStartModule_WEBCIT
828 (void)
829 {
830         HandlerHash = NewHash(1, NULL);
831 }
832
833
834 void 
835 ServerShutdownModule_WEBCIT
836 (void)
837 {
838         FreeStrBuf(&csslocal);
839         DeleteHash(&HandlerHash);
840 }
841
842
843
844 void
845 SessionNewModule_WEBCIT
846 (wcsession *sess)
847 {
848         sess->ImportantMsg = NewStrBuf();
849         sess->WBuf = NewStrBufPlain(NULL, SIZ * 4);
850         sess->HBuf = NewStrBufPlain(NULL, SIZ / 4);
851 }
852
853 void
854 SessionDetachModule_WEBCIT
855 (wcsession *sess)
856 {
857         DeleteHash(&sess->Directory);
858
859         FreeStrBuf(&sess->upload);
860         sess->upload_length = 0;
861         
862         FreeStrBuf(&sess->trailing_javascript);
863
864         if (StrLength(sess->WBuf) > SIZ * 30) /* Bigger than 120K? release. */
865         {
866                 FreeStrBuf(&sess->WBuf);
867                 sess->WBuf = NewStrBuf();
868         }
869         else
870                 FlushStrBuf(sess->WBuf);
871         FlushStrBuf(sess->HBuf);
872 }
873
874 void 
875 SessionDestroyModule_WEBCIT
876 (wcsession *sess)
877 {
878         FreeStrBuf(&sess->WBuf);
879         FreeStrBuf(&sess->HBuf);
880         FreeStrBuf(&sess->ImportantMsg);
881 }
882