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