demo.c (29259B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2013 Christian Grothoff (and other contributing authors) 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21 /** 22 * @file demo.c 23 * @brief complex demonstration site: create directory index, offer 24 * upload via form and HTTP POST, download with mime type detection 25 * and error reporting (403, etc.) --- and all of this with 26 * high-performance settings (large buffers, thread pool). 27 * If you want to benchmark MHD, this code should be used to 28 * run tests against. Note that the number of threads may need 29 * to be adjusted depending on the number of available cores. 30 * @author Christian Grothoff 31 * @author Karlson2k (Evgeny Grin) 32 */ 33 #include "MHD_config.h" 34 #include "platform.h" 35 #include <microhttpd.h> 36 #include <unistd.h> 37 #include <pthread.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <dirent.h> 41 #ifdef MHD_HAVE_LIBMAGIC 42 #include <magic.h> 43 #endif /* MHD_HAVE_LIBMAGIC */ 44 #include <limits.h> 45 #include <ctype.h> 46 #include <errno.h> 47 48 #if defined(MHD_CPU_COUNT) && (MHD_CPU_COUNT + 0) < 2 49 #undef MHD_CPU_COUNT 50 #endif 51 #if ! defined(MHD_CPU_COUNT) 52 #define MHD_CPU_COUNT 2 53 #endif 54 55 #ifndef PATH_MAX 56 /* Some platforms (namely: GNU Hurd) do no define PATH_MAX. 57 As it is only example for MHD, just use reasonable value for PATH_MAX. */ 58 #define PATH_MAX 16384 59 #endif 60 61 /** 62 * Number of threads to run in the thread pool. Should (roughly) match 63 * the number of cores on your system. 64 */ 65 #define NUMBER_OF_THREADS MHD_CPU_COUNT 66 67 #ifdef MHD_HAVE_LIBMAGIC 68 /** 69 * How many bytes of a file do we give to libmagic to determine the mime type? 70 * 16k might be a bit excessive, but ought not hurt performance much anyway, 71 * and should definitively be on the safe side. 72 */ 73 #define MAGIC_HEADER_SIZE (16 * 1024) 74 #endif /* MHD_HAVE_LIBMAGIC */ 75 76 77 /** 78 * Page returned for file-not-found. 79 */ 80 #define FILE_NOT_FOUND_PAGE \ 81 "<html><head><title>File not found</title></head><body>File not found</body></html>" 82 83 84 /** 85 * Page returned for internal errors. 86 */ 87 #define INTERNAL_ERROR_PAGE \ 88 "<html><head><title>Internal error</title></head><body>Internal error</body></html>" 89 90 91 /** 92 * Page returned for refused requests. 93 */ 94 #define REQUEST_REFUSED_PAGE \ 95 "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>" 96 97 98 /** 99 * Head of index page. 100 */ 101 #define INDEX_PAGE_HEADER \ 102 "<html>\n<head><title>Welcome</title></head>\n<body>\n" \ 103 "<h1>Upload</h1>\n" \ 104 "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n" \ 105 "<dl><dt>Content type:</dt><dd>" \ 106 "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>" \ 107 "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>" \ 108 "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>" \ 109 "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>" \ 110 "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n" \ 111 "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input></dd>" \ 112 "<dt>Language:</dt><dd>" \ 113 "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>" \ 114 "<input type=\"radio\" name=\"language\" value=\"en\">English</input>" \ 115 "<input type=\"radio\" name=\"language\" value=\"de\">German</input>" \ 116 "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>" \ 117 "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input></dd>\n" \ 118 "<dt>File:</dt><dd>" \ 119 "<input type=\"file\" name=\"upload\"/></dd></dl>" \ 120 "<input type=\"submit\" value=\"Send!\"/>\n" \ 121 "</form>\n" \ 122 "<h1>Download</h1>\n" \ 123 "<ol>\n" 124 125 /** 126 * Footer of index page. 127 */ 128 #define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>" 129 130 131 /** 132 * NULL-terminated array of supported upload categories. Should match HTML 133 * in the form. 134 */ 135 static const char *const categories[] = { 136 "books", 137 "images", 138 "music", 139 "software", 140 "videos", 141 "other", 142 NULL, 143 }; 144 145 146 /** 147 * Specification of a supported language. 148 */ 149 struct Language 150 { 151 /** 152 * Directory name for the language. 153 */ 154 const char *dirname; 155 156 /** 157 * Long name for humans. 158 */ 159 const char *longname; 160 161 }; 162 163 /** 164 * NULL-terminated array of supported upload categories. Should match HTML 165 * in the form. 166 */ 167 static const struct Language languages[] = { 168 { "no-lang", "No language specified" }, 169 { "en", "English" }, 170 { "de", "German" }, 171 { "fr", "French" }, 172 { "es", "Spanish" }, 173 { NULL, NULL }, 174 }; 175 176 177 /** 178 * Response returned if the requested file does not exist (or is not accessible). 179 */ 180 static struct MHD_Response *file_not_found_response; 181 182 /** 183 * Response returned for internal errors. 184 */ 185 static struct MHD_Response *internal_error_response; 186 187 /** 188 * Response returned for '/' (GET) to list the contents of the directory and allow upload. 189 */ 190 static struct MHD_Response *cached_directory_response; 191 192 /** 193 * Response returned for refused uploads. 194 */ 195 static struct MHD_Response *request_refused_response; 196 197 /** 198 * Mutex used when we update the cached directory response object. 199 */ 200 static pthread_mutex_t mutex; 201 202 #ifdef MHD_HAVE_LIBMAGIC 203 /** 204 * Global handle to MAGIC data. 205 */ 206 static magic_t magic; 207 #endif /* MHD_HAVE_LIBMAGIC */ 208 209 210 /** 211 * Mark the given response as HTML for the browser. 212 * 213 * @param response response to mark 214 */ 215 static void 216 mark_as_html (struct MHD_Response *response) 217 { 218 (void) MHD_add_response_header (response, 219 MHD_HTTP_HEADER_CONTENT_TYPE, 220 "text/html"); 221 } 222 223 224 /** 225 * Replace the existing 'cached_directory_response' with the 226 * given response. 227 * 228 * @param response new directory response 229 */ 230 static void 231 update_cached_response (struct MHD_Response *response) 232 { 233 (void) pthread_mutex_lock (&mutex); 234 if (NULL != cached_directory_response) 235 MHD_destroy_response (cached_directory_response); 236 cached_directory_response = response; 237 (void) pthread_mutex_unlock (&mutex); 238 } 239 240 241 /** 242 * Context keeping the data for the response we're building. 243 */ 244 struct ResponseDataContext 245 { 246 /** 247 * Response data string. 248 */ 249 char *buf; 250 251 /** 252 * Number of bytes allocated for 'buf'. 253 */ 254 size_t buf_len; 255 256 /** 257 * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'. 258 */ 259 size_t off; 260 261 }; 262 263 264 /** 265 * Create a listing of the files in 'dirname' in HTML. 266 * 267 * @param rdc where to store the list of files 268 * @param dirname name of the directory to list 269 * @return #MHD_YES on success, #MHD_NO on error 270 */ 271 static enum MHD_Result 272 list_directory (struct ResponseDataContext *rdc, 273 const char *dirname) 274 { 275 char fullname[PATH_MAX]; 276 struct stat sbuf; 277 DIR *dir; 278 struct dirent *de; 279 280 if (NULL == (dir = opendir (dirname))) 281 return MHD_NO; 282 while (NULL != (de = readdir (dir))) 283 { 284 int res; 285 if ('.' == de->d_name[0]) 286 continue; 287 if (sizeof (fullname) <= (unsigned int) 288 snprintf (fullname, sizeof (fullname), 289 "%s/%s", 290 dirname, de->d_name)) 291 continue; /* ugh, file too long? how can this be!? */ 292 if (0 != stat (fullname, &sbuf)) 293 continue; /* ugh, failed to 'stat' */ 294 if (! S_ISREG (sbuf.st_mode)) 295 continue; /* not a regular file, skip */ 296 if (rdc->off + 1024 > rdc->buf_len) 297 { 298 void *r; 299 300 if ( (2 * rdc->buf_len + 1024) < rdc->buf_len) 301 break; /* more than SIZE_T _index_ size? Too big for us */ 302 rdc->buf_len = 2 * rdc->buf_len + 1024; 303 if (NULL == (r = realloc (rdc->buf, rdc->buf_len))) 304 break; /* out of memory */ 305 rdc->buf = r; 306 } 307 res = snprintf (&rdc->buf[rdc->off], 308 rdc->buf_len - rdc->off, 309 "<li><a href=\"/%s\">%s</a></li>\n", 310 fullname, 311 de->d_name); 312 if (0 >= res) 313 continue; /* snprintf() error */ 314 if (rdc->buf_len - rdc->off <= (size_t) res) 315 continue; /* buffer too small?? */ 316 rdc->off += (size_t) res; 317 } 318 (void) closedir (dir); 319 return MHD_YES; 320 } 321 322 323 /** 324 * Re-scan our local directory and re-build the index. 325 */ 326 static void 327 update_directory (void) 328 { 329 static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */ 330 struct MHD_Response *response; 331 struct ResponseDataContext rdc; 332 unsigned int language_idx; 333 unsigned int category_idx; 334 const struct Language *language; 335 const char *category; 336 char dir_name[128]; 337 struct stat sbuf; 338 int res; 339 size_t len; 340 341 rdc.buf_len = initial_allocation; 342 if (NULL == (rdc.buf = malloc (rdc.buf_len))) 343 { 344 update_cached_response (NULL); 345 return; 346 } 347 len = strlen (INDEX_PAGE_HEADER); 348 if (rdc.buf_len <= len) 349 { /* buffer too small */ 350 free (rdc.buf); 351 update_cached_response (NULL); 352 return; 353 } 354 memcpy (rdc.buf, INDEX_PAGE_HEADER, len); 355 rdc.off = len; 356 for (language_idx = 0; NULL != languages[language_idx].dirname; 357 language_idx++) 358 { 359 language = &languages[language_idx]; 360 361 if (0 != stat (language->dirname, &sbuf)) 362 continue; /* empty */ 363 /* we ensured always +1k room, filenames are ~256 bytes, 364 so there is always still enough space for the header 365 without need for an additional reallocation check. */ 366 res = snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, 367 "<h2>%s</h2>\n", 368 language->longname); 369 if (0 >= res) 370 continue; /* snprintf() error */ 371 if (rdc.buf_len - rdc.off <= (size_t) res) 372 continue; /* buffer too small?? */ 373 rdc.off += (size_t) res; 374 for (category_idx = 0; NULL != categories[category_idx]; category_idx++) 375 { 376 category = categories[category_idx]; 377 res = snprintf (dir_name, sizeof (dir_name), 378 "%s/%s", 379 language->dirname, 380 category); 381 if ((0 >= res) || (sizeof (dir_name) <= (size_t) res)) 382 continue; /* cannot print dir name */ 383 if (0 != stat (dir_name, &sbuf)) 384 continue; /* empty */ 385 386 /* we ensured always +1k room, filenames are ~256 bytes, 387 so there is always still enough space for the header 388 without need for an additional reallocation check. */ 389 res = snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off, 390 "<h3>%s</h3>\n", 391 category); 392 if (0 >= res) 393 continue; /* snprintf() error */ 394 if (rdc.buf_len - rdc.off <= (size_t) res) 395 continue; /* buffer too small?? */ 396 rdc.off += (size_t) res; 397 398 if (MHD_NO == list_directory (&rdc, dir_name)) 399 { 400 free (rdc.buf); 401 update_cached_response (NULL); 402 return; 403 } 404 } 405 } 406 /* we ensured always +1k room, filenames are ~256 bytes, 407 so there is always still enough space for the footer 408 without need for a final reallocation check. */ 409 len = strlen (INDEX_PAGE_FOOTER); 410 if (rdc.buf_len - rdc.off <= len) 411 { /* buffer too small */ 412 free (rdc.buf); 413 update_cached_response (NULL); 414 return; 415 } 416 memcpy (&rdc.buf[rdc.off], INDEX_PAGE_FOOTER, len); 417 rdc.off += len; 418 initial_allocation = rdc.buf_len; /* remember for next time */ 419 response = 420 MHD_create_response_from_buffer_with_free_callback (rdc.off, 421 rdc.buf, 422 &free); 423 mark_as_html (response); 424 #ifdef FORCE_CLOSE 425 (void) MHD_add_response_header (response, 426 MHD_HTTP_HEADER_CONNECTION, 427 "close"); 428 #endif 429 update_cached_response (response); 430 } 431 432 433 /** 434 * Context we keep for an upload. 435 */ 436 struct UploadContext 437 { 438 /** 439 * Handle where we write the uploaded file to. 440 */ 441 int fd; 442 443 /** 444 * Name of the file on disk (used to remove on errors). 445 */ 446 char *filename; 447 448 /** 449 * Language for the upload. 450 */ 451 char *language; 452 453 /** 454 * Category for the upload. 455 */ 456 char *category; 457 458 /** 459 * Post processor we're using to process the upload. 460 */ 461 struct MHD_PostProcessor *pp; 462 463 /** 464 * Handle to connection that we're processing the upload for. 465 */ 466 struct MHD_Connection *connection; 467 468 /** 469 * Response to generate, NULL to use directory. 470 */ 471 struct MHD_Response *response; 472 }; 473 474 475 /** 476 * Append the 'size' bytes from 'data' to '*ret', adding 477 * 0-termination. If '*ret' is NULL, allocate an empty string first. 478 * 479 * @param ret string to update, NULL or 0-terminated 480 * @param data data to append 481 * @param size number of bytes in 'data' 482 * @return #MHD_NO on allocation failure, #MHD_YES on success 483 */ 484 static enum MHD_Result 485 do_append (char **ret, 486 const char *data, 487 size_t size) 488 { 489 char *buf; 490 size_t old_len; 491 492 if (NULL == *ret) 493 old_len = 0; 494 else 495 old_len = strlen (*ret); 496 if (NULL == (buf = malloc (old_len + size + 1))) 497 return MHD_NO; 498 if (NULL != *ret) 499 { 500 memcpy (buf, 501 *ret, 502 old_len); 503 free (*ret); 504 } 505 memcpy (&buf[old_len], 506 data, 507 size); 508 buf[old_len + size] = '\0'; 509 *ret = buf; 510 return MHD_YES; 511 } 512 513 514 /** 515 * Iterator over key-value pairs where the value 516 * maybe made available in increments and/or may 517 * not be zero-terminated. Used for processing 518 * POST data. 519 * 520 * @param cls user-specified closure 521 * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD 522 * @param key 0-terminated key for the value 523 * @param filename name of the uploaded file, NULL if not known 524 * @param content_type mime-type of the data, NULL if not known 525 * @param transfer_encoding encoding of the data, NULL if not known 526 * @param data pointer to size bytes of data at the 527 * specified offset 528 * @param off offset of data in the overall value 529 * @param size number of bytes in data available 530 * @return #MHD_YES to continue iterating, 531 * #MHD_NO to abort the iteration 532 */ 533 static enum MHD_Result 534 process_upload_data (void *cls, 535 enum MHD_ValueKind kind, 536 const char *key, 537 const char *filename, 538 const char *content_type, 539 const char *transfer_encoding, 540 const char *data, 541 uint64_t off, 542 size_t size) 543 { 544 struct UploadContext *uc = cls; 545 size_t i; 546 int res; 547 (void) kind; /* Unused. Silent compiler warning. */ 548 (void) content_type; /* Unused. Silent compiler warning. */ 549 (void) transfer_encoding; /* Unused. Silent compiler warning. */ 550 (void) off; /* Unused. Silent compiler warning. */ 551 552 if (0 == strcmp (key, "category")) 553 return do_append (&uc->category, data, size); 554 if (0 == strcmp (key, "language")) 555 return do_append (&uc->language, data, size); 556 if (0 != strcmp (key, "upload")) 557 { 558 fprintf (stderr, 559 "Ignoring unexpected form value `%s'\n", 560 key); 561 return MHD_YES; /* ignore */ 562 } 563 if (NULL == filename) 564 { 565 fprintf (stderr, "No filename, aborting upload.\n"); 566 return MHD_NO; /* no filename, error */ 567 } 568 if ( (NULL == uc->category) || 569 (NULL == uc->language) ) 570 { 571 fprintf (stderr, 572 "Missing form data for upload `%s'\n", 573 filename); 574 uc->response = request_refused_response; 575 return MHD_NO; 576 } 577 if (-1 == uc->fd) 578 { 579 char fn[PATH_MAX]; 580 581 if ( (NULL != strstr (filename, "..")) || 582 (NULL != strchr (filename, '/')) || 583 (NULL != strchr (filename, '\\')) ) 584 { 585 uc->response = request_refused_response; 586 return MHD_NO; 587 } 588 /* create directories -- if they don't exist already */ 589 #ifdef WINDOWS 590 (void) mkdir (uc->language); 591 #else 592 (void) mkdir (uc->language, S_IRWXU); 593 #endif 594 snprintf (fn, sizeof (fn), 595 "%s/%s", 596 uc->language, 597 uc->category); 598 #ifdef WINDOWS 599 (void) mkdir (fn); 600 #else 601 (void) mkdir (fn, S_IRWXU); 602 #endif 603 /* open file */ 604 res = snprintf (fn, sizeof (fn), 605 "%s/%s/%s", 606 uc->language, 607 uc->category, 608 filename); 609 if ((0 >= res) || (sizeof (fn) <= (size_t) res)) 610 { 611 uc->response = request_refused_response; 612 return MHD_NO; 613 } 614 for (i = 0; i < (size_t) res; i++) 615 if (! isprint ((unsigned char) fn[i])) 616 fn[i] = '_'; 617 uc->fd = open (fn, 618 O_CREAT | O_EXCL 619 #ifdef O_LARGEFILE 620 | O_LARGEFILE 621 #endif 622 | O_WRONLY, 623 S_IRUSR | S_IWUSR); 624 if (-1 == uc->fd) 625 { 626 fprintf (stderr, 627 "Error opening file `%s' for upload: %s\n", 628 fn, 629 strerror (errno)); 630 uc->response = request_refused_response; 631 return MHD_NO; 632 } 633 uc->filename = strdup (fn); 634 } 635 if ( (0 != size) && 636 #if ! defined(_WIN32) || defined(__CYGWIN__) 637 (size != (size_t) write (uc->fd, data, size)) 638 #else /* Native W32 */ 639 (size != (size_t) write (uc->fd, data, (unsigned int) size)) 640 #endif /* Native W32 */ 641 ) 642 { 643 /* write failed; likely: disk full */ 644 fprintf (stderr, 645 "Error writing to file `%s': %s\n", 646 uc->filename, 647 strerror (errno)); 648 uc->response = internal_error_response; 649 (void) close (uc->fd); 650 uc->fd = -1; 651 if (NULL != uc->filename) 652 { 653 unlink (uc->filename); 654 free (uc->filename); 655 uc->filename = NULL; 656 } 657 return MHD_NO; 658 } 659 return MHD_YES; 660 } 661 662 663 /** 664 * Function called whenever a request was completed. 665 * Used to clean up 'struct UploadContext' objects. 666 * 667 * @param cls client-defined closure, NULL 668 * @param connection connection handle 669 * @param req_cls value as set by the last call to 670 * the MHD_AccessHandlerCallback, points to NULL if this was 671 * not an upload 672 * @param toe reason for request termination 673 */ 674 static void 675 response_completed_callback (void *cls, 676 struct MHD_Connection *connection, 677 void **req_cls, 678 enum MHD_RequestTerminationCode toe) 679 { 680 struct UploadContext *uc = *req_cls; 681 (void) cls; /* Unused. Silent compiler warning. */ 682 (void) connection; /* Unused. Silent compiler warning. */ 683 (void) toe; /* Unused. Silent compiler warning. */ 684 685 if (NULL == uc) 686 return; /* this request wasn't an upload request */ 687 if (NULL != uc->pp) 688 { 689 MHD_destroy_post_processor (uc->pp); 690 uc->pp = NULL; 691 } 692 if (-1 != uc->fd) 693 { 694 (void) close (uc->fd); 695 if (NULL != uc->filename) 696 { 697 fprintf (stderr, 698 "Upload of file `%s' failed (incomplete or aborted), removing file.\n", 699 uc->filename); 700 (void) unlink (uc->filename); 701 } 702 } 703 if (NULL != uc->filename) 704 free (uc->filename); 705 free (uc); 706 } 707 708 709 /** 710 * Return the current directory listing. 711 * 712 * @param connection connection to return the directory for 713 * @return #MHD_YES on success, #MHD_NO on error 714 */ 715 static enum MHD_Result 716 return_directory_response (struct MHD_Connection *connection) 717 { 718 enum MHD_Result ret; 719 720 (void) pthread_mutex_lock (&mutex); 721 if (NULL == cached_directory_response) 722 ret = MHD_queue_response (connection, 723 MHD_HTTP_INTERNAL_SERVER_ERROR, 724 internal_error_response); 725 else 726 ret = MHD_queue_response (connection, 727 MHD_HTTP_OK, 728 cached_directory_response); 729 (void) pthread_mutex_unlock (&mutex); 730 return ret; 731 } 732 733 734 /** 735 * Main callback from MHD, used to generate the page. 736 * 737 * @param cls NULL 738 * @param connection connection handle 739 * @param url requested URL 740 * @param method GET, PUT, POST, etc. 741 * @param version HTTP version 742 * @param upload_data data from upload (PUT/POST) 743 * @param upload_data_size number of bytes in @a upload_data 744 * @param req_cls our context 745 * @return #MHD_YES on success, #MHD_NO to drop connection 746 */ 747 static enum MHD_Result 748 generate_page (void *cls, 749 struct MHD_Connection *connection, 750 const char *url, 751 const char *method, 752 const char *version, 753 const char *upload_data, 754 size_t *upload_data_size, void **req_cls) 755 { 756 struct MHD_Response *response; 757 enum MHD_Result ret; 758 int fd; 759 struct stat buf; 760 (void) cls; /* Unused. Silent compiler warning. */ 761 (void) version; /* Unused. Silent compiler warning. */ 762 763 if (0 != strcmp (url, "/")) 764 { 765 /* should be file download */ 766 #ifdef MHD_HAVE_LIBMAGIC 767 char file_data[MAGIC_HEADER_SIZE]; 768 ssize_t got; 769 #endif /* MHD_HAVE_LIBMAGIC */ 770 const char *mime; 771 772 if ( (0 != strcmp (method, MHD_HTTP_METHOD_GET)) && 773 (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)) ) 774 return MHD_NO; /* unexpected method (we're not polite...) */ 775 fd = -1; 776 if ( (NULL == strstr (&url[1], "..")) && 777 ('/' != url[1]) ) 778 { 779 fd = open (&url[1], O_RDONLY); 780 if ( (-1 != fd) && 781 ( (0 != fstat (fd, &buf)) || 782 (! S_ISREG (buf.st_mode)) ) ) 783 { 784 (void) close (fd); 785 fd = -1; 786 } 787 } 788 if (-1 == fd) 789 return MHD_queue_response (connection, 790 MHD_HTTP_NOT_FOUND, 791 file_not_found_response); 792 #ifdef MHD_HAVE_LIBMAGIC 793 /* read beginning of the file to determine mime type */ 794 got = read (fd, file_data, sizeof (file_data)); 795 (void) lseek (fd, 0, SEEK_SET); 796 if (0 < got) 797 mime = magic_buffer (magic, file_data, (size_t) got); 798 else 799 #endif /* MHD_HAVE_LIBMAGIC */ 800 mime = NULL; 801 { 802 /* Set mime-type by file-extension in some cases */ 803 const char *ldot = strrchr (&url[1], '.'); 804 805 if (NULL != ldot) 806 { 807 if (0 == strcasecmp (ldot, 808 ".html")) 809 mime = "text/html"; 810 if (0 == strcasecmp (ldot, 811 ".css")) 812 mime = "text/css"; 813 if (0 == strcasecmp (ldot, 814 ".css3")) 815 mime = "text/css"; 816 if (0 == strcasecmp (ldot, 817 ".js")) 818 mime = "application/javascript"; 819 } 820 821 } 822 823 if (NULL == (response = MHD_create_response_from_fd ((size_t) buf.st_size, 824 fd))) 825 { 826 /* internal error (i.e. out of memory) */ 827 (void) close (fd); 828 return MHD_NO; 829 } 830 831 /* add mime type if we had one */ 832 if (NULL != mime) 833 (void) MHD_add_response_header (response, 834 MHD_HTTP_HEADER_CONTENT_TYPE, 835 mime); 836 ret = MHD_queue_response (connection, 837 MHD_HTTP_OK, 838 response); 839 MHD_destroy_response (response); 840 return ret; 841 } 842 843 if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) 844 { 845 /* upload! */ 846 struct UploadContext *uc = *req_cls; 847 848 if (NULL == uc) 849 { 850 if (NULL == (uc = malloc (sizeof (struct UploadContext)))) 851 return MHD_NO; /* out of memory, close connection */ 852 memset (uc, 0, sizeof (struct UploadContext)); 853 uc->fd = -1; 854 uc->connection = connection; 855 uc->pp = MHD_create_post_processor (connection, 856 64 * 1024 /* buffer size */, 857 &process_upload_data, uc); 858 if (NULL == uc->pp) 859 { 860 /* out of memory, close connection */ 861 free (uc); 862 return MHD_NO; 863 } 864 *req_cls = uc; 865 return MHD_YES; 866 } 867 if (0 != *upload_data_size) 868 { 869 if (NULL == uc->response) 870 (void) MHD_post_process (uc->pp, 871 upload_data, 872 *upload_data_size); 873 *upload_data_size = 0; 874 return MHD_YES; 875 } 876 /* end of upload, finish it! */ 877 MHD_destroy_post_processor (uc->pp); 878 uc->pp = NULL; 879 if (-1 != uc->fd) 880 { 881 close (uc->fd); 882 uc->fd = -1; 883 } 884 if (NULL != uc->response) 885 { 886 return MHD_queue_response (connection, 887 MHD_HTTP_FORBIDDEN, 888 uc->response); 889 } 890 else 891 { 892 update_directory (); 893 return return_directory_response (connection); 894 } 895 } 896 if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) || 897 (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) ) 898 { 899 return return_directory_response (connection); 900 } 901 902 /* unexpected request, refuse */ 903 return MHD_queue_response (connection, 904 MHD_HTTP_FORBIDDEN, 905 request_refused_response); 906 } 907 908 909 #ifndef MINGW 910 /** 911 * Function called if we get a SIGPIPE. Does nothing. 912 * 913 * @param sig will be SIGPIPE (ignored) 914 */ 915 static void 916 catcher (int sig) 917 { 918 (void) sig; /* Unused. Silent compiler warning. */ 919 /* do nothing */ 920 } 921 922 923 /** 924 * setup handlers to ignore SIGPIPE. 925 */ 926 static void 927 ignore_sigpipe (void) 928 { 929 struct sigaction oldsig; 930 struct sigaction sig; 931 932 sig.sa_handler = &catcher; 933 sigemptyset (&sig.sa_mask); 934 #ifdef SA_INTERRUPT 935 sig.sa_flags = SA_INTERRUPT; /* SunOS */ 936 #else 937 sig.sa_flags = SA_RESTART; 938 #endif 939 if (0 != sigaction (SIGPIPE, &sig, &oldsig)) 940 fprintf (stderr, 941 "Failed to install SIGPIPE handler: %s\n", strerror (errno)); 942 } 943 944 945 #endif 946 947 948 /** 949 * Entry point to demo. Note: this HTTP server will make all 950 * files in the current directory and its subdirectories available 951 * to anyone. Press ENTER to stop the server once it has started. 952 * 953 * @param argc number of arguments in argv 954 * @param argv first and only argument should be the port number 955 * @return 0 on success 956 */ 957 int 958 main (int argc, char *const *argv) 959 { 960 struct MHD_Daemon *d; 961 unsigned int port; 962 963 if ( (argc != 2) || 964 (1 != sscanf (argv[1], "%u", &port)) || 965 (UINT16_MAX < port) ) 966 { 967 fprintf (stderr, 968 "%s PORT\n", argv[0]); 969 return 1; 970 } 971 #ifndef MINGW 972 ignore_sigpipe (); 973 #endif 974 #ifdef MHD_HAVE_LIBMAGIC 975 magic = magic_open (MAGIC_MIME_TYPE); 976 (void) magic_load (magic, NULL); 977 #endif /* MHD_HAVE_LIBMAGIC */ 978 979 (void) pthread_mutex_init (&mutex, NULL); 980 file_not_found_response = 981 MHD_create_response_from_buffer_static (strlen (FILE_NOT_FOUND_PAGE), 982 (const void *) FILE_NOT_FOUND_PAGE); 983 mark_as_html (file_not_found_response); 984 request_refused_response = 985 MHD_create_response_from_buffer_static (strlen (REQUEST_REFUSED_PAGE), 986 (const void *) 987 REQUEST_REFUSED_PAGE); 988 mark_as_html (request_refused_response); 989 internal_error_response = 990 MHD_create_response_from_buffer_static (strlen (INTERNAL_ERROR_PAGE), 991 (const void *) INTERNAL_ERROR_PAGE); 992 mark_as_html (internal_error_response); 993 update_directory (); 994 d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD 995 | MHD_USE_ERROR_LOG, 996 (uint16_t) port, 997 NULL, NULL, 998 &generate_page, NULL, 999 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 1000 * 1024), 1001 #ifdef PRODUCTION 1002 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64), 1003 #endif 1004 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned 1005 int) (120 /* seconds */) 1006 , 1007 MHD_OPTION_THREAD_POOL_SIZE, (unsigned 1008 int) NUMBER_OF_THREADS, 1009 MHD_OPTION_NOTIFY_COMPLETED, 1010 &response_completed_callback, NULL, 1011 MHD_OPTION_END); 1012 if (NULL == d) 1013 return 1; 1014 fprintf (stderr, "HTTP server running. Press ENTER to stop the server.\n"); 1015 (void) getc (stdin); 1016 MHD_stop_daemon (d); 1017 MHD_destroy_response (file_not_found_response); 1018 MHD_destroy_response (request_refused_response); 1019 MHD_destroy_response (internal_error_response); 1020 update_cached_response (NULL); 1021 (void) pthread_mutex_destroy (&mutex); 1022 #ifdef MHD_HAVE_LIBMAGIC 1023 magic_close (magic); 1024 #endif /* MHD_HAVE_LIBMAGIC */ 1025 return 0; 1026 } 1027 1028 1029 /* end of demo.c */