aboutsummaryrefslogtreecommitdiff
path: root/src/gns/gnunet-bcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gns/gnunet-bcd.c')
-rw-r--r--src/gns/gnunet-bcd.c677
1 files changed, 0 insertions, 677 deletions
diff --git a/src/gns/gnunet-bcd.c b/src/gns/gnunet-bcd.c
deleted file mode 100644
index 60fe25945..000000000
--- a/src/gns/gnunet-bcd.c
+++ /dev/null
@@ -1,677 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2013 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file gns/gnunet-bcd.c
23 * @author Christian Grothoff
24 * @brief HTTP server to create GNS business cards
25 */
26
27#include "platform.h"
28#include <microhttpd.h>
29#include "gnunet_util_lib.h"
30#include "gnunet_identity_service.h"
31#include "gnunet_mhd_compat.h"
32
33struct StaticResource
34{
35 /**
36 * Handle to file on disk.
37 */
38 struct GNUNET_DISK_FileHandle *handle;
39
40 /**
41 * Size in bytes of the file.
42 */
43 uint64_t size;
44
45 /**
46 * Cached response object to send to clients.
47 */
48 struct MHD_Response *response;
49};
50
51struct ParameterMap
52{
53 /**
54 * Name of the parameter from the request.
55 */
56 char *name;
57
58 /**
59 * Name of the definition in the TeX output.
60 */
61 char *definition;
62};
63
64/**
65 * Handle to the HTTP server as provided by libmicrohttpd
66 */
67static struct MHD_Daemon *httpd = NULL;
68
69/**
70 * Our primary task for the HTTPD.
71 */
72static struct GNUNET_SCHEDULER_Task *httpd_task = NULL;
73
74/**
75 * Index file resource (simple result).
76 */
77static struct StaticResource *index_simple = NULL;
78
79/**
80 * Index file resource (full result).
81 */
82static struct StaticResource *index_full = NULL;
83
84/**
85 * Error: invalid gns key.
86 */
87static struct StaticResource *key_error = NULL;
88
89/**
90 * Error: 404
91 */
92static struct StaticResource *notfound_error = NULL;
93
94/**
95 * Errors after receiving the form data.
96 */
97static struct StaticResource *internal_error = NULL;
98
99/**
100 * Other errors.
101 */
102static struct StaticResource *forbidden_error = NULL;
103
104/**
105 * Full path to the TeX template file (simple result)
106 */
107static char *tex_file_simple = NULL;
108
109/**
110 * Full path to the TeX template file (full result)
111 */
112static char *tex_file_full = NULL;
113
114/**
115 * Full path to the TeX template file (PNG result)
116 */
117static char *tex_file_png = NULL;
118
119/**
120 * Used as a sort of singleton to send exactly one 100 CONTINUE per request.
121 */
122static int continue_100 = 100;
123
124/**
125 * Map of names with TeX definitions, used during PDF generation.
126 */
127static const struct ParameterMap pmap[] = {
128 {"prefix", "prefix"},
129 {"name", "name"},
130 {"suffix", "suffix"},
131 {"street", "street"},
132 {"city", "city"},
133 {"phone", "phone"},
134 {"fax", "fax"},
135 {"email", "email"},
136 {"homepage", "homepage"},
137 {"org", "organization"},
138 {"department", "department"},
139 {"subdepartment", "subdepartment"},
140 {"jobtitle", "jobtitle"},
141 {NULL, NULL},
142};
143
144/**
145 * Port number.
146 */
147static uint16_t port = 8888;
148
149/**
150 * Task ran at shutdown to clean up everything.
151 *
152 * @param cls unused
153 */
154static void
155do_shutdown (void *cls)
156{
157 /* We cheat a bit here: the file descriptor is implicitly closed by MHD, so
158 calling `GNUNET_DISK_file_close' would generate a spurious warning message
159 in the log. Since that function does nothing but close the descriptor and
160 free the allocated memory, After destroying the response all that's left to
161 do is call `GNUNET_free'. */
162 if (NULL != index_simple)
163 {
164 MHD_destroy_response (index_simple->response);
165 GNUNET_free (index_simple->handle);
166 GNUNET_free (index_simple);
167 }
168 if (NULL != index_full)
169 {
170 MHD_destroy_response (index_full->response);
171 GNUNET_free (index_full->handle);
172 GNUNET_free (index_full);
173 }
174 if (NULL != key_error)
175 {
176 MHD_destroy_response (key_error->response);
177 GNUNET_free (key_error->handle);
178 GNUNET_free (key_error);
179 }
180 if (NULL != notfound_error)
181 {
182 MHD_destroy_response (notfound_error->response);
183 GNUNET_free (notfound_error->handle);
184 GNUNET_free (notfound_error);
185 }
186 if (NULL != internal_error)
187 {
188 MHD_destroy_response (internal_error->response);
189 GNUNET_free (internal_error->handle);
190 GNUNET_free (internal_error);
191 }
192 if (NULL != forbidden_error)
193 {
194 MHD_destroy_response (forbidden_error->response);
195 GNUNET_free (forbidden_error->handle);
196 GNUNET_free (forbidden_error);
197 }
198
199 if (NULL != httpd_task)
200 {
201 GNUNET_SCHEDULER_cancel (httpd_task);
202 }
203 if (NULL != httpd)
204 {
205 MHD_stop_daemon (httpd);
206 }
207}
208
209/**
210 * Called when the HTTP server has some pending operations.
211 *
212 * @param cls unused
213 */
214static void
215do_httpd (void *cls);
216
217/**
218 * Schedule a task to run MHD.
219 */
220static void
221run_httpd (void)
222{
223 fd_set rs;
224 fd_set ws;
225 fd_set es;
226
227 struct GNUNET_NETWORK_FDSet *grs = GNUNET_NETWORK_fdset_create ();
228 struct GNUNET_NETWORK_FDSet *gws = GNUNET_NETWORK_fdset_create ();
229 struct GNUNET_NETWORK_FDSet *ges = GNUNET_NETWORK_fdset_create ();
230
231 FD_ZERO (&rs);
232 FD_ZERO (&ws);
233 FD_ZERO (&es);
234
235 int max = -1;
236 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
237
238 unsigned MHD_LONG_LONG timeout = 0;
239 struct GNUNET_TIME_Relative gtime = GNUNET_TIME_UNIT_FOREVER_REL;
240 if (MHD_YES == MHD_get_timeout (httpd, &timeout))
241 {
242 gtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
243 timeout);
244 }
245
246 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
247 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
248 GNUNET_NETWORK_fdset_copy_native (ges, &es, max + 1);
249
250 httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
251 gtime,
252 grs,
253 gws,
254 &do_httpd,
255 NULL);
256 GNUNET_NETWORK_fdset_destroy (grs);
257 GNUNET_NETWORK_fdset_destroy (gws);
258 GNUNET_NETWORK_fdset_destroy (ges);
259}
260
261/**
262 * Called when the HTTP server has some pending operations.
263 *
264 * @param cls unused
265 */
266static void
267do_httpd (void *cls)
268{
269 httpd_task = NULL;
270 MHD_run (httpd);
271 run_httpd ();
272}
273
274/**
275 * Send a response back to a connected client.
276 *
277 * @param cls unused
278 * @param connection the connection with the client
279 * @param url the requested address
280 * @param method the HTTP method used
281 * @param version the protocol version (including the "HTTP/" part)
282 * @param upload_data data sent with a POST request
283 * @param upload_data_size length in bytes of the POST data
284 * @param ptr used to pass data between request handling phases
285 * @return MHD_NO on error
286 */
287static MHD_RESULT
288create_response (void *cls,
289 struct MHD_Connection *connection,
290 const char *url,
291 const char *method,
292 const char *version,
293 const char *upload_data,
294 size_t *upload_data_size,
295 void **ptr)
296{
297 (void) cls;
298 (void) version;
299 (void) upload_data;
300 (void) upload_data_size;
301
302 bool isget = (0 == strcmp (method, MHD_HTTP_METHOD_GET));
303 bool ishead = (0 == strcmp (method, MHD_HTTP_METHOD_HEAD));
304
305 if (!isget && !ishead)
306 {
307 return MHD_queue_response (connection,
308 MHD_HTTP_NOT_IMPLEMENTED,
309 forbidden_error->response);
310 }
311
312 if (ishead)
313 {
314 /* Dedicated branch in case we want to provide a different result for some
315 reason (e.g. a non-web browser application using the web UI) */
316 return MHD_queue_response (connection,
317 MHD_HTTP_OK,
318 index_simple->response);
319 }
320
321 /* Send a 100 CONTINUE response to tell clients that the result of the
322 request might take some time */
323 if (NULL == *ptr)
324 {
325 *ptr = &continue_100;
326 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending 100 CONTINUE\n");
327 return MHD_YES;
328 }
329
330 if (0 == strcmp ("/", url))
331 {
332 return MHD_queue_response (connection,
333 MHD_HTTP_OK,
334 index_simple->response);
335 }
336
337 if (0 == strcmp ("/full", url))
338 {
339 return MHD_queue_response (connection,
340 MHD_HTTP_OK,
341 index_full->response);
342 }
343
344 bool isfull = (0 == strcmp ("/submit/full", url));
345 bool issimple = (0 == strcmp ("/submit/simple", url));
346
347 if (!isfull && !issimple)
348 {
349 return MHD_queue_response (connection,
350 MHD_HTTP_NOT_FOUND,
351 notfound_error->response);
352 }
353
354 const char *gpgfp = MHD_lookup_connection_value (connection,
355 MHD_GET_ARGUMENT_KIND,
356 "gpgfingerprint");
357 const char *gnsnick = MHD_lookup_connection_value (connection,
358 MHD_GET_ARGUMENT_KIND,
359 "gnsnick");
360 const char *gnskey = MHD_lookup_connection_value (connection,
361 MHD_GET_ARGUMENT_KIND,
362 "gnskey");
363 const char *qrpng = MHD_lookup_connection_value (connection,
364 MHD_GET_ARGUMENT_KIND,
365 "gnspng");
366
367 struct GNUNET_IDENTITY_PublicKey pk;
368 if (NULL == gnskey
369 || GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (gnskey, &pk))
370 {
371 return MHD_queue_response (connection,
372 MHD_HTTP_BAD_REQUEST,
373 key_error->response);
374 }
375
376 char *tmpd = GNUNET_DISK_mkdtemp (gnskey);
377 if (NULL == tmpd)
378 {
379 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "mktemp", gnskey);
380 return MHD_queue_response (connection,
381 MHD_HTTP_INTERNAL_SERVER_ERROR,
382 internal_error->response);
383 }
384
385 char *defpath = NULL;
386 GNUNET_asprintf (&defpath, "%s%s%s", tmpd, DIR_SEPARATOR_STR, "def.tex");
387
388 FILE *deffile = fopen (defpath, "w");
389 if (NULL == deffile)
390 {
391 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", defpath);
392 GNUNET_free (defpath);
393 GNUNET_DISK_directory_remove (tmpd);
394 GNUNET_free (tmpd);
395 return MHD_queue_response (connection,
396 MHD_HTTP_INTERNAL_SERVER_ERROR,
397 internal_error->response);
398 }
399
400 GNUNET_free (defpath);
401
402 for (size_t i=0; NULL!=pmap[i].name; ++i)
403 {
404 const char *value = MHD_lookup_connection_value (connection,
405 MHD_GET_ARGUMENT_KIND,
406 pmap[i].name);
407 fprintf (deffile,
408 "\\def\\%s{%s}\n",
409 pmap[i].definition,
410 (NULL == value) ? "" : value);
411 }
412
413 if (NULL != gpgfp)
414 {
415 size_t len = strlen (gpgfp);
416 char *line1 = GNUNET_strndup (gpgfp, len/2);
417 char *line2 = GNUNET_strdup (&gpgfp[len/2]);
418 fprintf (deffile,
419 "\\def\\gpglineone{%s}\n\\def\\gpglinetwo{%s}\n",
420 line1,
421 line2);
422 GNUNET_free (line1);
423 GNUNET_free (line2);
424 }
425
426 fprintf (deffile,
427 "\\def\\gns{%s/%s}\n",
428 gnskey,
429 (NULL == gnsnick) ? "" : gnsnick);
430
431 fclose (deffile);
432
433 char *command = NULL;
434 GNUNET_asprintf (&command,
435 "cd %s; cp %s gns-bcd.tex; "
436 "pdflatex %s gns-bcd.tex >/dev/null 2>&1",
437 tmpd,
438 (isfull) ? tex_file_full :
439 ((NULL == qrpng) ? tex_file_simple : tex_file_png),
440 (NULL == qrpng) ? "" : "-shell-escape");
441
442 int ret = system (command);
443
444 GNUNET_free (command);
445
446 if (WIFSIGNALED (ret) || 0 != WEXITSTATUS (ret))
447 {
448 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "system", command);
449 }
450
451 GNUNET_asprintf (&defpath,
452 "%s%s%s",
453 tmpd,
454 DIR_SEPARATOR_STR,
455 (NULL == qrpng) ? "gns-bcd.pdf" : "gns-bcd.png");
456
457 int pdf = open (defpath, O_RDONLY);
458 if (-1 == pdf)
459 {
460 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", defpath);
461 GNUNET_free (defpath);
462 GNUNET_DISK_directory_remove (tmpd);
463 GNUNET_free (tmpd);
464 return MHD_queue_response (connection,
465 MHD_HTTP_INTERNAL_SERVER_ERROR,
466 internal_error->response);
467 }
468
469 struct stat statret;
470 GNUNET_break (0 == stat (defpath, &statret));
471
472 GNUNET_free (defpath);
473
474 struct MHD_Response *pdfrs =
475 MHD_create_response_from_fd ((size_t) statret.st_size, pdf);
476 if (NULL == pdfrs)
477 {
478 GNUNET_break (0);
479 GNUNET_break (0 == close (pdf));
480 GNUNET_DISK_directory_remove (tmpd);
481 GNUNET_free (tmpd);
482 return MHD_queue_response (connection,
483 MHD_HTTP_INTERNAL_SERVER_ERROR,
484 internal_error->response);
485 }
486
487 MHD_add_response_header (pdfrs,
488 MHD_HTTP_HEADER_CONTENT_TYPE,
489 (NULL == qrpng) ? "application/pdf" : "image/png");
490 MHD_add_response_header (pdfrs,
491 MHD_HTTP_HEADER_CONTENT_DISPOSITION,
492 (NULL == qrpng) ?
493 "attachment; filename=\"gns-business-card.pdf\"" :
494 "attachment; filename=\"gns-qr-code.png\"");
495 MHD_RESULT r = MHD_queue_response (connection, MHD_HTTP_OK, pdfrs);
496
497 MHD_destroy_response (pdfrs);
498 GNUNET_DISK_directory_remove (tmpd);
499 GNUNET_free (tmpd);
500
501 return r;
502}
503
504/**
505 * Open a file on disk and generate a response for it.
506 *
507 * @param name name of the file to open
508 * @param basedir directory where the file is located
509 * @return NULL on error
510 */
511static struct StaticResource *
512open_static_resource (const char *name, const char *basedir)
513{
514 char *fullname = NULL;
515 GNUNET_asprintf (&fullname, "%s%s%s", basedir, DIR_SEPARATOR_STR, name);
516
517 struct GNUNET_DISK_FileHandle *f =
518 GNUNET_DISK_file_open (fullname,
519 GNUNET_DISK_OPEN_READ,
520 GNUNET_DISK_PERM_NONE);
521
522 GNUNET_free (fullname);
523
524 if (NULL == f)
525 {
526 return NULL;
527 }
528
529 off_t size = 0;
530 if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size))
531 {
532 GNUNET_DISK_file_close (f);
533 return NULL;
534 }
535
536 struct MHD_Response *response = MHD_create_response_from_fd64 (size, f->fd);
537
538 if (NULL == response)
539 {
540 GNUNET_DISK_file_close (f);
541 return NULL;
542 }
543
544 struct StaticResource *res = GNUNET_new (struct StaticResource);
545 res->handle = f;
546 res->size = (uint64_t) size;
547 res->response = response;
548
549 return res;
550}
551
552/**
553 * Main function that will be run.
554 *
555 * @param cls closure
556 * @param args remaining command-line arguments
557 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
558 * @param c configuration
559 */
560static void
561run (void *cls,
562 char *const *args,
563 const char *cfgfile,
564 const struct GNUNET_CONFIGURATION_Handle *c)
565{
566 (void) cls;
567 (void) args;
568 (void) cfgfile;
569
570 if (0 == port)
571 {
572 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
573 _ ("Invalid port number %u\n"),
574 port);
575 GNUNET_SCHEDULER_shutdown ();
576 return;
577 }
578
579 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
580
581 char *datadir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
582 GNUNET_assert (NULL != datadir);
583
584 GNUNET_asprintf (&tex_file_full,
585 "%s%s%s",
586 datadir,
587 DIR_SEPARATOR_STR,
588 "gns-bcd.tex");
589 GNUNET_asprintf (&tex_file_simple,
590 "%s%s%s",
591 datadir,
592 DIR_SEPARATOR_STR,
593 "gns-bcd-simple.tex");
594 GNUNET_asprintf(&tex_file_png,
595 "%s%s%s",
596 datadir,
597 DIR_SEPARATOR_STR,
598 "gns-bcd-png.tex");
599
600 index_simple = open_static_resource ("gns-bcd-simple.html", datadir);
601 index_full = open_static_resource ("gns-bcd.html", datadir);
602 key_error = open_static_resource ("gns-bcd-invalid-key.html", datadir);
603 notfound_error = open_static_resource ("gns-bcd-not-found.html", datadir);
604 internal_error = open_static_resource ("gns-bcd-internal-error.html", datadir);
605 forbidden_error = open_static_resource ("gns-bcd-forbidden.html", datadir);
606
607 GNUNET_free (datadir);
608
609 if ((NULL == index_simple) || (NULL == index_full)
610 || (NULL == key_error) || (NULL == notfound_error)
611 || (NULL == internal_error) || (NULL == forbidden_error))
612 {
613 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
614 _ ("Unable to set up the daemon\n"));
615 GNUNET_SCHEDULER_shutdown ();
616 return;
617 }
618
619 int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
620 do
621 {
622 httpd = MHD_start_daemon (flags,
623 port,
624 NULL, NULL,
625 &create_response, NULL,
626 MHD_OPTION_CONNECTION_LIMIT, 512,
627 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 2,
628 MHD_OPTION_CONNECTION_TIMEOUT, 60,
629 MHD_OPTION_CONNECTION_MEMORY_LIMIT, 16 * 1024,
630 MHD_OPTION_END);
631 flags = MHD_USE_DEBUG;
632 } while (NULL == httpd && flags != MHD_USE_DEBUG);
633
634 if (NULL == httpd)
635 {
636 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
637 _ ("Failed to start HTTP server\n"));
638 GNUNET_SCHEDULER_shutdown ();
639 return;
640 }
641
642 run_httpd ();
643}
644
645/**
646 * The main function for gnunet-gns.
647 *
648 * @param argc number of arguments from the command line
649 * @param argv command line arguments
650 * @return 0 ok, 1 on error
651 */
652int
653main (int argc, char *const *argv)
654{
655 struct GNUNET_GETOPT_CommandLineOption options[] = {
656 GNUNET_GETOPT_option_uint16 (
657 'p',
658 "port",
659 "PORT",
660 gettext_noop ("Run HTTP server on port PORT (default is 8888)"),
661 &port),
662 GNUNET_GETOPT_OPTION_END,
663 };
664
665 return ((GNUNET_OK ==
666 GNUNET_PROGRAM_run (argc,
667 argv,
668 "gnunet-bcd",
669 _ ("GNUnet HTTP server to create business cards"),
670 options,
671 &run,
672 NULL))
673 ? 0
674 : 1);
675}
676
677/* end of gnunet-bcd.c */