Blame src/examples/demo.c

Packit 875988
/*
Packit 875988
     This file is part of libmicrohttpd
Packit 875988
     Copyright (C) 2013 Christian Grothoff (and other contributing authors)
Packit 875988
Packit 875988
     This library is free software; you can redistribute it and/or
Packit 875988
     modify it under the terms of the GNU Lesser General Public
Packit 875988
     License as published by the Free Software Foundation; either
Packit 875988
     version 2.1 of the License, or (at your option) any later version.
Packit 875988
Packit 875988
     This library is distributed in the hope that it will be useful,
Packit 875988
     but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 875988
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 875988
     Lesser General Public License for more details.
Packit 875988
Packit 875988
     You should have received a copy of the GNU Lesser General Public
Packit 875988
     License along with this library; if not, write to the Free Software
Packit 875988
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 875988
*/
Packit 875988
Packit 875988
/**
Packit 875988
 * @file demo.c
Packit 875988
 * @brief complex demonstration site: create directory index, offer
Packit 875988
 *        upload via form and HTTP POST, download with mime type detection
Packit 875988
 *        and error reporting (403, etc.) --- and all of this with
Packit 875988
 *        high-performance settings (large buffers, thread pool).
Packit 875988
 *        If you want to benchmark MHD, this code should be used to
Packit 875988
 *        run tests against.  Note that the number of threads may need
Packit 875988
 *        to be adjusted depending on the number of available cores.
Packit 875988
 * @author Christian Grothoff
Packit 875988
 */
Packit 875988
#include "platform.h"
Packit 875988
#include <microhttpd.h>
Packit 875988
#include <unistd.h>
Packit 875988
#include <pthread.h>
Packit 875988
#include <sys/types.h>
Packit 875988
#include <sys/stat.h>
Packit 875988
#include <dirent.h>
Packit 875988
#ifdef MHD_HAVE_LIBMAGIC
Packit 875988
#include <magic.h>
Packit 875988
#endif /* MHD_HAVE_LIBMAGIC */
Packit 875988
#include <limits.h>
Packit 875988
#include <ctype.h>
Packit 875988
Packit 875988
#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2
Packit 875988
#undef CPU_COUNT
Packit 875988
#endif
Packit 875988
#if !defined(CPU_COUNT)
Packit 875988
#define CPU_COUNT 2
Packit 875988
#endif
Packit 875988
Packit 875988
/**
Packit 875988
 * Number of threads to run in the thread pool.  Should (roughly) match
Packit 875988
 * the number of cores on your system.
Packit 875988
 */
Packit 875988
#define NUMBER_OF_THREADS CPU_COUNT
Packit 875988
Packit 875988
#ifdef MHD_HAVE_LIBMAGIC
Packit 875988
/**
Packit 875988
 * How many bytes of a file do we give to libmagic to determine the mime type?
Packit 875988
 * 16k might be a bit excessive, but ought not hurt performance much anyway,
Packit 875988
 * and should definitively be on the safe side.
Packit 875988
 */
Packit 875988
#define MAGIC_HEADER_SIZE (16 * 1024)
Packit 875988
#endif /* MHD_HAVE_LIBMAGIC */
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Page returned for file-not-found.
Packit 875988
 */
Packit 875988
#define FILE_NOT_FOUND_PAGE "<html><head><title>File not found</title></head><body>File not found</body></html>"
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Page returned for internal errors.
Packit 875988
 */
Packit 875988
#define INTERNAL_ERROR_PAGE "<html><head><title>Internal error</title></head><body>Internal error</body></html>"
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Page returned for refused requests.
Packit 875988
 */
Packit 875988
#define REQUEST_REFUSED_PAGE "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>"
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Head of index page.
Packit 875988
 */
Packit 875988
#define INDEX_PAGE_HEADER "<html>\n<head><title>Welcome</title></head>\n<body>\n"\
Packit 875988
   "

Upload

\n"\
Packit 875988
   "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"/\">\n"\
Packit 875988
   "
Content type:
"\
Packit 875988
   "<input type=\"radio\" name=\"category\" value=\"books\">Book</input>"\
Packit 875988
   "<input type=\"radio\" name=\"category\" value=\"images\">Image</input>"\
Packit 875988
   "<input type=\"radio\" name=\"category\" value=\"music\">Music</input>"\
Packit 875988
   "<input type=\"radio\" name=\"category\" value=\"software\">Software</input>"\
Packit 875988
   "<input type=\"radio\" name=\"category\" value=\"videos\">Videos</input>\n"\
Packit 875988
   "<input type=\"radio\" name=\"category\" value=\"other\" checked>Other</input>"\
Packit 875988
   "
Language:
"\
Packit 875988
   "<input type=\"radio\" name=\"language\" value=\"no-lang\" checked>none</input>"\
Packit 875988
   "<input type=\"radio\" name=\"language\" value=\"en\">English</input>"\
Packit 875988
   "<input type=\"radio\" name=\"language\" value=\"de\">German</input>"\
Packit 875988
   "<input type=\"radio\" name=\"language\" value=\"fr\">French</input>"\
Packit 875988
   "<input type=\"radio\" name=\"language\" value=\"es\">Spanish</input>\n"\
Packit 875988
   "
File:
"\
Packit 875988
   "<input type=\"file\" name=\"upload\"/>"\
Packit 875988
   "<input type=\"submit\" value=\"Send!\"/>\n"\
Packit 875988
   "</form>\n"\
Packit 875988
   "

Download

\n"\
Packit 875988
   "
    \n"
Packit 875988
Packit 875988
/**
Packit 875988
 * Footer of index page.
Packit 875988
 */
Packit 875988
#define INDEX_PAGE_FOOTER "\n</body>\n</html>"
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * NULL-terminated array of supported upload categories.  Should match HTML
Packit 875988
 * in the form.
Packit 875988
 */
Packit 875988
static const char * const categories[] =
Packit 875988
  {
Packit 875988
    "books",
Packit 875988
    "images",
Packit 875988
    "music",
Packit 875988
    "software",
Packit 875988
    "videos",
Packit 875988
    "other",
Packit 875988
    NULL,
Packit 875988
  };
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Specification of a supported language.
Packit 875988
 */
Packit 875988
struct Language
Packit 875988
{
Packit 875988
  /**
Packit 875988
   * Directory name for the language.
Packit 875988
   */
Packit 875988
  const char *dirname;
Packit 875988
Packit 875988
  /**
Packit 875988
   * Long name for humans.
Packit 875988
   */
Packit 875988
  const char *longname;
Packit 875988
Packit 875988
};
Packit 875988
Packit 875988
/**
Packit 875988
 * NULL-terminated array of supported upload categories.  Should match HTML
Packit 875988
 * in the form.
Packit 875988
 */
Packit 875988
static const struct Language languages[] =
Packit 875988
  {
Packit 875988
    { "no-lang", "No language specified" },
Packit 875988
    { "en", "English" },
Packit 875988
    { "de", "German" },
Packit 875988
    { "fr", "French" },
Packit 875988
    { "es", "Spanish" },
Packit 875988
    { NULL, NULL },
Packit 875988
  };
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Response returned if the requested file does not exist (or is not accessible).
Packit 875988
 */
Packit 875988
static struct MHD_Response *file_not_found_response;
Packit 875988
Packit 875988
/**
Packit 875988
 * Response returned for internal errors.
Packit 875988
 */
Packit 875988
static struct MHD_Response *internal_error_response;
Packit 875988
Packit 875988
/**
Packit 875988
 * Response returned for '/' (GET) to list the contents of the directory and allow upload.
Packit 875988
 */
Packit 875988
static struct MHD_Response *cached_directory_response;
Packit 875988
Packit 875988
/**
Packit 875988
 * Response returned for refused uploads.
Packit 875988
 */
Packit 875988
static struct MHD_Response *request_refused_response;
Packit 875988
Packit 875988
/**
Packit 875988
 * Mutex used when we update the cached directory response object.
Packit 875988
 */
Packit 875988
static pthread_mutex_t mutex;
Packit 875988
Packit 875988
#ifdef MHD_HAVE_LIBMAGIC
Packit 875988
/**
Packit 875988
 * Global handle to MAGIC data.
Packit 875988
 */
Packit 875988
static magic_t magic;
Packit 875988
#endif /* MHD_HAVE_LIBMAGIC */
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Mark the given response as HTML for the brower.
Packit 875988
 *
Packit 875988
 * @param response response to mark
Packit 875988
 */
Packit 875988
static void
Packit 875988
mark_as_html (struct MHD_Response *response)
Packit 875988
{
Packit 875988
  (void) MHD_add_response_header (response,
Packit 875988
				  MHD_HTTP_HEADER_CONTENT_TYPE,
Packit 875988
				  "text/html");
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Replace the existing 'cached_directory_response' with the
Packit 875988
 * given response.
Packit 875988
 *
Packit 875988
 * @param response new directory response
Packit 875988
 */
Packit 875988
static void
Packit 875988
update_cached_response (struct MHD_Response *response)
Packit 875988
{
Packit 875988
  (void) pthread_mutex_lock (&mutex);
Packit 875988
  if (NULL != cached_directory_response)
Packit 875988
    MHD_destroy_response (cached_directory_response);
Packit 875988
  cached_directory_response = response;
Packit 875988
  (void) pthread_mutex_unlock (&mutex);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Context keeping the data for the response we're building.
Packit 875988
 */
Packit 875988
struct ResponseDataContext
Packit 875988
{
Packit 875988
  /**
Packit 875988
   * Response data string.
Packit 875988
   */
Packit 875988
  char *buf;
Packit 875988
Packit 875988
  /**
Packit 875988
   * Number of bytes allocated for 'buf'.
Packit 875988
   */
Packit 875988
  size_t buf_len;
Packit 875988
Packit 875988
  /**
Packit 875988
   * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
Packit 875988
   */
Packit 875988
  size_t off;
Packit 875988
Packit 875988
};
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Create a listing of the files in 'dirname' in HTML.
Packit 875988
 *
Packit 875988
 * @param rdc where to store the list of files
Packit 875988
 * @param dirname name of the directory to list
Packit 875988
 * @return #MHD_YES on success, #MHD_NO on error
Packit 875988
 */
Packit 875988
static int
Packit 875988
list_directory (struct ResponseDataContext *rdc,
Packit 875988
		const char *dirname)
Packit 875988
{
Packit 875988
  char fullname[PATH_MAX];
Packit 875988
  struct stat sbuf;
Packit 875988
  DIR *dir;
Packit 875988
  struct dirent *de;
Packit 875988
Packit 875988
  if (NULL == (dir = opendir (dirname)))
Packit 875988
    return MHD_NO;
Packit 875988
  while (NULL != (de = readdir (dir)))
Packit 875988
    {
Packit 875988
      if ('.' == de->d_name[0])
Packit 875988
	continue;
Packit 875988
      if (sizeof (fullname) <= (unsigned int)
Packit 875988
	  snprintf (fullname, sizeof (fullname),
Packit 875988
		    "%s/%s",
Packit 875988
		    dirname, de->d_name))
Packit 875988
	continue; /* ugh, file too long? how can this be!? */
Packit 875988
      if (0 != stat (fullname, &sbuf))
Packit 875988
	continue; /* ugh, failed to 'stat' */
Packit 875988
      if (! S_ISREG (sbuf.st_mode))
Packit 875988
	continue; /* not a regular file, skip */
Packit 875988
      if (rdc->off + 1024 > rdc->buf_len)
Packit 875988
	{
Packit 875988
	  void *r;
Packit 875988
Packit 875988
	  if ( (2 * rdc->buf_len + 1024) < rdc->buf_len)
Packit 875988
	    break; /* more than SIZE_T _index_ size? Too big for us */
Packit 875988
	  rdc->buf_len = 2 * rdc->buf_len + 1024;
Packit 875988
	  if (NULL == (r = realloc (rdc->buf, rdc->buf_len)))
Packit 875988
	    break; /* out of memory */
Packit 875988
	  rdc->buf = r;
Packit 875988
	}
Packit 875988
      rdc->off += snprintf (&rdc->buf[rdc->off],
Packit 875988
			    rdc->buf_len - rdc->off,
Packit 875988
			    "
  • %s
  • \n",
    Packit 875988
    			    fullname,
    Packit 875988
    			    de->d_name);
    Packit 875988
        }
    Packit 875988
      (void) closedir (dir);
    Packit 875988
      return MHD_YES;
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Re-scan our local directory and re-build the index.
    Packit 875988
     */
    Packit 875988
    static void
    Packit 875988
    update_directory ()
    Packit 875988
    {
    Packit 875988
      static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */
    Packit 875988
      struct MHD_Response *response;
    Packit 875988
      struct ResponseDataContext rdc;
    Packit 875988
      unsigned int language_idx;
    Packit 875988
      unsigned int category_idx;
    Packit 875988
      const struct Language *language;
    Packit 875988
      const char *category;
    Packit 875988
      char dir_name[128];
    Packit 875988
      struct stat sbuf;
    Packit 875988
    Packit 875988
      rdc.buf_len = initial_allocation;
    Packit 875988
      if (NULL == (rdc.buf = malloc (rdc.buf_len)))
    Packit 875988
        {
    Packit 875988
          update_cached_response (NULL);
    Packit 875988
          return;
    Packit 875988
        }
    Packit 875988
      rdc.off = snprintf (rdc.buf, rdc.buf_len,
    Packit 875988
    		      "%s",
    Packit 875988
    		      INDEX_PAGE_HEADER);
    Packit 875988
      for (language_idx = 0; NULL != languages[language_idx].dirname; language_idx++)
    Packit 875988
        {
    Packit 875988
          language = &languages[language_idx];
    Packit 875988
    Packit 875988
          if (0 != stat (language->dirname, &sbuf))
    Packit 875988
    	continue; /* empty */
    Packit 875988
          /* we ensured always +1k room, filenames are ~256 bytes,
    Packit 875988
    	 so there is always still enough space for the header
    Packit 875988
    	 without need for an additional reallocation check. */
    Packit 875988
          rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    Packit 875988
    			   "

    %s

    \n",
    Packit 875988
    			   language->longname);
    Packit 875988
          for (category_idx = 0; NULL != categories[category_idx]; category_idx++)
    Packit 875988
    	{
    Packit 875988
    	  category = categories[category_idx];
    Packit 875988
    	  snprintf (dir_name, sizeof (dir_name),
    Packit 875988
    		    "%s/%s",
    Packit 875988
    		    language->dirname,
    Packit 875988
    		    category);
    Packit 875988
    	  if (0 != stat (dir_name, &sbuf))
    Packit 875988
    	    continue; /* empty */
    Packit 875988
    Packit 875988
    	  /* we ensured always +1k room, filenames are ~256 bytes,
    Packit 875988
    	     so there is always still enough space for the header
    Packit 875988
    	     without need for an additional reallocation check. */
    Packit 875988
    	  rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    Packit 875988
    			       "

    %s

    \n",
    Packit 875988
    			       category);
    Packit 875988
    Packit 875988
    	  if (MHD_NO == list_directory (&rdc, dir_name))
    Packit 875988
    	    {
    Packit 875988
    	      free (rdc.buf);
    Packit 875988
    	      update_cached_response (NULL);
    Packit 875988
    	      return;
    Packit 875988
    	    }
    Packit 875988
    	}
    Packit 875988
        }
    Packit 875988
      /* we ensured always +1k room, filenames are ~256 bytes,
    Packit 875988
         so there is always still enough space for the footer
    Packit 875988
         without need for a final reallocation check. */
    Packit 875988
      rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
    Packit 875988
    		       "%s",
    Packit 875988
    		       INDEX_PAGE_FOOTER);
    Packit 875988
      initial_allocation = rdc.buf_len; /* remember for next time */
    Packit 875988
      response = MHD_create_response_from_buffer (rdc.off,
    Packit 875988
    					      rdc.buf,
    Packit 875988
    					      MHD_RESPMEM_MUST_FREE);
    Packit 875988
      mark_as_html (response);
    Packit 875988
    #if FORCE_CLOSE
    Packit 875988
      (void) MHD_add_response_header (response,
    Packit 875988
    				  MHD_HTTP_HEADER_CONNECTION,
    Packit 875988
    				  "close");
    Packit 875988
    #endif
    Packit 875988
      update_cached_response (response);
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Context we keep for an upload.
    Packit 875988
     */
    Packit 875988
    struct UploadContext
    Packit 875988
    {
    Packit 875988
      /**
    Packit 875988
       * Handle where we write the uploaded file to.
    Packit 875988
       */
    Packit 875988
      int fd;
    Packit 875988
    Packit 875988
      /**
    Packit 875988
       * Name of the file on disk (used to remove on errors).
    Packit 875988
       */
    Packit 875988
      char *filename;
    Packit 875988
    Packit 875988
      /**
    Packit 875988
       * Language for the upload.
    Packit 875988
       */
    Packit 875988
      char *language;
    Packit 875988
    Packit 875988
      /**
    Packit 875988
       * Category for the upload.
    Packit 875988
       */
    Packit 875988
      char *category;
    Packit 875988
    Packit 875988
      /**
    Packit 875988
       * Post processor we're using to process the upload.
    Packit 875988
       */
    Packit 875988
      struct MHD_PostProcessor *pp;
    Packit 875988
    Packit 875988
      /**
    Packit 875988
       * Handle to connection that we're processing the upload for.
    Packit 875988
       */
    Packit 875988
      struct MHD_Connection *connection;
    Packit 875988
    Packit 875988
      /**
    Packit 875988
       * Response to generate, NULL to use directory.
    Packit 875988
       */
    Packit 875988
      struct MHD_Response *response;
    Packit 875988
    };
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Append the 'size' bytes from 'data' to '*ret', adding
    Packit 875988
     * 0-termination.  If '*ret' is NULL, allocate an empty string first.
    Packit 875988
     *
    Packit 875988
     * @param ret string to update, NULL or 0-terminated
    Packit 875988
     * @param data data to append
    Packit 875988
     * @param size number of bytes in 'data'
    Packit 875988
     * @return MHD_NO on allocation failure, MHD_YES on success
    Packit 875988
     */
    Packit 875988
    static int
    Packit 875988
    do_append (char **ret,
    Packit 875988
    	   const char *data,
    Packit 875988
    	   size_t size)
    Packit 875988
    {
    Packit 875988
      char *buf;
    Packit 875988
      size_t old_len;
    Packit 875988
    Packit 875988
      if (NULL == *ret)
    Packit 875988
        old_len = 0;
    Packit 875988
      else
    Packit 875988
        old_len = strlen (*ret);
    Packit 875988
      buf = malloc (old_len + size + 1);
    Packit 875988
      if (NULL == buf)
    Packit 875988
        return MHD_NO;
    Packit 875988
      memcpy (buf, *ret, old_len);
    Packit 875988
      if (NULL != *ret)
    Packit 875988
        free (*ret);
    Packit 875988
      memcpy (&buf[old_len], data, size);
    Packit 875988
      buf[old_len + size] = '\0';
    Packit 875988
      *ret = buf;
    Packit 875988
      return MHD_YES;
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Iterator over key-value pairs where the value
    Packit 875988
     * maybe made available in increments and/or may
    Packit 875988
     * not be zero-terminated.  Used for processing
    Packit 875988
     * POST data.
    Packit 875988
     *
    Packit 875988
     * @param cls user-specified closure
    Packit 875988
     * @param kind type of the value, always MHD_POSTDATA_KIND when called from MHD
    Packit 875988
     * @param key 0-terminated key for the value
    Packit 875988
     * @param filename name of the uploaded file, NULL if not known
    Packit 875988
     * @param content_type mime-type of the data, NULL if not known
    Packit 875988
     * @param transfer_encoding encoding of the data, NULL if not known
    Packit 875988
     * @param data pointer to size bytes of data at the
    Packit 875988
     *              specified offset
    Packit 875988
     * @param off offset of data in the overall value
    Packit 875988
     * @param size number of bytes in data available
    Packit 875988
     * @return MHD_YES to continue iterating,
    Packit 875988
     *         MHD_NO to abort the iteration
    Packit 875988
     */
    Packit 875988
    static int
    Packit 875988
    process_upload_data (void *cls,
    Packit 875988
    		     enum MHD_ValueKind kind,
    Packit 875988
    		     const char *key,
    Packit 875988
    		     const char *filename,
    Packit 875988
    		     const char *content_type,
    Packit 875988
    		     const char *transfer_encoding,
    Packit 875988
    		     const char *data,
    Packit 875988
    		     uint64_t off,
    Packit 875988
    		     size_t size)
    Packit 875988
    {
    Packit 875988
      struct UploadContext *uc = cls;
    Packit 875988
      int i;
    Packit 875988
      (void)kind;              /* Unused. Silent compiler warning. */
    Packit 875988
      (void)content_type;      /* Unused. Silent compiler warning. */
    Packit 875988
      (void)transfer_encoding; /* Unused. Silent compiler warning. */
    Packit 875988
      (void)off;               /* Unused. Silent compiler warning. */
    Packit 875988
    Packit 875988
      if (0 == strcmp (key, "category"))
    Packit 875988
        return do_append (&uc->category, data, size);
    Packit 875988
      if (0 == strcmp (key, "language"))
    Packit 875988
        return do_append (&uc->language, data, size);
    Packit 875988
      if (0 != strcmp (key, "upload"))
    Packit 875988
        {
    Packit 875988
          fprintf (stderr,
    Packit 875988
    	       "Ignoring unexpected form value `%s'\n",
    Packit 875988
    	       key);
    Packit 875988
          return MHD_YES; /* ignore */
    Packit 875988
        }
    Packit 875988
      if (NULL == filename)
    Packit 875988
        {
    Packit 875988
          fprintf (stderr, "No filename, aborting upload\n");
    Packit 875988
          return MHD_NO; /* no filename, error */
    Packit 875988
        }
    Packit 875988
      if ( (NULL == uc->category) ||
    Packit 875988
           (NULL == uc->language) )
    Packit 875988
        {
    Packit 875988
          fprintf (stderr,
    Packit 875988
    	       "Missing form data for upload `%s'\n",
    Packit 875988
    	       filename);
    Packit 875988
          uc->response = request_refused_response;
    Packit 875988
          return MHD_NO;
    Packit 875988
        }
    Packit 875988
      if (-1 == uc->fd)
    Packit 875988
        {
    Packit 875988
          char fn[PATH_MAX];
    Packit 875988
    Packit 875988
          if ( (NULL != strstr (filename, "..")) ||
    Packit 875988
    	   (NULL != strchr (filename, '/')) ||
    Packit 875988
    	   (NULL != strchr (filename, '\\')) )
    Packit 875988
    	{
    Packit 875988
    	  uc->response = request_refused_response;
    Packit 875988
    	  return MHD_NO;
    Packit 875988
    	}
    Packit 875988
          /* create directories -- if they don't exist already */
    Packit 875988
    #ifdef WINDOWS
    Packit 875988
          (void) mkdir (uc->language);
    Packit 875988
    #else
    Packit 875988
          (void) mkdir (uc->language, S_IRWXU);
    Packit 875988
    #endif
    Packit 875988
          snprintf (fn, sizeof (fn),
    Packit 875988
    		"%s/%s",
    Packit 875988
    		uc->language,
    Packit 875988
    		uc->category);
    Packit 875988
    #ifdef WINDOWS
    Packit 875988
          (void) mkdir (fn);
    Packit 875988
    #else
    Packit 875988
          (void) mkdir (fn, S_IRWXU);
    Packit 875988
    #endif
    Packit 875988
          /* open file */
    Packit 875988
          snprintf (fn, sizeof (fn),
    Packit 875988
    		"%s/%s/%s",
    Packit 875988
    		uc->language,
    Packit 875988
    		uc->category,
    Packit 875988
    		filename);
    Packit 875988
          for (i=strlen (fn)-1;i>=0;i--)
    Packit 875988
    	if (! isprint ((unsigned char) fn[i]))
    Packit 875988
    	  fn[i] = '_';
    Packit 875988
          uc->fd = open (fn,
    Packit 875988
    		     O_CREAT | O_EXCL
    Packit 875988
    #if O_LARGEFILE
    Packit 875988
    		     | O_LARGEFILE
    Packit 875988
    #endif
    Packit 875988
    		     | O_WRONLY,
    Packit 875988
    		     S_IRUSR | S_IWUSR);
    Packit 875988
          if (-1 == uc->fd)
    Packit 875988
    	{
    Packit 875988
    	  fprintf (stderr,
    Packit 875988
    		   "Error opening file `%s' for upload: %s\n",
    Packit 875988
    		   fn,
    Packit 875988
    		   strerror (errno));
    Packit 875988
    	  uc->response = request_refused_response;
    Packit 875988
    	  return MHD_NO;
    Packit 875988
    	}
    Packit 875988
          uc->filename = strdup (fn);
    Packit 875988
        }
    Packit 875988
      if ( (0 != size) &&
    Packit 875988
           (size != (size_t) write (uc->fd, data, size)) )
    Packit 875988
        {
    Packit 875988
          /* write failed; likely: disk full */
    Packit 875988
          fprintf (stderr,
    Packit 875988
    	       "Error writing to file `%s': %s\n",
    Packit 875988
    	       uc->filename,
    Packit 875988
    	       strerror (errno));
    Packit 875988
          uc->response = internal_error_response;
    Packit 875988
          (void) close (uc->fd);
    Packit 875988
          uc->fd = -1;
    Packit 875988
          if (NULL != uc->filename)
    Packit 875988
    	{
    Packit 875988
    	  unlink (uc->filename);
    Packit 875988
    	  free (uc->filename);
    Packit 875988
    	  uc->filename = NULL;
    Packit 875988
    	}
    Packit 875988
          return MHD_NO;
    Packit 875988
        }
    Packit 875988
      return MHD_YES;
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Function called whenever a request was completed.
    Packit 875988
     * Used to clean up 'struct UploadContext' objects.
    Packit 875988
     *
    Packit 875988
     * @param cls client-defined closure, NULL
    Packit 875988
     * @param connection connection handle
    Packit 875988
     * @param con_cls value as set by the last call to
    Packit 875988
     *        the MHD_AccessHandlerCallback, points to NULL if this was
    Packit 875988
     *            not an upload
    Packit 875988
     * @param toe reason for request termination
    Packit 875988
     */
    Packit 875988
    static void
    Packit 875988
    response_completed_callback (void *cls,
    Packit 875988
    			     struct MHD_Connection *connection,
    Packit 875988
    			     void **con_cls,
    Packit 875988
    			     enum MHD_RequestTerminationCode toe)
    Packit 875988
    {
    Packit 875988
      struct UploadContext *uc = *con_cls;
    Packit 875988
      (void)cls;         /* Unused. Silent compiler warning. */
    Packit 875988
      (void)connection;  /* Unused. Silent compiler warning. */
    Packit 875988
      (void)toe;         /* Unused. Silent compiler warning. */
    Packit 875988
    Packit 875988
      if (NULL == uc)
    Packit 875988
        return; /* this request wasn't an upload request */
    Packit 875988
      if (NULL != uc->pp)
    Packit 875988
        {
    Packit 875988
          MHD_destroy_post_processor (uc->pp);
    Packit 875988
          uc->pp = NULL;
    Packit 875988
        }
    Packit 875988
      if (-1 != uc->fd)
    Packit 875988
      {
    Packit 875988
        (void) close (uc->fd);
    Packit 875988
        if (NULL != uc->filename)
    Packit 875988
          {
    Packit 875988
    	fprintf (stderr,
    Packit 875988
    		 "Upload of file `%s' failed (incomplete or aborted), removing file.\n",
    Packit 875988
    		 uc->filename);
    Packit 875988
    	(void) unlink (uc->filename);
    Packit 875988
          }
    Packit 875988
      }
    Packit 875988
      if (NULL != uc->filename)
    Packit 875988
        free (uc->filename);
    Packit 875988
      free (uc);
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Return the current directory listing.
    Packit 875988
     *
    Packit 875988
     * @param connection connection to return the directory for
    Packit 875988
     * @return MHD_YES on success, MHD_NO on error
    Packit 875988
     */
    Packit 875988
    static int
    Packit 875988
    return_directory_response (struct MHD_Connection *connection)
    Packit 875988
    {
    Packit 875988
      int ret;
    Packit 875988
    Packit 875988
      (void) pthread_mutex_lock (&mutex);
    Packit 875988
      if (NULL == cached_directory_response)
    Packit 875988
        ret = MHD_queue_response (connection,
    Packit 875988
    			      MHD_HTTP_INTERNAL_SERVER_ERROR,
    Packit 875988
    			      internal_error_response);
    Packit 875988
      else
    Packit 875988
        ret = MHD_queue_response (connection,
    Packit 875988
    			      MHD_HTTP_OK,
    Packit 875988
    			      cached_directory_response);
    Packit 875988
      (void) pthread_mutex_unlock (&mutex);
    Packit 875988
      return ret;
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Main callback from MHD, used to generate the page.
    Packit 875988
     *
    Packit 875988
     * @param cls NULL
    Packit 875988
     * @param connection connection handle
    Packit 875988
     * @param url requested URL
    Packit 875988
     * @param method GET, PUT, POST, etc.
    Packit 875988
     * @param version HTTP version
    Packit 875988
     * @param upload_data data from upload (PUT/POST)
    Packit 875988
     * @param upload_data_size number of bytes in "upload_data"
    Packit 875988
     * @param ptr our context
    Packit 875988
     * @return MHD_YES on success, MHD_NO to drop connection
    Packit 875988
     */
    Packit 875988
    static int
    Packit 875988
    generate_page (void *cls,
    Packit 875988
    	       struct MHD_Connection *connection,
    Packit 875988
    	       const char *url,
    Packit 875988
    	       const char *method,
    Packit 875988
    	       const char *version,
    Packit 875988
    	       const char *upload_data,
    Packit 875988
    	       size_t *upload_data_size, void **ptr)
    Packit 875988
    {
    Packit 875988
      struct MHD_Response *response;
    Packit 875988
      int ret;
    Packit 875988
      int fd;
    Packit 875988
      struct stat buf;
    Packit 875988
      (void)cls;               /* Unused. Silent compiler warning. */
    Packit 875988
      (void)version;           /* Unused. Silent compiler warning. */
    Packit 875988
    Packit 875988
      if (0 != strcmp (url, "/"))
    Packit 875988
        {
    Packit 875988
          /* should be file download */
    Packit 875988
    #ifdef MHD_HAVE_LIBMAGIC
    Packit 875988
          char file_data[MAGIC_HEADER_SIZE];
    Packit 875988
          ssize_t got;
    Packit 875988
    #endif /* MHD_HAVE_LIBMAGIC */
    Packit 875988
          const char *mime;
    Packit 875988
    Packit 875988
          if ( (0 != strcmp (method, MHD_HTTP_METHOD_GET)) &&
    Packit 875988
               (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)) )
    Packit 875988
            return MHD_NO;  /* unexpected method (we're not polite...) */
    Packit 875988
          fd = -1;
    Packit 875988
          if ( (NULL == strstr (&url[1], "..")) &&
    Packit 875988
    	   ('/' != url[1]) )
    Packit 875988
            {
    Packit 875988
              fd = open (&url[1], O_RDONLY);
    Packit 875988
              if ( (-1 != fd) &&
    Packit 875988
                   ( (0 != fstat (fd, &buf)) ||
    Packit 875988
                     (! S_ISREG (buf.st_mode)) ) )
    Packit 875988
                {
    Packit 875988
                  (void) close (fd);
    Packit 875988
                  fd = -1;
    Packit 875988
                }
    Packit 875988
            }
    Packit 875988
          if (-1 == fd)
    Packit 875988
    	return MHD_queue_response (connection,
    Packit 875988
    				   MHD_HTTP_NOT_FOUND,
    Packit 875988
    				   file_not_found_response);
    Packit 875988
    #ifdef MHD_HAVE_LIBMAGIC
    Packit 875988
          /* read beginning of the file to determine mime type  */
    Packit 875988
          got = read (fd, file_data, sizeof (file_data));
    Packit 875988
          (void) lseek (fd, 0, SEEK_SET);
    Packit 875988
          if (-1 != got)
    Packit 875988
    	mime = magic_buffer (magic, file_data, got);
    Packit 875988
          else
    Packit 875988
    #endif /* MHD_HAVE_LIBMAGIC */
    Packit 875988
    	mime = NULL;
    Packit 875988
    Packit 875988
          if (NULL == (response = MHD_create_response_from_fd (buf.st_size,
    Packit 875988
    							   fd)))
    Packit 875988
    	{
    Packit 875988
    	  /* internal error (i.e. out of memory) */
    Packit 875988
    	  (void) close (fd);
    Packit 875988
    	  return MHD_NO;
    Packit 875988
    	}
    Packit 875988
    Packit 875988
          /* add mime type if we had one */
    Packit 875988
          if (NULL != mime)
    Packit 875988
    	(void) MHD_add_response_header (response,
    Packit 875988
    					MHD_HTTP_HEADER_CONTENT_TYPE,
    Packit 875988
    					mime);
    Packit 875988
          ret = MHD_queue_response (connection,
    Packit 875988
    				MHD_HTTP_OK,
    Packit 875988
    				response);
    Packit 875988
          MHD_destroy_response (response);
    Packit 875988
          return ret;
    Packit 875988
        }
    Packit 875988
    Packit 875988
      if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
    Packit 875988
        {
    Packit 875988
          /* upload! */
    Packit 875988
          struct UploadContext *uc = *ptr;
    Packit 875988
    Packit 875988
          if (NULL == uc)
    Packit 875988
    	{
    Packit 875988
    	  if (NULL == (uc = malloc (sizeof (struct UploadContext))))
    Packit 875988
    	    return MHD_NO; /* out of memory, close connection */
    Packit 875988
    	  memset (uc, 0, sizeof (struct UploadContext));
    Packit 875988
              uc->fd = -1;
    Packit 875988
    	  uc->connection = connection;
    Packit 875988
    	  uc->pp = MHD_create_post_processor (connection,
    Packit 875988
    					      64 * 1024 /* buffer size */,
    Packit 875988
    					      &process_upload_data, uc);
    Packit 875988
    	  if (NULL == uc->pp)
    Packit 875988
    	    {
    Packit 875988
    	      /* out of memory, close connection */
    Packit 875988
    	      free (uc);
    Packit 875988
    	      return MHD_NO;
    Packit 875988
    	    }
    Packit 875988
    	  *ptr = uc;
    Packit 875988
    	  return MHD_YES;
    Packit 875988
    	}
    Packit 875988
          if (0 != *upload_data_size)
    Packit 875988
    	{
    Packit 875988
    	  if (NULL == uc->response)
    Packit 875988
    	    (void) MHD_post_process (uc->pp,
    Packit 875988
    				     upload_data,
    Packit 875988
    				     *upload_data_size);
    Packit 875988
    	  *upload_data_size = 0;
    Packit 875988
    	  return MHD_YES;
    Packit 875988
    	}
    Packit 875988
          /* end of upload, finish it! */
    Packit 875988
          MHD_destroy_post_processor (uc->pp);
    Packit 875988
          uc->pp = NULL;
    Packit 875988
          if (-1 != uc->fd)
    Packit 875988
    	{
    Packit 875988
    	  close (uc->fd);
    Packit 875988
    	  uc->fd = -1;
    Packit 875988
    	}
    Packit 875988
          if (NULL != uc->response)
    Packit 875988
    	{
    Packit 875988
    	  return MHD_queue_response (connection,
    Packit 875988
    				     MHD_HTTP_FORBIDDEN,
    Packit 875988
    				     uc->response);
    Packit 875988
    	}
    Packit 875988
          else
    Packit 875988
    	{
    Packit 875988
    	  update_directory ();
    Packit 875988
    	  return return_directory_response (connection);
    Packit 875988
    	}
    Packit 875988
        }
    Packit 875988
      if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
    Packit 875988
           (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
    Packit 875988
      {
    Packit 875988
        return return_directory_response (connection);
    Packit 875988
      }
    Packit 875988
    Packit 875988
      /* unexpected request, refuse */
    Packit 875988
      return MHD_queue_response (connection,
    Packit 875988
    			     MHD_HTTP_FORBIDDEN,
    Packit 875988
    			     request_refused_response);
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    #ifndef MINGW
    Packit 875988
    /**
    Packit 875988
     * Function called if we get a SIGPIPE. Does nothing.
    Packit 875988
     *
    Packit 875988
     * @param sig will be SIGPIPE (ignored)
    Packit 875988
     */
    Packit 875988
    static void
    Packit 875988
    catcher (int sig)
    Packit 875988
    {
    Packit 875988
      (void)sig;	/* Unused. Silent compiler warning. */
    Packit 875988
      /* do nothing */
    Packit 875988
    }
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * setup handlers to ignore SIGPIPE.
    Packit 875988
     */
    Packit 875988
    static void
    Packit 875988
    ignore_sigpipe ()
    Packit 875988
    {
    Packit 875988
      struct sigaction oldsig;
    Packit 875988
      struct sigaction sig;
    Packit 875988
    Packit 875988
      sig.sa_handler = &catcher;
    Packit 875988
      sigemptyset (&sig.sa_mask);
    Packit 875988
    #ifdef SA_INTERRUPT
    Packit 875988
      sig.sa_flags = SA_INTERRUPT;  /* SunOS */
    Packit 875988
    #else
    Packit 875988
      sig.sa_flags = SA_RESTART;
    Packit 875988
    #endif
    Packit 875988
      if (0 != sigaction (SIGPIPE, &sig, &oldsig))
    Packit 875988
        fprintf (stderr,
    Packit 875988
                 "Failed to install SIGPIPE handler: %s\n", strerror (errno));
    Packit 875988
    }
    Packit 875988
    #endif
    Packit 875988
    Packit 875988
    Packit 875988
    /**
    Packit 875988
     * Entry point to demo.  Note: this HTTP server will make all
    Packit 875988
     * files in the current directory and its subdirectories available
    Packit 875988
     * to anyone.  Press ENTER to stop the server once it has started.
    Packit 875988
     *
    Packit 875988
     * @param argc number of arguments in argv
    Packit 875988
     * @param argv first and only argument should be the port number
    Packit 875988
     * @return 0 on success
    Packit 875988
     */
    Packit 875988
    int
    Packit 875988
    main (int argc, char *const *argv)
    Packit 875988
    {
    Packit 875988
      struct MHD_Daemon *d;
    Packit 875988
      unsigned int port;
    Packit 875988
    Packit 875988
      if ( (argc != 2) ||
    Packit 875988
           (1 != sscanf (argv[1], "%u", &port)) ||
    Packit 875988
           (UINT16_MAX < port) )
    Packit 875988
        {
    Packit 875988
          fprintf (stderr,
    Packit 875988
    	       "%s PORT\n", argv[0]);
    Packit 875988
          return 1;
    Packit 875988
        }
    Packit 875988
    #ifndef MINGW
    Packit 875988
      ignore_sigpipe ();
    Packit 875988
    #endif
    Packit 875988
    #ifdef MHD_HAVE_LIBMAGIC
    Packit 875988
      magic = magic_open (MAGIC_MIME_TYPE);
    Packit 875988
      (void) magic_load (magic, NULL);
    Packit 875988
    #endif /* MHD_HAVE_LIBMAGIC */
    Packit 875988
    Packit 875988
      (void) pthread_mutex_init (&mutex, NULL);
    Packit 875988
      file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE),
    Packit 875988
    							     (void *) FILE_NOT_FOUND_PAGE,
    Packit 875988
    							     MHD_RESPMEM_PERSISTENT);
    Packit 875988
      mark_as_html (file_not_found_response);
    Packit 875988
      request_refused_response = MHD_create_response_from_buffer (strlen (REQUEST_REFUSED_PAGE),
    Packit 875988
    							     (void *) REQUEST_REFUSED_PAGE,
    Packit 875988
    							     MHD_RESPMEM_PERSISTENT);
    Packit 875988
      mark_as_html (request_refused_response);
    Packit 875988
      internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE),
    Packit 875988
    							     (void *) INTERNAL_ERROR_PAGE,
    Packit 875988
    							     MHD_RESPMEM_PERSISTENT);
    Packit 875988
      mark_as_html (internal_error_response);
    Packit 875988
      update_directory ();
    Packit 875988
      d = MHD_start_daemon (MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG,
    Packit 875988
                            port,
    Packit 875988
                            NULL, NULL,
    Packit 875988
    			&generate_page, NULL,
    Packit 875988
    			MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024),
    Packit 875988
    #if PRODUCTION
    Packit 875988
    			MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64),
    Packit 875988
    #endif
    Packit 875988
    			MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) (120 /* seconds */),
    Packit 875988
    			MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS,
    Packit 875988
    			MHD_OPTION_NOTIFY_COMPLETED, &response_completed_callback, NULL,
    Packit 875988
    			MHD_OPTION_END);
    Packit 875988
      if (NULL == d)
    Packit 875988
        return 1;
    Packit 875988
      fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n");
    Packit 875988
      (void) getc (stdin);
    Packit 875988
      MHD_stop_daemon (d);
    Packit 875988
      MHD_destroy_response (file_not_found_response);
    Packit 875988
      MHD_destroy_response (request_refused_response);
    Packit 875988
      MHD_destroy_response (internal_error_response);
    Packit 875988
      update_cached_response (NULL);
    Packit 875988
      (void) pthread_mutex_destroy (&mutex);
    Packit 875988
    #ifdef MHD_HAVE_LIBMAGIC
    Packit 875988
      magic_close (magic);
    Packit 875988
    #endif /* MHD_HAVE_LIBMAGIC */
    Packit 875988
      return 0;
    Packit 875988
    }
    Packit 875988
    Packit 875988
    /* end of demo.c */