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