2
0

gnunet-bcd.c 16 KB

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