webcit-ng: fixed the way HTTP uploads are fed into the MIME Parser.
authorArt Cancro <ajc@citadel.org>
Mon, 6 Nov 2023 21:56:55 +0000 (16:56 -0500)
committerArt Cancro <ajc@citadel.org>
Mon, 6 Nov 2023 21:56:55 +0000 (16:56 -0500)
mime_parser() expects HEADERS+CONTENT but our h.request_body only contains CONTENT.
The fix: our transaction struct now has h.request_body_with_synth_headers that
begins with a synthetic Content-Type: header followed by a blank line and then the
request body.  h.request_body is now just a pointer into that buffer, starting at
the position where the actual body begins.

This gives us the ability to supply the body with or without headers, without having
to make two copies of it.

libcitadel/lib/mime_parser.c
webcit-ng/server/http.c
webcit-ng/server/upload.c
webcit-ng/server/webcit.h

index 65093a2fac298fbce79f5accab7332c8aed79d55..776e3fe4ac2f5af83f92ab676817b5ac533b3d54 100644 (file)
@@ -457,13 +457,14 @@ static int IsAsciiEncoding(interesting_mime_headers *m) {
 }
 
 
-static char *FindNextContent(char *ptr,
-                       char *content_end,
-                       interesting_mime_headers *SubMimeHeaders,
-                       interesting_mime_headers *m)
-{
+static char *FindNextContent(
+               char *ptr,
+               char *content_end,
+               interesting_mime_headers *SubMimeHeaders,
+               interesting_mime_headers *m
+) {
        char *next_boundary;
-       char  tmp;
+       char tmp;
 
        if (IsAsciiEncoding(SubMimeHeaders)) {
                tmp = *content_end;
@@ -482,8 +483,9 @@ static char *FindNextContent(char *ptr,
                                        
                        lines = SubMimeHeaders->content_length / 80;
                        pptr = ptr + SubMimeHeaders->content_length - lines - 10;
-                       if (pptr < content_end)
+                       if (pptr < content_end) {
                                ptr = pptr;
+                       }
                }
                        
                next_boundary = strstr(ptr, m->b[startary].Key);
@@ -512,8 +514,9 @@ static char *FindNextContent(char *ptr,
                                next_boundary = srch;
                                srch = content_end;
                        }
-                       else srch ++;
-
+                       else {
+                               srch++;
+                       }
                }
 
        }
@@ -535,15 +538,15 @@ static void recurseable_mime_parser(char *partnum,
                interesting_mime_headers *m
 ) {
        interesting_mime_headers *SubMimeHeaders;
-       char     *ptr;
-       char     *part_start;
-       char     *part_end = NULL;
-       char     *evaluate_crlf_ptr = NULL;
-       char     *next_boundary;
-       char      nested_partnum[256];
-       int       crlf_in_use = 0;
-       int       part_seq = 0;
-       CBufStr  *chosen_name;
+       char *ptr;
+       char *part_start;
+       char *part_end = NULL;
+       char *evaluate_crlf_ptr = NULL;
+       char *next_boundary;
+       char nested_partnum[256];
+       int crlf_in_use = 0;
+       int part_seq = 0;
+       CBufStr *chosen_name;
 
        // If this is a multipart message, then recursively process it
        ptr = content_start;
@@ -568,7 +571,7 @@ static void recurseable_mime_parser(char *partnum,
 
                // Figure out where the boundaries are
                m->b[startary].len = snprintf(m->b[startary].Key, SIZ, "--%s", m->b[boundary].Key);
-               SubMimeHeaders = InitInterestingMimes ();
+               SubMimeHeaders = InitInterestingMimes();
 
                while ((*ptr == '\r') || (*ptr == '\n')) {
                        ptr++;
@@ -610,14 +613,14 @@ static void recurseable_mime_parser(char *partnum,
 
                                if (!IsEmptyStr(partnum)) {
                                        snprintf(nested_partnum,
-                                                sizeof nested_partnum,
-                                                "%s.%d", partnum,
-                                                ++part_seq);
+                                               sizeof nested_partnum,
+                                               "%s.%d", partnum,
+                                               ++part_seq);
                                }
                                else {
                                        snprintf(nested_partnum,
-                                                sizeof nested_partnum,
-                                                "%d", ++part_seq);
+                                               sizeof nested_partnum,
+                                               "%d", ++part_seq);
                                }
                                recurseable_mime_parser(nested_partnum,
                                                        part_start, 
@@ -679,6 +682,7 @@ static void recurseable_mime_parser(char *partnum,
                        );
                }
        }
+
        // If it's not a multipart message, then do something with it
        else {
                size_t length;
@@ -803,9 +807,7 @@ void the_mime_parser(char *partnum,
        }
 
        m = InitInterestingMimes();
-
        if (!parse_MimeHeaders(m, &content_start, content_end)) {
-
                recurseable_mime_parser(partnum,
                                        content_start, content_end,
                                        CallBack,
index 82fd5f3c258cc07cbafcd3e2b71a23acfa2256d1..1740ccec83fdfac8456f84fa02d2a108e441adbd 100644 (file)
@@ -1,10 +1,15 @@
 // This module handles HTTP transactions.
 //
-// Copyright (c) 1996-2022 by the citadel.org team
+// Copyright (c) 1996-2023 by the citadel.org team
 //
 // This program is open source software.  Use, duplication, or
 // disclosure are subject to the GNU General Public License v3.
 
+// uncomment one or more of these to see raw http transactions
+//#define DEBUG_HTTP
+//#define REQUEST_BODY_TO_STDERR
+//#define RESPONSE_BODY_TO_STDERR
+
 #include "webcit.h"
 
 // Write data to the HTTP client.  Encrypt if necessary.
@@ -192,9 +197,9 @@ void perform_one_http_transaction(struct client_handle *ch) {
                syslog(LOG_DEBUG, "Client disconnected");
        }
        else {
-//#ifdef DEBUG_HTTP
+#ifdef DEBUG_HTTP
                syslog(LOG_DEBUG, "< %s %s", h.method, h.url);
-//#endif
+#endif
 
                // If there is a request body, read it now.
                char *ccl = header_val(&h, "Content-Length");
@@ -203,14 +208,30 @@ void perform_one_http_transaction(struct client_handle *ch) {
                }
                if (h.request_body_length > 0) {
                        syslog(LOG_DEBUG, "Reading request body (%ld bytes)", h.request_body_length);
-                       h.request_body = malloc(h.request_body_length);
+
+                       // Sometimes we need the request body by itself, sometimes we need it with headers.
+                       // So we save it with synthetic headers, and also provide a pointer into the place where the body begins.
+
+                       char *cct = header_val(&h, "Content-Type");
+                       if (cct) {
+                               h.request_body_with_synth_headers = malloc(h.request_body_length + 1024);
+                               memset(h.request_body_with_synth_headers, h.request_body_length + 1024, 0);
+                               sprintf(h.request_body_with_synth_headers, "Content-Type: %s\r\n\r\n", cct);
+                               h.request_body = h.request_body_with_synth_headers + strlen(h.request_body_with_synth_headers);
+                       }
+                       else {  // a request body absent a Content-Type: header is invalid, but handle it anyway.
+                               h.request_body_with_synth_headers = malloc(h.request_body_length);
+                               memset(h.request_body_with_synth_headers, h.request_body_length, 0);
+                               h.request_body = h.request_body_with_synth_headers;
+                       }
+
                        client_read(ch, h.request_body, h.request_body_length);
 
                        // Write the entire request body to stderr -- not what you want during normal operation.
-                       #ifdef BODY_TO_STDERR
-                       write(2, HKEY("---\n"));
+                       #ifdef REQUEST_BODY_TO_STDERR
+                       write(2, HKEY("--- REQUEST BODY BEGIN ---\n"));
                        write(2, h.request_body, h.request_body_length);
-                       write(2, HKEY("---\n"));
+                       write(2, HKEY("--- REQUEST BODY END ---\n"));
                        #endif
 
                }
@@ -219,10 +240,10 @@ void perform_one_http_transaction(struct client_handle *ch) {
                perform_request(&h);
 
                // Write the entire response body to stderr -- not what you want during normal operation.
-               #ifdef BODY_TO_STDERR
-               write(2, HKEY("---\n"));
+               #ifdef RESPONSE_BODY_TO_STDERR
+               write(2, HKEY("--- RESPONSE BODY BEGIN ---\n"));
                write(2, h.response_body, h.response_body_length);
-               write(2, HKEY("---\n"));
+               write(2, HKEY("--- RESPONSE BODY END ---\n"));
                #endif
 
                // Output the results back to the client.
@@ -281,8 +302,8 @@ void perform_one_http_transaction(struct client_handle *ch) {
        if (h.url) {
                free(h.url);
        }
-       if (h.request_body) {
-               free(h.request_body);
+       if (h.request_body_with_synth_headers) {
+               free(h.request_body_with_synth_headers);
        }
        if (h.response_string) {
                free(h.response_string);
index 356cd1d750493c087cc98ef1968e26be91a212e3..8c3fd44e0c5d12c771cfcb549ee0ccaa748f28a2 100644 (file)
@@ -26,6 +26,9 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp,
        syslog(LOG_DEBUG, "      length: %ld", length);
        syslog(LOG_DEBUG, "    encoding: %s", encoding);
        syslog(LOG_DEBUG, "          id: %s", cbid);
+       //fprintf(stderr, "\033[31m--------------------------------------------\n");
+       //write(content, length, 1, stderr);    // FIXME
+       //printf(stderr, "--------------------------------------------\033[0m\n");
 
        struct uploaded_file u;
 
@@ -85,8 +88,8 @@ void upload_files(struct http_transaction *h, struct ctdlsession *c) {
        // Normally the browser will upload only one file per transaction, but that behavior is not guaranteed.
        JsonValue *j_uploads = NewJsonArray(HKEY(""));
 
-       // h->request_body will contain the upload(s) in MIME format
-       mime_parser(h->request_body, (h->request_body + h->request_body_length), *upload_handler, NULL, NULL, j_uploads, 0);
+       // h->request_body_with_synth_headers will contain the upload(s) in MIME format including headers
+       mime_parser(h->request_body_with_synth_headers, (h->request_body+h->request_body_length), *upload_handler, NULL, NULL, j_uploads, 0);
 
        // probably do something more clever here
        h->response_code = 200;
index a8c1275a88858ec809fedec59554baa94c5fb4e0..8af98ec26cf4ebd1cf21f5d991845e589ff9ab12 100644 (file)
@@ -64,7 +64,8 @@ struct http_transaction {                     // The lifetime of an HTTP request goes through this
        char *http_version;                     // stack and sent back down to the web server, which transmits it to
        char *site_prefix;                      // the client.
        Array *request_headers;
-       char *request_body;
+       char *request_body_with_synth_headers;  // This is the request body with some synthetic headers prepended into it.
+       char *request_body;                     // this is just going to be a pointer into request_body_with_synth_headers
        long request_body_length;
        Array *request_parms;                   // anything after the "?" in the URL
        int response_code;