sync-httpd2.c (27699B)
1 /* 2 This file is part of TALER 3 (C) 2019--2025 Taler Systems SA 4 5 TALER 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 TALER 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 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file sync/sync-httpd2.c 18 * @brief HTTP serving layer intended to provide basic backup operations 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <gnunet/gnunet_util_lib.h> 23 #include "sync/sync_util.h" 24 #include "sync-httpd2.h" 25 #include "sync/sync_database_lib.h" 26 #include "sync-httpd2_config.h" 27 #include "sync-httpd2_post-backups-KEY-blocks-NONCE.h" 28 #include "sync-httpd2_delete-backups-KEY-blocks-NONCE.h" 29 #include "sync-httpd2_get-backups-KEY-objects-UID.h" 30 #include "sync-httpd2_post-backups-KEY-objects-UID.h" 31 #include "sync-httpd2_get-backups-KEY.h" 32 #include "sync-httpd2_get-backups-KEY-blocks.h" 33 #include "sync-httpd2_get-backups-KEY-blocks-reconcile.h" 34 35 36 /** 37 * @brief A handler for a backup sub-operation under /backups/$KEY/. 38 * 39 * Each entry maps an operation name ("block" or "object") and an 40 * HTTP method to a handler function. 41 */ 42 struct SH_BackupOperationHandler 43 { 44 45 /** 46 * Operation name, e.g. "block" or "object". 47 */ 48 const char *operation; 49 50 /** 51 * HTTP method this handler is for. 52 */ 53 enum MHD_HTTP_Method method; 54 55 /** 56 * Function to call. 57 * 58 * @param request the MHD request to handle 59 * @param account_pub public key of the account 60 * @param args path segments after the operation name 61 * @param upload_size number of bytes being uploaded 62 * @param is_update whether the operation is an update 63 * @return MHD action 64 */ 65 const struct MHD_Action *(*handler)( 66 struct MHD_Request *request, 67 const struct SYNC_AccountPublicKeyP *account_pub, 68 const char *const args[], 69 uint_fast64_t upload_size, 70 bool is_update); 71 72 /** 73 * Number of path segments this handler expects 74 * after the operation name. 75 */ 76 unsigned int nargs; 77 78 /** 79 * Is @e nargs only an upper bound? 80 */ 81 bool nargs_is_upper_bound; 82 }; 83 84 85 /** 86 * Should a "Connection: close" header be added to each HTTP response? 87 */ 88 static int SH_sync_connection_close; 89 90 /** 91 * Upload limit to the service, in megabytes. 92 */ 93 unsigned long long int SH_upload_limit_mb; 94 95 /** 96 * How many uploads of the maximum permitted size the shared upload 97 * buffer pool must have room for. 98 * 99 * Block uploads are handed to the handler in one piece 100 * (#MHD_action_process_upload_full()), so MHD buffers the whole body 101 * out of a daemon-wide pool. The pool is a budget, not a 102 * pre-allocation -- MHD mallocs per request and gives the space back 103 * when the request ends -- so this bounds the worst-case resident size 104 * of concurrent uploads rather than costing anything up front. 105 */ 106 #define UPLOAD_POOL_CONCURRENCY 8 107 108 109 bool 110 SH_upload_too_large (uint_fast64_t upload_size, 111 size_t *buff_size) 112 { 113 uint_fast64_t limit; 114 115 limit = ((uint_fast64_t) SH_upload_limit_mb) * 1024 * 1024; 116 if (MHD_SIZE_UNKNOWN == upload_size) 117 { 118 /* Chunked upload: the length is not known yet, so bound the buffer 119 by the limit and let MHD fail the request if it does not fit. */ 120 *buff_size = (size_t) GNUNET_MIN (limit, 121 (uint_fast64_t) SIZE_MAX); 122 return false; 123 } 124 if ( (upload_size >= limit) || 125 (upload_size > SIZE_MAX) ) 126 return true; 127 *buff_size = (size_t) upload_size; 128 return false; 129 } 130 131 132 /** 133 * Annual fee for the backup account. 134 */ 135 struct TALER_Amount SH_annual_fee; 136 137 /** 138 * Our Taler backend to process payments. 139 */ 140 char *SH_backend_url; 141 142 /** 143 * Our fulfillment URL. 144 */ 145 char *SH_fulfillment_url; 146 147 /** 148 * Our context for making HTTP requests. 149 */ 150 struct GNUNET_CURL_Context *SH_ctx; 151 152 /** 153 * Reschedule context for #SH_ctx. 154 */ 155 static struct GNUNET_CURL_RescheduleContext *rc; 156 157 /** 158 * Global return code 159 */ 160 static int global_ret; 161 162 /** 163 * Set to true if we have started an MHD daemons. 164 */ 165 static bool have_daemons; 166 167 /** 168 * Username and password to use for client authentication 169 * (optional). 170 */ 171 static char *userpass; 172 173 /** 174 * Type of the client's TLS certificate (optional). 175 */ 176 static char *certtype; 177 178 /** 179 * File with the client's TLS certificate (optional). 180 */ 181 static char *certfile; 182 183 /** 184 * File with the client's TLS private key (optional). 185 */ 186 static char *keyfile; 187 188 /** 189 * This value goes in the Authorization:-header. 190 */ 191 static char *apikey; 192 193 /** 194 * Passphrase to decrypt client's TLS private key file (optional). 195 */ 196 static char *keypass; 197 198 /** 199 * Amount of insurance. 200 */ 201 struct TALER_Amount SH_insurance; 202 203 204 /** 205 * Function to respond to GET requests on '/'. 206 * 207 * @param request the MHD request to handle 208 * @param upload_size number of bytes uploaded 209 * @return MHD action 210 */ 211 static const struct MHD_Action * 212 respond_root (struct MHD_Request *request, 213 uint_fast64_t upload_size) 214 { 215 const char *msg = "Hello, I'm sync. This HTTP server is not for humans.\n"; 216 struct MHD_Response *resp; 217 218 GNUNET_break_op (0 == upload_size); 219 resp = MHD_response_from_buffer_static ( 220 MHD_HTTP_STATUS_OK, 221 strlen (msg), 222 msg); 223 GNUNET_break (MHD_SC_OK == 224 MHD_response_add_header (resp, 225 MHD_HTTP_HEADER_CONTENT_TYPE, 226 "text/plain")); 227 return MHD_action_from_response (request, 228 resp); 229 } 230 231 232 /** 233 * Function to respond to GET requests on unknown URLs. 234 * 235 * @param request the MHD request to handle 236 * @param upload_size number of bytes uploaded 237 * @return MHD action 238 */ 239 static const struct MHD_Action * 240 respond_404 (struct MHD_Request *request, 241 uint_fast64_t upload_size) 242 { 243 const char *msg = "<html><title>404: not found</title></html>"; 244 struct MHD_Response *resp; 245 246 GNUNET_break_op (0 == upload_size); 247 resp = MHD_response_from_buffer_static ( 248 MHD_HTTP_STATUS_NOT_FOUND, 249 strlen (msg), 250 msg); 251 GNUNET_break (MHD_SC_OK == 252 MHD_response_add_header (resp, 253 MHD_HTTP_HEADER_CONTENT_TYPE, 254 "text/html")); 255 return MHD_action_from_response (request, 256 resp); 257 } 258 259 260 /** 261 * Function to respond to GET requests on '/agpl'. 262 * 263 * @param request the MHD request to handle 264 * @param upload_size number of bytes uploaded 265 * @return MHD action 266 */ 267 static const struct MHD_Action * 268 respond_agpl (struct MHD_Request *request, 269 uint_fast64_t upload_size) 270 { 271 GNUNET_break_op (0 == upload_size); 272 return TALER_MHD2_reply_agpl (request, 273 "https://git.taler.net/sync.git/"); 274 } 275 276 277 /** 278 * Maximum length of the encoded account public key in the URL. 279 * An EdDSA public key encodes to 52 characters. 280 */ 281 #define ACCOUNT_KEY_MAX 64 282 283 /** 284 * Maximum reasonable subpath length after /backups/$KEY/. 285 */ 286 #define SUBPATH_MAX 256 287 288 /** 289 * Maximum number of path segments we split a subpath into, 290 * including the operation name. 291 */ 292 #define SUBPATH_MAX_SEGMENTS 3 293 294 295 /** 296 * Handle a request on a subpath under /backups/$KEY/. 297 * 298 * Parses the subpath (e.g. "block/NONCE" or "object/UID"), 299 * splits it into segments, and dispatches to the appropriate 300 * handler using the data-driven #SH_BackupOperationHandler 301 * table. 302 * 303 * @param request the MHD request to handle 304 * @param account_pub public key of the account 305 * @param method HTTP method of the request 306 * @param subpath remainder of the URL after /backups/$KEY/ 307 * @param upload_size number of bytes being uploaded 308 * @return MHD action 309 */ 310 static const struct MHD_Action * 311 handle_backup_subpath (struct MHD_Request *request, 312 const struct SYNC_AccountPublicKeyP *account_pub, 313 enum MHD_HTTP_Method method, 314 const char *subpath, 315 uint_fast64_t upload_size) 316 { 317 static struct SH_BackupOperationHandler bhandlers[] = { 318 { 319 .operation = "blocks", 320 .method = MHD_HTTP_METHOD_POST, 321 .handler = &SH_backup_block_post, 322 .nargs = 1, 323 }, 324 { 325 .operation = "blocks", 326 /* FIXME: replace with PATCH when mhd2 supports it */ 327 .method = MHD_HTTP_METHOD_PUT, 328 .handler = &SH_backup_block_post, 329 .nargs = 1, 330 }, 331 { 332 .operation = "blocks", 333 .method = MHD_HTTP_METHOD_DELETE, 334 .handler = &SH_backup_block_delete, 335 .nargs = 1, 336 }, 337 { 338 .operation = "blocks", 339 .method = MHD_HTTP_METHOD_GET, 340 .handler = &SH_backup_blocks_get, 341 .nargs = 0, 342 }, 343 { 344 .operation = "blocks", 345 .method = MHD_HTTP_METHOD_GET, 346 .handler = &SH_backup_blocks_reconcile_get, 347 .nargs = 1, 348 .nargs_is_upper_bound = true, 349 }, 350 { 351 .operation = "objects", 352 .method = MHD_HTTP_METHOD_GET, 353 .handler = &SH_backup_object_get, 354 .nargs = 1, 355 }, 356 { 357 .operation = "objects", 358 .method = MHD_HTTP_METHOD_POST, 359 .handler = &SH_backup_object_post, 360 .nargs = 1, 361 }, 362 { .operation = NULL } 363 }; 364 size_t slen; 365 char d[SUBPATH_MAX]; 366 char *sp; 367 /* NULL-terminated, hence one more entry than we ever fill */ 368 const char *args[SUBPATH_MAX_SEGMENTS + 1] = { NULL }; 369 unsigned int i; 370 371 GNUNET_assert (NULL != request); 372 GNUNET_assert (NULL != subpath); 373 slen = strlen (subpath); 374 if (slen >= SUBPATH_MAX) 375 { 376 GNUNET_break_op (0); 377 return TALER_MHD2_reply_with_error ( 378 request, 379 MHD_HTTP_STATUS_URI_TOO_LONG, 380 TALER_EC_GENERIC_URI_TOO_LONG, 381 subpath); 382 } 383 memcpy (d, 384 subpath, 385 slen + 1); 386 387 i = 0; 388 args[i++] = strtok_r (d, 389 "/", 390 &sp); 391 while ( (NULL != args[i - 1]) && 392 (i < SUBPATH_MAX_SEGMENTS) ) 393 args[i++] = strtok_r (NULL, 394 "/", 395 &sp); 396 397 if (NULL == args[0]) 398 { 399 GNUNET_break_op (0); 400 return TALER_MHD2_reply_with_error ( 401 request, 402 MHD_HTTP_STATUS_NOT_FOUND, 403 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 404 subpath); 405 } 406 407 if (MHD_HTTP_METHOD_OPTIONS == method) 408 return TALER_MHD2_reply_cors_preflight (request); 409 410 for (unsigned int j = 0; NULL != bhandlers[j].operation; j++) 411 { 412 const struct SH_BackupOperationHandler *bh = &bhandlers[j]; 413 unsigned int nargs; 414 415 if (0 != strcmp (args[0], 416 bh->operation)) 417 continue; 418 if (bh->method != method) 419 continue; 420 421 /* Count path segments after the operation name */ 422 nargs = 0; 423 while (NULL != args[nargs + 1]) 424 nargs++; 425 426 if (bh->nargs_is_upper_bound 427 ? (nargs > bh->nargs) 428 : (nargs != bh->nargs)) 429 { 430 /* Not this handler's shape; there may be another handler for 431 the same (operation, method) with a different number of 432 arguments (e.g. GET blocks vs. GET blocks/reconcile). */ 433 continue; 434 } 435 return bh->handler (request, 436 account_pub, 437 &args[1], 438 upload_size, 439 /* FIXME: replace with PATCH when mhd2 supports it */ 440 MHD_HTTP_METHOD_PUT == bh->method); 441 } 442 443 GNUNET_break_op (0); 444 return TALER_MHD2_reply_with_error ( 445 request, 446 MHD_HTTP_STATUS_NOT_FOUND, 447 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 448 args[0]); 449 } 450 451 452 /** 453 * A client has requested the given url using the given method 454 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, 455 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback 456 * must call MHD callbacks to provide content to give back to the 457 * client. 458 * 459 * @param cls argument given together with the function 460 * pointer when the handler was registered with MHD 461 * @param request the request to handle 462 * @param path the requested uri (without arguments after "?") 463 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, 464 * #MHD_HTTP_METHOD_PUT, etc.) 465 * @param upload_size the size of the message upload content payload, 466 * #MHD_SIZE_UNKNOWN for chunked uploads (if the final chunk 467 * has not been processed yet) 468 * @return next action 469 */ 470 static const struct MHD_Action * 471 url_handler (void *cls, 472 struct MHD_Request *request, 473 const struct MHD_String *path, 474 enum MHD_HTTP_Method method, 475 uint_fast64_t upload_size) 476 { 477 static struct SH_RequestHandler handlers[] = { 478 /* Landing page, tell humans to go away. */ 479 { 480 .url = "/", 481 .method = MHD_HTTP_METHOD_GET, 482 .handler = &respond_root 483 }, 484 { 485 .url = "/agpl", 486 .method = MHD_HTTP_METHOD_GET, 487 .handler = &respond_agpl, 488 }, 489 { 490 .url = "/config", 491 .method = MHD_HTTP_METHOD_GET, 492 .handler = &SH_handler_config, 493 }, 494 { 495 .url = NULL 496 } 497 }; 498 struct SYNC_AccountPublicKeyP account_pub; 499 500 (void) cls; 501 /* Backup API */ 502 if (0 == strncmp (path->cstr, 503 "/backups/", 504 strlen ("/backups/"))) 505 { 506 const char *ac = path->cstr + strlen ("/backups/"); 507 const char *next_slash; 508 size_t ac_len; 509 510 /* Find end of account key (next '/' or end of string) */ 511 next_slash = strchr (ac, 512 '/'); 513 if (NULL == next_slash) 514 ac_len = strlen (ac); 515 else 516 ac_len = next_slash - ac; 517 518 { 519 /* Bound before we put it on the stack; the segment is 520 client-controlled */ 521 char ac_str[ACCOUNT_KEY_MAX + 1]; 522 523 if (ac_len > ACCOUNT_KEY_MAX) 524 { 525 GNUNET_break_op (0); 526 return TALER_MHD2_reply_with_error ( 527 request, 528 MHD_HTTP_STATUS_BAD_REQUEST, 529 TALER_EC_GENERIC_PARAMETER_MALFORMED, 530 "account public key"); 531 } 532 memcpy (ac_str, 533 ac, 534 ac_len); 535 ac_str[ac_len] = '\0'; 536 if (GNUNET_OK != 537 GNUNET_CRYPTO_eddsa_public_key_from_string ( 538 ac_str, 539 ac_len, 540 &account_pub.eddsa_pub)) 541 { 542 GNUNET_break_op (0); 543 return TALER_MHD2_reply_with_error ( 544 request, 545 MHD_HTTP_STATUS_BAD_REQUEST, 546 TALER_EC_GENERIC_PARAMETER_MALFORMED, 547 ac_str); 548 } 549 } 550 551 /* Without a subpath this is the account itself; only GET makes 552 sense on it, everything else needs an operation */ 553 if ( (NULL == next_slash) || 554 ('\0' == next_slash[1]) ) 555 { 556 if (MHD_HTTP_METHOD_OPTIONS == method) 557 return TALER_MHD2_reply_cors_preflight (request); 558 if (MHD_HTTP_METHOD_GET == method) 559 return SH_backup_account_get (request, 560 &account_pub); 561 GNUNET_break_op (0); 562 return TALER_MHD2_reply_with_error ( 563 request, 564 MHD_HTTP_STATUS_NOT_FOUND, 565 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 566 path->cstr); 567 } 568 569 /* Dispatch to sub-handler */ 570 return handle_backup_subpath (request, 571 &account_pub, 572 method, 573 next_slash + 1, 574 upload_size); 575 } 576 577 for (unsigned int i = 0; NULL != handlers[i].url; i++) 578 { 579 struct SH_RequestHandler *rh = &handlers[i]; 580 581 if (0 == strcmp (path->cstr, 582 rh->url)) 583 { 584 if (MHD_HTTP_METHOD_OPTIONS == method) 585 { 586 return TALER_MHD2_reply_cors_preflight (request); 587 } 588 if (rh->method == method) 589 { 590 return rh->handler (request, 591 upload_size); 592 } 593 } 594 } 595 return respond_404 (request, 596 upload_size); 597 } 598 599 600 /** 601 * Shutdown task. Invoked when the application is being terminated. 602 * 603 * @param cls NULL 604 */ 605 static void 606 do_shutdown (void *cls) 607 { 608 (void) cls; 609 TALER_MHD2_daemons_halt (); 610 if (NULL != SH_ctx) 611 { 612 GNUNET_CURL_fini (SH_ctx); 613 SH_ctx = NULL; 614 } 615 if (NULL != rc) 616 { 617 GNUNET_CURL_gnunet_rc_destroy (rc); 618 rc = NULL; 619 } 620 TALER_MHD2_daemons_destroy (); 621 SYNCDB_fini (); 622 } 623 624 625 /** 626 * Kick MHD to run now, to be called after MHD_request_resume(). 627 * Basically, we need to explicitly resume MHD's event loop whenever 628 * we made progress serving a request. This function re-schedules 629 * the task processing MHD's activities to run immediately. 630 */ 631 /* FIXME: replace with direct call... */ 632 void 633 SH_trigger_daemon (void) 634 { 635 TALER_MHD2_daemons_trigger (); 636 } 637 638 639 /** 640 * Kick GNUnet Curl scheduler to begin curl interactions. 641 */ 642 void 643 SH_trigger_curl (void) 644 { 645 GNUNET_CURL_gnunet_scheduler_reschedule (&rc); 646 } 647 648 649 /** 650 * Callback invoked on every listen socket to start the 651 * respective MHD HTTP daemon. 652 * 653 * @param cls unused 654 * @param lsock the listen socket 655 */ 656 static void 657 start_daemon (void *cls, 658 int lsock) 659 { 660 struct MHD_Daemon *mhd; 661 662 (void) cls; 663 GNUNET_assert (-1 != lsock); 664 mhd = MHD_daemon_create (&url_handler, 665 NULL); 666 if (NULL == mhd) 667 { 668 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 669 "Failed to launch HTTP service, exiting.\n"); 670 global_ret = EXIT_NO_RESTART; 671 GNUNET_SCHEDULER_shutdown (); 672 return; 673 } 674 /* Without this, the pool defaults to a fraction of the memory the 675 daemon would use for connections -- about 1 MB, far below 676 UPLOAD_LIMIT_MB -- and MHD answers anything bigger with its own 677 413 before the handler ever runs, whatever the configured limit 678 says. See the FIXME in sync-httpd2_post-backups-KEY-blocks-NONCE.c 679 for the incremental-upload alternative that would drop the pool 680 dependency entirely. */ 681 GNUNET_assert (MHD_SC_OK == 682 MHD_DAEMON_SET_OPTIONS ( 683 mhd, 684 MHD_D_OPTION_DEFAULT_TIMEOUT_MILSEC (10000), 685 MHD_D_OPTION_LARGE_POOL_SIZE ( 686 (size_t) GNUNET_MIN ( 687 ((uint_fast64_t) SH_upload_limit_mb) 688 * 1024 * 1024 689 * UPLOAD_POOL_CONCURRENCY, 690 (uint_fast64_t) SIZE_MAX)), 691 MHD_D_OPTION_LISTEN_SOCKET (lsock))); 692 have_daemons = true; 693 TALER_MHD2_daemon_start (mhd); 694 } 695 696 697 /** 698 * Main function that will be run by the scheduler. 699 * 700 * @param cls closure 701 * @param args remaining command-line arguments 702 * @param cfgfile name of the configuration file used (for saving, can be 703 * NULL!) 704 * @param config configuration 705 */ 706 static void 707 run (void *cls, 708 char *const *args, 709 const char *cfgfile, 710 const struct GNUNET_CONFIGURATION_Handle *config) 711 { 712 enum TALER_MHD2_GlobalOptions go; 713 714 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 715 "Starting sync-httpd2\n"); 716 go = TALER_MHD2_GO_NONE; 717 if (SH_sync_connection_close) 718 go |= TALER_MHD2_GO_FORCE_CONNECTION_CLOSE; 719 TALER_MHD2_setup (go); 720 global_ret = EXIT_NOTCONFIGURED; 721 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 722 NULL); 723 if (GNUNET_OK != 724 GNUNET_CONFIGURATION_get_value_number (config, 725 "sync", 726 "UPLOAD_LIMIT_MB", 727 &SH_upload_limit_mb)) 728 { 729 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 730 "sync", 731 "UPLOAD_LIMIT_MB"); 732 GNUNET_SCHEDULER_shutdown (); 733 return; 734 } 735 if (GNUNET_OK != 736 TALER_config_get_amount (config, 737 "sync", 738 "INSURANCE", 739 &SH_insurance)) 740 { 741 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 742 "sync", 743 "INSURANCE"); 744 GNUNET_SCHEDULER_shutdown (); 745 return; 746 } 747 if (GNUNET_OK != 748 TALER_config_get_amount (config, 749 "sync", 750 "ANNUAL_FEE", 751 &SH_annual_fee)) 752 { 753 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 754 "sync", 755 "ANNUAL_FEE"); 756 GNUNET_SCHEDULER_shutdown (); 757 return; 758 } 759 if (GNUNET_OK != 760 GNUNET_CONFIGURATION_get_value_string (config, 761 "sync", 762 "PAYMENT_BACKEND_URL", 763 &SH_backend_url)) 764 { 765 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 766 "sync", 767 "PAYMENT_BACKEND_URL"); 768 GNUNET_SCHEDULER_shutdown (); 769 return; 770 } 771 /* The merchant backend API expects the base URL to end with '/' 772 (see TALER_url_join), so ensure it does. */ 773 if ('/' != SH_backend_url[strlen (SH_backend_url) - 1]) 774 { 775 char *tmp; 776 777 GNUNET_asprintf (&tmp, 778 "%s/", 779 SH_backend_url); 780 GNUNET_free (SH_backend_url); 781 SH_backend_url = tmp; 782 } 783 /* The payment flow turns this into a "taler://" URI, so it needs a 784 scheme and a host */ 785 { 786 const char *host = NULL; 787 788 if (0 == strncasecmp ("https://", 789 SH_backend_url, 790 strlen ("https://"))) 791 host = &SH_backend_url[strlen ("https://")]; 792 else if (0 == strncasecmp ("http://", 793 SH_backend_url, 794 strlen ("http://"))) 795 host = &SH_backend_url[strlen ("http://")]; 796 if ( (NULL == host) || 797 ('\0' == *host) ) 798 { 799 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 800 "sync", 801 "PAYMENT_BACKEND_URL", 802 "must be an http:// or https:// URL" 803 " with a host name"); 804 GNUNET_SCHEDULER_shutdown (); 805 return; 806 } 807 } 808 if (GNUNET_OK != 809 GNUNET_CONFIGURATION_get_value_string (config, 810 "sync", 811 "FULFILLMENT_URL", 812 &SH_fulfillment_url)) 813 { 814 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 815 "sync", 816 "FULFILLMENT_URL"); 817 GNUNET_SCHEDULER_shutdown (); 818 return; 819 } 820 821 /* setup HTTP client event loop */ 822 SH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 823 &rc); 824 if (NULL == SH_ctx) 825 { 826 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 827 "Failed to initialize HTTP client, exiting.\n"); 828 global_ret = EXIT_NO_RESTART; 829 GNUNET_SCHEDULER_shutdown (); 830 return; 831 } 832 rc = GNUNET_CURL_gnunet_rc_create (SH_ctx); 833 if (NULL != userpass) 834 GNUNET_CURL_set_userpass (SH_ctx, 835 userpass); 836 if (NULL != keyfile) 837 GNUNET_CURL_set_tlscert (SH_ctx, 838 certtype, 839 certfile, 840 keyfile, 841 keypass); 842 if (GNUNET_OK == 843 GNUNET_CONFIGURATION_get_value_string (config, 844 "sync", 845 "API_KEY", 846 &apikey)) 847 { 848 char *auth_header; 849 850 GNUNET_asprintf (&auth_header, 851 "%s: %s", 852 MHD_HTTP_HEADER_AUTHORIZATION, 853 apikey); 854 if (GNUNET_OK != 855 GNUNET_CURL_append_header (SH_ctx, 856 auth_header)) 857 { 858 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 859 "Failed to set %s header, trying without\n", 860 MHD_HTTP_HEADER_AUTHORIZATION); 861 } 862 GNUNET_free (auth_header); 863 } 864 865 if (GNUNET_OK != 866 SYNCDB_init (config)) 867 { 868 global_ret = EXIT_NOTCONFIGURED; 869 GNUNET_SCHEDULER_shutdown (); 870 return; 871 } 872 if (GNUNET_OK != 873 SYNCDB_preflight ()) 874 { 875 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 876 "Database not setup. Did you run sync-dbinit?\n"); 877 GNUNET_SCHEDULER_shutdown (); 878 return; 879 } 880 { 881 enum GNUNET_GenericReturnValue ret; 882 883 ret = TALER_MHD2_listen_bind (config, 884 "sync", 885 &start_daemon, 886 NULL); 887 switch (ret) 888 { 889 case GNUNET_SYSERR: 890 global_ret = EXIT_NOTCONFIGURED; 891 GNUNET_SCHEDULER_shutdown (); 892 return; 893 case GNUNET_NO: 894 if (! have_daemons) 895 { 896 global_ret = EXIT_NOTCONFIGURED; 897 GNUNET_SCHEDULER_shutdown (); 898 return; 899 } 900 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 901 "Could not open all configured listen sockets\n"); 902 break; 903 case GNUNET_OK: 904 break; 905 } 906 } 907 global_ret = EXIT_SUCCESS; 908 } 909 910 911 /** 912 * The main function of the serve tool 913 * 914 * @param argc number of arguments from the command line 915 * @param argv command line arguments 916 * @return 0 ok, 1 on error 917 */ 918 int 919 main (int argc, 920 char *const *argv) 921 { 922 struct GNUNET_GETOPT_CommandLineOption options[] = { 923 GNUNET_GETOPT_option_string ('A', 924 "auth", 925 "USERNAME:PASSWORD", 926 "use the given USERNAME and PASSWORD for client authentication", 927 &userpass), 928 /* Note: -c, -h, -L, -l and -v are reserved by GNUNET_PROGRAM_run(). 929 We use -E for the client certificate, as curl does. */ 930 GNUNET_GETOPT_option_string ('E', 931 "cert", 932 "CERTFILE", 933 "file with the TLS client certificate for TLS client authentication", 934 &certfile), 935 GNUNET_GETOPT_option_flag ('C', 936 "connection-close", 937 "force HTTP connections to be closed after each request", 938 &SH_sync_connection_close), 939 GNUNET_GETOPT_option_string ('k', 940 "key", 941 "KEYFILE", 942 "file with the private TLS key for TLS client authentication", 943 &keyfile), 944 GNUNET_GETOPT_option_string ('p', 945 "pass", 946 "KEYFILEPASSPHRASE", 947 "passphrase needed to decrypt the TLS client private key file", 948 &keypass), 949 GNUNET_GETOPT_option_string ('t', 950 "type", 951 "CERTTYPE", 952 "type of the TLS client certificate, defaults to PEM if not specified", 953 &certtype), 954 GNUNET_GETOPT_OPTION_END 955 }; 956 enum GNUNET_GenericReturnValue ret; 957 958 ret = GNUNET_PROGRAM_run (SYNC_project_data (), 959 argc, argv, 960 "sync-httpd2", 961 "sync HTTP interface", 962 options, 963 &run, NULL); 964 if (GNUNET_NO == ret) 965 return EXIT_SUCCESS; 966 if (GNUNET_SYSERR == ret) 967 return EXIT_INVALIDARGUMENT; 968 return global_ret; 969 }