2 This file is part of GNUnet.
3 Copyright (C) 2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * @file gns/gnunet-bcd.c
23 * @author Christian Grothoff
24 * @brief HTTP server to create GNS business cards
28 #include <microhttpd.h>
29 #include "gnunet_util_lib.h"
32 * Error page to display if submitted GNS key is invalid.
34 #define INVALID_GNSKEY "<html><head><title>Error</title><body>Invalid GNS public key given.</body></html>"
37 * Error page to display on 404.
39 #define NOT_FOUND "<html><head><title>Error</title><body>404 not found</body></html>"
42 * Handle to the HTTP server as provided by libmicrohttpd
44 static struct MHD_Daemon *daemon_handle;
49 static const struct GNUNET_CONFIGURATION_Handle *cfg;
52 * Our primary task for the HTTPD.
54 static struct GNUNET_SCHEDULER_Task * http_task;
59 static struct MHD_Response *main_response;
62 * Error: invalid gns key.
64 static struct MHD_Response *invalid_gnskey_response;
69 static struct MHD_Response *not_found_response;
72 * Absolute name of the 'gns-bcd.tex' file.
79 static unsigned int port = 8888;
90 * Main request handler.
93 access_handler_callback (void *cls, struct MHD_Connection *connection,
94 const char *url, const char *method,
95 const char *version, const char *upload_data,
96 size_t * upload_data_size, void **con_cls)
99 static const struct Entry map[] = {
100 { "prefix", "prefix" },
102 { "suffix", "suffix" },
103 { "street", "street" },
105 { "phone", "phone" },
108 { "homepage", "homepage" },
110 { "departmenti18n", "departmentde"},
111 { "departmenten", "departmenten"},
112 { "subdepartmenti18n", "subdepartmentde"},
113 { "subdepartmenten", "subdepartmenten"},
114 { "jobtitlei18n", "jobtitlegerman"},
115 { "jobtitleen", "jobtitleenglish"},
116 { "subdepartmenten", "subdepartmenten"},
120 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
122 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
123 _("Refusing `%s' request to HTTP server\n"),
127 if (NULL == *con_cls)
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131 "Sending 100 CONTINUE reply\n");
132 return MHD_YES; /* send 100 continue */
134 if (0 == strcasecmp (url, "/"))
135 return MHD_queue_response (connection,
138 if (0 == strcasecmp (url, "/submit.pdf"))
144 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
148 struct MHD_Response *response;
152 const char *gpg_fp = MHD_lookup_connection_value (connection,
153 MHD_GET_ARGUMENT_KIND,
155 const char *gns_nick = MHD_lookup_connection_value (connection,
156 MHD_GET_ARGUMENT_KIND,
158 const char *gnskey = MHD_lookup_connection_value (connection,
159 MHD_GET_ARGUMENT_KIND,
161 if ( (NULL == gnskey) ||
163 GNUNET_CRYPTO_ecdsa_public_key_from_string (gnskey,
167 return MHD_queue_response (connection,
169 invalid_gnskey_response);
171 tmp = GNUNET_DISK_mkdtemp (gnskey);
174 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mktemp", gnskey);
177 GNUNET_asprintf (&deffile,
179 tmp, DIR_SEPARATOR_STR, "def.tex");
180 f = FOPEN (deffile, "w");
183 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", deffile);
184 GNUNET_free (deffile);
185 GNUNET_DISK_directory_remove (tmp);
189 for (i=0; NULL != map[i].formname; i++)
191 const char *val = MHD_lookup_connection_value (connection,
192 MHD_GET_ARGUMENT_KIND,
197 map[i].texname, val);
208 slen = strlen (gpg_fp);
209 gpg1 = GNUNET_strndup (gpg_fp, slen / 2);
210 gpg2 = GNUNET_strdup (&gpg_fp[slen / 2]);
212 "\\def\\gpglineone{%s}\n\\def\\gpglinetwo{%s}\n",
218 "\\def\\gns{%s/%s}\n",
220 (NULL == gns_nick) ? "" : gns_nick);
223 "cd %s; cp %s gns-bcd.tex | pdflatex --enable-write18 gns-bcd.tex > /dev/null 2> /dev/null",
226 GNUNET_free (deffile);
228 if (WIFSIGNALED (ret) || (0 != WEXITSTATUS(ret)))
229 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
232 GNUNET_asprintf (&deffile,
234 tmp, DIR_SEPARATOR_STR, "gns-bcd.pdf");
235 fd = OPEN (deffile, O_RDONLY);
238 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
241 GNUNET_free (deffile);
243 GNUNET_DISK_directory_remove (tmp);
247 GNUNET_break (0 == STAT (deffile, &st));
248 if (NULL == (response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
251 GNUNET_break (0 == CLOSE (fd));
252 GNUNET_free (deffile);
254 GNUNET_DISK_directory_remove (tmp);
258 (void) MHD_add_response_header (response,
259 MHD_HTTP_HEADER_CONTENT_TYPE,
261 ret = MHD_queue_response (connection,
264 MHD_destroy_response (response);
265 GNUNET_free (deffile);
267 GNUNET_DISK_directory_remove (tmp);
271 return MHD_queue_response (connection,
278 * Function that queries MHD's select sets and
279 * starts the task waiting for them.
281 static struct GNUNET_SCHEDULER_Task *
282 prepare_daemon (struct MHD_Daemon *daemon_handle);
286 * Call MHD to process pending requests and then go back
287 * and schedule the next run.
290 run_daemon (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
292 struct MHD_Daemon *daemon_handle = cls;
295 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
297 GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
298 http_task = prepare_daemon (daemon_handle);
303 * Function that queries MHD's select sets and
304 * starts the task waiting for them.
306 static struct GNUNET_SCHEDULER_Task *
307 prepare_daemon (struct MHD_Daemon *daemon_handle)
309 struct GNUNET_SCHEDULER_Task * ret;
313 struct GNUNET_NETWORK_FDSet *wrs;
314 struct GNUNET_NETWORK_FDSet *wws;
316 MHD_UNSIGNED_LONG_LONG timeout;
318 struct GNUNET_TIME_Relative tv;
323 wrs = GNUNET_NETWORK_fdset_create ();
324 wws = GNUNET_NETWORK_fdset_create ();
326 GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
327 haveto = MHD_get_timeout (daemon_handle, &timeout);
328 if (haveto == MHD_YES)
329 tv.rel_value_us = (uint64_t) timeout * 1000LL;
331 tv = GNUNET_TIME_UNIT_FOREVER_REL;
332 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
333 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
335 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
337 &run_daemon, daemon_handle);
338 GNUNET_NETWORK_fdset_destroy (wrs);
339 GNUNET_NETWORK_fdset_destroy (wws);
345 * Start server offering our hostlist.
347 * @return #GNUNET_OK on success
352 if ((0 == port) || (port > UINT16_MAX))
354 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
355 _("Invalid port number %llu. Exiting.\n"),
357 return GNUNET_SYSERR;
359 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
360 _("Businesscard HTTP server starts on %llu\n"),
362 daemon_handle = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_DEBUG,
364 NULL /* accept_policy_callback */, NULL,
365 &access_handler_callback, NULL,
366 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 512,
367 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 2,
368 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 60,
369 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
371 if (NULL == daemon_handle)
373 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
374 _("Could not start businesscard HTTP server on port %u\n"),
375 (unsigned short) port);
376 return GNUNET_SYSERR;
378 http_task = prepare_daemon (daemon_handle);
387 server_stop (void *cls,
388 const struct GNUNET_SCHEDULER_TaskContext *tc)
390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
391 "HTTP server shutdown\n");
392 if (NULL != http_task)
394 GNUNET_SCHEDULER_cancel (http_task);
397 if (NULL != daemon_handle)
399 MHD_stop_daemon (daemon_handle);
400 daemon_handle = NULL;
402 if (NULL != main_response)
404 MHD_destroy_response (main_response);
405 main_response = NULL;
407 if (NULL != invalid_gnskey_response)
409 MHD_destroy_response (invalid_gnskey_response);
410 invalid_gnskey_response = NULL;
412 if (NULL != not_found_response)
414 MHD_destroy_response (not_found_response);
415 not_found_response = NULL;
419 GNUNET_free (resfile);
426 * Main function that will be run.
429 * @param args remaining command-line arguments
430 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
431 * @param c configuration
437 const struct GNUNET_CONFIGURATION_Handle *c)
445 dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
446 GNUNET_assert (NULL != dir);
447 GNUNET_asprintf (&fn,
452 GNUNET_asprintf (&resfile,
458 fd = OPEN (fn, O_RDONLY);
461 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
467 if (0 != STAT (fn, &st))
469 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
477 if (NULL == (main_response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
480 GNUNET_break (0 == CLOSE (fd));
483 (void) MHD_add_response_header (main_response,
484 MHD_HTTP_HEADER_CONTENT_TYPE,
486 invalid_gnskey_response = MHD_create_response_from_buffer (strlen (INVALID_GNSKEY),
488 MHD_RESPMEM_PERSISTENT);
489 (void) MHD_add_response_header (invalid_gnskey_response,
490 MHD_HTTP_HEADER_CONTENT_TYPE,
492 not_found_response = MHD_create_response_from_buffer (strlen (NOT_FOUND),
494 MHD_RESPMEM_PERSISTENT);
495 (void) MHD_add_response_header (not_found_response,
496 MHD_HTTP_HEADER_CONTENT_TYPE,
501 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
508 * The main function for gnunet-gns.
510 * @param argc number of arguments from the command line
511 * @param argv command line arguments
512 * @return 0 ok, 1 on error
515 main (int argc, char *const *argv)
517 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
518 {'p', "port", "PORT",
519 gettext_noop ("Run HTTP serve on port PORT (default is 8888)"), 1,
520 &GNUNET_GETOPT_set_uint, &port},
521 GNUNET_GETOPT_OPTION_END
525 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
527 GNUNET_log_setup ("gnunet-bcd", "WARNING", NULL);
530 GNUNET_PROGRAM_run (argc, argv, "gnunet-bcd",
531 _("GNUnet HTTP server to create business cards"),
533 &run, NULL)) ? 0 : 1;
534 GNUNET_free ((void*) argv);
539 /* end of gnunet-bcd.c */