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.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
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 \
35 "<html><head><title>Error</title><body>Invalid GNS public key given.</body></html>"
38 * Error page to display on 404.
41 "<html><head><title>Error</title><body>404 not found</body></html>"
44 * Handle to the HTTP server as provided by libmicrohttpd
46 static struct MHD_Daemon *daemon_handle;
51 static const struct GNUNET_CONFIGURATION_Handle *cfg;
54 * Our primary task for the HTTPD.
56 static struct GNUNET_SCHEDULER_Task *http_task;
61 static struct MHD_Response *main_response;
64 * Error: invalid gns key.
66 static struct MHD_Response *invalid_gnskey_response;
71 static struct MHD_Response *not_found_response;
74 * Absolute name of the 'gns-bcd.tex' file.
81 static uint16_t port = 8888;
92 * Main request handler.
95 access_handler_callback (void *cls,
96 struct MHD_Connection *connection,
100 const char *upload_data,
101 size_t *upload_data_size,
105 static const struct Entry map[] = {{"prefix", "prefix"},
107 {"suffix", "suffix"},
108 {"street", "street"},
113 {"homepage", "homepage"},
115 {"departmenti18n", "departmentde"},
116 {"departmenten", "departmenten"},
117 {"subdepartmenti18n", "subdepartmentde"},
118 {"subdepartmenten", "subdepartmenten"},
119 {"jobtitlei18n", "jobtitlegerman"},
120 {"jobtitleen", "jobtitleenglish"},
121 {"subdepartmenten", "subdepartmenten"},
127 (void) upload_data_size;
128 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
130 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
131 _ ("Refusing `%s' request to HTTP server\n"),
135 if (NULL == *con_cls)
138 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending 100 CONTINUE reply\n");
139 return MHD_YES; /* send 100 continue */
141 if (0 == strcasecmp (url, "/"))
142 return MHD_queue_response (connection, MHD_HTTP_OK, main_response);
143 if (0 == strcasecmp (url, "/submit.pdf"))
149 struct GNUNET_CRYPTO_EcdsaPublicKey pub;
153 struct MHD_Response *response;
157 const char *gpg_fp = MHD_lookup_connection_value (connection,
158 MHD_GET_ARGUMENT_KIND,
160 const char *gns_nick = MHD_lookup_connection_value (connection,
161 MHD_GET_ARGUMENT_KIND,
164 MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "gnskey");
165 if ((NULL == gnskey) ||
167 GNUNET_CRYPTO_ecdsa_public_key_from_string (gnskey,
171 return MHD_queue_response (connection,
173 invalid_gnskey_response);
175 tmp = GNUNET_DISK_mkdtemp (gnskey);
178 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mktemp", gnskey);
181 GNUNET_asprintf (&deffile, "%s%s%s", tmp, DIR_SEPARATOR_STR, "def.tex");
182 f = fopen (deffile, "w");
185 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", deffile);
186 GNUNET_free (deffile);
187 GNUNET_DISK_directory_remove (tmp);
191 for (i = 0; NULL != map[i].formname; i++)
193 const char *val = MHD_lookup_connection_value (connection,
194 MHD_GET_ARGUMENT_KIND,
197 fprintf (f, "\\def\\%s{%s}\n", map[i].texname, val);
199 fprintf (f, "\\def\\%s{}\n", map[i].texname);
206 slen = strlen (gpg_fp);
207 gpg1 = GNUNET_strndup (gpg_fp, slen / 2);
208 gpg2 = GNUNET_strdup (&gpg_fp[slen / 2]);
209 fprintf (f, "\\def\\gpglineone{%s}\n\\def\\gpglinetwo{%s}\n", gpg1, gpg2);
214 "\\def\\gns{%s/%s}\n",
216 (NULL == gns_nick) ? "" : gns_nick);
220 "cd %s; cp %s gns-bcd.tex | pdflatex --enable-write18 gns-bcd.tex > /dev/null 2> /dev/null",
223 GNUNET_free (deffile);
225 if (WIFSIGNALED (ret) || (0 != WEXITSTATUS (ret)))
226 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "system", p);
227 GNUNET_asprintf (&deffile, "%s%s%s", tmp, DIR_SEPARATOR_STR, "gns-bcd.pdf");
228 fd = OPEN (deffile, O_RDONLY);
231 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", deffile);
232 GNUNET_free (deffile);
234 GNUNET_DISK_directory_remove (tmp);
238 GNUNET_break (0 == STAT (deffile, &st));
240 (response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
243 GNUNET_break (0 == CLOSE (fd));
244 GNUNET_free (deffile);
246 GNUNET_DISK_directory_remove (tmp);
250 (void) MHD_add_response_header (response,
251 MHD_HTTP_HEADER_CONTENT_TYPE,
253 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
254 MHD_destroy_response (response);
255 GNUNET_free (deffile);
257 GNUNET_DISK_directory_remove (tmp);
261 return MHD_queue_response (connection,
268 * Function that queries MHD's select sets and
269 * starts the task waiting for them.
271 static struct GNUNET_SCHEDULER_Task *
272 prepare_daemon (struct MHD_Daemon *daemon_handle);
276 * Call MHD to process pending requests and then go back
277 * and schedule the next run.
280 run_daemon (void *cls)
282 struct MHD_Daemon *daemon_handle = cls;
285 GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
286 http_task = prepare_daemon (daemon_handle);
291 * Function that queries MHD's select sets and
292 * starts the task waiting for them.
294 static struct GNUNET_SCHEDULER_Task *
295 prepare_daemon (struct MHD_Daemon *daemon_handle)
297 struct GNUNET_SCHEDULER_Task *ret;
301 struct GNUNET_NETWORK_FDSet *wrs;
302 struct GNUNET_NETWORK_FDSet *wws;
304 MHD_UNSIGNED_LONG_LONG timeout;
306 struct GNUNET_TIME_Relative tv;
311 wrs = GNUNET_NETWORK_fdset_create ();
312 wws = GNUNET_NETWORK_fdset_create ();
314 GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
315 haveto = MHD_get_timeout (daemon_handle, &timeout);
316 if (haveto == MHD_YES)
317 tv.rel_value_us = (uint64_t) timeout * 1000LL;
319 tv = GNUNET_TIME_UNIT_FOREVER_REL;
320 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
321 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
322 ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
328 GNUNET_NETWORK_fdset_destroy (wrs);
329 GNUNET_NETWORK_fdset_destroy (wws);
335 * Start server offering our hostlist.
337 * @return #GNUNET_OK on success
344 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
345 _ ("Invalid port number %u. Exiting.\n"),
347 return GNUNET_SYSERR;
349 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
350 _ ("Businesscard HTTP server starts on %u\n"),
352 daemon_handle = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_DEBUG,
354 NULL /* accept_policy_callback */,
356 &access_handler_callback,
358 MHD_OPTION_CONNECTION_LIMIT,
360 MHD_OPTION_PER_IP_CONNECTION_LIMIT,
362 MHD_OPTION_CONNECTION_TIMEOUT,
364 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
365 (size_t) (16 * 1024),
367 if (NULL == daemon_handle)
369 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
370 _ ("Could not start businesscard HTTP server on port %u\n"),
371 (unsigned int) port);
372 return GNUNET_SYSERR;
374 http_task = prepare_daemon (daemon_handle);
383 server_stop (void *cls)
386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "HTTP server shutdown\n");
387 if (NULL != http_task)
389 GNUNET_SCHEDULER_cancel (http_task);
392 if (NULL != daemon_handle)
394 MHD_stop_daemon (daemon_handle);
395 daemon_handle = NULL;
397 if (NULL != main_response)
399 MHD_destroy_response (main_response);
400 main_response = NULL;
402 if (NULL != invalid_gnskey_response)
404 MHD_destroy_response (invalid_gnskey_response);
405 invalid_gnskey_response = NULL;
407 if (NULL != not_found_response)
409 MHD_destroy_response (not_found_response);
410 not_found_response = NULL;
414 GNUNET_free (resfile);
421 * Main function that will be run.
424 * @param args remaining command-line arguments
425 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
426 * @param c configuration
432 const struct GNUNET_CONFIGURATION_Handle *c)
443 dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
444 GNUNET_assert (NULL != dir);
445 GNUNET_asprintf (&fn, "%s%s%s", dir, DIR_SEPARATOR_STR, "gns-bcd.html");
446 GNUNET_asprintf (&resfile, "%s%s%s", dir, DIR_SEPARATOR_STR, "gns-bcd.tex");
448 fd = OPEN (fn, O_RDONLY);
451 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
455 if (0 != STAT (fn, &st))
457 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
459 GNUNET_break (0 == CLOSE (fd));
464 (main_response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
467 GNUNET_break (0 == CLOSE (fd));
470 (void) MHD_add_response_header (main_response,
471 MHD_HTTP_HEADER_CONTENT_TYPE,
473 invalid_gnskey_response =
474 MHD_create_response_from_buffer (strlen (INVALID_GNSKEY),
476 MHD_RESPMEM_PERSISTENT);
477 (void) MHD_add_response_header (invalid_gnskey_response,
478 MHD_HTTP_HEADER_CONTENT_TYPE,
480 not_found_response = MHD_create_response_from_buffer (strlen (NOT_FOUND),
482 MHD_RESPMEM_PERSISTENT);
483 (void) MHD_add_response_header (not_found_response,
484 MHD_HTTP_HEADER_CONTENT_TYPE,
486 if (GNUNET_OK != server_start ())
488 GNUNET_SCHEDULER_add_shutdown (&server_stop, NULL);
493 * The main function for gnunet-gns.
495 * @param argc number of arguments from the command line
496 * @param argv command line arguments
497 * @return 0 ok, 1 on error
500 main (int argc, char *const *argv)
502 struct GNUNET_GETOPT_CommandLineOption options[] = {
504 GNUNET_GETOPT_option_uint16 ('p',
508 "Run HTTP serve on port PORT (default is 8888)"),
510 GNUNET_GETOPT_OPTION_END};
513 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
515 GNUNET_log_setup ("gnunet-bcd", "WARNING", NULL);
517 GNUNET_PROGRAM_run (argc,
520 _ ("GNUnet HTTP server to create business cards"),
526 GNUNET_free ((void *) argv);
531 /* end of gnunet-bcd.c */