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