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