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 */
117static 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 */
132struct 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 */
150static 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 */
164static struct MHD_Response *file_not_found_response;
165
166/**
167 * Response returned for internal errors.
168 */
169static struct MHD_Response *internal_error_response;
170
171/**
172 * Response returned for '/' (GET) to list the contents of the directory and allow upload.
173 */
174static struct MHD_Response *cached_directory_response;
175
176/**
177 * Response returned for refused uploads.
178 */
179static struct MHD_Response *request_refused_response;
180
181/**
182 * Mutex used when we update the cached directory response object.
183 */
184static pthread_mutex_t mutex;
185
186/**
187 * Global handle to MAGIC data.
188 */
189static magic_t magic;
190
191
192/**
193 * Mark the given response as HTML for the brower.
194 *
195 * @param response response to mark
196 */
197static void
198mark_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 */
212static void
213update_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 */
226struct 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 */
253static int
254list_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 */
302static void
303update_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 */
384struct 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 */
432static int
433do_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 */
476static int
477process_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 */
602static void
603response_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 */
640static int
641return_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 */
672static int
673generate_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 */
803static void
804catcher (int sig)
805{
806  /* do nothing */
807}
808
809
810/**
811 * setup handlers to ignore SIGPIPE.
812 */
813#ifndef MINGW
814static void
815ignore_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 */
843int
844main (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