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