diff options
Diffstat (limited to 'src/examples/demo.c')
-rw-r--r-- | src/examples/demo.c | 748 |
1 files changed, 381 insertions, 367 deletions
diff --git a/src/examples/demo.c b/src/examples/demo.c index e93fb34b..375f2a63 100644 --- a/src/examples/demo.c +++ b/src/examples/demo.c | |||
@@ -41,10 +41,10 @@ | |||
41 | #include <limits.h> | 41 | #include <limits.h> |
42 | #include <ctype.h> | 42 | #include <ctype.h> |
43 | 43 | ||
44 | #if defined(CPU_COUNT) && (CPU_COUNT+0) < 2 | 44 | #if defined(CPU_COUNT) && (CPU_COUNT + 0) < 2 |
45 | #undef CPU_COUNT | 45 | #undef CPU_COUNT |
46 | #endif | 46 | #endif |
47 | #if !defined(CPU_COUNT) | 47 | #if ! defined(CPU_COUNT) |
48 | #define CPU_COUNT 2 | 48 | #define CPU_COUNT 2 |
49 | #endif | 49 | #endif |
50 | 50 | ||
@@ -73,46 +73,50 @@ | |||
73 | /** | 73 | /** |
74 | * Page returned for file-not-found. | 74 | * Page returned for file-not-found. |
75 | */ | 75 | */ |
76 | #define FILE_NOT_FOUND_PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>" | 76 | #define FILE_NOT_FOUND_PAGE \ |
77 | "<html><head><title>File not found</title></head><body>File not found</body></html>" | ||
77 | 78 | ||
78 | 79 | ||
79 | /** | 80 | /** |
80 | * Page returned for internal errors. | 81 | * Page returned for internal errors. |
81 | */ | 82 | */ |
82 | #define INTERNAL_ERROR_PAGE "<html><head><title>Internal error</title></head><body>Internal error</body></html>" | 83 | #define INTERNAL_ERROR_PAGE \ |
84 | "<html><head><title>Internal error</title></head><body>Internal error</body></html>" | ||
83 | 85 | ||
84 | 86 | ||
85 | /** | 87 | /** |
86 | * Page returned for refused requests. | 88 | * Page returned for refused requests. |
87 | */ | 89 | */ |
88 | #define REQUEST_REFUSED_PAGE "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>" | 90 | #define REQUEST_REFUSED_PAGE \ |
91 | "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>" | ||
89 | 92 | ||
90 | 93 | ||
91 | /** | 94 | /** |
92 | * Head of index page. | 95 | * Head of index page. |
93 | */ | 96 | */ |
94 | #define INDEX_PAGE_HEADER "<html>\n<head><title>Welcome</title></head>\n<body>\n"\ | 97 | #define INDEX_PAGE_HEADER \ |
95 | "<h1>Upload</h1>\n"\ | 98 | "<html>\n<head><title>Welcome</title></head>\n<body>\n" \ |
96 | "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n"\ | 99 | "<h1>Upload</h1>\n" \ |
97 | "<dl><dt>Content type:</dt><dd>"\ | 100 | "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n" \ |
98 | "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>"\ | 101 | "<dl><dt>Content type:</dt><dd>" \ |
99 | "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>"\ | 102 | "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>" \ |
100 | "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>"\ | 103 | "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>" \ |
101 | "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>"\ | 104 | "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>" \ |
102 | "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n"\ | 105 | "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>" \ |
103 | "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>"\ | 106 | "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n" \ |
104 | "<dt>Language:</dt><dd>"\ | 107 | "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>" \ |
105 | "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>"\ | 108 | "<dt>Language:</dt><dd>" \ |
106 | "<input type=\"radio\" name=\"language\" value=\"en\">English</input>"\ | 109 | "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>" \ |
107 | "<input type=\"radio\" name=\"language\" value=\"de\">German</input>"\ | 110 | "<input type=\"radio\" name=\"language\" value=\"en\">English</input>" \ |
108 | "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>"\ | 111 | "<input type=\"radio\" name=\"language\" value=\"de\">German</input>" \ |
109 | "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n"\ | 112 | "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>" \ |
110 | "<dt>File:</dt><dd>"\ | 113 | "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n" \ |
111 | "<input type=\"file\" name=\"upload\"/></dd></dl>"\ | 114 | "<dt>File:</dt><dd>" \ |
112 | "<input type=\"submit\" value=\"Send!\"/>\n"\ | 115 | "<input type=\"file\" name=\"upload\"/></dd></dl>" \ |
113 | "</form>\n"\ | 116 | "<input type=\"submit\" value=\"Send!\"/>\n" \ |
114 | "<h1>Download</h1>\n"\ | 117 | "</form>\n" \ |
115 | "<ol>\n" | 118 | "<h1>Download</h1>\n" \ |
119 | "<ol>\n" | ||
116 | 120 | ||
117 | /** | 121 | /** |
118 | * Footer of index page. | 122 | * Footer of index page. |
@@ -124,16 +128,15 @@ | |||
124 | * NULL-terminated array of supported upload categories. Should match HTML | 128 | * NULL-terminated array of supported upload categories. Should match HTML |
125 | * in the form. | 129 | * in the form. |
126 | */ | 130 | */ |
127 | static const char * const categories[] = | 131 | static const char *const categories[] = { |
128 | { | 132 | "books", |
129 | "books", | 133 | "images", |
130 | "images", | 134 | "music", |
131 | "music", | 135 | "software", |
132 | "software", | 136 | "videos", |
133 | "videos", | 137 | "other", |
134 | "other", | 138 | NULL, |
135 | NULL, | 139 | }; |
136 | }; | ||
137 | 140 | ||
138 | 141 | ||
139 | /** | 142 | /** |
@@ -157,15 +160,14 @@ struct Language | |||
157 | * NULL-terminated array of supported upload categories. Should match HTML | 160 | * NULL-terminated array of supported upload categories. Should match HTML |
158 | * in the form. | 161 | * in the form. |
159 | */ | 162 | */ |
160 | static const struct Language languages[] = | 163 | static const struct Language languages[] = { |
161 | { | 164 | { "no-lang", "No language specified" }, |
162 | { "no-lang", "No language specified" }, | 165 | { "en", "English" }, |
163 | { "en", "English" }, | 166 | { "de", "German" }, |
164 | { "de", "German" }, | 167 | { "fr", "French" }, |
165 | { "fr", "French" }, | 168 | { "es", "Spanish" }, |
166 | { "es", "Spanish" }, | 169 | { NULL, NULL }, |
167 | { NULL, NULL }, | 170 | }; |
168 | }; | ||
169 | 171 | ||
170 | 172 | ||
171 | /** | 173 | /** |
@@ -210,8 +212,8 @@ static void | |||
210 | mark_as_html (struct MHD_Response *response) | 212 | mark_as_html (struct MHD_Response *response) |
211 | { | 213 | { |
212 | (void) MHD_add_response_header (response, | 214 | (void) MHD_add_response_header (response, |
213 | MHD_HTTP_HEADER_CONTENT_TYPE, | 215 | MHD_HTTP_HEADER_CONTENT_TYPE, |
214 | "text/html"); | 216 | "text/html"); |
215 | } | 217 | } |
216 | 218 | ||
217 | 219 | ||
@@ -264,7 +266,7 @@ struct ResponseDataContext | |||
264 | */ | 266 | */ |
265 | static int | 267 | static int |
266 | list_directory (struct ResponseDataContext *rdc, | 268 | list_directory (struct ResponseDataContext *rdc, |
267 | const char *dirname) | 269 | const char *dirname) |
268 | { | 270 | { |
269 | char fullname[PATH_MAX]; | 271 | char fullname[PATH_MAX]; |
270 | struct stat sbuf; | 272 | struct stat sbuf; |
@@ -274,35 +276,35 @@ list_directory (struct ResponseDataContext *rdc, | |||
274 | if (NULL == (dir = opendir (dirname))) | 276 | if (NULL == (dir = opendir (dirname))) |
275 | return MHD_NO; | 277 | return MHD_NO; |
276 | while (NULL != (de = readdir (dir))) | 278 | while (NULL != (de = readdir (dir))) |
279 | { | ||
280 | if ('.' == de->d_name[0]) | ||
281 | continue; | ||
282 | if (sizeof (fullname) <= (unsigned int) | ||
283 | snprintf (fullname, sizeof (fullname), | ||
284 | "%s/%s", | ||
285 | dirname, de->d_name)) | ||
286 | continue; /* ugh, file too long? how can this be!? */ | ||
287 | if (0 != stat (fullname, &sbuf)) | ||
288 | continue; /* ugh, failed to 'stat' */ | ||
289 | if (! S_ISREG (sbuf.st_mode)) | ||
290 | continue; /* not a regular file, skip */ | ||
291 | if (rdc->off + 1024 > rdc->buf_len) | ||
277 | { | 292 | { |
278 | if ('.' == de->d_name[0]) | 293 | void *r; |
279 | continue; | 294 | |
280 | if (sizeof (fullname) <= (unsigned int) | 295 | if ( (2 * rdc->buf_len + 1024) < rdc->buf_len) |
281 | snprintf (fullname, sizeof (fullname), | 296 | break; /* more than SIZE_T _index_ size? Too big for us */ |
282 | "%s/%s", | 297 | rdc->buf_len = 2 * rdc->buf_len + 1024; |
283 | dirname, de->d_name)) | 298 | if (NULL == (r = realloc (rdc->buf, rdc->buf_len))) |
284 | continue; /* ugh, file too long? how can this be!? */ | 299 | break; /* out of memory */ |
285 | if (0 != stat (fullname, &sbuf)) | 300 | rdc->buf = r; |
286 | continue; /* ugh, failed to 'stat' */ | ||
287 | if (! S_ISREG (sbuf.st_mode)) | ||
288 | continue; /* not a regular file, skip */ | ||
289 | if (rdc->off + 1024 > rdc->buf_len) | ||
290 | { | ||
291 | void *r; | ||
292 | |||
293 | if ( (2 * rdc->buf_len + 1024) < rdc->buf_len) | ||
294 | break; /* more than SIZE_T _index_ size? Too big for us */ | ||
295 | rdc->buf_len = 2 * rdc->buf_len + 1024; | ||
296 | if (NULL == (r = realloc (rdc->buf, rdc->buf_len))) | ||
297 | break; /* out of memory */ | ||
298 | rdc->buf = r; | ||
299 | } | ||
300 | rdc->off += snprintf (&rdc->buf[rdc->off], | ||
301 | rdc->buf_len - rdc->off, | ||
302 | "<li><a href=\"/%s\">%s</a></li>\n", | ||
303 | fullname, | ||
304 | de->d_name); | ||
305 | } | 301 | } |
302 | rdc->off += snprintf (&rdc->buf[rdc->off], | ||
303 | rdc->buf_len - rdc->off, | ||
304 | "<li><a href=\"/%s\">%s</a></li>\n", | ||
305 | fullname, | ||
306 | de->d_name); | ||
307 | } | ||
306 | (void) closedir (dir); | 308 | (void) closedir (dir); |
307 | return MHD_YES; | 309 | return MHD_YES; |
308 | } | 310 | } |
@@ -326,65 +328,66 @@ update_directory () | |||
326 | 328 | ||
327 | rdc.buf_len = initial_allocation; | 329 | rdc.buf_len = initial_allocation; |
328 | if (NULL == (rdc.buf = malloc (rdc.buf_len))) | 330 | if (NULL == (rdc.buf = malloc (rdc.buf_len))) |
329 | { | 331 | { |
330 | update_cached_response (NULL); | 332 | update_cached_response (NULL); |
331 | return; | 333 | return; |
332 | } | 334 | } |
333 | rdc.off = snprintf (rdc.buf, rdc.buf_len, | 335 | rdc.off = snprintf (rdc.buf, rdc.buf_len, |
334 | "%s", | 336 | "%s", |
335 | INDEX_PAGE_HEADER); | 337 | INDEX_PAGE_HEADER); |
336 | for (language_idx = 0; NULL != languages[language_idx].dirname; language_idx++) | 338 | for (language_idx = 0; NULL != languages[language_idx].dirname; |
339 | language_idx++) | ||
340 | { | ||
341 | language = &languages[language_idx]; | ||
342 | |||
343 | if (0 != stat (language->dirname, &sbuf)) | ||
344 | continue; /* empty */ | ||
345 | /* we ensured always +1k room, filenames are ~256 bytes, | ||
346 | so there is always still enough space for the header | ||
347 | without need for an additional reallocation check. */ | ||
348 | rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, | ||
349 | "<h2>%s</h2>\n", | ||
350 | language->longname); | ||
351 | for (category_idx = 0; NULL != categories[category_idx]; category_idx++) | ||
337 | { | 352 | { |
338 | language = &languages[language_idx]; | 353 | category = categories[category_idx]; |
354 | snprintf (dir_name, sizeof (dir_name), | ||
355 | "%s/%s", | ||
356 | language->dirname, | ||
357 | category); | ||
358 | if (0 != stat (dir_name, &sbuf)) | ||
359 | continue; /* empty */ | ||
339 | 360 | ||
340 | if (0 != stat (language->dirname, &sbuf)) | ||
341 | continue; /* empty */ | ||
342 | /* we ensured always +1k room, filenames are ~256 bytes, | 361 | /* we ensured always +1k room, filenames are ~256 bytes, |
343 | so there is always still enough space for the header | 362 | so there is always still enough space for the header |
344 | without need for an additional reallocation check. */ | 363 | without need for an additional reallocation check. */ |
345 | rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, | 364 | rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, |
346 | "<h2>%s</h2>\n", | 365 | "<h3>%s</h3>\n", |
347 | language->longname); | 366 | category); |
348 | for (category_idx = 0; NULL != categories[category_idx]; category_idx++) | 367 | |
349 | { | 368 | if (MHD_NO == list_directory (&rdc, dir_name)) |
350 | category = categories[category_idx]; | 369 | { |
351 | snprintf (dir_name, sizeof (dir_name), | 370 | free (rdc.buf); |
352 | "%s/%s", | 371 | update_cached_response (NULL); |
353 | language->dirname, | 372 | return; |
354 | category); | 373 | } |
355 | if (0 != stat (dir_name, &sbuf)) | ||
356 | continue; /* empty */ | ||
357 | |||
358 | /* we ensured always +1k room, filenames are ~256 bytes, | ||
359 | so there is always still enough space for the header | ||
360 | without need for an additional reallocation check. */ | ||
361 | rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, | ||
362 | "<h3>%s</h3>\n", | ||
363 | category); | ||
364 | |||
365 | if (MHD_NO == list_directory (&rdc, dir_name)) | ||
366 | { | ||
367 | free (rdc.buf); | ||
368 | update_cached_response (NULL); | ||
369 | return; | ||
370 | } | ||
371 | } | ||
372 | } | 374 | } |
375 | } | ||
373 | /* we ensured always +1k room, filenames are ~256 bytes, | 376 | /* we ensured always +1k room, filenames are ~256 bytes, |
374 | so there is always still enough space for the footer | 377 | so there is always still enough space for the footer |
375 | without need for a final reallocation check. */ | 378 | without need for a final reallocation check. */ |
376 | rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, | 379 | rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, |
377 | "%s", | 380 | "%s", |
378 | INDEX_PAGE_FOOTER); | 381 | INDEX_PAGE_FOOTER); |
379 | initial_allocation = rdc.buf_len; /* remember for next time */ | 382 | initial_allocation = rdc.buf_len; /* remember for next time */ |
380 | response = MHD_create_response_from_buffer (rdc.off, | 383 | response = MHD_create_response_from_buffer (rdc.off, |
381 | rdc.buf, | 384 | rdc.buf, |
382 | MHD_RESPMEM_MUST_FREE); | 385 | MHD_RESPMEM_MUST_FREE); |
383 | mark_as_html (response); | 386 | mark_as_html (response); |
384 | #if FORCE_CLOSE | 387 | #if FORCE_CLOSE |
385 | (void) MHD_add_response_header (response, | 388 | (void) MHD_add_response_header (response, |
386 | MHD_HTTP_HEADER_CONNECTION, | 389 | MHD_HTTP_HEADER_CONNECTION, |
387 | "close"); | 390 | "close"); |
388 | #endif | 391 | #endif |
389 | update_cached_response (response); | 392 | update_cached_response (response); |
390 | } | 393 | } |
@@ -443,8 +446,8 @@ struct UploadContext | |||
443 | */ | 446 | */ |
444 | static int | 447 | static int |
445 | do_append (char **ret, | 448 | do_append (char **ret, |
446 | const char *data, | 449 | const char *data, |
447 | size_t size) | 450 | size_t size) |
448 | { | 451 | { |
449 | char *buf; | 452 | char *buf; |
450 | size_t old_len; | 453 | size_t old_len; |
@@ -456,15 +459,15 @@ do_append (char **ret, | |||
456 | if (NULL == (buf = malloc (old_len + size + 1))) | 459 | if (NULL == (buf = malloc (old_len + size + 1))) |
457 | return MHD_NO; | 460 | return MHD_NO; |
458 | if (NULL != *ret) | 461 | if (NULL != *ret) |
459 | { | 462 | { |
460 | memcpy (buf, | 463 | memcpy (buf, |
461 | *ret, | 464 | *ret, |
462 | old_len); | 465 | old_len); |
463 | free (*ret); | 466 | free (*ret); |
464 | } | 467 | } |
465 | memcpy (&buf[old_len], | 468 | memcpy (&buf[old_len], |
466 | data, | 469 | data, |
467 | size); | 470 | size); |
468 | buf[old_len + size] = '\0'; | 471 | buf[old_len + size] = '\0'; |
469 | *ret = buf; | 472 | *ret = buf; |
470 | return MHD_YES; | 473 | return MHD_YES; |
@@ -492,119 +495,119 @@ do_append (char **ret, | |||
492 | */ | 495 | */ |
493 | static int | 496 | static int |
494 | process_upload_data (void *cls, | 497 | process_upload_data (void *cls, |
495 | enum MHD_ValueKind kind, | 498 | enum MHD_ValueKind kind, |
496 | const char *key, | 499 | const char *key, |
497 | const char *filename, | 500 | const char *filename, |
498 | const char *content_type, | 501 | const char *content_type, |
499 | const char *transfer_encoding, | 502 | const char *transfer_encoding, |
500 | const char *data, | 503 | const char *data, |
501 | uint64_t off, | 504 | uint64_t off, |
502 | size_t size) | 505 | size_t size) |
503 | { | 506 | { |
504 | struct UploadContext *uc = cls; | 507 | struct UploadContext *uc = cls; |
505 | int i; | 508 | int i; |
506 | (void)kind; /* Unused. Silent compiler warning. */ | 509 | (void) kind; /* Unused. Silent compiler warning. */ |
507 | (void)content_type; /* Unused. Silent compiler warning. */ | 510 | (void) content_type; /* Unused. Silent compiler warning. */ |
508 | (void)transfer_encoding; /* Unused. Silent compiler warning. */ | 511 | (void) transfer_encoding; /* Unused. Silent compiler warning. */ |
509 | (void)off; /* Unused. Silent compiler warning. */ | 512 | (void) off; /* Unused. Silent compiler warning. */ |
510 | 513 | ||
511 | if (0 == strcmp (key, "category")) | 514 | if (0 == strcmp (key, "category")) |
512 | return do_append (&uc->category, data, size); | 515 | return do_append (&uc->category, data, size); |
513 | if (0 == strcmp (key, "language")) | 516 | if (0 == strcmp (key, "language")) |
514 | return do_append (&uc->language, data, size); | 517 | return do_append (&uc->language, data, size); |
515 | if (0 != strcmp (key, "upload")) | 518 | if (0 != strcmp (key, "upload")) |
516 | { | 519 | { |
517 | fprintf (stderr, | 520 | fprintf (stderr, |
518 | "Ignoring unexpected form value `%s'\n", | 521 | "Ignoring unexpected form value `%s'\n", |
519 | key); | 522 | key); |
520 | return MHD_YES; /* ignore */ | 523 | return MHD_YES; /* ignore */ |
521 | } | 524 | } |
522 | if (NULL == filename) | 525 | if (NULL == filename) |
523 | { | 526 | { |
524 | fprintf (stderr, "No filename, aborting upload\n"); | 527 | fprintf (stderr, "No filename, aborting upload\n"); |
525 | return MHD_NO; /* no filename, error */ | 528 | return MHD_NO; /* no filename, error */ |
526 | } | 529 | } |
527 | if ( (NULL == uc->category) || | 530 | if ( (NULL == uc->category) || |
528 | (NULL == uc->language) ) | 531 | (NULL == uc->language) ) |
532 | { | ||
533 | fprintf (stderr, | ||
534 | "Missing form data for upload `%s'\n", | ||
535 | filename); | ||
536 | uc->response = request_refused_response; | ||
537 | return MHD_NO; | ||
538 | } | ||
539 | if (-1 == uc->fd) | ||
540 | { | ||
541 | char fn[PATH_MAX]; | ||
542 | |||
543 | if ( (NULL != strstr (filename, "..")) || | ||
544 | (NULL != strchr (filename, '/')) || | ||
545 | (NULL != strchr (filename, '\\')) ) | ||
529 | { | 546 | { |
530 | fprintf (stderr, | ||
531 | "Missing form data for upload `%s'\n", | ||
532 | filename); | ||
533 | uc->response = request_refused_response; | 547 | uc->response = request_refused_response; |
534 | return MHD_NO; | 548 | return MHD_NO; |
535 | } | 549 | } |
536 | if (-1 == uc->fd) | 550 | /* create directories -- if they don't exist already */ |
537 | { | ||
538 | char fn[PATH_MAX]; | ||
539 | |||
540 | if ( (NULL != strstr (filename, "..")) || | ||
541 | (NULL != strchr (filename, '/')) || | ||
542 | (NULL != strchr (filename, '\\')) ) | ||
543 | { | ||
544 | uc->response = request_refused_response; | ||
545 | return MHD_NO; | ||
546 | } | ||
547 | /* create directories -- if they don't exist already */ | ||
548 | #ifdef WINDOWS | 551 | #ifdef WINDOWS |
549 | (void) mkdir (uc->language); | 552 | (void) mkdir (uc->language); |
550 | #else | 553 | #else |
551 | (void) mkdir (uc->language, S_IRWXU); | 554 | (void) mkdir (uc->language, S_IRWXU); |
552 | #endif | 555 | #endif |
553 | snprintf (fn, sizeof (fn), | 556 | snprintf (fn, sizeof (fn), |
554 | "%s/%s", | 557 | "%s/%s", |
555 | uc->language, | 558 | uc->language, |
556 | uc->category); | 559 | uc->category); |
557 | #ifdef WINDOWS | 560 | #ifdef WINDOWS |
558 | (void) mkdir (fn); | 561 | (void) mkdir (fn); |
559 | #else | 562 | #else |
560 | (void) mkdir (fn, S_IRWXU); | 563 | (void) mkdir (fn, S_IRWXU); |
561 | #endif | 564 | #endif |
562 | /* open file */ | 565 | /* open file */ |
563 | snprintf (fn, sizeof (fn), | 566 | snprintf (fn, sizeof (fn), |
564 | "%s/%s/%s", | 567 | "%s/%s/%s", |
565 | uc->language, | 568 | uc->language, |
566 | uc->category, | 569 | uc->category, |
567 | filename); | 570 | filename); |
568 | for (i=strlen (fn)-1;i>=0;i--) | 571 | for (i = strlen (fn) - 1; i>=0; i--) |
569 | if (! isprint ((unsigned char) fn[i])) | 572 | if (! isprint ((unsigned char) fn[i])) |
570 | fn[i] = '_'; | 573 | fn[i] = '_'; |
571 | uc->fd = open (fn, | 574 | uc->fd = open (fn, |
572 | O_CREAT | O_EXCL | 575 | O_CREAT | O_EXCL |
573 | #if O_LARGEFILE | 576 | #if O_LARGEFILE |
574 | | O_LARGEFILE | 577 | | O_LARGEFILE |
575 | #endif | 578 | #endif |
576 | | O_WRONLY, | 579 | | O_WRONLY, |
577 | S_IRUSR | S_IWUSR); | 580 | S_IRUSR | S_IWUSR); |
578 | if (-1 == uc->fd) | 581 | if (-1 == uc->fd) |
579 | { | 582 | { |
580 | fprintf (stderr, | 583 | fprintf (stderr, |
581 | "Error opening file `%s' for upload: %s\n", | 584 | "Error opening file `%s' for upload: %s\n", |
582 | fn, | 585 | fn, |
583 | strerror (errno)); | 586 | strerror (errno)); |
584 | uc->response = request_refused_response; | 587 | uc->response = request_refused_response; |
585 | return MHD_NO; | 588 | return MHD_NO; |
586 | } | ||
587 | uc->filename = strdup (fn); | ||
588 | } | 589 | } |
590 | uc->filename = strdup (fn); | ||
591 | } | ||
589 | if ( (0 != size) && | 592 | if ( (0 != size) && |
590 | (size != (size_t) write (uc->fd, data, size)) ) | 593 | (size != (size_t) write (uc->fd, data, size)) ) |
594 | { | ||
595 | /* write failed; likely: disk full */ | ||
596 | fprintf (stderr, | ||
597 | "Error writing to file `%s': %s\n", | ||
598 | uc->filename, | ||
599 | strerror (errno)); | ||
600 | uc->response = internal_error_response; | ||
601 | (void) close (uc->fd); | ||
602 | uc->fd = -1; | ||
603 | if (NULL != uc->filename) | ||
591 | { | 604 | { |
592 | /* write failed; likely: disk full */ | 605 | unlink (uc->filename); |
593 | fprintf (stderr, | 606 | free (uc->filename); |
594 | "Error writing to file `%s': %s\n", | 607 | uc->filename = NULL; |
595 | uc->filename, | ||
596 | strerror (errno)); | ||
597 | uc->response = internal_error_response; | ||
598 | (void) close (uc->fd); | ||
599 | uc->fd = -1; | ||
600 | if (NULL != uc->filename) | ||
601 | { | ||
602 | unlink (uc->filename); | ||
603 | free (uc->filename); | ||
604 | uc->filename = NULL; | ||
605 | } | ||
606 | return MHD_NO; | ||
607 | } | 608 | } |
609 | return MHD_NO; | ||
610 | } | ||
608 | return MHD_YES; | 611 | return MHD_YES; |
609 | } | 612 | } |
610 | 613 | ||
@@ -622,32 +625,32 @@ process_upload_data (void *cls, | |||
622 | */ | 625 | */ |
623 | static void | 626 | static void |
624 | response_completed_callback (void *cls, | 627 | response_completed_callback (void *cls, |
625 | struct MHD_Connection *connection, | 628 | struct MHD_Connection *connection, |
626 | void **con_cls, | 629 | void **con_cls, |
627 | enum MHD_RequestTerminationCode toe) | 630 | enum MHD_RequestTerminationCode toe) |
628 | { | 631 | { |
629 | struct UploadContext *uc = *con_cls; | 632 | struct UploadContext *uc = *con_cls; |
630 | (void)cls; /* Unused. Silent compiler warning. */ | 633 | (void) cls; /* Unused. Silent compiler warning. */ |
631 | (void)connection; /* Unused. Silent compiler warning. */ | 634 | (void) connection; /* Unused. Silent compiler warning. */ |
632 | (void)toe; /* Unused. Silent compiler warning. */ | 635 | (void) toe; /* Unused. Silent compiler warning. */ |
633 | 636 | ||
634 | if (NULL == uc) | 637 | if (NULL == uc) |
635 | return; /* this request wasn't an upload request */ | 638 | return; /* this request wasn't an upload request */ |
636 | if (NULL != uc->pp) | 639 | if (NULL != uc->pp) |
637 | { | 640 | { |
638 | MHD_destroy_post_processor (uc->pp); | 641 | MHD_destroy_post_processor (uc->pp); |
639 | uc->pp = NULL; | 642 | uc->pp = NULL; |
640 | } | 643 | } |
641 | if (-1 != uc->fd) | 644 | if (-1 != uc->fd) |
642 | { | 645 | { |
643 | (void) close (uc->fd); | 646 | (void) close (uc->fd); |
644 | if (NULL != uc->filename) | 647 | if (NULL != uc->filename) |
645 | { | 648 | { |
646 | fprintf (stderr, | 649 | fprintf (stderr, |
647 | "Upload of file `%s' failed (incomplete or aborted), removing file.\n", | 650 | "Upload of file `%s' failed (incomplete or aborted), removing file.\n", |
648 | uc->filename); | 651 | uc->filename); |
649 | (void) unlink (uc->filename); | 652 | (void) unlink (uc->filename); |
650 | } | 653 | } |
651 | } | 654 | } |
652 | if (NULL != uc->filename) | 655 | if (NULL != uc->filename) |
653 | free (uc->filename); | 656 | free (uc->filename); |
@@ -669,12 +672,12 @@ return_directory_response (struct MHD_Connection *connection) | |||
669 | (void) pthread_mutex_lock (&mutex); | 672 | (void) pthread_mutex_lock (&mutex); |
670 | if (NULL == cached_directory_response) | 673 | if (NULL == cached_directory_response) |
671 | ret = MHD_queue_response (connection, | 674 | ret = MHD_queue_response (connection, |
672 | MHD_HTTP_INTERNAL_SERVER_ERROR, | 675 | MHD_HTTP_INTERNAL_SERVER_ERROR, |
673 | internal_error_response); | 676 | internal_error_response); |
674 | else | 677 | else |
675 | ret = MHD_queue_response (connection, | 678 | ret = MHD_queue_response (connection, |
676 | MHD_HTTP_OK, | 679 | MHD_HTTP_OK, |
677 | cached_directory_response); | 680 | cached_directory_response); |
678 | (void) pthread_mutex_unlock (&mutex); | 681 | (void) pthread_mutex_unlock (&mutex); |
679 | return ret; | 682 | return ret; |
680 | } | 683 | } |
@@ -695,12 +698,12 @@ return_directory_response (struct MHD_Connection *connection) | |||
695 | */ | 698 | */ |
696 | static int | 699 | static int |
697 | generate_page (void *cls, | 700 | generate_page (void *cls, |
698 | struct MHD_Connection *connection, | 701 | struct MHD_Connection *connection, |
699 | const char *url, | 702 | const char *url, |
700 | const char *method, | 703 | const char *method, |
701 | const char *version, | 704 | const char *version, |
702 | const char *upload_data, | 705 | const char *upload_data, |
703 | size_t *upload_data_size, void **ptr) | 706 | size_t *upload_data_size, void **ptr) |
704 | { | 707 | { |
705 | struct MHD_Response *response; | 708 | struct MHD_Response *response; |
706 | int ret; | 709 | int ret; |
@@ -710,117 +713,117 @@ generate_page (void *cls, | |||
710 | (void) version; /* Unused. Silent compiler warning. */ | 713 | (void) version; /* Unused. Silent compiler warning. */ |
711 | 714 | ||
712 | if (0 != strcmp (url, "/")) | 715 | if (0 != strcmp (url, "/")) |
713 | { | 716 | { |
714 | /* should be file download */ | 717 | /* should be file download */ |
715 | #ifdef MHD_HAVE_LIBMAGIC | 718 | #ifdef MHD_HAVE_LIBMAGIC |
716 | char file_data[MAGIC_HEADER_SIZE]; | 719 | char file_data[MAGIC_HEADER_SIZE]; |
717 | ssize_t got; | 720 | ssize_t got; |
718 | #endif /* MHD_HAVE_LIBMAGIC */ | 721 | #endif /* MHD_HAVE_LIBMAGIC */ |
719 | const char *mime; | 722 | const char *mime; |
720 | 723 | ||
721 | if ( (0 != strcmp (method, MHD_HTTP_METHOD_GET)) && | 724 | if ( (0 != strcmp (method, MHD_HTTP_METHOD_GET)) && |
722 | (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)) ) | 725 | (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)) ) |
723 | return MHD_NO; /* unexpected method (we're not polite...) */ | 726 | return MHD_NO; /* unexpected method (we're not polite...) */ |
724 | fd = -1; | 727 | fd = -1; |
725 | if ( (NULL == strstr (&url[1], "..")) && | 728 | if ( (NULL == strstr (&url[1], "..")) && |
726 | ('/' != url[1]) ) | 729 | ('/' != url[1]) ) |
727 | { | 730 | { |
728 | fd = open (&url[1], O_RDONLY); | 731 | fd = open (&url[1], O_RDONLY); |
729 | if ( (-1 != fd) && | 732 | if ( (-1 != fd) && |
730 | ( (0 != fstat (fd, &buf)) || | 733 | ( (0 != fstat (fd, &buf)) || |
731 | (! S_ISREG (buf.st_mode)) ) ) | 734 | (! S_ISREG (buf.st_mode)) ) ) |
732 | { | 735 | { |
733 | (void) close (fd); | 736 | (void) close (fd); |
734 | fd = -1; | 737 | fd = -1; |
735 | } | 738 | } |
736 | } | 739 | } |
737 | if (-1 == fd) | 740 | if (-1 == fd) |
738 | return MHD_queue_response (connection, | 741 | return MHD_queue_response (connection, |
739 | MHD_HTTP_NOT_FOUND, | 742 | MHD_HTTP_NOT_FOUND, |
740 | file_not_found_response); | 743 | file_not_found_response); |
741 | #ifdef MHD_HAVE_LIBMAGIC | 744 | #ifdef MHD_HAVE_LIBMAGIC |
742 | /* read beginning of the file to determine mime type */ | 745 | /* read beginning of the file to determine mime type */ |
743 | got = read (fd, file_data, sizeof (file_data)); | 746 | got = read (fd, file_data, sizeof (file_data)); |
744 | (void) lseek (fd, 0, SEEK_SET); | 747 | (void) lseek (fd, 0, SEEK_SET); |
745 | if (-1 != got) | 748 | if (-1 != got) |
746 | mime = magic_buffer (magic, file_data, got); | 749 | mime = magic_buffer (magic, file_data, got); |
747 | else | 750 | else |
748 | #endif /* MHD_HAVE_LIBMAGIC */ | 751 | #endif /* MHD_HAVE_LIBMAGIC */ |
749 | mime = NULL; | 752 | mime = NULL; |
750 | 753 | ||
751 | if (NULL == (response = MHD_create_response_from_fd (buf.st_size, | 754 | if (NULL == (response = MHD_create_response_from_fd (buf.st_size, |
752 | fd))) | 755 | fd))) |
753 | { | 756 | { |
754 | /* internal error (i.e. out of memory) */ | 757 | /* internal error (i.e. out of memory) */ |
755 | (void) close (fd); | 758 | (void) close (fd); |
756 | return MHD_NO; | 759 | return MHD_NO; |
757 | } | ||
758 | |||
759 | /* add mime type if we had one */ | ||
760 | if (NULL != mime) | ||
761 | (void) MHD_add_response_header (response, | ||
762 | MHD_HTTP_HEADER_CONTENT_TYPE, | ||
763 | mime); | ||
764 | ret = MHD_queue_response (connection, | ||
765 | MHD_HTTP_OK, | ||
766 | response); | ||
767 | MHD_destroy_response (response); | ||
768 | return ret; | ||
769 | } | 760 | } |
770 | 761 | ||
762 | /* add mime type if we had one */ | ||
763 | if (NULL != mime) | ||
764 | (void) MHD_add_response_header (response, | ||
765 | MHD_HTTP_HEADER_CONTENT_TYPE, | ||
766 | mime); | ||
767 | ret = MHD_queue_response (connection, | ||
768 | MHD_HTTP_OK, | ||
769 | response); | ||
770 | MHD_destroy_response (response); | ||
771 | return ret; | ||
772 | } | ||
773 | |||
771 | if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) | 774 | if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) |
775 | { | ||
776 | /* upload! */ | ||
777 | struct UploadContext *uc = *ptr; | ||
778 | |||
779 | if (NULL == uc) | ||
780 | { | ||
781 | if (NULL == (uc = malloc (sizeof (struct UploadContext)))) | ||
782 | return MHD_NO; /* out of memory, close connection */ | ||
783 | memset (uc, 0, sizeof (struct UploadContext)); | ||
784 | uc->fd = -1; | ||
785 | uc->connection = connection; | ||
786 | uc->pp = MHD_create_post_processor (connection, | ||
787 | 64 * 1024 /* buffer size */, | ||
788 | &process_upload_data, uc); | ||
789 | if (NULL == uc->pp) | ||
790 | { | ||
791 | /* out of memory, close connection */ | ||
792 | free (uc); | ||
793 | return MHD_NO; | ||
794 | } | ||
795 | *ptr = uc; | ||
796 | return MHD_YES; | ||
797 | } | ||
798 | if (0 != *upload_data_size) | ||
799 | { | ||
800 | if (NULL == uc->response) | ||
801 | (void) MHD_post_process (uc->pp, | ||
802 | upload_data, | ||
803 | *upload_data_size); | ||
804 | *upload_data_size = 0; | ||
805 | return MHD_YES; | ||
806 | } | ||
807 | /* end of upload, finish it! */ | ||
808 | MHD_destroy_post_processor (uc->pp); | ||
809 | uc->pp = NULL; | ||
810 | if (-1 != uc->fd) | ||
811 | { | ||
812 | close (uc->fd); | ||
813 | uc->fd = -1; | ||
814 | } | ||
815 | if (NULL != uc->response) | ||
816 | { | ||
817 | return MHD_queue_response (connection, | ||
818 | MHD_HTTP_FORBIDDEN, | ||
819 | uc->response); | ||
820 | } | ||
821 | else | ||
772 | { | 822 | { |
773 | /* upload! */ | 823 | update_directory (); |
774 | struct UploadContext *uc = *ptr; | 824 | return return_directory_response (connection); |
775 | |||
776 | if (NULL == uc) | ||
777 | { | ||
778 | if (NULL == (uc = malloc (sizeof (struct UploadContext)))) | ||
779 | return MHD_NO; /* out of memory, close connection */ | ||
780 | memset (uc, 0, sizeof (struct UploadContext)); | ||
781 | uc->fd = -1; | ||
782 | uc->connection = connection; | ||
783 | uc->pp = MHD_create_post_processor (connection, | ||
784 | 64 * 1024 /* buffer size */, | ||
785 | &process_upload_data, uc); | ||
786 | if (NULL == uc->pp) | ||
787 | { | ||
788 | /* out of memory, close connection */ | ||
789 | free (uc); | ||
790 | return MHD_NO; | ||
791 | } | ||
792 | *ptr = uc; | ||
793 | return MHD_YES; | ||
794 | } | ||
795 | if (0 != *upload_data_size) | ||
796 | { | ||
797 | if (NULL == uc->response) | ||
798 | (void) MHD_post_process (uc->pp, | ||
799 | upload_data, | ||
800 | *upload_data_size); | ||
801 | *upload_data_size = 0; | ||
802 | return MHD_YES; | ||
803 | } | ||
804 | /* end of upload, finish it! */ | ||
805 | MHD_destroy_post_processor (uc->pp); | ||
806 | uc->pp = NULL; | ||
807 | if (-1 != uc->fd) | ||
808 | { | ||
809 | close (uc->fd); | ||
810 | uc->fd = -1; | ||
811 | } | ||
812 | if (NULL != uc->response) | ||
813 | { | ||
814 | return MHD_queue_response (connection, | ||
815 | MHD_HTTP_FORBIDDEN, | ||
816 | uc->response); | ||
817 | } | ||
818 | else | ||
819 | { | ||
820 | update_directory (); | ||
821 | return return_directory_response (connection); | ||
822 | } | ||
823 | } | 825 | } |
826 | } | ||
824 | if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) || | 827 | if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) || |
825 | (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) ) | 828 | (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) ) |
826 | { | 829 | { |
@@ -829,8 +832,8 @@ generate_page (void *cls, | |||
829 | 832 | ||
830 | /* unexpected request, refuse */ | 833 | /* unexpected request, refuse */ |
831 | return MHD_queue_response (connection, | 834 | return MHD_queue_response (connection, |
832 | MHD_HTTP_FORBIDDEN, | 835 | MHD_HTTP_FORBIDDEN, |
833 | request_refused_response); | 836 | request_refused_response); |
834 | } | 837 | } |
835 | 838 | ||
836 | 839 | ||
@@ -843,7 +846,7 @@ generate_page (void *cls, | |||
843 | static void | 846 | static void |
844 | catcher (int sig) | 847 | catcher (int sig) |
845 | { | 848 | { |
846 | (void)sig; /* Unused. Silent compiler warning. */ | 849 | (void) sig; /* Unused. Silent compiler warning. */ |
847 | /* do nothing */ | 850 | /* do nothing */ |
848 | } | 851 | } |
849 | 852 | ||
@@ -889,11 +892,11 @@ main (int argc, char *const *argv) | |||
889 | if ( (argc != 2) || | 892 | if ( (argc != 2) || |
890 | (1 != sscanf (argv[1], "%u", &port)) || | 893 | (1 != sscanf (argv[1], "%u", &port)) || |
891 | (UINT16_MAX < port) ) | 894 | (UINT16_MAX < port) ) |
892 | { | 895 | { |
893 | fprintf (stderr, | 896 | fprintf (stderr, |
894 | "%s PORT\n", argv[0]); | 897 | "%s PORT\n", argv[0]); |
895 | return 1; | 898 | return 1; |
896 | } | 899 | } |
897 | #ifndef MINGW | 900 | #ifndef MINGW |
898 | ignore_sigpipe (); | 901 | ignore_sigpipe (); |
899 | #endif | 902 | #endif |
@@ -903,31 +906,42 @@ main (int argc, char *const *argv) | |||
903 | #endif /* MHD_HAVE_LIBMAGIC */ | 906 | #endif /* MHD_HAVE_LIBMAGIC */ |
904 | 907 | ||
905 | (void) pthread_mutex_init (&mutex, NULL); | 908 | (void) pthread_mutex_init (&mutex, NULL); |
906 | file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE), | 909 | file_not_found_response = MHD_create_response_from_buffer (strlen ( |
907 | (void *) FILE_NOT_FOUND_PAGE, | 910 | FILE_NOT_FOUND_PAGE), |
908 | MHD_RESPMEM_PERSISTENT); | 911 | (void *) |
912 | FILE_NOT_FOUND_PAGE, | ||
913 | MHD_RESPMEM_PERSISTENT); | ||
909 | mark_as_html (file_not_found_response); | 914 | mark_as_html (file_not_found_response); |
910 | request_refused_response = MHD_create_response_from_buffer (strlen (REQUEST_REFUSED_PAGE), | 915 | request_refused_response = MHD_create_response_from_buffer (strlen ( |
911 | (void *) REQUEST_REFUSED_PAGE, | 916 | REQUEST_REFUSED_PAGE), |
912 | MHD_RESPMEM_PERSISTENT); | 917 | (void *) |
918 | REQUEST_REFUSED_PAGE, | ||
919 | MHD_RESPMEM_PERSISTENT); | ||
913 | mark_as_html (request_refused_response); | 920 | mark_as_html (request_refused_response); |
914 | internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE), | 921 | internal_error_response = MHD_create_response_from_buffer (strlen ( |
915 | (void *) INTERNAL_ERROR_PAGE, | 922 | INTERNAL_ERROR_PAGE), |
916 | MHD_RESPMEM_PERSISTENT); | 923 | (void *) |
924 | INTERNAL_ERROR_PAGE, | ||
925 | MHD_RESPMEM_PERSISTENT); | ||
917 | mark_as_html (internal_error_response); | 926 | mark_as_html (internal_error_response); |
918 | update_directory (); | 927 | update_directory (); |
919 | d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, | 928 | d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD |
929 | | MHD_USE_ERROR_LOG, | ||
920 | port, | 930 | port, |
921 | NULL, NULL, | 931 | NULL, NULL, |
922 | &generate_page, NULL, | 932 | &generate_page, NULL, |
923 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024), | 933 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 |
934 | * 1024), | ||
924 | #if PRODUCTION | 935 | #if PRODUCTION |
925 | MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64), | 936 | MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64), |
926 | #endif | 937 | #endif |
927 | MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) (120 /* seconds */), | 938 | MHD_OPTION_CONNECTION_TIMEOUT, (unsigned |
928 | MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS, | 939 | int) (120 /* seconds */), |
929 | MHD_OPTION_NOTIFY_COMPLETED, &response_completed_callback, NULL, | 940 | MHD_OPTION_THREAD_POOL_SIZE, (unsigned |
930 | MHD_OPTION_END); | 941 | int) NUMBER_OF_THREADS, |
942 | MHD_OPTION_NOTIFY_COMPLETED, | ||
943 | &response_completed_callback, NULL, | ||
944 | MHD_OPTION_END); | ||
931 | if (NULL == d) | 945 | if (NULL == d) |
932 | return 1; | 946 | return 1; |
933 | fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n"); | 947 | fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n"); |