2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
17 * @file gns/gnunet-bcd.c
18 * @author Christian Grothoff
19 * @brief HTTP server to create GNS business cards
23 #include <microhttpd.h>
24 #include "gnunet_util_lib.h"
27 * Error page to display if submitted GNS key is invalid.
29 #define INVALID_GNSKEY "<html><head><title>Error</title><body>Invalid GNS public key given.</body></html>"
32 * Error page to display on 404.
34 #define NOT_FOUND "<html><head><title>Error</title><body>404 not found</body></html>"
37 * Handle to the HTTP server as provided by libmicrohttpd
39 static struct MHD_Daemon *daemon_handle;
44 static const struct GNUNET_CONFIGURATION_Handle *cfg;
47 * Our primary task for the HTTPD.
49 static struct GNUNET_SCHEDULER_Task * http_task;
54 static struct MHD_Response *main_response;
57 * Error: invalid gns key.
59 static struct MHD_Response *invalid_gnskey_response;
64 static struct MHD_Response *not_found_response;
67 * Absolute name of the 'gns-bcd.tex' file.
74 static uint16_t port = 8888;
85 * Main request handler.
88 access_handler_callback (void *cls,
89 struct MHD_Connection *connection,
93 const char *upload_data,
94 size_t * upload_data_size,
98 static const struct Entry map[] = {
99 { "prefix", "prefix" },
101 { "suffix", "suffix" },
102 { "street", "street" },
104 { "phone", "phone" },
107 { "homepage", "homepage" },
109 { "departmenti18n", "departmentde"},
110 { "departmenten", "departmenten"},
111 { "subdepartmenti18n", "subdepartmentde"},
112 { "subdepartmenten", "subdepartmenten"},
113 { "jobtitlei18n", "jobtitlegerman"},
114 { "jobtitleen", "jobtitleenglish"},
115 { "subdepartmenten", "subdepartmenten"},
119 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
121 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
122 _("Refusing `%s' request to HTTP server\n"),
126 if (NULL == *con_cls)
129 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
130 "Sending 100 CONTINUE reply\n");
131 return MHD_YES; /* send 100 continue */
133 if (0 == strcasecmp (url, "/"))
134 return MHD_queue_response (connection,
137 if (0 == strcasecmp (url, "/submit.pdf"))
143 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
147 struct MHD_Response *response;
151 const char *gpg_fp = MHD_lookup_connection_value (connection,
152 MHD_GET_ARGUMENT_KIND,
154 const char *gns_nick = MHD_lookup_connection_value (connection,
155 MHD_GET_ARGUMENT_KIND,
157 const char *gnskey = MHD_lookup_connection_value (connection,
158 MHD_GET_ARGUMENT_KIND,
160 if ( (NULL == gnskey) ||
162 GNUNET_CRYPTO_ecdsa_public_key_from_string (gnskey,
166 return MHD_queue_response (connection,
168 invalid_gnskey_response);
170 tmp = GNUNET_DISK_mkdtemp (gnskey);
173 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mktemp", gnskey);
176 GNUNET_asprintf (&deffile,
178 tmp, DIR_SEPARATOR_STR, "def.tex");
179 f = FOPEN (deffile, "w");
182 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", deffile);
183 GNUNET_free (deffile);
184 GNUNET_DISK_directory_remove (tmp);
188 for (i=0; NULL != map[i].formname; i++)
190 const char *val = MHD_lookup_connection_value (connection,
191 MHD_GET_ARGUMENT_KIND,
196 map[i].texname, val);
207 slen = strlen (gpg_fp);
208 gpg1 = GNUNET_strndup (gpg_fp, slen / 2);
209 gpg2 = GNUNET_strdup (&gpg_fp[slen / 2]);
211 "\\def\\gpglineone{%s}\n\\def\\gpglinetwo{%s}\n",
217 "\\def\\gns{%s/%s}\n",
219 (NULL == gns_nick) ? "" : gns_nick);
222 "cd %s; cp %s gns-bcd.tex | pdflatex --enable-write18 gns-bcd.tex > /dev/null 2> /dev/null",
225 GNUNET_free (deffile);
227 if (WIFSIGNALED (ret) || (0 != WEXITSTATUS(ret)))
228 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
231 GNUNET_asprintf (&deffile,
233 tmp, DIR_SEPARATOR_STR, "gns-bcd.pdf");
234 fd = OPEN (deffile, O_RDONLY);
237 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
240 GNUNET_free (deffile);
242 GNUNET_DISK_directory_remove (tmp);
246 GNUNET_break (0 == STAT (deffile, &st));
247 if (NULL == (response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
250 GNUNET_break (0 == CLOSE (fd));
251 GNUNET_free (deffile);
253 GNUNET_DISK_directory_remove (tmp);
257 (void) MHD_add_response_header (response,
258 MHD_HTTP_HEADER_CONTENT_TYPE,
260 ret = MHD_queue_response (connection,
263 MHD_destroy_response (response);
264 GNUNET_free (deffile);
266 GNUNET_DISK_directory_remove (tmp);
270 return MHD_queue_response (connection,
277 * Function that queries MHD's select sets and
278 * starts the task waiting for them.
280 static struct GNUNET_SCHEDULER_Task *
281 prepare_daemon (struct MHD_Daemon *daemon_handle);
285 * Call MHD to process pending requests and then go back
286 * and schedule the next run.
289 run_daemon (void *cls)
291 struct MHD_Daemon *daemon_handle = cls;
294 GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
295 http_task = prepare_daemon (daemon_handle);
300 * Function that queries MHD's select sets and
301 * starts the task waiting for them.
303 static struct GNUNET_SCHEDULER_Task *
304 prepare_daemon (struct MHD_Daemon *daemon_handle)
306 struct GNUNET_SCHEDULER_Task * ret;
310 struct GNUNET_NETWORK_FDSet *wrs;
311 struct GNUNET_NETWORK_FDSet *wws;
313 MHD_UNSIGNED_LONG_LONG timeout;
315 struct GNUNET_TIME_Relative tv;
320 wrs = GNUNET_NETWORK_fdset_create ();
321 wws = GNUNET_NETWORK_fdset_create ();
323 GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
324 haveto = MHD_get_timeout (daemon_handle, &timeout);
325 if (haveto == MHD_YES)
326 tv.rel_value_us = (uint64_t) timeout * 1000LL;
328 tv = GNUNET_TIME_UNIT_FOREVER_REL;
329 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
330 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
332 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
334 &run_daemon, daemon_handle);
335 GNUNET_NETWORK_fdset_destroy (wrs);
336 GNUNET_NETWORK_fdset_destroy (wws);
342 * Start server offering our hostlist.
344 * @return #GNUNET_OK on success
351 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
352 _("Invalid port number %u. Exiting.\n"),
354 return GNUNET_SYSERR;
356 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
357 _("Businesscard HTTP server starts on %u\n"),
359 daemon_handle = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_DEBUG,
361 NULL /* accept_policy_callback */, NULL,
362 &access_handler_callback, NULL,
363 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 512,
364 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
365 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 60,
366 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
368 if (NULL == daemon_handle)
370 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371 _("Could not start businesscard HTTP server on port %u\n"),
372 (unsigned int) port);
373 return GNUNET_SYSERR;
375 http_task = prepare_daemon (daemon_handle);
384 server_stop (void *cls)
386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387 "HTTP server shutdown\n");
388 if (NULL != http_task)
390 GNUNET_SCHEDULER_cancel (http_task);
393 if (NULL != daemon_handle)
395 MHD_stop_daemon (daemon_handle);
396 daemon_handle = NULL;
398 if (NULL != main_response)
400 MHD_destroy_response (main_response);
401 main_response = NULL;
403 if (NULL != invalid_gnskey_response)
405 MHD_destroy_response (invalid_gnskey_response);
406 invalid_gnskey_response = NULL;
408 if (NULL != not_found_response)
410 MHD_destroy_response (not_found_response);
411 not_found_response = NULL;
415 GNUNET_free (resfile);
422 * Main function that will be run.
425 * @param args remaining command-line arguments
426 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
427 * @param c configuration
433 const struct GNUNET_CONFIGURATION_Handle *c)
441 dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
442 GNUNET_assert (NULL != dir);
443 GNUNET_asprintf (&fn,
448 GNUNET_asprintf (&resfile,
454 fd = OPEN (fn, O_RDONLY);
457 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
463 if (0 != STAT (fn, &st))
465 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
473 if (NULL == (main_response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
476 GNUNET_break (0 == CLOSE (fd));
479 (void) MHD_add_response_header (main_response,
480 MHD_HTTP_HEADER_CONTENT_TYPE,
482 invalid_gnskey_response = MHD_create_response_from_buffer (strlen (INVALID_GNSKEY),
484 MHD_RESPMEM_PERSISTENT);
485 (void) MHD_add_response_header (invalid_gnskey_response,
486 MHD_HTTP_HEADER_CONTENT_TYPE,
488 not_found_response = MHD_create_response_from_buffer (strlen (NOT_FOUND),
490 MHD_RESPMEM_PERSISTENT);
491 (void) MHD_add_response_header (not_found_response,
492 MHD_HTTP_HEADER_CONTENT_TYPE,
497 GNUNET_SCHEDULER_add_shutdown (&server_stop,
503 * The main function for gnunet-gns.
505 * @param argc number of arguments from the command line
506 * @param argv command line arguments
507 * @return 0 ok, 1 on error
510 main (int argc, char *const *argv)
512 struct GNUNET_GETOPT_CommandLineOption options[] = {
514 GNUNET_GETOPT_option_uint16 ('p',
517 gettext_noop ("Run HTTP serve on port PORT (default is 8888)"),
519 GNUNET_GETOPT_OPTION_END
524 GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
526 GNUNET_log_setup ("gnunet-bcd", "WARNING", NULL);
529 GNUNET_PROGRAM_run (argc, argv, "gnunet-bcd",
530 _("GNUnet HTTP server to create business cards"),
532 &run, NULL)) ? 0 : 1;
533 GNUNET_free ((void*) argv);
538 /* end of gnunet-bcd.c */