diff options
| -rw-r--r-- | cgi.c | 16 | ||||
| -rw-r--r-- | http_parser.c | 80 | ||||
| -rw-r--r-- | main.h | 2 | 
3 files changed, 62 insertions, 36 deletions
| @@ -81,9 +81,7 @@ int print_on_correct_printer(void * user_data, unsigned flags, cups_dest_t * des | |||
| 81 | return 0; | 81 | return 0; | 
| 82 | } | 82 | } | 
| 83 | 83 | ||
| 84 | void send_answer_file(Http_Header * http_header, int fd_socket) { | 84 | void send_answer_file(Http_Header * http_header, FILE * f) { | 
| 85 | |||
| 86 | FILE * f = fdopen((size_t) fd_socket, "w"); | ||
| 87 | fputs("HTTP/1.0 200 OK\n", f); | 85 | fputs("HTTP/1.0 200 OK\n", f); | 
| 88 | fputs("content-type: text/html\n\n", f); | 86 | fputs("content-type: text/html\n\n", f); | 
| 89 | fflush(f); | 87 | fflush(f); | 
| @@ -96,23 +94,20 @@ void send_answer_file(Http_Header * http_header, int fd_socket) { | |||
| 96 | for(;;) { | 94 | for(;;) { | 
| 97 | read_size = fread(buffer, 1, BUFFER_SIZE, file); | 95 | read_size = fread(buffer, 1, BUFFER_SIZE, file); | 
| 98 | fwrite(buffer, 1, read_size, f); | 96 | fwrite(buffer, 1, read_size, f); | 
| 99 | fflush(f); | ||
| 100 | if(feof(file)) | 97 | if(feof(file)) | 
| 101 | break; | 98 | break; | 
| 102 | } | 99 | } | 
| 103 | } else { | 100 | } else { | 
| 104 | if(http_header->url) // TODO: too dangerous to check that here, that is too late. | ||
| 105 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | 101 | fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]); | 
| 106 | } | 102 | } | 
| 107 | 103 | ||
| 108 | fflush(f); | ||
| 109 | fclose(f); | ||
| 110 | return; | 104 | return; | 
| 111 | } | 105 | } | 
| 112 | 106 | ||
| 113 | void send_answer(Http_Header * http_header, int fd_socket) { | 107 | void send_answer(Http_Header * http_header, FILE * f) { | 
| 108 | |||
| 109 | char * printer_name = "Brother_QL-720NW"; // TODO: make configurable | ||
| 114 | 110 | ||
| 115 | char * printer_name = "Brother_QL-720NW"; | ||
| 116 | cupsEnumDests(CUPS_DEST_FLAGS_NONE, | 111 | cupsEnumDests(CUPS_DEST_FLAGS_NONE, | 
| 117 | 0, | 112 | 0, | 
| 118 | NULL, | 113 | NULL, | 
| @@ -124,8 +119,7 @@ void send_answer(Http_Header * http_header, int fd_socket) { | |||
| 124 | line1 = NULL; | 119 | line1 = NULL; | 
| 125 | line2 = NULL; | 120 | line2 = NULL; | 
| 126 | 121 | ||
| 127 | http_header->url = "/index.html"; | 122 | send_answer_file(http_header, f); | 
| 128 | send_answer_file(http_header, fd_socket); | ||
| 129 | } | 123 | } | 
| 130 | 124 | ||
| 131 | 125 | ||
| 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 @@ | |||
| 37 | 37 | ||
| 38 | #include "main.h" | 38 | #include "main.h" | 
| 39 | 39 | ||
| 40 | static inline char * handle_colon(char ** start, char ** end, char ** search) { | ||
| 41 | // HTTP headers and content are separated by an empty line (which is two | ||
| 42 | // newlines), but before that there are pairs of names and values, separated | ||
| 43 | // by a colon, e.g. Content-Type: text/html and this function is called, when | ||
| 44 | // *end reaches a colon. | ||
| 45 | |||
| 46 | *end[0] = '\0'; // remember header 'names' and search for the value | ||
| 47 | (*end)++; // jump over the colon | ||
| 48 | |||
| 49 | if (0 == strcasecmp("Content-Type", *start)) { | ||
| 50 | *search = "\r\n;"; // (more unlikely) also search for a semicolon in Content-Type: [...]; boundary=[...] | ||
| 51 | } else { | ||
| 52 | *search = "\r\n"; // (likely) search for some kind of newline | ||
| 53 | } | ||
| 54 | return *start; // remember, where name starts, will be important in the newline case | ||
| 55 | } | ||
| 56 | |||
| 57 | static inline void handle_semicolon(char ** start, char ** end, char ** search) { | ||
| 58 | // find the form-data boundary in the main header | ||
| 59 | *start += strspn(*start, "; "); // remove spaces and semicolons (boundary check implicit; also stops at '\0') | ||
| 60 | |||
| 61 | const char s_multipart_form_data[] = "boundary="; | ||
| 62 | if(0 < strcasecmp(*start, s_multipart_form_data)) | ||
| 63 | { | ||
| 64 | *end = *end + sizeof(s_multipart_form_data) + 1; | ||
| 65 | *end += strspn(*end, "-"); | ||
| 66 | DEBUG("> Boundary found, now looking where it ends...\n"); | ||
| 67 | } | ||
| 68 | |||
| 69 | *search = "\r\n"; // do not search further semicolons | ||
| 70 | } | ||
| 71 | |||
| 40 | void parse_http(size_t new_socket, char * request, size_t request_length) { | 72 | void parse_http(size_t new_socket, char * request, size_t request_length) { | 
| 73 | // this http parser modifies the input buffer and replaces single characters | ||
| 74 | // with \0-chars to terminate them, but it does not copy strings, because | ||
| 75 | // that would require tedious length checks and would also allow the user | ||
| 76 | // to submit forms with unreasonable long strings in strange places or to | ||
| 77 | // make it shorter: it would make boundary checks necessary for every single | ||
| 78 | // heaer name, value, sub-value and content. This implementation is more | ||
| 79 | // generic and only requires content length bounary checks. | ||
| 80 | |||
| 41 | char * start = request; | 81 | char * start = request; | 
| 42 | char * end = NULL; | 82 | char * end = NULL; | 
| 43 | char * search = "\r\n"; | 83 | char * search = "\r\n"; | 
| @@ -50,30 +90,12 @@ void parse_http(size_t new_socket, char * request, size_t request_length) { | |||
| 50 | size_t matchlen = strspn(end, search); | 90 | size_t matchlen = strspn(end, search); | 
| 51 | switch(end[0]) { | 91 | switch(end[0]) { | 
| 52 | case ':': | 92 | case ':': | 
| 53 | end[0] = '\0'; // {{{ remember header 'names' and search for the value | 93 | handle_colon(&start, &end, &search); | 
| 54 | end++; // jump over the colon | 94 | name = start; | 
| 55 | 95 | break; | |
| 56 | name = start; // remember, where name starts, will be important in the newline case | ||
| 57 | |||
| 58 | if (0 == strcasecmp("Content-Type", start)) { | ||
| 59 | search = "\r\n;"; // (more unlikely) also search for a semicolon in Content-Type: [...]; boundary=[...] | ||
| 60 | } else { | ||
| 61 | search = "\r\n"; // (likely) search for some kind of newline | ||
| 62 | } // }}} | ||
| 63 | break; | ||
| 64 | case ';': | 96 | case ';': | 
| 65 | // {{{ find the form-data boundary in the main header | 97 | handle_semicolon(&start, &end, &search); | 
| 66 | start += strspn(start, "; "); // remove spaces and semicolons (boundary check implicit; also stops at '\0') | 98 | http_header.boundary = end; | 
| 67 | |||
| 68 | const char s_multipart_form_data[] = "boundary="; | ||
| 69 | if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data)) | ||
| 70 | { | ||
| 71 | http_header.boundary = end + sizeof(s_multipart_form_data) + 1; | ||
| 72 | http_header.boundary += strspn(http_header.boundary, "-"); | ||
| 73 | DEBUG("> Boundary found, now looking where it ends...\n"); | ||
| 74 | search = "\r\n"; | ||
| 75 | continue; | ||
| 76 | } /// }}} | ||
| 77 | break; | 99 | break; | 
| 78 | case '\r': // fallthrough | 100 | case '\r': // fallthrough | 
| 79 | case '\n': | 101 | case '\n': | 
| @@ -165,8 +187,18 @@ void parse_http(size_t new_socket, char * request, size_t request_length) { | |||
| 165 | start = end + matchlen; | 187 | start = end + matchlen; | 
| 166 | } | 188 | } | 
| 167 | 189 | ||
| 190 | // failed to find URL? Use a default to avoid NULL pointer exceptions later | ||
| 191 | if(!http_header.url || http_header.url[0] == '\0' || http_header.url[1] == '\0') { | ||
| 192 | http_header.url = "/index.html"; | ||
| 193 | DEBUG("Warning: Request had no URL and is probably invalid: %d", http_header.url[0]); | ||
| 194 | } | ||
| 195 | |||
| 168 | DEBUG("> sending answer...\n"); | 196 | DEBUG("> sending answer...\n"); | 
| 169 | send_answer(&http_header, new_socket); | 197 | FILE * f = fdopen((size_t) new_socket, "w"); | 
| 198 | send_answer(&http_header, f); | ||
| 199 | |||
| 200 | fflush(f); | ||
| 201 | fclose(f); | ||
| 170 | DEBUG("> answer sent.\n"); | 202 | DEBUG("> answer sent.\n"); | 
| 171 | } | 203 | } | 
| 172 | 204 | ||
| @@ -81,7 +81,7 @@ typedef struct { | |||
| 81 | } Http_Header; | 81 | } Http_Header; | 
| 82 | 82 | ||
| 83 | void next_part(Http_Header * http_header, const char * content, size_t content_size); | 83 | void next_part(Http_Header * http_header, const char * content, size_t content_size); | 
| 84 | void send_answer(Http_Header * http_header, int fd_socket); | 84 | void send_answer(Http_Header * http_header, FILE * socket); | 
| 85 | void parse_http(size_t new_socket, char * request, size_t request_length); | 85 | void parse_http(size_t new_socket, char * request, size_t request_length); | 
| 86 | 86 | ||
| 87 | // modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker foldenable | 87 | // modeline for vim: shiftwidth=2 tabstop=2 number foldmethod=marker foldenable | 
