summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Christian Pohle2021-11-20 02:20:05 +0100
committerMax Christian Pohle2021-11-20 02:20:05 +0100
commitc9fd3111a73f3fa66b99b1819ecb8e54688b87d0 (patch)
treea2fe86f996464b32a258d747889353981997eb58
parent076a37921f533b97413340288d56572e11e1395b (diff)
downloadohmycgi-c9fd3111a73f3fa66b99b1819ecb8e54688b87d0.tar.bz2
ohmycgi-c9fd3111a73f3fa66b99b1819ecb8e54688b87d0.zip
The server actually serves
-rw-r--r--Makefile7
-rw-r--r--main.c459
-rw-r--r--test.sh3
3 files changed, 139 insertions, 330 deletions
diff --git a/Makefile b/Makefile
index 1f8a372..bfb3df5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,8 @@
1BUILD := debug
2
3cflags.debug := -Wall -o0 -g -DDEBUG=1
4cflags.release := -os -s
5CFLAGS := ${cflags.${BUILD}}
1 6
2test: main 7test: main
3 ./main 8 ./main
@@ -8,5 +13,5 @@ clean:
8 rm -f ./main 13 rm -f ./main
9 14
10%: 15%:
11 $(CC) -pthread -g -o $@ $@.c 16 $(CC) $(CFLAGS) -o $@ $@.c
12 17
diff --git a/main.c b/main.c
index 18b9900..2a01a4e 100644
--- a/main.c
+++ b/main.c
@@ -1,139 +1,129 @@
1#include <asm-generic/errno-base.h> 1/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *
2#include <asm-generic/errno.h> 2 * OHMYCGI *
3#include <netinet/in.h> 3 * 2021 by Max Christian Pohle <max@coderonline.de> *
4#include <stdio.h> 4 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
5#include <stdio_ext.h> 5
6// {{{ INCLUDES
6#include <stdlib.h> 7#include <stdlib.h>
8#include <stdio.h>
7#include <string.h> 9#include <string.h>
8#include <sys/socket.h>
9#include <sys/epoll.h>
10#include <sys/sendfile.h>
11#include <fcntl.h>
12#include <unistd.h> 10#include <unistd.h>
13#include <poll.h> 11#include <sys/socket.h>
14
15#include <err.h> 12#include <err.h>
16#include <errno.h> 13#include <errno.h>
17 14#include <arpa/inet.h>
18#include <pthread.h> 15#include <fcntl.h>
16#include <sys/sendfile.h>
17#include <sys/stat.h>
18// #include <pthread.h> // maybe later
19// }}}
20// {{{ MACROS
21#define EWOULDBLOCK_DELAY 100
22#define READ_BUFFER_LENGTH 9000 // jumboframe?
23#define POST_DATA_MAX_LENGTH 18000
24#define DEBUG_SLEEP_TIME 50000
25
26#ifndef DEBUG
27#define DEBUG(X, ...) // (X, ...)
28#else
19#include <stdarg.h> 29#include <stdarg.h>
20 30static inline int verbose(const char * format, ...) {
21 31 va_list va; va_start(va, format); usleep(DEBUG_SLEEP_TIME); return vprintf(format, va);
22void send_answer(FILE * f) {
23 fputs("HTTP/1.0 200 OK\n", f);
24 fputs("content-type: text/html\n\n", f);
25 fputs("<html>", f);
26 fputs("<head>", f);
27 fputs("<title>test</title>", f);
28 fputs("<style>label { display:block }</style>", f);
29 fputs("</head>", f);
30 fputs("<form action=\"/\" method=\"post\" enctype=\"multipart/form-data\">", f);
31 fputs("<label>Line1<br/>\n", f);
32 fputs("<textarea type=\"text\" name=\"text\"></textarea>", f);
33 fputs("</label><br/>\n", f);
34 fputs("<input type=\"file\" name=\"okay\" value=\"ok\" />", f);
35 fputs("<input type=\"submit\" name=\"okay\" value=\"ok\" />", f);
36 fputs("</form>", f);
37 fputs("</html>", f);
38} 32}
33#undef DEBUG
34#define DEBUG verbose
35#endif
36// }}}
39 37
38typedef struct {
39 int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2)
40 char * method;
41 char * boundary;
42 size_t boundary_size;
43 char * content_type;
44 char * content_disposition;
45 char * url;
46} Http_Header;
40 47
41#define EWOULDBLOCK_DELAY 1000 48void send_answer(int fd_socket, Http_Header * http_header) {
42#define READ_BUFFER_LENGTH 10 49 FILE * f = fdopen((size_t) fd_socket, "w");
43#define DEBUG_SLEEP_TIME 50000 50 fputs("HTTP/1.0 200 OK\n", f);
51 fputs("content-type: text/plain\n\n", f);
52 fflush(f);
53
54 int file = open(&http_header->url[1], O_RDONLY);
55 if(0 < file) {
56 struct stat stat;
57 fstat(file, &stat);
58 sendfile (fileno(f), file, NULL, stat.st_size);
59 } else {
60 fprintf(f, "could not open file \"%s\"\n", &http_header->url[1]);
61 }
44 62
45static inline int verbose(const char * format, ...) { 63 fclose(f);
46 va_list va; va_start(va, format); usleep(DEBUG_SLEEP_TIME); return vprintf(format, va); 64
65 // fputs("<html>", f);
66 // fputs("<head>", f);
67 // fputs("<title>test</title>", f);
68 // fputs("<style>label { display:block }</style>", f);
69 // fputs("</head>", f);
70 // fputs("<form action=\"/\" method=\"post\" enctype=\"multipart/form-data\">", f);
71 // fputs("<label>Line1<br/>\n", f);
72 // fputs("<textarea type=\"text\" name=\"text\"></textarea>", f);
73 // fputs("</label><br/>\n", f);
74 // fputs("<input type=\"file\" name=\"okay\" value=\"ok\" />", f);
75 // fputs("<input type=\"submit\" name=\"okay\" value=\"ok\" />", f);
76 // fputs("</form>", f);
77 // fputs("</html>", f);
47} 78}
48 79
49 80int read_everything(FILE * f_r, FILE * output) {
50void read_everything(FILE * f_r, FILE * output) {
51 const int read_buffer_length = READ_BUFFER_LENGTH; 81 const int read_buffer_length = READ_BUFFER_LENGTH;
52 char read_buffer[read_buffer_length]; 82 char read_buffer[read_buffer_length];
53 for(size_t size = -2 ; ; size = fread(read_buffer, read_buffer_length, 1, f_r)) { 83 for(size_t size = -2 ; ; size = fread(read_buffer, 1, read_buffer_length, f_r)) {
54 if(-2 == size || (-1 == size && EWOULDBLOCK == errno)) { 84 if(-2 == size || (-1 == size && EWOULDBLOCK == errno)) {
55 usleep(EWOULDBLOCK_DELAY); // try again a little later 85 usleep(EWOULDBLOCK_DELAY); // try again a little later
56 continue; 86 continue;
57 } 87 }
58 88
59 fwrite(read_buffer, read_buffer_length, 1, output); 89 fwrite(read_buffer, read_buffer_length, 1, output);
60 fwrite(read_buffer, read_buffer_length, 1, stdout);
61
62 if (1 != size) { // I expect one nmemb of data
63 break;
64 }
65 }
66 fflush(stdout);
67 fflush(output);
68}
69 90
70void header_next(const char * key, const char * value) { 91 if (read_buffer_length > POST_DATA_MAX_LENGTH)
71 // this sample function will be removed later 92 return EXIT_FAILURE;
72 typedef struct {
73 const char * name;
74 const char * value;
75 } NameValue;
76 93
77 NameValue namevalue[] = { 94 if (size < read_buffer_length) { // I expect one nmemb of data
78 {"Content-Type", NULL},
79 {"Content-Length", NULL}
80 };
81 for(int i=0; i < sizeof(namevalue) / sizeof(NameValue); i++) {
82 // printf("next name: %s\n", namevalue[i].name);
83 if(NULL == namevalue[i].value && 0 == strcasecmp(namevalue[i].name, key)) {
84 namevalue[i].value = value;
85 break; 95 break;
86 } 96 }
87 } 97 }
98 fflush(output);
99 return EXIT_SUCCESS;
88} 100}
89 101
90typedef struct {
91 int newline_length; // lenght of one newline in bytes (\n has 1, CR/LF has 2)
92 char * method;
93 char * boundary;
94 size_t boundary_size;
95 char * content_type;
96 char * content_disposition;
97} Http_Header;
98
99void * next_customer(size_t new_socket) { 102void * next_customer(size_t new_socket) {
100 FILE * f_r = fdopen((size_t) new_socket, "rw"); 103 FILE * f_r = fdopen((size_t) new_socket, "r");
101 104
102 char * output_buffer = NULL; 105 char * output_buffer = NULL;
103 size_t output_buffer_length = 0; 106 size_t output_buffer_length = 0;
104 FILE * output = open_memstream(&output_buffer, &output_buffer_length); 107 FILE * output = open_memstream(&output_buffer, &output_buffer_length);
105 108
106 verbose("\n\n########################################## Content Reader [%d]\n", f_r); 109 DEBUG("\n\n########################################## Content Reader [%d]\n", f_r);
107 read_everything(f_r, output); 110 read_everything(f_r, output);
108 shutdown(new_socket, SHUT_RD); // shutdown the reading half of the connection 111 shutdown(new_socket, SHUT_RD); // shutdown the reading half of the connection
109 verbose("\n\n########################################## Content Parser\n"); 112 DEBUG("\n\n########################################## Content Parser\n");
110
111 // token parser...
112 char * key = output_buffer;
113
114 char * saveptr = NULL;
115 113
116 char * start = output_buffer; 114 char * start = output_buffer;
117 char * end = NULL; 115 char * end = NULL;
118 char * search = "\r\n"; 116 char * search = "\r\n";
119 117
120 Http_Header http_header; 118 Http_Header http_header = {0};
121 http_header.boundary_size = 0;
122 http_header.newline_length = 1;
123 http_header.method = NULL;
124 http_header.boundary = NULL;
125 http_header.content_disposition = NULL;
126 http_header.content_type = NULL;
127 119
128 char * name = NULL; 120 char * name = NULL;
129 // while(NULL != (end = strpbrk(start, search))) {
130 while(NULL != (end = strpbrk(start, search))) { 121 while(NULL != (end = strpbrk(start, search))) {
131 // verbose("\033[31m[%ld|%ld|%ld]\033[0m\n", start - output_buffer, end - start, end - output_buffer);
132 122
133 size_t matchlen = strspn(end, search); 123 size_t matchlen = strspn(end, search);
134 switch(end[0]) { 124 switch(end[0]) {
135 case ':': 125 case ':':
136 end[0] = '\0'; 126 end[0] = '\0'; // {{{ remember header 'names' and search for the value
137 end++; 127 end++;
138 128
139 name = start; 129 name = start;
@@ -142,52 +132,56 @@ void * next_customer(size_t new_socket) {
142 search = "\r\n;"; 132 search = "\r\n;";
143 } else { 133 } else {
144 search = "\r\n"; 134 search = "\r\n";
145 } 135 } // }}}
146 break; 136 break;
147 case ';': 137 case ';':
138 start += strspn(start, "; "); // {{{ find the form-data boundary in the main header
139
140 const char s_multipart_form_data[] = "boundary=";
141 if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data))
148 { 142 {
149 start += strspn(start, "; "); 143 http_header.boundary = end + sizeof(s_multipart_form_data) + 1;
150 144 http_header.boundary += strspn(http_header.boundary, "-");
151 const char s_multipart_form_data[] = "boundary="; 145 DEBUG("> Boundary found, now looking where it ends...\n");
152 if(NULL == http_header.boundary && 0 < strcasecmp(start, s_multipart_form_data)) 146 search = "\r\n";
153 { 147 continue;
154 http_header.boundary = end + sizeof(s_multipart_form_data) + 1; 148 } /// }}}
155 http_header.boundary += strspn(http_header.boundary, "-"); 149 break;
156 // verbose("GESCHAFFT %s [%ld] QQQQ %s\n", http_header.boundary, http_header.boundary_size, end);
157 search = "\r\n";
158 continue;
159 }
160 break;
161 }
162 case '\r': // fallthrough 150 case '\r': // fallthrough
163 case '\n': 151 case '\n':
152 // {{{ newlines are special: sometimes content parts follow and sometimes headers, guess what...
164 end[0] = '\0'; 153 end[0] = '\0';
165 search = ":"; // we will continue to search for headers 154 search = ":"; // we will continue to search for headers
166
167
168 if(NULL == name) { 155 if(NULL == name) {
169 if(NULL == http_header.method) { 156 if(NULL == http_header.method) {
170 verbose("[%ld]> HTTP REQUEST LINE :: %s \n", matchlen, start); 157 DEBUG("[%ld]> HTTP REQUEST LINE :: %s \n", matchlen, start);
158 end[0] = '\0';
159
160 while(NULL != (start = memchr(start, ' ', end - start))) {
161 if(NULL == http_header.url)
162 http_header.url = ++start;
163 else
164 start[0] = '\0';
165 }
171 http_header.method = start; 166 http_header.method = start;
172 http_header.newline_length = matchlen; 167 http_header.newline_length = matchlen;
173 } else { 168 } else {
174 verbose("[...]\n"); // if we want to intentially skip something, we land here by setting name = NUL; 169 DEBUG("[...]\n"); // if we want to intentially skip something, we land here by setting name = NUL;
175 break; 170 break;
176 } 171 }
177 } else { // we know that name is not NULL and can work with it 172 } else { // we know that name is not NULL and can work with it
178 if (0 == strcasecmp("Content-Disposition", name)) 173 if (0 == strcasecmp("Content-Disposition", name))
179 { http_header.content_disposition = start; } 174 { http_header.content_disposition = start; }
180 } 175 } // }}}
181 176 DEBUG("\033[32m[%ld]> '% 20s' = '%s'\033[0m\n", matchlen, name, start);
182 verbose("\033[32m[%ld]> '% 20s' = '%s'\033[0m\n", matchlen, name, start); 177 // {{{ check if a http header ended (e.g. two newlines)
183
184 if(matchlen > http_header.newline_length) { 178 if(matchlen > http_header.newline_length) {
185 verbose("> END HEADERS, because there were %d newlines; boundary='%s'[%ld]\n", matchlen / http_header.newline_length, http_header.boundary, http_header.boundary_size); 179 DEBUG("> END HEADERS, because there were %d newlines; boundary='%s'[%ld]\n", matchlen / http_header.newline_length, http_header.boundary, http_header.boundary_size);
186 end += matchlen; 180 end += matchlen;
187 181
188 // if it was the first header, we calculate the boundary size and expect more headers to come after a boundary 182 // if it was the first header, we calculate the boundary size and expect more headers to come after a boundary
189 if(http_header.boundary && http_header.boundary_size == 0) { 183 if(http_header.boundary && http_header.boundary_size == 0) {
190 verbose("================================================================================\n"); 184 DEBUG("================================================================================\n");
191 http_header.boundary_size = strlen(http_header.boundary); 185 http_header.boundary_size = strlen(http_header.boundary);
192 // skip the first header and boundary... 186 // skip the first header and boundary...
193 start = end; 187 start = end;
@@ -200,95 +194,68 @@ void * next_customer(size_t new_socket) {
200 while(1) 194 while(1)
201 { 195 {
202 size_t size_remaining = (size_t) output_buffer_length - (end - output_buffer) - 1; 196 size_t size_remaining = (size_t) output_buffer_length - (end - output_buffer) - 1;
203 verbose("%ld remaining.\n", size_remaining); fflush(stdout); 197 DEBUG("%ld remaining.\n", size_remaining);
204 198
205 if(size_remaining <= 0) { 199 if(size_remaining <= 0) {
206 verbose("> not even the boundary would fit in that what is left.\n"); 200 DEBUG("> not even the boundary would fit in that what is left.\n");
207 break; 201 break;
208 } 202 }
209 203
210 if(NULL == (end = memchr((void*) end, '-', size_remaining))) { 204 if(NULL == (end = memchr((void*) end, '-', size_remaining))) {
211 verbose("no further '-' found\n"); 205 DEBUG("no further '-' found\n");
212 break; 206 break;
213 } 207 }
214 208
215 char * content_end = end - http_header.newline_length; 209 char * content_end = end - http_header.newline_length;
216 210
217
218 end += strspn(end, "-"); 211 end += strspn(end, "-");
219 if(0 == strncmp(end, http_header.boundary, http_header.boundary_size)) { 212 if(0 == strncmp(end, http_header.boundary, http_header.boundary_size)) {
220 size_t file_size = content_end - content_start; 213 size_t file_size = content_end - content_start;
221 verbose("> Content ends here, size of the last file is %ld: {begin}", file_size); 214 DEBUG("> Content ends here, size of the last file is %ld.", file_size);
222 fwrite(content_start, file_size, 1, stdout);
223 verbose("{end}\n");
224
225
226 // puts(" <[last 20 bytes]\n");
227
228 215
229 end += http_header.boundary_size; 216 end += http_header.boundary_size;
230 matchlen = strspn(end, "\r\n"); 217 matchlen = strspn(end, "\r\n");
231 // matchlen = http_header.boundary_size; 218 DEBUG("> end is at %p, matchlen is %ld\n", end, matchlen);
232 verbose("> end is at %p, matchlen is %ld\n", end, matchlen);
233
234 219
235 search = ":"; 220 search = ":";
236 break; 221 break;
237 } else { 222 } else {
238 // printf("[%ld] kack: %c\n", size_remaining, p[0]); fflush(stdout);
239 end = end + 1; 223 end = end + 1;
240 } 224 }
241 } 225 }
242 } 226 }
243 /*
244 char * content_start = end;
245 content_start += strspn(content_start, "-");
246 // verbose("CONTENT: %s\n", content_start);
247 if(0 <= strcmp(content_start, http_header.boundary)) {
248 verbose("MATCH for %s // %s\n", http_header.content_type, http_header.content_disposition);
249
250 search = "\r\n";
251 matchlen = http_header.boundary_size;
252 name = NULL;
253 } else {
254 verbose("NO MATCH\n");
255 }
256 */
257 break; 227 break;
258 } 228 } // }}} if condition after a header
259 } 229 } // switch
260 230
261 // verbose("> end is at %p, matchlen is %ld\n", end, matchlen); 231 if(NULL == end)
262 start = end + matchlen; 232 break;
263 // fwrite(start, 42, 1, stdout); 233 else
264 // printf(" <[first 42 bytes of new start; matchlen was %ld; searching for %s\n", matchlen, search); 234 start = end + matchlen;
265 } 235 }
266 printf("last known position: %p / %p with %s\n", start, end, search);
267 236
268 if(http_header.boundary) 237 DEBUG("> sending answer...");
269 verbose("http-boundary: %s", http_header.boundary); 238 send_answer(new_socket, &http_header);
239 DEBUG("> answer sent.");
270 240
271 verbose("> sending answer...");
272 send_answer(f_r);
273 fclose(f_r); 241 fclose(f_r);
274 242
275 verbose("> answer sent."); 243 fclose(output);
244 free(output_buffer);
276 245
277 return NULL; 246 return NULL;
278} 247}
279 248
280
281
282int serve(int server_fd) 249int serve(int server_fd)
283{ 250{
284 struct sockaddr_in address; 251 struct sockaddr_in address;
285 socklen_t address_len = sizeof(address); 252 socklen_t address_len = sizeof(address);
286 verbose("waiting for connections on server file descriptor %d", server_fd); 253 DEBUG("waiting for connections on server file descriptor %d", server_fd);
287 254
288 size_t new_socket = -1; 255 size_t new_socket = -1;
289 while(-1 != (new_socket = accept(server_fd, (struct sockaddr*) &address, &address_len))) 256 while(-1 != (new_socket = accept(server_fd, (struct sockaddr*) &address, &address_len)))
290 { 257 {
291 verbose("> Client %ld is connected via port %d", new_socket, address.sin_port); 258 DEBUG("> Client %ld is connected via port %d", new_socket, address.sin_port);
292 259
293 // set non blocking mode... 260 // set non blocking mode...
294 fcntl( 261 fcntl(
@@ -299,23 +266,13 @@ int serve(int server_fd)
299 266
300 next_customer(new_socket); 267 next_customer(new_socket);
301 268
302 /* 269#ifdef VALGRIND
303 if(fork()) { 270 break; // only run once, so that valgrind can test allocations&frees
304 close(new_socket); 271#endif
305 } else {
306 close(server_fd); // give the server free
307 next_customer(new_socket);
308 shutdown(new_socket, SHUT_RDWR);
309 exit(0);
310 }
311 */
312
313 // pthread_t thread_id;
314 // pthread_create(&thread_id, NULL, next_customer, (void*) new_socket);
315 // pthread_join(thread_id, NULL);
316 } 272 }
317 273
318 err(errno, "error serving"); 274 err(errno, "error serving");
275 close(server_fd);
319} 276}
320 277
321#define PORT 8080 278#define PORT 8080
@@ -341,163 +298,7 @@ int main(int argc, char const *argv[])
341 : serve(server_fd) 298 : serve(server_fd)
342 ? err(errno, NULL) 299 ? err(errno, NULL)
343 : exit(EXIT_SUCCESS) 300 : exit(EXIT_SUCCESS)
344 ; 301 ; return EXIT_FAILURE;
345 return EXIT_FAILURE;
346} 302}
347 303
348// void * next_customer_old(int new_socket)
349// {
350// char *hello = "Hello from server";
351// char buffer[1024] = {0};
352// int valread = read(new_socket, buffer, 1024);
353// printf("%s %d\n", buffer, valread);
354//
355// send(new_socket, hello, strlen(hello), 0);
356//
357// printf("Hello message sent\n");
358// return NULL;
359// }
360
361// IDEA ...
362//
363// #define FOREACH_HEADER(HEADER) \
364// HEADER("Content-Type: multipart/form-data; boundary=")
365// #define GENERATE_HEADERS(H) sizeof(H), H
366//
367// typedef struct {
368// const int length;
369// const char * string;
370// } length_value;
371//
372// const length_value headers[] = {
373// FOREACH_HEADER(GENERATE_HEADERS)
374// };
375//
376
377
378// READ WITH recv ...
379 /*
380 for(size_t size = -1 ; ; (size = recv(new_socket, read_buffer, read_buffer_length, 0))) {
381 if(-1 == size) {
382 usleep(10000);
383 printf("size: %ld\n", size);
384 if(EWOULDBLOCK == errno){
385 continue;
386 } else if (size == 0 || size < read_buffer_length) {
387 break;
388 }
389 } else {
390 read_buffer[size] = '\0';
391
392 fwrite(read_buffer, size, 1, output);
393 fwrite(read_buffer, size, 1, stdout);
394
395 // if(output_buffer_length > 4
396 // && (strspn(&output_buffer[output_buffer_length - 2], "\n") == 2
397 // || strspn(&output_buffer[output_buffer_length - 4], "\r\n") == 4))
398 // break;
399 }
400 }*/
401
402
403// fully working implementation...
404/*
405void * next_customer(void * new_socket) {
406
407 const int MAX_HEADER_LINE_LENGTH = 1024;
408 const int MAX_BOUNDARY_LENGTH = 64;
409
410 char boundary[64] = {0};
411 int boundary_size = 0;
412
413 char current_name[64] = {0};
414
415 usleep(100); // wait to avoid poll, epoll or evil select
416
417 char * output_buffer = NULL;
418 size_t output_buffer_length = 0;
419 FILE * output = open_memstream(&output_buffer, &output_buffer_length);
420
421 FILE * f = fdopen((size_t) new_socket, "r");
422 char buffer[MAX_HEADER_LINE_LENGTH];
423 char * s = NULL;
424 while(1) {
425 // printf("[errno: %d] %s", errno, buffer);
426 s = fgets(buffer, MAX_HEADER_LINE_LENGTH, f);
427
428 if(boundary[0] != '\0' && buffer[0] == '-') { // first char minus. could be a new chunk of post data
429 int i = 0;
430 for(i = 1; i < MAX_HEADER_LINE_LENGTH; i++) {
431 if(buffer[i] != '-') { // skip all minus
432 if(0 == strncmp(&buffer[i], boundary, boundary_size)) {
433 fflush(output);
434 printf("> name='%s' value='%*s'\n", current_name, (int) output_buffer_length, output_buffer);
435 setenv(current_name, output_buffer, 0);
436 current_name[0] = '\0';
437 rewind(output);
438 i = 0;
439
440 puts("================================================================================");
441 }
442 break;
443 }
444 }
445 if (i == 0) continue;
446 }
447
448
449 if(!s || strspn(buffer, "\n\r") > 0) { // either LF or CR/LF (windows)
450 puts("END HEADER\n");
451 // s != NULL ? break : continue;
452 if(!s) break; else continue; // either eof or only end of (chunk-)header
453 }
454
455 const char header_multipart[] = "Content-Type: multipart/form-data; boundary=";
456 if (!boundary[0] && 0 == strncasecmp(buffer, header_multipart, sizeof(header_multipart) - 1)) {
457 // we could potentially optimize this part with strspn and moving the buffer point, why not?
458 for(int i=sizeof(header_multipart); i<MAX_HEADER_LINE_LENGTH; i++)
459 if(buffer[i] != '-') {
460 strncpy(boundary, &buffer[i], 64);
461 boundary_size = strnlen(boundary, 64);
462 boundary[boundary_size] = '\0';
463 printf("> [%s] %s\n", header_multipart, boundary);
464 break;
465 }
466 continue;
467 }
468
469 const char header_disposition[] = "Content-Disposition: form-data; ";
470 if (boundary[0] && 0 == strncasecmp(header_disposition, buffer, sizeof(header_disposition) - 1)) {
471 printf("> %s :: %s\n", header_disposition, buffer);
472 const char header_disposition_name[] = "name=\"";
473 s = strstr(&buffer[sizeof(header_disposition) - 1], header_disposition_name);
474 if(s) {
475 s += sizeof(header_disposition_name) - 1;
476 s[strcspn(s, "\"")] = '\0';
477 strncpy(current_name, s, 64);
478 printf("> /%s.*%s/ :: \"%s\"\n", header_disposition, header_disposition_name, s);
479 } else {
480 warnx("string not found in \"%s\".", &buffer[sizeof(header_disposition)]);
481 }
482 continue;
483 }// else { printf("FUCK: %*s [%d/%ld]\n", (int) sizeof(header_disposition), buffer, strncasecmp(header_disposition, buffer, sizeof(header_disposition)), sizeof(header_disposition)); }
484
485 printf("> [unknown] %s", buffer);
486 fputs(buffer, output);
487
488
489 // I think another if could now search for "Content-Disposition: form-data; " and store the remaining fields
490 // somewhere, e.g. name="okay"; filename="1.gif". Also there may also be a content-type per chunk, which we
491 // could also add and erase, where I have currently NEXT CHUNK.
492 // After that the text output (printf("%s", buffer)) will only contain content without headers, so that printf
493 // can then be replaced with some fwrite to a memstream and we are done :)
494 }
495
496 puts("> sending answer...");
497 send_answer(new_socket);
498 puts("> answer sent.");
499
500 return NULL;
501}
502*/
503// vim: shiftwidth=2 tabstop=2 number foldmethod=marker 304// vim: shiftwidth=2 tabstop=2 number foldmethod=marker
diff --git a/test.sh b/test.sh
new file mode 100644
index 0000000..6cfe39c
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,3 @@
1#!/bin/bash
2
3(echo -e "POST / HTTP/1.1\nContent-Type: text/html; boundary=--1234\n\n--1234\nContent-Disposition: fooo\n\n" ; dd if=/dev/urandom) | nc localhost 8080
..