* rename urlstring parser
[citadel.git] / webcit / webcit.c
index dfdf1aea4f0f3114d029e074c1d554e27596f912..c14dacfdc101570710d3e637a4c6241300a2211a 100644 (file)
@@ -95,19 +95,15 @@ void free_url(void *U)
 /*
  * Extract variables from the URL.
  */
-void addurls(StrBuf *url)
+void ParseURLParams(StrBuf *url)
 {
        const char *aptr, *bptr, *eptr, *up;
-///    char *buf;
        int len, keylen;
        urlcontent *u;
        struct wcsession *WCC = WC;
 
        if (WCC->urlstrings == NULL)
                WCC->urlstrings = NewHash(1, NULL);
-//     buf = (char*) malloc (ulen + 1);
-//     memcpy(buf, url, ulen);
-///    buf[ulen] = '\0';
        eptr = ChrPtr(url) + StrLength(url);
        up = ChrPtr(url);
        while ((up < eptr) && (!IsEmptyStr(up))) {
@@ -115,17 +111,14 @@ void addurls(StrBuf *url)
                while ((aptr < eptr) && (*aptr != '\0') && (*aptr != '='))
                        aptr++;
                if (*aptr != '=') {
-                       ///free(buf);
                        return;
                }
-               ///*aptr = '\0';
                aptr++;
                bptr = aptr;
                while ((bptr < eptr) && (*bptr != '\0')
                      && (*bptr != '&') && (*bptr != '?') && (*bptr != ' ')) {
                        bptr++;
                }
-               //*bptr = '\0';
                keylen = aptr - up - 1; /* -1 -> '=' */
                if(keylen > sizeof(u->url_key)) {
                        lprintf(1, "URLkey to long! [%s]", up);
@@ -149,10 +142,12 @@ void addurls(StrBuf *url)
                up = bptr;
                ++up;
 #ifdef DEBUG_URLSTRINGS
-               lprintf(9, "%s = [%ld]  %s\n", u->url_key, u->url_data_size, ChrPtr(u->url_data)); 
+               lprintf(9, "%s = [%ld]  %s\n", 
+                       u->url_key, 
+                       StrLength(u->url_data), 
+                       ChrPtr(u->url_data)); 
 #endif
        }
-       //free(buf);
 }
 
 /*
@@ -187,7 +182,7 @@ void dump_vars(void)
  * Return the value of a variable supplied to the current web page (from the url or a form)
  */
 
-const char *XBstr(char *key, size_t keylen, size_t *len)
+const char *XBstr(const char *key, size_t keylen, size_t *len)
 {
        void *U;
 
@@ -202,7 +197,7 @@ const char *XBstr(char *key, size_t keylen, size_t *len)
        }
 }
 
-const char *XBSTR(char *key, size_t *len)
+const char *XBSTR(const char *key, size_t *len)
 {
        void *U;
 
@@ -218,7 +213,7 @@ const char *XBSTR(char *key, size_t *len)
 }
 
 
-const char *BSTR(char *key)
+const char *BSTR(const char *key)
 {
        void *U;
 
@@ -229,7 +224,7 @@ const char *BSTR(char *key)
                return ("");
 }
 
-const char *Bstr(char *key, size_t keylen)
+const char *Bstr(const char *key, size_t keylen)
 {
        void *U;
 
@@ -262,7 +257,7 @@ const StrBuf *SBstr(const char *key, size_t keylen)
                return NULL;
 }
 
-long LBstr(char *key, size_t keylen)
+long LBstr(const char *key, size_t keylen)
 {
        void *U;
 
@@ -273,7 +268,7 @@ long LBstr(char *key, size_t keylen)
                return (0);
 }
 
-long LBSTR(char *key)
+long LBSTR(const char *key)
 {
        void *U;
 
@@ -284,7 +279,7 @@ long LBSTR(char *key)
                return (0);
 }
 
-int IBstr(char *key, size_t keylen)
+int IBstr(const char *key, size_t keylen)
 {
        void *U;
 
@@ -295,7 +290,7 @@ int IBstr(char *key, size_t keylen)
                return (0);
 }
 
-int IBSTR(char *key)
+int IBSTR(const char *key)
 {
        void *U;
 
@@ -306,7 +301,7 @@ int IBSTR(char *key)
                return (0);
 }
 
-int HaveBstr(char *key, size_t keylen)
+int HaveBstr(const char *key, size_t keylen)
 {
        void *U;
 
@@ -317,7 +312,7 @@ int HaveBstr(char *key, size_t keylen)
                return (0);
 }
 
-int HAVEBSTR(char *key)
+int HAVEBSTR(const char *key)
 {
        void *U;
 
@@ -329,7 +324,7 @@ int HAVEBSTR(char *key)
 }
 
 
-int YesBstr(char *key, size_t keylen)
+int YesBstr(const char *key, size_t keylen)
 {
        void *U;
 
@@ -340,7 +335,7 @@ int YesBstr(char *key, size_t keylen)
                return (0);
 }
 
-int YESBSTR(char *key)
+int YESBSTR(const char *key)
 {
        void *U;
 
@@ -611,17 +606,22 @@ void jsescputs(char *strbuf)
  */
 void msgescputs1( char *strbuf)
 {
-       StrBuf *OutBuf = NewStrBuf();
+       StrBuf *OutBuf;
 
+       if ((strbuf == NULL) || IsEmptyStr(strbuf))
+               return;
+       OutBuf = NewStrBuf();
        StrMsgEscAppend(OutBuf, NULL, strbuf);
        StrEscAppend(WC->WBuf, OutBuf, NULL, 0, 0);
+       FreeStrBuf(&OutBuf);
 }
 
 /*
  * print a string to the client after cleaning it with msgesc()
  */
 void msgescputs(char *strbuf) {
-       StrMsgEscAppend(WC->WBuf, NULL, strbuf);
+       if ((strbuf != NULL) && !IsEmptyStr(strbuf))
+               StrMsgEscAppend(WC->WBuf, NULL, strbuf);
 }
 
 
@@ -701,7 +701,7 @@ void output_headers(        int do_httpheaders,     /* 1 = output HTTP headers
 
        /* ICONBAR */
        if (do_htmlhead) {
-
+               begin_burst();
 
                /* check for ImportantMessages (these display in a div overlaying the main screen) */
                if (!IsEmptyStr(WC->ImportantMessage)) {
@@ -759,6 +759,10 @@ void http_redirect(const char *whichpage) {
 void http_transmit_thing(const char *content_type,
                         int is_static) {
 
+       lprintf(9, "http_transmit_thing(%s)%s\n",
+               content_type,
+               (is_static ? " (static)" : "")
+       );
        output_headers(0, 0, 0, 0, 0, is_static);
 
        hprintf("Content-type: %s\r\n"
@@ -953,13 +957,11 @@ void mimepart(const char *msgnum, const char *partnum, int force_download)
        char buf[256];
        off_t bytes;
        char content_type[256];
-       char *content = NULL;
        
        serv_printf("OPNA %s|%s", msgnum, partnum);
        serv_getln(buf, sizeof buf);
        if (buf[0] == '2') {
                bytes = extract_long(&buf[4], 0);
-               content = malloc(bytes + 2);
                if (force_download) {
                        strcpy(content_type, "application/octet-stream");
                }
@@ -979,7 +981,6 @@ void mimepart(const char *msgnum, const char *partnum, int force_download)
                wprintf(_("An error occurred while retrieving this part: %s\n"), &buf[4]);
                end_burst();
        }
-
 }
 
 
@@ -1008,7 +1009,6 @@ char *load_mimepart(long msgnum, char *partnum)
        else {
                return(NULL);
        }
-
 }
 
 
@@ -1050,7 +1050,8 @@ void blank_page(void) {
 void url_do_template(void) {
        const StrBuf *Tmpl = sbstr("template");
        begin_burst();
-       DoTemplate(ChrPtr(Tmpl), StrLength(Tmpl), NULL, NULL);
+       output_headers(1, 0, 0, 0, 1, 0);
+       DoTemplate(ChrPtr(Tmpl), StrLength(Tmpl), NULL, NULL, 0);
        end_burst();
 }
 
@@ -1059,7 +1060,7 @@ void url_do_template(void) {
 /*
  * Offer to make any page the user's "start page."
  */
-void offer_start_page(void) {
+void offer_start_page(StrBuf *Target, int nArgs, WCTemplateToken *Token, void *Context, int ContextType) {
        wprintf("<a href=\"change_start_page?startpage=");
        urlescputs(WC->this_page);
        wprintf("\">");
@@ -1138,11 +1139,12 @@ void authorization_required(const char *message)
  * cbcharset   Character set
  * length      Content length
  * encoding    MIME encoding type (not needed)
+ * cbid                Content ID (not needed)
  * userdata    Not used here
  */
 void upload_handler(char *name, char *filename, char *partnum, char *disp,
                        void *content, char *cbtype, char *cbcharset,
-                       size_t length, char *encoding, void *userdata)
+                       size_t length, char *encoding, char *cbid, void *userdata)
 {
        urlcontent *u;
 #ifdef DEBUG_URLSTRINGS
@@ -1160,7 +1162,10 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp,
                
                Put(WC->urlstrings, u->url_key, strlen(u->url_key), u, free_url);
 #ifdef DEBUG_URLSTRINGS
-               lprintf(9, "Key: <%s> len: [%ld] Data: <%s>\n", u->url_key, u->url_data_size, u->url_data);
+               lprintf(9, "Key: <%s> len: [%ld] Data: <%s>\n", 
+                       u->url_key, 
+                       StrLength(u->url_data), 
+                       ChrPtr(u->url_data));
 #endif
        }
 
@@ -1186,14 +1191,14 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp,
  * Convenience functions to wrap around asynchronous ajax responses
  */
 void begin_ajax_response(void) {
+       struct wcsession *WCC = WC;
+
+       FlushStrBuf(WCC->HBuf);
         output_headers(0, 0, 0, 0, 0, 0);
 
         hprintf("Content-type: text/html; charset=UTF-8\r\n"
                 "Server: %s\r\n"
                 "Connection: close\r\n"
-                "Pragma: no-cache\r\n"
-                "Cache-Control: no-cache\r\n"
-               "Expires: -1\r\n"
                ,
                 PACKAGE_STRING);
         begin_burst();
@@ -1203,7 +1208,6 @@ void begin_ajax_response(void) {
  * print ajax response footer 
  */
 void end_ajax_response(void) {
-        ///hprintf("\r\n");///// todo: is this right?
         wDumpContent(0);
 }
 
@@ -1272,11 +1276,11 @@ void seconds_since_last_gexp(void)
 {
        char buf[256];
 
-       begin_ajax_response();
        if ( (time(NULL) - WC->last_pager_check) < 30) {
                wprintf("NO\n");
        }
        else {
+               memset(buf, 5, 0);
                serv_puts("NOOP");
                serv_getln(buf, sizeof buf);
                if (buf[3] == '*') {
@@ -1286,7 +1290,6 @@ void seconds_since_last_gexp(void)
                        wprintf("NO");
                }
        }
-       end_ajax_response();
 }
 
 /**
@@ -1312,22 +1315,21 @@ int is_mobile_ua(char *user_agent) {
 /*
  * Entry point for WebCit transaction
  */
-void session_loop(struct httprequest *req)
+void session_loop(HashList *HTTPHeaders, StrBuf *ReqLine, StrBuf *request_method, StrBuf *ReadBuf)
 {
-       char cmd[1024];
+       const char *pch, *pchs, *pche;
+       void *vLine;
        char action[1024];
        char arg[8][128];
        size_t sizes[10];
        char *index[10];
        char buf[SIZ];
-       char request_method[128];
-       char pathname[1024];
-       int a, b, nBackDots, nEmpty;
+       int a, nBackDots, nEmpty;
        int ContentLength = 0;
-       char ContentType[512];
-       char *content = NULL;
-       char *content_end = NULL;
-       struct httprequest *hptr;
+       StrBuf *ContentType = NULL;
+       StrBuf *UrlLine = NULL;
+       StrBuf *content = NULL;
+       const char *content_end = NULL;
        char browser_host[256];
        char user_agent[256];
        int body_start = 0;
@@ -1344,7 +1346,6 @@ void session_loop(struct httprequest *req)
        char c_httpauth_string[SIZ];
        char c_httpauth_user[SIZ];
        char c_httpauth_pass[SIZ];
-       char cookie[SIZ];
        struct wcsession *WCC;
        
        safestrncpy(c_username, "", sizeof c_username);
@@ -1368,14 +1369,6 @@ void session_loop(struct httprequest *req)
        WCC->upload = NULL;
        WCC->is_mobile = 0;
 
-       hptr = req;
-       if (hptr == NULL) return;
-
-       safestrncpy(cmd, hptr->line, sizeof cmd);
-       hptr = hptr->next;
-       extract_token(request_method, cmd, 0, ' ', sizeof request_method);
-       extract_token(pathname, cmd, 1, ' ', sizeof pathname);
-
        /** Figure out the action */
        index[0] = action;
        sizes[0] = sizeof action;
@@ -1389,7 +1382,7 @@ void session_loop(struct httprequest *req)
        nEmpty = 0;
        for ( a = 0; a < 9; ++a)
        {
-               extract_token(index[a], pathname, a + 1, '/', sizes[a]);
+               extract_token(index[a], ChrPtr(ReqLine), a + 1, '/', sizes[a]);
                if (strstr(index[a], "?")) *strstr(index[a], "?") = 0;
                if (strstr(index[a], "&")) *strstr(index[a], "&") = 0;
                if (strstr(index[a], " ")) *strstr(index[a], " ") = 0;
@@ -1399,107 +1392,111 @@ void session_loop(struct httprequest *req)
                        nEmpty++;
        }
 
-       while (hptr != NULL) {
-               safestrncpy(buf, hptr->line, sizeof buf);
-               /* lprintf(9, "HTTP HEADER: %s\n", buf); */
-               hptr = hptr->next;
 
-               if (!strncasecmp(buf, "Cookie: webcit=", 15)) {
-                       safestrncpy(cookie, &buf[15], sizeof cookie);
-                       cookie_to_stuff(cookie, NULL,
-                                       c_username, sizeof c_username,
-                                       c_password, sizeof c_password,
-                                       c_roomname, sizeof c_roomname);
-               }
-               else if (!strncasecmp(buf, "Authorization: Basic ", 21)) {
-                       CtdlDecodeBase64(c_httpauth_string, &buf[21], strlen(&buf[21]));
-                       extract_token(c_httpauth_user, c_httpauth_string, 0, ':', sizeof c_httpauth_user);
-                       extract_token(c_httpauth_pass, c_httpauth_string, 1, ':', sizeof c_httpauth_pass);
-               }
-               else if (!strncasecmp(buf, "Content-length: ", 16)) {
-                       ContentLength = atoi(&buf[16]);
-               }
-               else if (!strncasecmp(buf, "Content-type: ", 14)) {
-                       safestrncpy(ContentType, &buf[14], sizeof ContentType);
-               }
-               else if (!strncasecmp(buf, "User-agent: ", 12)) {
-                       safestrncpy(user_agent, &buf[12], sizeof user_agent);
-                       #ifdef TECH_PREVIEW
-                       if (is_mobile_ua(&buf[12])) {
-                               WCC->is_mobile = 1;
-                       }
-                       #endif
-               }
-               else if (!strncasecmp(buf, "X-Forwarded-Host: ", 18)) {
-                       if (follow_xff) {
-                               safestrncpy(WCC->http_host, &buf[18], sizeof WCC->http_host);
-                       }
+       if (GetHash(HTTPHeaders, HKEY("COOKIE"), &vLine) && 
+           (vLine != NULL)){
+               cookie_to_stuff((StrBuf *)vLine, NULL,
+                               c_username, sizeof c_username,
+                               c_password, sizeof c_password,
+                               c_roomname, sizeof c_roomname);
+       }
+       if (GetHash(HTTPHeaders, HKEY("AUTHORIZATION"), &vLine) &&
+           (vLine!=NULL)) {
+               CtdlDecodeBase64(c_httpauth_string, ChrPtr((StrBuf*)vLine), StrLength((StrBuf*)vLine));
+               extract_token(c_httpauth_user, c_httpauth_string, 0, ':', sizeof c_httpauth_user);
+               extract_token(c_httpauth_pass, c_httpauth_string, 1, ':', sizeof c_httpauth_pass);
+       }
+       if (GetHash(HTTPHeaders, HKEY("CONTENT-LENGTH"), &vLine) &&
+           (vLine!=NULL)) {
+               ContentLength = StrToi((StrBuf*)vLine);
+       }
+       if (GetHash(HTTPHeaders, HKEY("CONTENT-TYPE"), &vLine) &&
+           (vLine!=NULL)) {
+               ContentType = (StrBuf*)vLine;
+       }
+       if (GetHash(HTTPHeaders, HKEY("USER-AGENT"), &vLine) &&
+           (vLine!=NULL)) {
+               safestrncpy(user_agent, ChrPtr((StrBuf*)vLine), sizeof user_agent);
+#ifdef TECH_PREVIEW
+               if ((WCC->is_mobile < 0) && is_mobile_ua(&buf[12])) {                   
+                       WCC->is_mobile = 1;
                }
-               else if (!strncasecmp(buf, "Host: ", 6)) {
-                       if (IsEmptyStr(WCC->http_host)) {
-                               safestrncpy(WCC->http_host, &buf[6], sizeof WCC->http_host);
-                       }
+               else {
+                       WCC->is_mobile = 0;
                }
-               else if (!strncasecmp(buf, "X-Forwarded-For: ", 17)) {
-                       safestrncpy(browser_host, &buf[17], sizeof browser_host);
-                       while (num_tokens(browser_host, ',') > 1) {
-                               remove_token(browser_host, 0, ',');
-                       }
-                       striplt(browser_host);
+#endif
+       }
+       if ((follow_xff) &&
+           GetHash(HTTPHeaders, HKEY("X-FORWARDED-HOST"), &vLine) &&
+           (vLine != NULL)) {
+               safestrncpy(WCC->http_host, 
+                           ChrPtr((StrBuf*)vLine), 
+                           sizeof WCC->http_host);
+       }
+       if (IsEmptyStr(WCC->http_host) && 
+           GetHash(HTTPHeaders, HKEY("HOST"), &vLine) &&
+           (vLine!=NULL)) {
+               safestrncpy(WCC->http_host, 
+                           ChrPtr((StrBuf*)vLine), 
+                           sizeof WCC->http_host);
+               
+       }
+       if (GetHash(HTTPHeaders, HKEY("X-FORWARDED-FOR"), &vLine) &&
+           (vLine!=NULL)) {
+               safestrncpy(browser_host, 
+                           ChrPtr((StrBuf*) vLine), 
+                           sizeof browser_host);
+               while (num_tokens(browser_host, ',') > 1) {
+                       remove_token(browser_host, 0, ',');
                }
+               striplt(browser_host);
        }
 
        if (ContentLength > 0) {
-               int BuffSize;
-
-               BuffSize = ContentLength + SIZ;
-               content = malloc(BuffSize);
-               memset(content, 0, BuffSize);
+               content = NewStrBuf();
+               StrBufPrintf(content, "Content-type: %s\n"
+                        "Content-length: %d\n\n",
+                        ChrPtr(ContentType), ContentLength);
+/*
                hprintf("Content-type: %s\n"
                        "Content-length: %d\n\n",
                        ContentType, ContentLength);
-               body_start = strlen(content);
+*/
+               body_start = StrLength(content);
 
                /** Read the entire input data at once. */
-               client_read(WCC->http_sock, &content[body_start], ContentLength);
-
-               if (!strncasecmp(ContentType, "application/x-www-form-urlencoded", 33)) {
-                       StrBuf *Content;
-
-                       Content = _NewConstStrBuf(&content[body_start],ContentLength);
-                       addurls(Content);
-                       FreeStrBuf(&Content);
-               } else if (!strncasecmp(ContentType, "multipart", 9)) {
-                       content_end = content + ContentLength + body_start;
-                       mime_parser(content, content_end, *upload_handler, NULL, NULL, NULL, 0);
+               client_read(&WCC->http_sock, content, ReadBuf, ContentLength + body_start);
+
+               if (!strncasecmp(ChrPtr(ContentType), "application/x-www-form-urlencoded", 33)) {
+                       StrBufCutLeft(content, body_start);
+                       ParseURLParams(content);
+               } else if (!strncasecmp(ChrPtr(ContentType), "multipart", 9)) {
+                       content_end = ChrPtr(content) + ContentLength + body_start;
+                       mime_parser(ChrPtr(content), content_end, *upload_handler, NULL, NULL, NULL, 0);
                }
        } else {
                content = NULL;
        }
 
        /* make a note of where we are in case the user wants to save it */
-       safestrncpy(WCC->this_page, cmd, sizeof(WCC->this_page));
+       safestrncpy(WCC->this_page, ChrPtr(ReqLine), sizeof(WCC->this_page));
        remove_token(WCC->this_page, 2, ' ');
        remove_token(WCC->this_page, 0, ' ');
 
        /* If there are variables in the URL, we must grab them now */
-       len = strlen(cmd);
-       for (a = 0; a < len; ++a) {
-               if ((cmd[a] == '?') || (cmd[a] == '&')) {
-                       StrBuf *Params;
-                       for (b = a; b < len; ++b) {
-                               if (isspace(cmd[b])){
-                                       cmd[b] = 0;
-                                       len = b - 1;
-                               }
-                       }
-                       //cmd[len - a] = '\0';
-                       Params = _NewConstStrBuf(&cmd[a + 1], len - a);
-                       addurls(Params);
-                       cmd[a] = 0;
-                       len = a - 1;
+       UrlLine = NewStrBufDup(ReqLine);
+       len = StrLength(UrlLine);
+       pch = pchs = ChrPtr(UrlLine);
+       pche = pchs + len;
+       while (pch < pche) {
+               if ((*pch == '?') || (*pch == '&')) {
+                       StrBufCutLeft(UrlLine, pch - pchs + 1);
+                       ParseURLParams(UrlLine);
+                       break;
                }
+               pch ++;
        }
+       FreeStrBuf(&UrlLine);
 
        /* If it's a "force 404" situation then display the error and bail. */
        if (!strcmp(action, "404")) {
@@ -1512,7 +1509,7 @@ void session_loop(struct httprequest *req)
 
        /* Static content can be sent without connecting to Citadel. */
        is_static = 0;
-       for (a=0; a<ndirs; ++a) {
+       for (a=0; a<ndirs && ! is_static; ++a) {
                if (!strcasecmp(action, (char*)static_content_dirs[a])) { /* map web to disk location */
                        is_static = 1;
                        n_static = a;
@@ -1611,7 +1608,7 @@ void session_loop(struct httprequest *req)
                        }
                }
        }
-////////todo: restorte language in this case
+////////todo: restore language in this case
        /*
         * Functions which can be performed without logging in
         */
@@ -1620,7 +1617,7 @@ void session_loop(struct httprequest *req)
                goto SKIP_ALL_THIS_CRAP;
        }
        if (!strcasecmp(action, "freebusy")) {
-               do_freebusy(cmd);
+               do_freebusy(ChrPtr(ReqLine));
                goto SKIP_ALL_THIS_CRAP;
        }
 
@@ -1662,8 +1659,10 @@ void session_loop(struct httprequest *req)
         * our session's authentication.
         */
        if (!strncasecmp(action, "groupdav", 8)) {
-               groupdav_main(req, ContentType, /* do GroupDAV methods */
-                       ContentLength, content+body_start);
+               groupdav_main(HTTPHeaders, 
+                             ReqLine, request_method,
+                             ContentType, /* do GroupDAV methods */
+                             ContentLength, content, body_start);
                if (!WCC->logged_in) {
                        WCC->killthis = 1;      /* If not logged in, don't */
                }                               /* keep the session active */
@@ -1675,9 +1674,10 @@ void session_loop(struct httprequest *req)
         * Automatically send requests with any method other than GET or
         * POST to the GroupDAV code as well.
         */
-       if ((strcasecmp(request_method, "GET")) && (strcasecmp(request_method, "POST"))) {
-               groupdav_main(req, ContentType, /** do GroupDAV methods */
-                       ContentLength, content+body_start);
+       if ((strcasecmp(ChrPtr(request_method), "GET")) && (strcasecmp(ChrPtr(request_method), "POST"))) {
+               groupdav_main(HTTPHeaders, ReqLine, 
+                             request_method, ContentType, /** do GroupDAV methods */
+                             ContentLength, content, body_start);
                if (!WCC->logged_in) {
                        WCC->killthis = 1;      /** If not logged in, don't */
                }                               /** keep the session active */
@@ -1760,7 +1760,7 @@ void session_loop(struct httprequest *req)
 SKIP_ALL_THIS_CRAP:
        fflush(stdout);
        if (content != NULL) {
-               free(content);
+               FreeStrBuf(&content);
                content = NULL;
        }
        free_urls();
@@ -1788,9 +1788,9 @@ void diagnostics(void)
        output_headers(1, 1, 1, 0, 0, 0);
        wprintf("Session: %d<hr />\n", WC->wc_session);
        wprintf("Command: <br /><PRE>\n");
-       escputs(WC->UrlFragment1);
+       StrEscPuts(WC->UrlFragment1);
        wprintf("<br />\n");
-       escputs(WC->UrlFragment2);
+       StrEscPuts(WC->UrlFragment2);
        wprintf("</PRE><hr />\n");
        wprintf("Variables: <br /><PRE>\n");
        dump_vars();
@@ -1810,6 +1810,44 @@ void download_mimepart(void) {
                 1);
 }
 
+
+int ConditionalImportantMesage(WCTemplateToken *Tokens, void *Context, int ContextType)
+{
+       struct wcsession *WCC = WC;
+       if (WCC != NULL)
+               return (!IsEmptyStr(WCC->ImportantMessage));
+       else
+               return 0;
+}
+
+void tmplput_importantmessage(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
+{
+       struct wcsession *WCC = WC;
+       
+       if (WCC != NULL) {
+               StrEscAppend(Target, NULL, WCC->ImportantMessage, 0, 0);
+                       WCC->ImportantMessage[0] = '\0';
+       }
+}
+
+int ConditionalBstr(WCTemplateToken *Tokens, void *Context, int ContextType)
+{
+       if(Tokens->nParameters == 1)
+               return HaveBstr(Tokens->Params[0]->Start, 
+                               Tokens->Params[0]->len);
+       else
+               return strcmp(Bstr(Tokens->Params[0]->Start, 
+                                  Tokens->Params[0]->len),
+                             Tokens->Params[1]->Start) == 0;
+}
+
+void tmplput_bstr(StrBuf *Target, int nArgs, WCTemplateToken *Tokens, void *Context, int ContextType)
+{
+       StrBufAppendBuf(Target, 
+                       SBstr(Tokens->Params[0]->Start, 
+                             Tokens->Params[0]->len), 0);
+}
+
 void 
 InitModule_WEBCIT
 (void)
@@ -1824,4 +1862,10 @@ InitModule_WEBCIT
        WebcitAddUrlHandler(HKEY("mimepart"), view_mimepart, NEED_URL);
        WebcitAddUrlHandler(HKEY("mimepart_download"), download_mimepart, NEED_URL);
        WebcitAddUrlHandler(HKEY("diagnostics"), diagnostics, NEED_URL);
+
+       RegisterConditional(HKEY("COND:IMPMSG"), 0, ConditionalImportantMesage, CTX_NONE);
+       RegisterConditional(HKEY("COND:BSTR"), 1, ConditionalBstr, CTX_NONE);
+       RegisterNamespace("BSTR", 1, 2, tmplput_bstr, CTX_NONE);
+       RegisterNamespace("IMPORTANTMESSAGE", 0, 0, tmplput_importantmessage, CTX_NONE);
+       RegisterNamespace("OFFERSTARTPAGE", 0, 0, offer_start_page, CTX_NONE);
 }