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