From 9cf4f824ed6f214f0041bd855f69e75e8fba4bcf Mon Sep 17 00:00:00 2001 From: Max Christian Pohle Date: Sat, 27 Nov 2021 13:47:24 +0100 Subject: Refactoring, stable&functional intermediate state --- cgi.c | 16 ++++-------- http_parser.c | 80 +++++++++++++++++++++++++++++++++++++++++------------------ main.h | 2 +- 3 files changed, 62 insertions(+), 36 deletions(-) diff --git a/cgi.c b/cgi.c index aa86c9a..128eae7 100644 --- a/cgi.c +++ b/cgi.c @@ -81,9 +81,7 @@ int print_on_correct_printer(void * user_data, unsigned flags, cups_dest_t * des return 0; } -void send_answer_file(Http_Header * http_header, int fd_socket) { - - FILE * f = fdopen((size_t) fd_socket, "w"); +void send_answer_file(Http_Header * http_header, FILE * f) { fputs("HTTP/1.0 200 OK\n", f); fputs("content-type: text/html\n\n", f); fflush(f); @@ -96,23 +94,20 @@ void send_answer_file(Http_Header * http_header, int fd_socket) { for(;;) { read_size = fread(buffer, 1, BUFFER_SIZE, file); fwrite(buffer, 1, read_size, f); - fflush(f); if(feof(file)) break; } } else { - if(http_header->url) // TODO: too dangerous to check that here, that is too late. fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); } - fflush(f); - fclose(f); return; } -void send_answer(Http_Header * http_header, int fd_socket) { +void send_answer(Http_Header * http_header, FILE * f) { + + char * printer_name = "Brother_QL-720NW"; // TODO: make configurable - char * printer_name = "Brother_QL-720NW"; cupsEnumDests(CUPS_DEST_FLAGS_NONE, 0, NULL, @@ -124,8 +119,7 @@ void send_answer(Http_Header * http_header, int fd_socket) { line1 = NULL; line2 = NULL; - http_header->url = "/index.html"; - send_answer_file(http_header, fd_socket); + send_answer_file(http_header, f); } diff --git a/http_parser.c b/http_parser.c index 05cd094..30e74c8 100644 --- a/http_parser.c +++ b/http_parser.c @@ -37,7 +37,47 @@ #include "main.h" +static inline char * handle_colon(char ** start, char ** end, char ** search) { + // HTTP headers and content are separated by an empty line (which is two + // newlines), but before that there are pairs of names and values, separated + // by a colon, e.g. Content-Type: text/html and this function is called, when + // *end reaches a colon. + + *end[0] = '\0'; // remember header 'names' and search for the value + (*end)++; // jump over the colon + + if (0 == strcasecmp("Content-Type", *start)) { + *search = "\r\n;"; // (more unlikely) also search for a semicolon in Content-Type: [...]; boundary=[...] + } else { + *search = "\r\n"; // (likely) search for some kind of newline + } + return *start; // remember, where name starts, will be important in the newline case +} + +static inline void handle_semicolon(char ** start, char ** end, char ** search) { + // find the form-data boundary in the main header + *start += strspn(*start, "; "); // remove spaces and semicolons (boundary check implicit; also stops at '\0') + + const char s_multipart_form_data[] = "boundary="; + if(0 < strcasecmp(*start, s_multipart_form_data)) + { + *end = *end + sizeof(s_multipart_form_data) + 1; + *end += strspn(*end, "-"); + DEBUG("> Boundary found, now looking where it ends...\n"); + } + + *search = "\r\n"; // do not search further semicolons +} + void parse_http(size_t new_socket, char * request, size_t request_length) { + // this http parser modifies the input buffer and replaces single characters + // with \0-chars to terminate them, but it does not copy strings, because + // that would require tedious length checks and would also allow the user + // to submit forms with unreasonable long strings in strange places or to + // make it shorter: it would make boundary checks necessary for every single + // heaer name, value, sub-value and content. This implementation is more + // generic and only requires content length bounary checks. + char * start = request; char * end = NULL; char * search = "\r\n"; @@ -50,30 +90,12 @@ void parse_http(size_t new_socket, char * request, size_t request_length) { size_t matchlen = strspn(end, search); switch(end[0]) { case ':': - end[0] = '\0'; // {{{ remember header 'names' and search for the value - end++; // jump over the colon - - name = start; // remember, where name starts, will be important in the newline case - - if (0 == strcasecmp("Content-Type", start)) { - search = "\r\n;"; // (more unlikely) also search for a semicolon in Content-Type: [...]; boundary=[...] - } else { - search = "\r\n"; // (likely) search for some kind of newline - } // }}} - break; + handle_colon(&start, &end, &search); + name = start; + break; case ';': - // {{{ find the form-data boundary in the main header - start += strspn(start, "; "); // remove spaces and semicolons (boundary check implicit; also stops at '\0') - - const char s_multipart_form_data[] = "boundary="; - if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data)) - { - http_header.boundary = end + sizeof(s_multipart_form_data) + 1; - http_header.boundary += strspn(http_header.boundary, "-"); - DEBUG("> Boundary found, now looking where it ends...\n"); - search = "\r\n"; - continue; - } /// }}} + handle_semicolon(&start, &end, &search); + http_header.boundary = end; break; case '\r': // fallthrough case '\n': @@ -165,8 +187,18 @@ void parse_http(size_t new_socket, char * request, size_t request_length) { start = end + matchlen; } + // failed to find URL? Use a default to avoid NULL pointer exceptions later + if(!http_header.url || http_header.url[0] == '\0' || http_header.url[1] == '\0') { + http_header.url = "/index.html"; + DEBUG("Warning: Request had no URL and is probably invalid: %d", http_header.url[0]); + } + DEBUG("> sending answer...\n"); - send_answer(&http_header, new_socket); + FILE * f = fdopen((size_t) new_socket, "w"); + send_answer(&http_header, f); + + fflush(f); + fclose(f); DEBUG("> answer sent.\n"); } diff --git a/main.h b/main.h index af156f6..826ef47 100644 --- a/main.h +++ b/main.h @@ -81,7 +81,7 @@ typedef struct { } Http_Header; void next_part(Http_Header * http_header, const char * content, size_t content_size); -void send_answer(Http_Header * http_header, int fd_socket); +void send_answer(Http_Header * http_header, FILE * socket); void parse_http(size_t new_socket, char * request, size_t request_length); // modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker foldenable -- cgit v1.2.3