* add more module handlers:
[citadel.git] / webcit / webcit.c
index 6715fe4836a5d0537be72426a8b8b5ca631c663b..e1e9c5ded3184482631f9182f979f41e40acd405 100644 (file)
@@ -19,7 +19,7 @@
  * the exact minute.  :)
  */
 static char *unset = "; expires=28-May-1971 18:10:00 GMT";
-
+StrBuf *csslocal = NULL;
 HashList *HandlerHash = NULL;
 
 void WebcitAddUrlHandler(const char * UrlString, 
@@ -168,21 +168,34 @@ void output_headers(      int do_httpheaders,     /* 1 = output HTTP headers
                do_template("head", NULL);
 
                /* check for ImportantMessages (these display in a div overlaying the main screen) */
-               if (!IsEmptyStr(WC->ImportantMessage)) {
+               if (!IsEmptyStr(WCC->ImportantMessage)) {
                        wprintf("<div id=\"important_message\">\n"
                                "<span class=\"imsg\">");
-                       escputs(WC->ImportantMessage);
+                       StrEscAppend(WCC->WBuf, NULL, WCC->ImportantMessage, 0, 0);
                        wprintf("</span><br />\n"
                                "</div>\n"
                        );
-                       StrBufAppendPrintf(WCC->trailing_javascript,
-                               "setTimeout('hide_imsg_popup()', 5000); \n"
+                       StrBufAppendBufPlain(WCC->trailing_javascript,
+                                            HKEY("setTimeout('hide_imsg_popup()', 5000);       \n"), 
+                                            0
                        );
                        WCC->ImportantMessage[0] = 0;
                }
-
+               else if (StrLength(WCC->ImportantMsg) > 0) {
+                       wprintf("<div id=\"important_message\">\n"
+                               "<span class=\"imsg\">");
+                       StrEscAppend(WCC->WBuf, WCC->ImportantMsg, NULL, 0, 0);
+                       wprintf("</span><br />\n"
+                               "</div>\n"
+                       );
+                       StrBufAppendBufPlain(WCC->trailing_javascript,
+                                            HKEY("setTimeout('hide_imsg_popup()', 5000);       \n"),
+                                            0
+                       );
+                       FlushStrBuf(WCC->ImportantMsg);
+               }
                if ( (WCC->logged_in) && (!unset_cookies) ) {
-                 //DoTemplate(HKEY("iconbar"), NULL, &NoCtx);
+                       /*DoTemplate(HKEY("iconbar"), NULL, &NoCtx);*/
                        page_popup();
                }
 
@@ -283,7 +296,7 @@ void print_menu_box(char* Title, char *Class, int nLines, ...)
 /*
  * dump out static pages from disk
  */
-void output_static(char *what)
+void output_static(const char *what)
 {
        int fd;
        struct stat statbuf;
@@ -346,7 +359,7 @@ void output_static(char *what)
  * titlebarmsg         text to display in the title bar
  * messagetext         body of the box
  */
-void convenience_page(char *titlebarcolor, char *titlebarmsg, char *messagetext)
+void convenience_page(const char *titlebarcolor, const char *titlebarmsg, const char *messagetext)
 {
        hprintf("HTTP/1.1 200 OK\n");
        output_headers(1, 1, 2, 0, 0, 0);
@@ -378,7 +391,6 @@ void url_do_template(void) {
        const StrBuf *MimeType;
        const StrBuf *Tmpl = sbstr("template");
        begin_burst();
-       output_headers(0, 0, 0, 0, 1, 0);
        MimeType = DoTemplate(SKEY(Tmpl), NULL, &NoCtx);
        http_transmit_thing(ChrPtr(MimeType), 0);
 }
@@ -441,44 +453,54 @@ void end_ajax_response(void) {
  */
 void ajax_servcmd(void)
 {
-       char buf[1024];
-       char gcontent[1024];
+       wcsession *WCC = WC;
+       int Done = 0;
+       StrBuf *Buf;
        char *junk;
        size_t len;
 
        begin_ajax_response();
-
-       serv_printf("%s", bstr("g_cmd"));
-       serv_getln(buf, sizeof buf);
-       wprintf("%s\n", buf);
-
-       if (buf[0] == '8') {
-               serv_printf("\n\n000");
-       }
-       if ((buf[0] == '1') || (buf[0] == '8')) {
-               while (serv_getln(gcontent, sizeof gcontent), strcmp(gcontent, "000")) {
-                       wprintf("%s\n", gcontent);
+       Buf = NewStrBuf();
+       serv_puts(bstr("g_cmd"));
+       StrBuf_ServGetln(Buf);
+       StrBufAppendBuf(WCC->WBuf, Buf, 0);
+       StrBufAppendBufPlain(WCC->WBuf, HKEY("\n"), 0);
+       
+       switch (GetServerStatus(Buf, NULL)) {
+       case 8:
+               serv_puts("\n\n000");
+               if ( (StrLength(Buf)==3) && 
+                    !strcmp(ChrPtr(Buf), "000")) {
+                       StrBufAppendBufPlain(WCC->WBuf, HKEY("\000"), 0);
+                       break;
                }
-               wprintf("000");
-       }
-       if (buf[0] == '4') {
+       case 1:
+               while (!Done) {
+                       StrBuf_ServGetln(Buf);
+                       if ( (StrLength(Buf)==3) && 
+                            !strcmp(ChrPtr(Buf), "000")) {
+                               Done = 1;
+                       }
+                       StrBufAppendBuf(WCC->WBuf, Buf, 0);
+                       StrBufAppendBufPlain(WCC->WBuf, HKEY("\n"), 0);
+               }
+               break;
+       case 4:
                text_to_server(bstr("g_input"));
                serv_puts("000");
-       }
-       if (buf[0] == '6') {
-               len = atol(&buf[4]);
-               junk = malloc(len);
-               serv_read(junk, len);
-               free(junk);
-       }
-       if (buf[0] == '7') {
-               len = atol(&buf[4]);
+               break;
+       case 6:
+               len = atol(&ChrPtr(Buf)[4]);
+               StrBuf_ServGetBLOBBuffered(Buf, len);
+               break;
+       case 7:
+               len = atol(&ChrPtr(Buf)[4]);
                junk = malloc(len);
                memset(junk, 0, len);
                serv_write(junk, len);
                free(junk);
        }
-
+       
        end_ajax_response();
        
        /*
@@ -488,8 +510,9 @@ void ajax_servcmd(void)
         * that page_popup() doesn't try to open it a second time.
         */
        if (!strncasecmp(bstr("g_cmd"), "GEXP", 4)) {
-               WC->last_pager_check = time(NULL);
+               WCC->last_pager_check = time(NULL);
        }
+       FreeStrBuf(&Buf);
 }
 
 
@@ -540,7 +563,10 @@ int is_mobile_ua(char *user_agent) {
 /*
  * Entry point for WebCit transaction
  */
-void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method, StrBuf *ReadBuf)
+void session_loop(StrBuf *ReqLine, 
+                 StrBuf *request_method, 
+                 StrBuf *ReadBuf,
+                 const char **Pos)
 {
        StrBuf *Buf;
        const char *pch, *pchs, *pche;
@@ -564,6 +590,8 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
        int len = 0;
        void *vHandler;
        WebcitHandler *Handler;
+       struct timeval tx_start;
+       struct timeval tx_finish;
 
        /*
         * We stuff these with the values coming from the client cookies,
@@ -576,6 +604,8 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
        StrBuf *c_httpauth_user;
        StrBuf *c_httpauth_pass;
        wcsession *WCC;
+
+       gettimeofday(&tx_start, NULL);          /* start a stopwatch for performance timing */
        
        Buf = NewStrBuf();
        c_username = NewStrBuf();
@@ -586,13 +616,6 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
        c_httpauth_pass = NewStrBufPlain(HKEY(DEFAULT_HTTPAUTH_PASS));
 
        WCC= WC;
-       if (WCC->WBuf == NULL)
-               WC->WBuf = NewStrBufPlain(NULL, 32768);
-       FlushStrBuf(WCC->WBuf);
-
-       if (WCC->HBuf == NULL)
-               WCC->HBuf = NewStrBuf();
-       FlushStrBuf(WCC->HBuf);
 
        WCC->upload_length = 0;
        WCC->upload = NULL;
@@ -608,7 +631,6 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
                index[a] = arg[a-1];
                sizes[a] = sizeof arg[a-1];
        }
-/*///  index[9] = &foo; todo */
        nBackDots = 0;
        nEmpty = 0;
        for ( a = 0; a < 9; ++a)
@@ -624,31 +646,28 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
        }
 
 
-       if (GetHash(HTTPHeaders, HKEY("COOKIE"), &vLine) && 
+       if (GetHash(WCC->headers, HKEY("COOKIE"), &vLine) && 
            (vLine != NULL)){
                cookie_to_stuff((StrBuf *)vLine, NULL,
                                c_username,
                                c_password,
                                c_roomname);
        }
-       if (GetHash(HTTPHeaders, HKEY("AUTHORIZATION"), &vLine) &&
+       if (GetHash(WCC->headers, HKEY("AUTHORIZATION"), &vLine) &&
            (vLine!=NULL)) {
-/* TODO: wrap base64 in strbuf */
-               CtdlDecodeBase64(c_httpauth_string, ChrPtr((StrBuf*)vLine), StrLength((StrBuf*)vLine));
-               FlushStrBuf(Buf);
-               StrBufAppendBufPlain(Buf, c_httpauth_string, -1, 0);
-               StrBufExtract_token(c_httpauth_user, Buf, 0, ':');
-               StrBufExtract_token(c_httpauth_pass, Buf, 1, ':');
+               StrBufDecodeBase64((StrBuf*)vLine);
+               StrBufExtract_token(c_httpauth_user, (StrBuf*)vLine, 0, ':');
+               StrBufExtract_token(c_httpauth_pass, (StrBuf*)vLine, 1, ':');
        }
-       if (GetHash(HTTPHeaders, HKEY("CONTENT-LENGTH"), &vLine) &&
+       if (GetHash(WCC->headers, HKEY("CONTENT-LENGTH"), &vLine) &&
            (vLine!=NULL)) {
                ContentLength = StrToi((StrBuf*)vLine);
        }
-       if (GetHash(HTTPHeaders, HKEY("CONTENT-TYPE"), &vLine) &&
+       if (GetHash(WCC->headers, HKEY("CONTENT-TYPE"), &vLine) &&
            (vLine!=NULL)) {
                ContentType = (StrBuf*)vLine;
        }
-       if (GetHash(HTTPHeaders, HKEY("USER-AGENT"), &vLine) &&
+       if (GetHash(WCC->headers, HKEY("USER-AGENT"), &vLine) &&
            (vLine!=NULL)) {
                safestrncpy(user_agent, ChrPtr((StrBuf*)vLine), sizeof user_agent);
 #ifdef TECH_PREVIEW
@@ -661,17 +680,17 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
 #endif
        }
        if ((follow_xff) &&
-           GetHash(HTTPHeaders, HKEY("X-FORWARDED-HOST"), &vLine) &&
+           GetHash(WCC->headers, HKEY("X-FORWARDED-HOST"), &vLine) &&
            (vLine != NULL)) {
                WCC->http_host = (StrBuf*)vLine;
        }
        if ((StrLength(WCC->http_host) == 0) && 
-           GetHash(HTTPHeaders, HKEY("HOST"), &vLine) &&
+           GetHash(WCC->headers, HKEY("HOST"), &vLine) &&
            (vLine!=NULL)) {
                WCC->http_host = (StrBuf*)vLine;
        }
 
-       if (GetHash(HTTPHeaders, HKEY("X-FORWARDED-FOR"), &vLine) &&
+       if (GetHash(WCC->headers, HKEY("X-FORWARDED-FOR"), &vLine) &&
            (vLine!=NULL)) {
                browser_host = (StrBuf*) vLine;
 
@@ -694,7 +713,11 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
                body_start = StrLength(content);
 
                /** Read the entire input data at once. */
-               client_read(&WCC->http_sock, content, ReadBuf, ContentLength + body_start);
+               client_read_to(&WCC->http_sock, 
+                              content, 
+                              ReadBuf, Pos,
+                              ContentLength,
+                              SLEEPING);
 
                if (!strncasecmp(ChrPtr(ContentType), "application/x-www-form-urlencoded", 33)) {
                        StrBufCutLeft(content, body_start);
@@ -774,7 +797,7 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
        }
 
        /* If the client sent a nonce that is incorrect, kill the request. */
-       if (strlen(bstr("nonce")) > 0) {
+       if (havebstr("nonce")) {
                lprintf(9, "Comparing supplied nonce %s to session nonce %ld\n", 
                        bstr("nonce"), WCC->nonce);
                if (ibstr("nonce") != WCC->nonce) {
@@ -792,6 +815,8 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
         * connection now.
         */
        if (!WCC->connected) {
+               if (WCC->ReadBuf == NULL)
+                       WCC->ReadBuf = NewStrBuf();
                if (!strcasecmp(ctdlhost, "uds")) {
                        /* unix domain socket */
                        snprintf(buf, SIZ, "%s/citadel.socket", ctdlport);
@@ -804,6 +829,7 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
 
                if (WCC->serv_sock < 0) {
                        do_logout();
+                       FreeStrBuf(&WCC->ReadBuf);
                        goto SKIP_ALL_THIS_CRAP;
                }
                else {
@@ -827,13 +853,23 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
                        if ( (!follow_xff) || (StrLength(browser_host) == 0) ) {
                                if (browser_host == NULL) {
                                        browser_host = NewStrBuf();
-                                       Put(HTTPHeaders, HKEY("FreeMeWithTheOtherHeaders"), 
+                                       Put(WCC->headers, HKEY("FreeMeWithTheOtherHeaders"), 
                                            browser_host, HFreeStrBuf);
                                }
                                locate_host(browser_host, WCC->http_sock);
                        }
-
-                       WCC->serv_info = get_serv_info(browser_host, user_agent);
+                       if (WCC->serv_info == NULL)
+                               WCC->serv_info = get_serv_info(browser_host, user_agent);
+                       if (WCC->serv_info == NULL){
+                               begin_burst();
+                               wprintf(_("Received unexpected answer from Citadel "
+                                         "server; bailing out."));
+                               hprintf("HTTP/1.1 200 OK\r\n");
+                               hprintf("Content-type: text/plain; charset=utf-8\r\n");
+                               end_burst();
+                               end_webcit_session();
+                               goto SKIP_ALL_THIS_CRAP;
+                       }
                        if (WCC->serv_info->serv_rev_level < MINIMUM_CIT_VERSION) {
                                begin_burst();
                                wprintf(_("You are connected to a Citadel "
@@ -846,13 +882,15 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
                                                MINIMUM_CIT_VERSION / 100,
                                                MINIMUM_CIT_VERSION % 100
                                        );
+                               hprintf("HTTP/1.1 200 OK\r\n");
+                               hprintf("Content-type: text/plain; charset=utf-8\r\n");
                                end_burst();
                                end_webcit_session();
                                goto SKIP_ALL_THIS_CRAP;
                        }
                }
        }
-/*///////todo: restore language in this case */
+
        /*
         * Functions which can be performed without logging in
         */
@@ -916,7 +954,7 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
         * our session's authentication.
         */
        if (!strncasecmp(action, "groupdav", 8)) {
-               groupdav_main(HTTPHeaders, 
+               groupdav_main(WCC->headers, 
                              ReqLine, request_method,
                              ContentType, /* do GroupDAV methods */
                              ContentLength, content, body_start);
@@ -932,7 +970,7 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
         * POST to the GroupDAV code as well.
         */
        if ((strcasecmp(ChrPtr(request_method), "GET")) && (strcasecmp(ChrPtr(request_method), "POST"))) {
-               groupdav_main(HTTPHeaders, ReqLine, 
+               groupdav_main(WCC->headers, ReqLine, 
                              request_method, ContentType, /** do GroupDAV methods */
                              ContentLength, content, body_start);
                if (!WCC->logged_in) {
@@ -954,12 +992,7 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
                        serv_printf("PASS %s", ChrPtr(c_password));
                        StrBuf_ServGetln(Buf);
                        if (GetServerStatus(Buf, NULL) == 2) {
-                               StrBuf *Lang;
                                become_logged_in(c_username, c_password, Buf);
-                               if (get_preference("language", &Lang)) {
-                                       set_selected_language(ChrPtr(Lang));
-                                       go_selected_language();         /* set locale */
-                               }
                                get_preference("default_header_charset", &WCC->DefaultCharset);
                        }
                }
@@ -1005,9 +1038,12 @@ void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method
                                        WCC->UrlFragment2 = NewStrBuf();
                                if (WCC->UrlFragment3 == NULL)
                                        WCC->UrlFragment3 = NewStrBuf();
-                               StrBufPrintf(WCC->UrlFragment1, "%s", index[0]);
-                               StrBufPrintf(WCC->UrlFragment2, "%s", index[1]);
-                               StrBufPrintf(WCC->UrlFragment3, "%s", index[2]);
+                               if (WCC->UrlFragment4 == NULL)
+                                       WCC->UrlFragment4 = NewStrBuf();
+                               StrBufPlain(WCC->UrlFragment1, index[0], -1);
+                               StrBufPlain(WCC->UrlFragment2, index[1], -1);
+                               StrBufPlain(WCC->UrlFragment3, index[2], -1);
+                               StrBufPlain(WCC->UrlFragment4, index[3], -1);
                        }
                        if ((Handler->Flags & AJAX) != 0)
                                begin_ajax_response();
@@ -1048,6 +1084,14 @@ SKIP_ALL_THIS_CRAP:
        }
        FreeStrBuf(&WCC->trailing_javascript);
        WCC->http_host = NULL;
+
+       /* How long did this transaction take? */
+       gettimeofday(&tx_finish, NULL);
+       
+       lprintf(9, "Transaction completed in %ld.%06ld seconds.\n",
+               ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) / 1000000,
+               ((tx_finish.tv_sec*1000000 + tx_finish.tv_usec) - (tx_start.tv_sec*1000000 + tx_start.tv_usec)) % 1000000
+       );
 }
 
 
@@ -1068,7 +1112,8 @@ int ConditionalImportantMesage(StrBuf *Target, WCTemplputParams *TP)
 {
        wcsession *WCC = WC;
        if (WCC != NULL)
-               return (!IsEmptyStr(WCC->ImportantMessage));
+               return ((!IsEmptyStr(WCC->ImportantMessage)) || 
+                       (StrLength(WCC->ImportantMsg) > 0));
        else
                return 0;
 }
@@ -1078,12 +1123,14 @@ void tmplput_importantmessage(StrBuf *Target, WCTemplputParams *TP)
        wcsession *WCC = WC;
        
        if (WCC != NULL) {
-/*
-               StrBufAppendTemplate(Target, nArgs, Tokens, Context, ContextType,
-                                    WCC->ImportantMessage, 0);
-*/
-               StrEscAppend(Target, NULL, WCC->ImportantMessage, 0, 0);
-               WCC->ImportantMessage[0] = '\0';
+               if (!IsEmptyStr(WCC->ImportantMessage)) {
+                       StrEscAppend(Target, NULL, WCC->ImportantMessage, 0, 0);
+                       WCC->ImportantMessage[0] = '\0';
+               }
+               else if (StrLength(WCC->ImportantMsg) > 0) {
+                       StrEscAppend(Target, WCC->ImportantMsg, NULL, 0, 0);
+                       FlushStrBuf(WCC->ImportantMsg);
+               }
        }
 }
 
@@ -1097,18 +1144,17 @@ void tmplput_trailing_javascript(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_csslocal(StrBuf *Target, WCTemplputParams *TP)
 {
-       extern StrBuf *csslocal;
        StrBufAppendBuf(Target, 
                        csslocal, 0);
 }
 
-
-
+extern char static_local_dir[PATH_MAX];
 
 void 
 InitModule_WEBCIT
 (void)
 {
+       char dir[SIZ];
        WebcitAddUrlHandler(HKEY("blank"), blank_page, ANONYMOUS);
        WebcitAddUrlHandler(HKEY("do_template"), url_do_template, ANONYMOUS);
        WebcitAddUrlHandler(HKEY("sslg"), seconds_since_last_gexp, AJAX);
@@ -1118,4 +1164,68 @@ InitModule_WEBCIT
        RegisterNamespace("CSSLOCAL", 0, 0, tmplput_csslocal, CTX_NONE);
        RegisterNamespace("IMPORTANTMESSAGE", 0, 0, tmplput_importantmessage, CTX_NONE);
        RegisterNamespace("TRAILING_JAVASCRIPT", 0, 0, tmplput_trailing_javascript, CTX_NONE);
+
+       snprintf(dir, SIZ, "%s/static.local/webcit.css", static_local_dir);
+       if (!access(dir, R_OK)) {
+               lprintf(9, "Using local Stylesheet [%s]\n", dir);
+               csslocal = NewStrBufPlain(HKEY("<link href=\"static.local/webcit.css\" rel=\"stylesheet\" type=\"text/css\">"));
+       }
+       else
+               lprintf(9, "Didn't find site local Stylesheet [%s]\n", dir);
+
+}
+
+void
+ServerStartModule_WEBCIT
+(void)
+{
+       HandlerHash = NewHash(1, NULL);
+}
+
+
+void 
+ServerShutdownModule_WEBCIT
+(void)
+{
+       FreeStrBuf(&csslocal);
+       DeleteHash(&HandlerHash);
+}
+
+
+
+void
+SessionNewModule_WEBCIT
+(wcsession *sess)
+{
+       sess->ImportantMsg = NewStrBuf();
+       sess->WBuf = NewStrBuf();
+       sess->HBuf = NewStrBuf();
+}
+
+void
+SessionDetachModule_WEBCIT
+(wcsession *sess)
+{
+       if (StrLength(sess->WBuf) > SIZ * 30) /* Bigger than 120K? release. */
+       {
+               FreeStrBuf(&sess->WBuf);
+               sess->WBuf = NewStrBuf();
+       }
+       else
+               FlushStrBuf(sess->WBuf);
+       FlushStrBuf(sess->HBuf);
 }
+
+void 
+SessionDestroyModule_WEBCIT
+(wcsession *sess)
+{
+       FreeStrBuf(&sess->WBuf);
+       FreeStrBuf(&sess->HBuf);
+       FreeStrBuf(&sess->UrlFragment1);
+       FreeStrBuf(&sess->UrlFragment2);
+       FreeStrBuf(&sess->UrlFragment3);
+       FreeStrBuf(&sess->UrlFragment4);
+       FreeStrBuf(&sess->ImportantMsg);
+}
+