libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

demo.c (29259B)


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