Home | History | Annotate | Download | only in examples
      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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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