gnunet-bcd.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /*
  2. This file is part of GNUnet.
  3. Copyright (C) 2013 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file gns/gnunet-bcd.c
  18. * @author Christian Grothoff
  19. * @brief HTTP server to create GNS business cards
  20. */
  21. #include "platform.h"
  22. #include <microhttpd.h>
  23. #include "gnunet_util_lib.h"
  24. /**
  25. * Error page to display if submitted GNS key is invalid.
  26. */
  27. #define INVALID_GNSKEY \
  28. "<html><head><title>Error</title><body>Invalid GNS public key given.</body></html>"
  29. /**
  30. * Error page to display on 404.
  31. */
  32. #define NOT_FOUND \
  33. "<html><head><title>Error</title><body>404 not found</body></html>"
  34. /**
  35. * Handle to the HTTP server as provided by libmicrohttpd
  36. */
  37. static struct MHD_Daemon *daemon_handle;
  38. /**
  39. * Our configuration.
  40. */
  41. static const struct GNUNET_CONFIGURATION_Handle *cfg;
  42. /**
  43. * Our primary task for the HTTPD.
  44. */
  45. static struct GNUNET_SCHEDULER_Task *http_task;
  46. /**
  47. * Our main website.
  48. */
  49. static struct MHD_Response *main_response;
  50. /**
  51. * Error: invalid gns key.
  52. */
  53. static struct MHD_Response *invalid_gnskey_response;
  54. /**
  55. * Error: 404
  56. */
  57. static struct MHD_Response *not_found_response;
  58. /**
  59. * Absolute name of the 'gns-bcd.tex' file.
  60. */
  61. static char *resfile;
  62. /**
  63. * Port number.
  64. */
  65. static uint16_t port = 8888;
  66. struct Entry
  67. {
  68. const char *formname;
  69. const char *texname;
  70. };
  71. /**
  72. * Main request handler.
  73. */
  74. static int
  75. access_handler_callback (void *cls,
  76. struct MHD_Connection *connection,
  77. const char *url,
  78. const char *method,
  79. const char *version,
  80. const char *upload_data,
  81. size_t *upload_data_size,
  82. void **con_cls)
  83. {
  84. static int dummy;
  85. static const struct Entry map[] = { { "prefix", "prefix" },
  86. { "name", "name" },
  87. { "suffix", "suffix" },
  88. { "street", "street" },
  89. { "city", "city" },
  90. { "phone", "phone" },
  91. { "fax", "fax" },
  92. { "email", "email" },
  93. { "homepage", "homepage" },
  94. { "orga", "orga" },
  95. { "departmenti18n", "departmentde" },
  96. { "departmenten", "departmenten" },
  97. { "subdepartmenti18n",
  98. "subdepartmentde" },
  99. { "subdepartmenten", "subdepartmenten" },
  100. { "jobtitlei18n", "jobtitlegerman" },
  101. { "jobtitleen", "jobtitleenglish" },
  102. { "subdepartmenten", "subdepartmenten" },
  103. { NULL, NULL } };
  104. (void) cls;
  105. (void) version;
  106. (void) upload_data;
  107. (void) upload_data_size;
  108. if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
  109. {
  110. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  111. _ ("Refusing `%s' request to HTTP server\n"),
  112. method);
  113. return MHD_NO;
  114. }
  115. if (NULL == *con_cls)
  116. {
  117. (*con_cls) = &dummy;
  118. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending 100 CONTINUE reply\n");
  119. return MHD_YES; /* send 100 continue */
  120. }
  121. if (0 == strcasecmp (url, "/"))
  122. return MHD_queue_response (connection, MHD_HTTP_OK, main_response);
  123. if (0 == strcasecmp (url, "/submit.pdf"))
  124. {
  125. unsigned int i;
  126. char *p;
  127. char *tmp;
  128. char *deffile;
  129. struct GNUNET_CRYPTO_EcdsaPublicKey pub;
  130. size_t slen;
  131. FILE *f;
  132. struct stat st;
  133. struct MHD_Response *response;
  134. int fd;
  135. int ret;
  136. const char *gpg_fp = MHD_lookup_connection_value (connection,
  137. MHD_GET_ARGUMENT_KIND,
  138. "gpgfingerprint");
  139. const char *gns_nick = MHD_lookup_connection_value (connection,
  140. MHD_GET_ARGUMENT_KIND,
  141. "gnsnick");
  142. const char *gnskey =
  143. MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, "gnskey");
  144. if ((NULL == gnskey) ||
  145. (GNUNET_OK !=
  146. GNUNET_CRYPTO_ecdsa_public_key_from_string (gnskey,
  147. strlen (gnskey),
  148. &pub)))
  149. {
  150. return MHD_queue_response (connection,
  151. MHD_HTTP_OK,
  152. invalid_gnskey_response);
  153. }
  154. tmp = GNUNET_DISK_mkdtemp (gnskey);
  155. if (NULL == tmp)
  156. {
  157. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mktemp", gnskey);
  158. return MHD_NO;
  159. }
  160. GNUNET_asprintf (&deffile, "%s%s%s", tmp, DIR_SEPARATOR_STR, "def.tex");
  161. f = fopen (deffile, "w");
  162. if (NULL == f)
  163. {
  164. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", deffile);
  165. GNUNET_free (deffile);
  166. GNUNET_DISK_directory_remove (tmp);
  167. GNUNET_free (tmp);
  168. return MHD_NO;
  169. }
  170. for (i = 0; NULL != map[i].formname; i++)
  171. {
  172. const char *val = MHD_lookup_connection_value (connection,
  173. MHD_GET_ARGUMENT_KIND,
  174. map[i].formname);
  175. if (NULL != val)
  176. fprintf (f, "\\def\\%s{%s}\n", map[i].texname, val);
  177. else
  178. fprintf (f, "\\def\\%s{}\n", map[i].texname);
  179. }
  180. if (NULL != gpg_fp)
  181. {
  182. char *gpg1;
  183. char *gpg2;
  184. slen = strlen (gpg_fp);
  185. gpg1 = GNUNET_strndup (gpg_fp, slen / 2);
  186. gpg2 = GNUNET_strdup (&gpg_fp[slen / 2]);
  187. fprintf (f, "\\def\\gpglineone{%s}\n\\def\\gpglinetwo{%s}\n", gpg1, gpg2);
  188. GNUNET_free (gpg2);
  189. GNUNET_free (gpg1);
  190. }
  191. fprintf (f,
  192. "\\def\\gns{%s/%s}\n",
  193. gnskey,
  194. (NULL == gns_nick) ? "" : gns_nick);
  195. fclose (f);
  196. GNUNET_asprintf (
  197. &p,
  198. "cd %s; cp %s gns-bcd.tex | pdflatex --enable-write18 gns-bcd.tex > /dev/null 2> /dev/null",
  199. tmp,
  200. resfile);
  201. GNUNET_free (deffile);
  202. ret = system (p);
  203. if (WIFSIGNALED (ret) || (0 != WEXITSTATUS (ret)))
  204. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "system", p);
  205. GNUNET_asprintf (&deffile, "%s%s%s", tmp, DIR_SEPARATOR_STR, "gns-bcd.pdf");
  206. fd = open (deffile, O_RDONLY);
  207. if (-1 == fd)
  208. {
  209. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", deffile);
  210. GNUNET_free (deffile);
  211. GNUNET_free (p);
  212. GNUNET_DISK_directory_remove (tmp);
  213. GNUNET_free (tmp);
  214. return MHD_NO;
  215. }
  216. GNUNET_break (0 == stat (deffile, &st));
  217. if (NULL ==
  218. (response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
  219. {
  220. GNUNET_break (0);
  221. GNUNET_break (0 == close (fd));
  222. GNUNET_free (deffile);
  223. GNUNET_free (p);
  224. GNUNET_DISK_directory_remove (tmp);
  225. GNUNET_free (tmp);
  226. return MHD_NO;
  227. }
  228. (void) MHD_add_response_header (response,
  229. MHD_HTTP_HEADER_CONTENT_TYPE,
  230. "application/pdf");
  231. ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
  232. MHD_destroy_response (response);
  233. GNUNET_free (deffile);
  234. GNUNET_free (p);
  235. GNUNET_DISK_directory_remove (tmp);
  236. GNUNET_free (tmp);
  237. return ret;
  238. }
  239. return MHD_queue_response (connection,
  240. MHD_HTTP_NOT_FOUND,
  241. not_found_response);
  242. }
  243. /**
  244. * Function that queries MHD's select sets and
  245. * starts the task waiting for them.
  246. */
  247. static struct GNUNET_SCHEDULER_Task *
  248. prepare_daemon (struct MHD_Daemon *daemon_handle);
  249. /**
  250. * Call MHD to process pending requests and then go back
  251. * and schedule the next run.
  252. */
  253. static void
  254. run_daemon (void *cls)
  255. {
  256. struct MHD_Daemon *daemon_handle = cls;
  257. http_task = NULL;
  258. GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
  259. http_task = prepare_daemon (daemon_handle);
  260. }
  261. /**
  262. * Function that queries MHD's select sets and
  263. * starts the task waiting for them.
  264. */
  265. static struct GNUNET_SCHEDULER_Task *
  266. prepare_daemon (struct MHD_Daemon *daemon_handle)
  267. {
  268. struct GNUNET_SCHEDULER_Task *ret;
  269. fd_set rs;
  270. fd_set ws;
  271. fd_set es;
  272. struct GNUNET_NETWORK_FDSet *wrs;
  273. struct GNUNET_NETWORK_FDSet *wws;
  274. int max;
  275. MHD_UNSIGNED_LONG_LONG timeout;
  276. int haveto;
  277. struct GNUNET_TIME_Relative tv;
  278. FD_ZERO (&rs);
  279. FD_ZERO (&ws);
  280. FD_ZERO (&es);
  281. wrs = GNUNET_NETWORK_fdset_create ();
  282. wws = GNUNET_NETWORK_fdset_create ();
  283. max = -1;
  284. GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
  285. haveto = MHD_get_timeout (daemon_handle, &timeout);
  286. if (haveto == MHD_YES)
  287. tv.rel_value_us = (uint64_t) timeout * 1000LL;
  288. else
  289. tv = GNUNET_TIME_UNIT_FOREVER_REL;
  290. GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
  291. GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
  292. ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
  293. tv,
  294. wrs,
  295. wws,
  296. &run_daemon,
  297. daemon_handle);
  298. GNUNET_NETWORK_fdset_destroy (wrs);
  299. GNUNET_NETWORK_fdset_destroy (wws);
  300. return ret;
  301. }
  302. /**
  303. * Start server offering our hostlist.
  304. *
  305. * @return #GNUNET_OK on success
  306. */
  307. static int
  308. server_start ()
  309. {
  310. if (0 == port)
  311. {
  312. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  313. _ ("Invalid port number %u. Exiting.\n"),
  314. port);
  315. return GNUNET_SYSERR;
  316. }
  317. GNUNET_log (GNUNET_ERROR_TYPE_INFO,
  318. _ ("Businesscard HTTP server starts on %u\n"),
  319. port);
  320. daemon_handle = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_DEBUG,
  321. port,
  322. NULL /* accept_policy_callback */,
  323. NULL,
  324. &access_handler_callback,
  325. NULL,
  326. MHD_OPTION_CONNECTION_LIMIT,
  327. (unsigned int) 512,
  328. MHD_OPTION_PER_IP_CONNECTION_LIMIT,
  329. (unsigned int) 2,
  330. MHD_OPTION_CONNECTION_TIMEOUT,
  331. (unsigned int) 60,
  332. MHD_OPTION_CONNECTION_MEMORY_LIMIT,
  333. (size_t) (16 * 1024),
  334. MHD_OPTION_END);
  335. if (NULL == daemon_handle)
  336. {
  337. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  338. _ ("Could not start businesscard HTTP server on port %u\n"),
  339. (unsigned int) port);
  340. return GNUNET_SYSERR;
  341. }
  342. http_task = prepare_daemon (daemon_handle);
  343. return GNUNET_OK;
  344. }
  345. /**
  346. * Stop HTTP server.
  347. */
  348. static void
  349. server_stop (void *cls)
  350. {
  351. (void) cls;
  352. GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "HTTP server shutdown\n");
  353. if (NULL != http_task)
  354. {
  355. GNUNET_SCHEDULER_cancel (http_task);
  356. http_task = NULL;
  357. }
  358. if (NULL != daemon_handle)
  359. {
  360. MHD_stop_daemon (daemon_handle);
  361. daemon_handle = NULL;
  362. }
  363. if (NULL != main_response)
  364. {
  365. MHD_destroy_response (main_response);
  366. main_response = NULL;
  367. }
  368. if (NULL != invalid_gnskey_response)
  369. {
  370. MHD_destroy_response (invalid_gnskey_response);
  371. invalid_gnskey_response = NULL;
  372. }
  373. if (NULL != not_found_response)
  374. {
  375. MHD_destroy_response (not_found_response);
  376. not_found_response = NULL;
  377. }
  378. if (NULL != resfile)
  379. {
  380. GNUNET_free (resfile);
  381. resfile = NULL;
  382. }
  383. }
  384. /**
  385. * Main function that will be run.
  386. *
  387. * @param cls closure
  388. * @param args remaining command-line arguments
  389. * @param cfgfile name of the configuration file used (for saving, can be NULL!)
  390. * @param c configuration
  391. */
  392. static void
  393. run (void *cls,
  394. char *const *args,
  395. const char *cfgfile,
  396. const struct GNUNET_CONFIGURATION_Handle *c)
  397. {
  398. struct stat st;
  399. char *dir;
  400. char *fn;
  401. int fd;
  402. (void) cls;
  403. (void) args;
  404. (void) cfgfile;
  405. cfg = c;
  406. dir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
  407. GNUNET_assert (NULL != dir);
  408. GNUNET_asprintf (&fn, "%s%s%s", dir, DIR_SEPARATOR_STR, "gns-bcd.html");
  409. GNUNET_asprintf (&resfile, "%s%s%s", dir, DIR_SEPARATOR_STR, "gns-bcd.tex");
  410. GNUNET_free (dir);
  411. fd = open (fn, O_RDONLY);
  412. if (-1 == fd)
  413. {
  414. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
  415. GNUNET_free (fn);
  416. return;
  417. }
  418. if (0 != stat (fn, &st))
  419. {
  420. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
  421. GNUNET_free (fn);
  422. GNUNET_break (0 == close (fd));
  423. return;
  424. }
  425. GNUNET_free (fn);
  426. if (NULL ==
  427. (main_response = MHD_create_response_from_fd ((size_t) st.st_size, fd)))
  428. {
  429. GNUNET_break (0);
  430. GNUNET_break (0 == close (fd));
  431. return;
  432. }
  433. (void) MHD_add_response_header (main_response,
  434. MHD_HTTP_HEADER_CONTENT_TYPE,
  435. "text/html");
  436. invalid_gnskey_response =
  437. MHD_create_response_from_buffer (strlen (INVALID_GNSKEY),
  438. INVALID_GNSKEY,
  439. MHD_RESPMEM_PERSISTENT);
  440. (void) MHD_add_response_header (invalid_gnskey_response,
  441. MHD_HTTP_HEADER_CONTENT_TYPE,
  442. "text/html");
  443. not_found_response = MHD_create_response_from_buffer (strlen (NOT_FOUND),
  444. NOT_FOUND,
  445. MHD_RESPMEM_PERSISTENT);
  446. (void) MHD_add_response_header (not_found_response,
  447. MHD_HTTP_HEADER_CONTENT_TYPE,
  448. "text/html");
  449. if (GNUNET_OK != server_start ())
  450. return;
  451. GNUNET_SCHEDULER_add_shutdown (&server_stop, NULL);
  452. }
  453. /**
  454. * The main function for gnunet-gns.
  455. *
  456. * @param argc number of arguments from the command line
  457. * @param argv command line arguments
  458. * @return 0 ok, 1 on error
  459. */
  460. int
  461. main (int argc, char *const *argv)
  462. {
  463. struct GNUNET_GETOPT_CommandLineOption options[] = {
  464. GNUNET_GETOPT_option_uint16 ('p',
  465. "port",
  466. "PORT",
  467. gettext_noop (
  468. "Run HTTP serve on port PORT (default is 8888)"),
  469. &port),
  470. GNUNET_GETOPT_OPTION_END
  471. };
  472. int ret;
  473. if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
  474. return 2;
  475. GNUNET_log_setup ("gnunet-bcd", "WARNING", NULL);
  476. ret = (GNUNET_OK ==
  477. GNUNET_PROGRAM_run (argc,
  478. argv,
  479. "gnunet-bcd",
  480. _ ("GNUnet HTTP server to create business cards"),
  481. options,
  482. &run,
  483. NULL))
  484. ? 0
  485. : 1;
  486. GNUNET_free ((void *) argv);
  487. return ret;
  488. }
  489. /* end of gnunet-bcd.c */