aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-03-31 20:22:02 +0000
committerChristian Grothoff <christian@grothoff.org>2013-03-31 20:22:02 +0000
commit4b0588d78b33db21c483d52ade209bb0bf27a6dc (patch)
tree01658741162a9d8112dadc14782bd66634e097d5
parent7b8fb41d1e766ed2285f3ad57f9d21f174471a53 (diff)
downloadlibmicrohttpd-4b0588d78b33db21c483d52ade209bb0bf27a6dc.tar.gz
libmicrohttpd-4b0588d78b33db21c483d52ade209bb0bf27a6dc.zip
add mime types to demo
-rw-r--r--configure.ac8
-rw-r--r--src/examples/Makefile.am8
-rw-r--r--src/examples/demo.c301
3 files changed, 246 insertions, 71 deletions
diff --git a/configure.ac b/configure.ac
index 1a1e312a..ad3b1029 100644
--- a/configure.ac
+++ b/configure.ac
@@ -255,6 +255,14 @@ then
255 AC_DEFINE_UNQUOTED([MHD_REQ_CURL_GNUTLS_VERSION], "$MHD_REQ_CURL_GNUTLS_VERSION", [gnuTLS lib version - used in conjunction with cURL]) 255 AC_DEFINE_UNQUOTED([MHD_REQ_CURL_GNUTLS_VERSION], "$MHD_REQ_CURL_GNUTLS_VERSION", [gnuTLS lib version - used in conjunction with cURL])
256 AC_DEFINE_UNQUOTED([MHD_REQ_CURL_NSS_VERSION], "$MHD_REQ_CURL_NSS_VERSION", [NSS lib version - used in conjunction with cURL]) 256 AC_DEFINE_UNQUOTED([MHD_REQ_CURL_NSS_VERSION], "$MHD_REQ_CURL_NSS_VERSION", [NSS lib version - used in conjunction with cURL])
257fi 257fi
258
259AC_MSG_CHECKING(for magic_open -lmagic)
260AC_CHECK_LIB(magic, magic_open,
261 [AC_CHECK_HEADERS([magic.h],
262 AM_CONDITIONAL(HAVE_MAGIC, true),
263 AM_CONDITIONAL(HAVE_MAGIC, false))],
264 AM_CONDITIONAL(HAVE_MAGIC, false))
265
258LIBS=$SAVE_LIBS 266LIBS=$SAVE_LIBS
259AM_CONDITIONAL(HAVE_CURL, test x$curl = x1) 267AM_CONDITIONAL(HAVE_CURL, test x$curl = x1)
260 268
diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am
index a27e5268..ef4de773 100644
--- a/src/examples/Makefile.am
+++ b/src/examples/Makefile.am
@@ -29,8 +29,11 @@ noinst_PROGRAMS += https_fileserver_example
29endif 29endif
30if HAVE_POSTPROCESSOR 30if HAVE_POSTPROCESSOR
31noinst_PROGRAMS += \ 31noinst_PROGRAMS += \
32 demo \
33 post_example 32 post_example
33if HAVE_MAGIC
34noinst_PROGRAMS += \
35 demo
36endif
34endif 37endif
35 38
36if ENABLE_DAUTH 39if ENABLE_DAUTH
@@ -55,7 +58,8 @@ minimal_example_LDADD = \
55demo_SOURCES = \ 58demo_SOURCES = \
56 demo.c 59 demo.c
57demo_LDADD = \ 60demo_LDADD = \
58 $(top_builddir)/src/daemon/libmicrohttpd.la 61 $(top_builddir)/src/daemon/libmicrohttpd.la \
62 -lmagic
59 63
60dual_stack_example_SOURCES = \ 64dual_stack_example_SOURCES = \
61 dual_stack_example.c 65 dual_stack_example.c
diff --git a/src/examples/demo.c b/src/examples/demo.c
index 0a600519..600b0f11 100644
--- a/src/examples/demo.c
+++ b/src/examples/demo.c
@@ -23,17 +23,23 @@
23 * @author Christian Grothoff 23 * @author Christian Grothoff
24 * 24 *
25 * TODO: 25 * TODO:
26 * - even with LARGE memory pool & buffers, uploads proceed at 5000 bytes/call (ugh)
27 * - should have a slightly more ambitious upload form & file listing (structure!) 26 * - should have a slightly more ambitious upload form & file listing (structure!)
28 * - may want to add MIME-types to replies
29 */ 27 */
30#include "platform.h" 28#include "platform.h"
31#include <microhttpd.h> 29#include <microhttpd.h>
32#include <unistd.h> 30#include <unistd.h>
33#include <pthread.h> 31#include <pthread.h>
34#include <sys/types.h> 32#include <sys/types.h>
33#include <sys/stat.h>
35#include <dirent.h> 34#include <dirent.h>
35#include <magic.h>
36 36
37/**
38 * How many bytes of a file do we give to libmagic to determine the mime type?
39 * 16k might be a bit excessive, but ought not hurt performance much anyway,
40 * and should definitively be on the safe side.
41 */
42#define MAGIC_HEADER_SIZE (16 * 1024)
37 43
38/** 44/**
39 * Page returned for file-not-found. 45 * Page returned for file-not-found.
@@ -48,6 +54,12 @@
48 54
49 55
50/** 56/**
57 * Page returned for refused requests.
58 */
59#define REQUEST_REFUSED_PAGE "<html><head><title>Request refused</title></head><body>Request refused (file exists?)</body></html>"
60
61
62/**
51 * Head of index page. 63 * Head of index page.
52 */ 64 */
53#define INDEX_PAGE_HEADER "<html>\n<head><title>Welcome</title></head>\n<body>\n"\ 65#define INDEX_PAGE_HEADER "<html>\n<head><title>Welcome</title></head>\n<body>\n"\
@@ -63,6 +75,7 @@
63#define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>" 75#define INDEX_PAGE_FOOTER "</ol>\n</body>\n</html>"
64 76
65 77
78
66/** 79/**
67 * Response returned if the requested file does not exist (or is not accessible). 80 * Response returned if the requested file does not exist (or is not accessible).
68 */ 81 */
@@ -79,10 +92,34 @@ static struct MHD_Response *internal_error_response;
79static struct MHD_Response *cached_directory_response; 92static struct MHD_Response *cached_directory_response;
80 93
81/** 94/**
95 * Response returned for refused uploads.
96 */
97static struct MHD_Response *request_refused_response;
98
99/**
82 * Mutex used when we update the cached directory response object. 100 * Mutex used when we update the cached directory response object.
83 */ 101 */
84static pthread_mutex_t mutex; 102static pthread_mutex_t mutex;
85 103
104/**
105 * Global handle to MAGIC data.
106 */
107static magic_t magic;
108
109
110/**
111 * Mark the given response as HTML for the brower.
112 *
113 * @param response response to mark
114 */
115static void
116mark_as_html (struct MHD_Response *response)
117{
118 (void) MHD_add_response_header (response,
119 MHD_HTTP_HEADER_CONTENT_TYPE,
120 "text/html");
121}
122
86 123
87/** 124/**
88 * Replace the existing 'cached_directory_response' with the 125 * Replace the existing 'cached_directory_response' with the
@@ -102,68 +139,119 @@ update_cached_response (struct MHD_Response *response)
102 139
103 140
104/** 141/**
105 * Re-scan our local directory and re-build the index. 142 * Context keeping the data for the response we're building.
106 */ 143 */
107static void 144struct ResponseDataContext
108update_directory ()
109{ 145{
110 static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */ 146 /**
111 DIR *dir; 147 * Response data string.
112 struct dirent *de; 148 */
113 struct MHD_Response *response;
114 char *buf; 149 char *buf;
150
151 /**
152 * Number of bytes allocated for 'buf'.
153 */
115 size_t buf_len; 154 size_t buf_len;
155
156 /**
157 * Current position where we append to 'buf'. Must be smaller or equal to 'buf_len'.
158 */
116 size_t off; 159 size_t off;
117 160
118 dir = opendir ("."); 161};
119 if (NULL == dir) 162
120 goto err; 163
121 buf_len = initial_allocation; 164/**
122 buf = malloc (buf_len); 165 * Create a listing of the files in 'dirname' in HTML.
123 if (NULL == buf) 166 *
124 { 167 * @param rdc where to store the list of files
125 closedir (dir); 168 * @param dirname name of the directory to list
126 goto err; 169 * @return MHD_YES on success, MHD_NO on error
127 } 170 */
128 off = snprintf (buf, buf_len, 171static int
129 "%s", 172list_directory (struct ResponseDataContext *rdc,
130 INDEX_PAGE_HEADER); 173 const char *dirname)
174{
175 char fullname[PATH_MAX];
176 struct stat sbuf;
177 DIR *dir;
178 struct dirent *de;
179
180 if (NULL == (dir = opendir (dirname)))
181 return MHD_NO;
131 while (NULL != (de = readdir (dir))) 182 while (NULL != (de = readdir (dir)))
132 { 183 {
133 if ('.' == de->d_name[0]) 184 if ('.' == de->d_name[0])
134 continue; 185 continue;
135 if (off + 1024 > buf_len) 186 if (sizeof (fullname) <=
187 snprintf (fullname, sizeof (fullname),
188 "%s/%s",
189 dirname, de->d_name))
190 continue; /* ugh, file too long? how can this be!? */
191 if (0 != stat (fullname, &sbuf))
192 continue; /* ugh, failed to 'stat' */
193 if (! S_ISREG (sbuf.st_mode))
194 continue; /* not a regular file, skip */
195 if (rdc->off + 1024 > rdc->buf_len)
136 { 196 {
137 void *r; 197 void *r;
138 198
139 if ( (2 * buf_len + 1024) < buf_len) 199 if ( (2 * rdc->buf_len + 1024) < rdc->buf_len)
140 break; /* more than SIZE_T _index_ size? Too big for us */ 200 break; /* more than SIZE_T _index_ size? Too big for us */
141 buf_len = 2 * buf_len + 1024; 201 rdc->buf_len = 2 * rdc->buf_len + 1024;
142 if (NULL == (r = realloc (buf, buf_len))) 202 if (NULL == (r = realloc (rdc->buf, rdc->buf_len)))
143 break; /* out of memory */ 203 break; /* out of memory */
144 buf = r; 204 rdc->buf = r;
145 } 205 }
146 off += snprintf (&buf[off], buf_len - off, 206 rdc->off += snprintf (&rdc->buf[rdc->off],
147 "<li><a href=\"/%s\">%s</a></li>\n", 207 rdc->buf_len - rdc->off,
148 de->d_name, 208 "<li><a href=\"/%s\">%s</a></li>\n",
149 de->d_name); 209 de->d_name,
210 de->d_name);
211 }
212 (void) closedir (dir);
213 return MHD_YES;
214}
215
216
217/**
218 * Re-scan our local directory and re-build the index.
219 */
220static void
221update_directory ()
222{
223 static size_t initial_allocation = 32 * 1024; /* initial size for response buffer */
224 struct MHD_Response *response;
225 struct ResponseDataContext rdc;
226
227 rdc.buf_len = initial_allocation;
228 if (NULL == (rdc.buf = malloc (rdc.buf_len)))
229 {
230 update_cached_response (NULL);
231 return;
232 }
233 rdc.off = snprintf (rdc.buf, rdc.buf_len,
234 "%s",
235 INDEX_PAGE_HEADER);
236
237 if (MHD_NO == list_directory (&rdc, "."))
238 {
239 free (rdc.buf);
240 update_cached_response (NULL);
241 return;
150 } 242 }
151 /* we ensured always +1k room, filenames are ~256 bytes, 243 /* we ensured always +1k room, filenames are ~256 bytes,
152 so there is always still enough space for the footer 244 so there is always still enough space for the footer
153 without need for a final reallocation check. */ 245 without need for a final reallocation check. */
154 off += snprintf (&buf[off], buf_len - off, 246 rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
155 "%s", 247 "%s",
156 INDEX_PAGE_FOOTER); 248 INDEX_PAGE_FOOTER);
157 closedir (dir); 249 initial_allocation = rdc.buf_len; /* remember for next time */
158 initial_allocation = buf_len; /* remember for next time */ 250 response = MHD_create_response_from_buffer (rdc.off,
159 response = MHD_create_response_from_buffer (off, 251 rdc.buf,
160 buf,
161 MHD_RESPMEM_MUST_FREE); 252 MHD_RESPMEM_MUST_FREE);
253 mark_as_html (response);
162 update_cached_response (response); 254 update_cached_response (response);
163 return;
164 err:
165 /* failed to list directory, use error page */
166 update_cached_response (NULL);
167} 255}
168 256
169 257
@@ -178,6 +266,11 @@ struct UploadContext
178 int fd; 266 int fd;
179 267
180 /** 268 /**
269 * Name of the file on disk (used to remove on errors).
270 */
271 char *filename;
272
273 /**
181 * Post processor we're using to process the upload. 274 * Post processor we're using to process the upload.
182 */ 275 */
183 struct MHD_PostProcessor *pp; 276 struct MHD_PostProcessor *pp;
@@ -186,6 +279,11 @@ struct UploadContext
186 * Handle to connection that we're processing the upload for. 279 * Handle to connection that we're processing the upload for.
187 */ 280 */
188 struct MHD_Connection *connection; 281 struct MHD_Connection *connection;
282
283 /**
284 * Response to generate, NULL to use directory.
285 */
286 struct MHD_Response *response;
189}; 287};
190 288
191 289
@@ -228,32 +326,48 @@ process_upload_data (void *cls,
228 } 326 }
229 if (-1 == uc->fd) 327 if (-1 == uc->fd)
230 { 328 {
329 if ( (NULL != strstr (filename, "..")) ||
330 (NULL != strchr (filename, '/')) ||
331 (NULL != strchr (filename, '\\')) )
332 {
333 uc->response = request_refused_response;
334 return MHD_NO;
335 }
231 uc->fd = open (filename, 336 uc->fd = open (filename,
232 O_CREAT | O_EXCL 337 O_CREAT | O_EXCL
233#if O_LARGEFILE 338#if O_LARGEFILE
234 | O_LARGEFILE 339 | O_LARGEFILE
235#endif 340#endif
236#if O_NONBLOCK
237 | O_NONBLOCK
238#endif
239 | O_WRONLY, 341 | O_WRONLY,
240 S_IRUSR | S_IWUSR); 342 S_IRUSR | S_IWUSR);
241 if (-1 == uc->fd) 343 if (-1 == uc->fd)
242 { 344 {
243 // FIXME: generate error page NICELY 345 fprintf (stderr,
244 fprintf (stderr, "Error opening file to write!\n"); 346 "Error opening file `%s' for upload: %s\n",
347 filename,
348 strerror (errno));
349 uc->response = request_refused_response;
245 return MHD_NO; 350 return MHD_NO;
246 } 351 }
247 } 352 }
248 else if (0 == size) 353 uc->filename = strdup (filename);
249 sleep (1);
250
251
252 if ( (0 != size) && 354 if ( (0 != size) &&
253 (size != write (uc->fd, data, size)) ) 355 (size != write (uc->fd, data, size)) )
254 { 356 {
255 // FIXME: generate error page NICELY 357 /* write failed; likely: disk full */
256 fprintf (stderr, "Error writing to disk!\n"); 358 fprintf (stderr,
359 "Error writing to file `%s': %s\n",
360 filename,
361 strerror (errno));
362 uc->response = internal_error_response;
363 close (uc->fd);
364 uc->fd = -1;
365 if (NULL != uc->filename)
366 {
367 unlink (uc->filename);
368 free (uc->filename);
369 uc->filename = NULL;
370 }
257 return MHD_NO; 371 return MHD_NO;
258 } 372 }
259 return MHD_YES; 373 return MHD_YES;
@@ -288,10 +402,17 @@ response_completed_callback (void *cls,
288 } 402 }
289 if (-1 != uc->fd) 403 if (-1 != uc->fd)
290 { 404 {
291 close (uc->fd); 405 (void) close (uc->fd);
292 fprintf (stderr, "Possible upload failure, need to remove file?!\n"); 406 if (NULL != uc->filename)
293 /* FIXME: unlink here on error!? */ 407 {
408 fprintf (stderr,
409 "Upload of file `%s' failed (incomplete or aborted), removing file.\n",
410 uc->filename);
411 (void) unlink (uc->filename);
412 }
294 } 413 }
414 if (NULL != uc->filename)
415 free (uc->filename);
295 free (uc); 416 free (uc);
296} 417}
297 418
@@ -303,7 +424,7 @@ response_completed_callback (void *cls,
303 * @return MHD_YES on success, MHD_NO on error 424 * @return MHD_YES on success, MHD_NO on error
304 */ 425 */
305static int 426static int
306list_directory (struct MHD_Connection *connection) 427return_directory_response (struct MHD_Connection *connection)
307{ 428{
308 int ret; 429 int ret;
309 430
@@ -321,7 +442,6 @@ list_directory (struct MHD_Connection *connection)
321} 442}
322 443
323 444
324
325/** 445/**
326 * Main callback from MHD, used to generate the page. 446 * Main callback from MHD, used to generate the page.
327 * 447 *
@@ -352,6 +472,10 @@ generate_page (void *cls,
352 if (0 != strcmp (url, "/")) 472 if (0 != strcmp (url, "/"))
353 { 473 {
354 /* should be file download */ 474 /* should be file download */
475 char file_data[MAGIC_HEADER_SIZE];
476 ssize_t got;
477 const char *mime;
478
355 if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) 479 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
356 return MHD_NO; /* unexpected method (we're not polite...) */ 480 return MHD_NO; /* unexpected method (we're not polite...) */
357 if ( (0 == stat (&url[1], &buf)) && 481 if ( (0 == stat (&url[1], &buf)) &&
@@ -364,6 +488,14 @@ generate_page (void *cls,
364 return MHD_queue_response (connection, 488 return MHD_queue_response (connection,
365 MHD_HTTP_NOT_FOUND, 489 MHD_HTTP_NOT_FOUND,
366 file_not_found_response); 490 file_not_found_response);
491 /* read beginning of the file to determine mime type */
492 got = read (fd, file_data, sizeof (file_data));
493 if (-1 != got)
494 mime = magic_buffer (magic, file_data, got);
495 else
496 mime = NULL;
497 (void) lseek (fd, 0, SEEK_SET);
498
367 if (NULL == (response = MHD_create_response_from_fd (buf.st_size, 499 if (NULL == (response = MHD_create_response_from_fd (buf.st_size,
368 fd))) 500 fd)))
369 { 501 {
@@ -371,6 +503,12 @@ generate_page (void *cls,
371 (void) close (fd); 503 (void) close (fd);
372 return MHD_NO; 504 return MHD_NO;
373 } 505 }
506
507 /* add mime type if we had one */
508 if (NULL != mime)
509 (void) MHD_add_response_header (response,
510 MHD_HTTP_HEADER_CONTENT_TYPE,
511 mime);
374 ret = MHD_queue_response (connection, 512 ret = MHD_queue_response (connection,
375 MHD_HTTP_OK, 513 MHD_HTTP_OK,
376 response); 514 response);
@@ -387,6 +525,8 @@ generate_page (void *cls,
387 { 525 {
388 if (NULL == (uc = malloc (sizeof (struct UploadContext)))) 526 if (NULL == (uc = malloc (sizeof (struct UploadContext))))
389 return MHD_NO; /* out of memory, close connection */ 527 return MHD_NO; /* out of memory, close connection */
528 uc->response = NULL;
529 uc->filename = NULL;
390 uc->fd = -1; 530 uc->fd = -1;
391 uc->connection = connection; 531 uc->connection = connection;
392 uc->pp = MHD_create_post_processor (connection, 532 uc->pp = MHD_create_post_processor (connection,
@@ -400,14 +540,15 @@ generate_page (void *cls,
400 } 540 }
401 *ptr = uc; 541 *ptr = uc;
402 return MHD_YES; 542 return MHD_YES;
403 } 543 }
404 if (0 != *upload_data_size) 544 if (0 != *upload_data_size)
405 { 545 {
406 ret = MHD_post_process (uc->pp, 546 if (NULL != uc->response)
407 upload_data, 547 (void) MHD_post_process (uc->pp,
408 *upload_data_size); 548 upload_data,
549 *upload_data_size);
409 *upload_data_size = 0; 550 *upload_data_size = 0;
410 return ret; 551 return MHD_YES;
411 } 552 }
412 /* end of upload, finish it! */ 553 /* end of upload, finish it! */
413 MHD_destroy_post_processor (uc->pp); 554 MHD_destroy_post_processor (uc->pp);
@@ -417,22 +558,32 @@ generate_page (void *cls,
417 close (uc->fd); 558 close (uc->fd);
418 uc->fd = -1; 559 uc->fd = -1;
419 } 560 }
420 update_directory (); 561 if (NULL != uc->response)
421 return list_directory (connection); 562 {
563 return MHD_queue_response (connection,
564 MHD_HTTP_FORBIDDEN,
565 uc->response);
566 }
567 else
568 {
569 update_directory ();
570 return return_directory_response (connection);
571 }
422 } 572 }
423 if (0 == strcmp (method, MHD_HTTP_METHOD_GET)) 573 if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
424 return list_directory (connection); 574 return return_directory_response (connection);
425 575
426 /* unexpected request, refuse */ 576 /* unexpected request, refuse */
427 fprintf (stderr, "Unexpected request, refusing\n"); 577 return MHD_queue_response (connection,
428 return MHD_NO; 578 MHD_HTTP_FORBIDDEN,
579 request_refused_response);
429} 580}
430 581
431 582
432/** 583/**
433 * Entry point to demo. Note: this HTTP server will make all 584 * Entry point to demo. Note: this HTTP server will make all
434 * files in the current directory and its subdirectories available 585 * files in the current directory and its subdirectories available
435 * to anyone. 586 * to anyone. Press ENTER to stop the server once it has started.
436 * 587 *
437 * @param argc number of arguments in argv 588 * @param argc number of arguments in argv
438 * @param argv first and only argument should be the port number 589 * @param argv first and only argument should be the port number
@@ -452,13 +603,22 @@ main (int argc, char *const *argv)
452 "%s PORT\n", argv[0]); 603 "%s PORT\n", argv[0]);
453 return 1; 604 return 1;
454 } 605 }
606 magic = magic_open (MAGIC_MIME_TYPE);
607 (void) magic_load (magic, NULL);
608
455 (void) pthread_mutex_init (&mutex, NULL); 609 (void) pthread_mutex_init (&mutex, NULL);
456 file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE), 610 file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE),
457 (void *) FILE_NOT_FOUND_PAGE, 611 (void *) FILE_NOT_FOUND_PAGE,
458 MHD_RESPMEM_PERSISTENT); 612 MHD_RESPMEM_PERSISTENT);
613 mark_as_html (file_not_found_response);
614 request_refused_response = MHD_create_response_from_buffer (strlen (REQUEST_REFUSED_PAGE),
615 (void *) REQUEST_REFUSED_PAGE,
616 MHD_RESPMEM_PERSISTENT);
617 mark_as_html (request_refused_response);
459 internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE), 618 internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE),
460 (void *) INTERNAL_ERROR_PAGE, 619 (void *) INTERNAL_ERROR_PAGE,
461 MHD_RESPMEM_PERSISTENT); 620 MHD_RESPMEM_PERSISTENT);
621 mark_as_html (internal_error_response);
462 update_directory (); 622 update_directory ();
463 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, 623 d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG,
464 port, 624 port,
@@ -470,12 +630,15 @@ main (int argc, char *const *argv)
470 MHD_OPTION_END); 630 MHD_OPTION_END);
471 if (NULL == d) 631 if (NULL == d)
472 return 1; 632 return 1;
633 fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n");
473 (void) getc (stdin); 634 (void) getc (stdin);
474 MHD_stop_daemon (d); 635 MHD_stop_daemon (d);
475 MHD_destroy_response (file_not_found_response); 636 MHD_destroy_response (file_not_found_response);
637 MHD_destroy_response (request_refused_response);
476 MHD_destroy_response (internal_error_response); 638 MHD_destroy_response (internal_error_response);
477 update_cached_response (NULL); 639 update_cached_response (NULL);
478 (void) pthread_mutex_destroy (&mutex); 640 (void) pthread_mutex_destroy (&mutex);
641 magic_close (magic);
479 return 0; 642 return 0;
480} 643}
481 644