challenger-httpd.c (25555B)
1 /* 2 This file is part of Challenger 3 (C) 2023, 2024, 2025 Taler Systems SA 4 5 Challenger is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Challenger is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file challenger/challenger-httpd.c 18 * @brief OAuth 2.0 server challenging users to demonstrate ability to receive messages 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <microhttpd.h> 23 #include <gnunet/gnunet_util_lib.h> 24 #include <gnunet/gnunet_curl_lib.h> 25 #include "challenger_util.h" 26 #include "challenger-httpd.h" 27 #include "challenger-httpd_agpl.h" 28 #include "challenger-httpd_authorize.h" 29 #include "challenger-httpd_challenge.h" 30 #include "challenger-httpd_config.h" 31 #include "challenger-httpd_info.h" 32 #include "challenger-httpd_mhd.h" 33 #include "challenger-httpd_setup.h" 34 #include "challenger-httpd_solve.h" 35 #include "challenger-httpd_token.h" 36 #include "challenger-httpd_spa.h" 37 #include "challenger_database_lib.h" 38 #include "challenger-database/preflight.h" 39 40 41 /** 42 * Should a "Connection: close" header be added to each HTTP response? 43 */ 44 static int CH_challenger_connection_close; 45 46 /** 47 * Our context for making HTTP requests. 48 */ 49 struct GNUNET_CURL_Context *CH_ctx; 50 51 /** 52 * Reschedule context for #CH_ctx. 53 */ 54 static struct GNUNET_CURL_RescheduleContext *rc; 55 56 /** 57 * Global return code 58 */ 59 static int global_ret; 60 61 /** 62 * True if we started any HTTP daemon. 63 */ 64 static bool have_daemons; 65 66 struct CHALLENGERDB_PostgresContext *CH_context; 67 68 char *CH_base_url; 69 70 void *CH_message_template; 71 72 size_t CH_message_template_len; 73 74 struct GNUNET_TIME_Relative CH_validation_duration; 75 76 struct GNUNET_TIME_Relative CH_validation_expiration; 77 78 struct GNUNET_TIME_Relative CH_token_expiration; 79 80 struct GNUNET_TIME_Relative CH_pin_retransmission_frequency; 81 82 json_t *CH_restrictions; 83 84 char *CH_address_type; 85 86 char *CH_address_hint; 87 88 char *CH_auth_command; 89 90 91 /** 92 * Function called first by MHD with the full URL. 93 * 94 * @param cls NULL 95 * @param full_url the full URL 96 * @param con MHD connection object 97 * @return our handler context 98 */ 99 static void * 100 full_url_track_callback (void *cls, 101 const char *full_url, 102 struct MHD_Connection *con) 103 { 104 struct CH_HandlerContext *hc; 105 106 hc = GNUNET_new (struct CH_HandlerContext); 107 GNUNET_async_scope_fresh (&hc->async_scope_id); 108 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 109 hc->full_url = GNUNET_strdup (full_url); 110 return hc; 111 } 112 113 114 /** 115 * A client has requested the given url using the given method 116 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, 117 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback 118 * must call MHD callbacks to provide content to give back to the 119 * client and return an HTTP status code (i.e. #MHD_HTTP_OK, 120 * #MHD_HTTP_NOT_FOUND, etc.). 121 * 122 * @param cls argument given together with the function 123 * pointer when the handler was registered with MHD 124 * @param connection connection handle 125 * @param url the requested url 126 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, 127 * #MHD_HTTP_METHOD_PUT, etc.) 128 * @param version the HTTP version string (i.e. 129 * #MHD_HTTP_VERSION_1_1) 130 * @param upload_data the data being uploaded (excluding HEADERS, 131 * for a POST that fits into memory and that is encoded 132 * with a supported encoding, the POST data will NOT be 133 * given in upload_data and is instead available as 134 * part of #MHD_get_connection_values; very large POST 135 * data *will* be made available incrementally in 136 * @a upload_data) 137 * @param upload_data_size set initially to the size of the 138 * @a upload_data provided; the method must update this 139 * value to the number of bytes NOT processed; 140 * @param con_cls pointer that the callback can set to some 141 * address and that will be preserved by MHD for future 142 * calls for this request; since the access handler may 143 * be called many times (i.e., for a PUT/POST operation 144 * with plenty of upload data) this allows the application 145 * to easily associate some request-specific state. 146 * If necessary, this state can be cleaned up in the 147 * global #MHD_RequestCompletedCallback (which 148 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). 149 * Initially, `*con_cls` will be NULL. 150 * @return #MHD_YES if the connection was handled successfully, 151 * #MHD_NO if the socket must be closed due to a serious 152 * error while handling the request 153 */ 154 static enum MHD_Result 155 url_handler (void *cls, 156 struct MHD_Connection *connection, 157 const char *url, 158 const char *method, 159 const char *version, 160 const char *upload_data, 161 size_t *upload_data_size, 162 void **con_cls) 163 { 164 static struct CH_RequestHandler handlers[] = { 165 /* Landing page, tell humans to go away. */ 166 { 167 .url = "/", 168 .method = MHD_HTTP_METHOD_GET, 169 .handler = &CH_spa_redirect 170 }, 171 { 172 .url = "/webui/", 173 .method = MHD_HTTP_METHOD_GET, 174 .handler = &CH_handler_spa, 175 }, 176 { 177 .url = "/agpl", 178 .method = MHD_HTTP_METHOD_GET, 179 .handler = &CH_handler_agpl 180 }, 181 { 182 .url = "/config", 183 .method = MHD_HTTP_METHOD_GET, 184 .handler = &CH_handler_config 185 }, 186 { 187 .url = "/setup/", 188 .method = MHD_HTTP_METHOD_POST, 189 .handler = &CH_handler_setup 190 }, 191 { 192 .url = "/authorize/", 193 .method = MHD_HTTP_METHOD_GET, 194 .handler = &CH_handler_authorize 195 }, 196 { 197 .url = "/authorize/", 198 .method = MHD_HTTP_METHOD_POST, 199 .handler = &CH_handler_authorize 200 }, 201 { 202 .url = "/challenge/", 203 .method = MHD_HTTP_METHOD_POST, 204 .handler = &CH_handler_challenge 205 }, 206 { 207 .url = "/solve/", 208 .method = MHD_HTTP_METHOD_POST, 209 .handler = &CH_handler_solve 210 }, 211 { 212 .url = "/token", 213 .method = MHD_HTTP_METHOD_POST, 214 .handler = &CH_handler_token 215 }, 216 { 217 .url = "/info", 218 .method = MHD_HTTP_METHOD_GET, 219 .handler = &CH_handler_info 220 }, 221 { 222 NULL, NULL, NULL 223 } 224 }; 225 struct CH_HandlerContext *hc = *con_cls; 226 227 (void) cls; 228 (void) version; 229 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 230 "Handling %s request for `%s'\n", 231 method, 232 url); 233 if (NULL == hc->connection) 234 { 235 const char *correlation_id; 236 bool found = false; 237 238 hc->connection = connection; 239 /* We only read the correlation ID on the first callback for every client */ 240 correlation_id = MHD_lookup_connection_value (connection, 241 MHD_HEADER_KIND, 242 "Challenger-Correlation-Id"); 243 if ( (NULL != correlation_id) && 244 (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)) ) 245 { 246 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 247 "Illegal incoming correlation ID\n"); 248 correlation_id = NULL; 249 } 250 if (NULL != correlation_id) 251 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 252 "Handling request for (%s) URL '%s', correlation_id=%s\n", 253 method, 254 url, 255 correlation_id); 256 else 257 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 258 "Handling request (%s) for URL '%s'\n", 259 method, 260 url); 261 262 for (unsigned int i = 0; NULL != handlers[i].url; i++) 263 { 264 struct CH_RequestHandler *rh = &handlers[i]; 265 266 if ( (0 == strcmp (url, 267 rh->url)) || 268 ( (0 == strncmp (url, 269 rh->url, 270 strlen (rh->url))) && 271 (1 < strlen (rh->url)) && 272 ('/' == rh->url[strlen (rh->url) - 1]) ) ) 273 { 274 found = true; 275 if (0 == strcasecmp (method, 276 MHD_HTTP_METHOD_OPTIONS)) 277 { 278 return TALER_MHD_reply_cors_preflight (connection); 279 } 280 if (0 == strcasecmp (method, 281 rh->method)) 282 { 283 hc->rh = rh; 284 break; 285 } 286 } 287 } 288 if (NULL == hc->rh) 289 { 290 if (found) 291 { 292 char allow[128] = "OPTIONS"; 293 size_t aoff = strlen ("OPTIONS"); 294 struct MHD_Response *resp; 295 enum MHD_Result ret; 296 297 GNUNET_break_op (0); 298 /* Build Allow header: OPTIONS is always supported (handled above), 299 plus every method registered for this URL. */ 300 for (unsigned int j = 0; NULL != handlers[j].url; j++) 301 { 302 const struct CH_RequestHandler *rh2 = &handlers[j]; 303 304 if ( (0 != strcmp (url, rh2->url)) && 305 ! ( (0 == strncmp (url, rh2->url, strlen (rh2->url))) && 306 (1 < strlen (rh2->url)) && 307 ('/' == rh2->url[strlen (rh2->url) - 1]) ) ) 308 continue; 309 GNUNET_assert (aoff + strlen (rh2->method) + 3 < sizeof (allow)); 310 memcpy (&allow[aoff], ", ", 2); 311 aoff += 2; 312 memcpy (&allow[aoff], rh2->method, strlen (rh2->method)); 313 aoff += strlen (rh2->method); 314 allow[aoff] = '\0'; 315 } 316 317 resp = MHD_create_response_from_buffer (0, 318 NULL, 319 MHD_RESPMEM_PERSISTENT); 320 TALER_MHD_add_global_headers (resp, 321 false); 322 GNUNET_break (MHD_YES == 323 MHD_add_response_header (resp, 324 MHD_HTTP_HEADER_ALLOW, 325 allow)); 326 ret = MHD_queue_response (hc->connection, 327 MHD_HTTP_METHOD_NOT_ALLOWED, 328 resp); 329 MHD_destroy_response (resp); 330 return ret; 331 } 332 GNUNET_break_op (0); 333 return TALER_MHD_reply_static (hc->connection, 334 MHD_HTTP_NOT_FOUND, 335 "text/plain", 336 NULL, 337 0); 338 } 339 } 340 else 341 { 342 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 343 } 344 GNUNET_assert (NULL != hc->rh); 345 hc->path = &url[strlen (hc->rh->url)]; 346 return hc->rh->handler (hc, 347 upload_data, 348 upload_data_size); 349 } 350 351 352 /** 353 * Shutdown task. Invoked when the application is being terminated. 354 * 355 * @param cls NULL 356 */ 357 static void 358 do_shutdown (void *cls) 359 { 360 (void) cls; 361 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 362 "Stopping challenger-httpd\n"); 363 TALER_MHD_daemons_halt (); 364 CH_wakeup_challenge_on_shutdown (); 365 if ( (NULL != CH_message_template) && 366 (MAP_FAILED != CH_message_template) ) 367 { 368 if (0 != 369 munmap (CH_message_template, 370 CH_message_template_len)) 371 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 372 "munmap"); 373 CH_message_template = NULL; 374 CH_message_template_len = 0; 375 } 376 TALER_MHD_daemons_destroy (); 377 if (NULL != CH_ctx) 378 { 379 GNUNET_CURL_fini (CH_ctx); 380 CH_ctx = NULL; 381 } 382 if (NULL != rc) 383 { 384 GNUNET_CURL_gnunet_rc_destroy (rc); 385 rc = NULL; 386 } 387 if (NULL != CH_context) 388 { 389 CHALLENGERDB_disconnect (CH_context); 390 CH_context = NULL; 391 } 392 } 393 394 395 /** 396 * Function called whenever MHD is done with a request. If the 397 * request was a POST, we may have stored a `struct Buffer *` in the 398 * @a con_cls that might still need to be cleaned up. Call the 399 * respective function to free the memory. 400 * 401 * @param cls client-defined closure 402 * @param connection connection handle 403 * @param con_cls value as set by the last call to 404 * the #MHD_AccessHandlerCallback 405 * @param toe reason for request termination 406 * @see #MHD_OPTION_NOTIFY_COMPLETED 407 * @ingroup request 408 */ 409 static void 410 handle_mhd_completion_callback (void *cls, 411 struct MHD_Connection *connection, 412 void **con_cls, 413 enum MHD_RequestTerminationCode toe) 414 { 415 struct CH_HandlerContext *hc = *con_cls; 416 const union MHD_ConnectionInfo *ci; 417 418 (void) cls; 419 if (NULL == hc) 420 return; 421 GNUNET_assert (hc->connection == connection); 422 GNUNET_break (GNUNET_OK == 423 CHALLENGERDB_preflight (CH_context)); 424 ci = MHD_get_connection_info (connection, 425 MHD_CONNECTION_INFO_HTTP_STATUS); 426 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 427 "Finished handling request with status %d (HTTP status %u)\n", 428 (int) toe, 429 (NULL != ci) ? ci->http_status : 0); 430 if (NULL != hc->cc) 431 hc->cc (hc->ctx); 432 GNUNET_free (hc->full_url); 433 GNUNET_free (hc); 434 *con_cls = NULL; 435 } 436 437 438 /** 439 * Kick MHD to run now, to be called after MHD_resume_connection(). 440 * Basically, we need to explicitly resume MHD's event loop whenever 441 * we made progress serving a request. This function re-schedules 442 * the task processing MHD's activities to run immediately. 443 * 444 * FIXME: replace by direct call... 445 */ 446 void 447 CH_trigger_daemon () 448 { 449 TALER_MHD_daemon_trigger (); 450 } 451 452 453 /** 454 * Kick GNUnet Curl scheduler to begin curl interactions. 455 */ 456 void 457 CH_trigger_curl () 458 { 459 GNUNET_CURL_gnunet_scheduler_reschedule (&rc); 460 } 461 462 463 /** 464 * Callback invoked on every listen socket to start the 465 * respective MHD HTTP daemon. 466 * 467 * @param cls unused 468 * @param lsock the listen socket 469 */ 470 static void 471 start_daemon (void *cls, 472 int lsock) 473 { 474 struct MHD_Daemon *mhd; 475 476 (void) cls; 477 GNUNET_assert (-1 != lsock); 478 mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK, 479 0 /* port */, 480 NULL, NULL, 481 &url_handler, NULL, 482 MHD_OPTION_LISTEN_SOCKET, lsock, 483 MHD_OPTION_URI_LOG_CALLBACK, 484 &full_url_track_callback, NULL, 485 MHD_OPTION_NOTIFY_COMPLETED, 486 &handle_mhd_completion_callback, NULL, 487 MHD_OPTION_CONNECTION_TIMEOUT, 488 (unsigned int) 10 /* 10s */, 489 MHD_OPTION_END); 490 if (NULL == mhd) 491 { 492 global_ret = EXIT_NO_RESTART; 493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 494 "Failed to launch HTTP daemon.\n"); 495 GNUNET_SCHEDULER_shutdown (); 496 return; 497 } 498 have_daemons = true; 499 TALER_MHD_daemon_start (mhd); 500 } 501 502 503 /** 504 * Main function that will be run by the scheduler. 505 * 506 * @param cls closure 507 * @param args remaining command-line arguments 508 * @param cfgfile name of the configuration file used (for saving, can be 509 * NULL!) 510 * @param config configuration 511 */ 512 static void 513 run (void *cls, 514 char *const *args, 515 const char *cfgfile, 516 const struct GNUNET_CONFIGURATION_Handle *config) 517 { 518 enum TALER_MHD_GlobalOptions go; 519 520 (void) cls; 521 (void) args; 522 (void) cfgfile; 523 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 524 "Starting challenger-httpd\n"); 525 go = TALER_MHD_GO_NONE; 526 if (CH_challenger_connection_close) 527 go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; 528 TALER_MHD_setup (go); 529 530 if (GNUNET_OK != 531 CH_spa_init ()) 532 { 533 global_ret = EXIT_NOTCONFIGURED; 534 GNUNET_SCHEDULER_shutdown (); 535 return; 536 } 537 538 if (GNUNET_OK != 539 GNUNET_CONFIGURATION_get_value_time (config, 540 "CHALLENGER", 541 "VALIDATION_DURATION", 542 &CH_validation_duration)) 543 { 544 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 545 "CHALLENGER", 546 "VALIDATION_DURATION"); 547 global_ret = EXIT_NOTCONFIGURED; 548 GNUNET_SCHEDULER_shutdown (); 549 return; 550 } 551 if (GNUNET_OK != 552 GNUNET_CONFIGURATION_get_value_time (config, 553 "CHALLENGER", 554 "VALIDATION_EXPIRATION", 555 &CH_validation_expiration)) 556 { 557 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 558 "CHALLENGER", 559 "VALIDATION_EXPIRATION"); 560 global_ret = EXIT_NOTCONFIGURED; 561 GNUNET_SCHEDULER_shutdown (); 562 return; 563 } 564 if (GNUNET_OK != 565 GNUNET_CONFIGURATION_get_value_time (config, 566 "CHALLENGER", 567 "TOKEN_EXPIRATION", 568 &CH_token_expiration)) 569 { 570 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 571 "CHALLENGER", 572 "VALIDATION_EXPIRATION"); 573 CH_token_expiration = GNUNET_TIME_UNIT_HOURS; 574 } 575 if (GNUNET_OK != 576 GNUNET_CONFIGURATION_get_value_string (config, 577 "CHALLENGER", 578 "AUTH_COMMAND", 579 &CH_auth_command)) 580 { 581 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 582 "CHALLENGER", 583 "AUTH_COMMAND"); 584 global_ret = EXIT_NOTCONFIGURED; 585 GNUNET_SCHEDULER_shutdown (); 586 return; 587 } 588 if (GNUNET_OK != 589 GNUNET_CONFIGURATION_get_value_string (config, 590 "CHALLENGER", 591 "ADDRESS_TYPE", 592 &CH_address_type)) 593 { 594 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 595 "CHALLENGER", 596 "ADDRESS_TYPE"); 597 global_ret = EXIT_NOTCONFIGURED; 598 GNUNET_SCHEDULER_shutdown (); 599 return; 600 } 601 if (GNUNET_OK != 602 GNUNET_CONFIGURATION_get_value_string (config, 603 "CHALLENGER", 604 "ADDRESS_HINT", 605 &CH_address_hint)) 606 { 607 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 608 "CHALLENGER", 609 "ADDRESS_HINT"); 610 CH_address_hint = GNUNET_strdup (""); 611 } 612 if (GNUNET_OK != 613 GNUNET_CONFIGURATION_get_value_string (config, 614 "CHALLENGER", 615 "BASE_URL", 616 &CH_base_url)) 617 { 618 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 619 "CHALLENGER", 620 "BASE_URL"); 621 global_ret = EXIT_NOTCONFIGURED; 622 GNUNET_SCHEDULER_shutdown (); 623 return; 624 } 625 if ( (! TALER_url_valid_charset (CH_base_url)) || 626 (! TALER_is_web_url (CH_base_url) ) || 627 (0 == strlen (CH_base_url)) || 628 ('/' != CH_base_url[strlen (CH_base_url) - 1]) ) 629 { 630 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 631 "CHALLENGER", 632 "BASE_URL", 633 "invalid URL"); 634 global_ret = EXIT_NOTCONFIGURED; 635 GNUNET_SCHEDULER_shutdown (); 636 return; 637 } 638 { 639 char *tmpl_file; 640 int fd; 641 struct stat s; 642 643 if (GNUNET_OK != 644 GNUNET_CONFIGURATION_get_value_filename (config, 645 "CHALLENGER", 646 "MESSAGE_TEMPLATE_FILE", 647 &tmpl_file)) 648 { 649 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 650 "CHALLENGER", 651 "MESSAGE_TEMPLATE_FILE"); 652 } 653 else 654 { 655 fd = open (tmpl_file, 656 O_RDONLY); 657 GNUNET_free (tmpl_file); 658 if (-1 == fd) 659 { 660 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 661 "CHALLENGER", 662 "MESSAGE_TEMPLATE_FILE", 663 strerror (errno)); 664 global_ret = EXIT_NOTCONFIGURED; 665 GNUNET_SCHEDULER_shutdown (); 666 return; 667 } 668 if (0 != fstat (fd, 669 &s)) 670 { 671 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 672 "CHALLENGER", 673 "MESSAGE_TEMPLATE_FILE", 674 strerror (errno)); 675 GNUNET_free (tmpl_file); 676 GNUNET_break (0 == close (fd)); 677 global_ret = EXIT_NOTCONFIGURED; 678 GNUNET_SCHEDULER_shutdown (); 679 return; 680 } 681 CH_message_template_len = (size_t) s.st_size; 682 CH_message_template = mmap (NULL, 683 CH_message_template_len, 684 PROT_READ, 685 MAP_SHARED, 686 fd, 687 0); 688 GNUNET_break (0 == close (fd)); 689 if (MAP_FAILED == CH_message_template) 690 { 691 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 692 "CHALLENGER", 693 "MESSAGE_TEMPLATE_FILE", 694 strerror (errno)); 695 GNUNET_free (tmpl_file); 696 global_ret = EXIT_NOTCONFIGURED; 697 GNUNET_SCHEDULER_shutdown (); 698 return; 699 } 700 } 701 } 702 703 { 704 char *restrictions; 705 706 if (GNUNET_OK == 707 GNUNET_CONFIGURATION_get_value_string (config, 708 "CHALLENGER", 709 "ADDRESS_RESTRICTIONS", 710 &restrictions)) 711 { 712 json_error_t err; 713 714 CH_restrictions = json_loads (restrictions, 715 JSON_REJECT_DUPLICATES, 716 &err); 717 GNUNET_free (restrictions); 718 if (NULL == CH_restrictions) 719 { 720 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 721 "CHALLENGER", 722 "ADDRESS_RESTRICTIONS", 723 err.text); 724 global_ret = EXIT_NOTCONFIGURED; 725 GNUNET_SCHEDULER_shutdown (); 726 return; 727 } 728 } 729 else 730 { 731 CH_restrictions = json_object (); 732 GNUNET_assert (NULL != CH_restrictions); 733 } 734 } 735 736 global_ret = EXIT_NOTCONFIGURED; 737 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 738 NULL); 739 /* setup HTTP client event loop */ 740 CH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 741 &rc); 742 rc = GNUNET_CURL_gnunet_rc_create (CH_ctx); 743 if (NULL == 744 (CH_context = CHALLENGERDB_connect (config, 745 false))) 746 { 747 global_ret = EXIT_NOTINSTALLED; 748 GNUNET_SCHEDULER_shutdown (); 749 return; 750 } 751 if (GNUNET_OK != 752 CHALLENGERDB_preflight (CH_context)) 753 { 754 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 755 "Database not setup. Did you run challenger-dbinit?\n"); 756 GNUNET_SCHEDULER_shutdown (); 757 return; 758 } 759 { 760 enum GNUNET_GenericReturnValue ret; 761 762 ret = TALER_MHD_listen_bind (config, 763 "challenger", 764 &start_daemon, 765 NULL); 766 switch (ret) 767 { 768 case GNUNET_SYSERR: 769 global_ret = EXIT_NOTCONFIGURED; 770 GNUNET_SCHEDULER_shutdown (); 771 return; 772 case GNUNET_NO: 773 if (! have_daemons) 774 { 775 global_ret = EXIT_NOTCONFIGURED; 776 GNUNET_SCHEDULER_shutdown (); 777 return; 778 } 779 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 780 "Could not open all configured listen sockets\n"); 781 break; 782 case GNUNET_OK: 783 break; 784 } 785 } 786 global_ret = EXIT_SUCCESS; 787 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 788 "Challenger-httpd ready\n"); 789 } 790 791 792 /** 793 * The main function of the serve tool 794 * 795 * @param argc number of arguments from the command line 796 * @param argv command line arguments 797 * @return 0 ok, 1 on error 798 */ 799 int 800 main (int argc, 801 char *const *argv) 802 { 803 struct GNUNET_GETOPT_CommandLineOption options[] = { 804 GNUNET_GETOPT_option_flag ('C', 805 "connection-close", 806 "force HTTP connections to be closed after each request", 807 &CH_challenger_connection_close), 808 GNUNET_GETOPT_OPTION_END 809 }; 810 enum GNUNET_GenericReturnValue ret; 811 812 ret = GNUNET_PROGRAM_run (CHALLENGER_project_data (), 813 argc, argv, 814 "challenger-httpd", 815 "challenger REST and OAuth 2.0 API", 816 options, 817 &run, NULL); 818 if (GNUNET_NO == ret) 819 return EXIT_SUCCESS; 820 if (GNUNET_SYSERR == ret) 821 return EXIT_INVALIDARGUMENT; 822 return global_ret; 823 }