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