taler-merchant-httpd.c (56322B)
1 /* 2 This file is part of TALER 3 (C) 2014-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 src/backend/taler-merchant-httpd.c 18 * @brief HTTP serving layer intended to perform crypto-work and 19 * communication with the exchange 20 * @author Marcello Stanisci 21 * @author Christian Grothoff 22 * @author Florian Dold 23 * @author Priscilla HUANG 24 */ 25 #include "platform.h" 26 #include <taler/taler_dbevents.h> 27 #include <taler/taler_bank_service.h> 28 #include <taler/taler_mhd_lib.h> 29 #include <taler/taler_templating_lib.h> 30 #include <taler/taler_exchange_service.h> 31 #include <donau/donau_service.h> 32 #include "taler/taler_merchant_util.h" 33 #include "taler-merchant-httpd_auth.h" 34 #include "taler-merchant-httpd_dispatcher.h" 35 #include "taler-merchant-httpd_exchanges.h" 36 #include "taler-merchant-httpd_helper.h" 37 #include "taler-merchant-httpd_mhd.h" 38 #include "taler-merchant-httpd_mfa.h" 39 #include "taler-merchant-httpd_post-private-orders.h" 40 #include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h" 41 #include "taler-merchant-httpd_post-challenge-ID.h" 42 #include "taler-merchant-httpd_get-orders-ORDER_ID.h" 43 #include "taler-merchant-httpd_get-sessions-SESSION_ID.h" 44 #include "taler-merchant-httpd_get-templates-TEMPLATE_ID.h" 45 #include "taler-merchant-httpd_get-exchanges.h" 46 #include "taler-merchant-httpd_get-webui.h" 47 #include "taler-merchant-httpd_get-terms.h" 48 #include "taler-merchant-httpd_get-private-kyc.h" 49 #include "taler-merchant-httpd_get-private-statistics-report-transactions.h" 50 #include "taler-merchant-httpd_post-private-donau.h" 51 #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h" 52 #include "taler-merchant-httpd_get-private-orders.h" 53 #include "taler-merchant-httpd_post-orders-ORDER_ID-pay.h" 54 #include "taler-merchant-httpd_post-orders-ORDER_ID-refund.h" 55 #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h" 56 #include "merchant-database/iterate_instances.h" 57 #include "merchant-database/set_instance.h" 58 #include "merchant-database/iterate_accounts_by_instance.h" 59 #include "merchant-database/event_listen.h" 60 #include "merchant-database/preflight.h" 61 #include "merchant-database/event_notify.h" 62 63 /** 64 * Backlog for listen operation on unix-domain sockets. 65 */ 66 #define UNIX_BACKLOG 500 67 68 /** 69 * Default maximum upload size permitted. Can be overridden 70 * per handler. 71 */ 72 #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024) 73 74 char *TMH_currency; 75 76 char *TMH_base_url; 77 78 char *TMH_spa_dir; 79 80 char *TMH_helper_email; 81 82 char *TMH_helper_sms; 83 84 char *TMH_phone_regex; 85 86 regex_t TMH_phone_rx; 87 88 char *TMH_allowed_payment_targets; 89 90 char *TMH_default_persona; 91 92 char *TMH_payment_target_regex; 93 94 regex_t TMH_payment_target_re; 95 96 int TMH_force_audit; 97 98 struct TALER_MERCHANTDB_PostgresContext *TMH_db; 99 100 struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map; 101 102 struct GNUNET_TIME_Relative TMH_default_pay_delay; 103 104 struct GNUNET_TIME_Relative TMH_default_refund_delay; 105 106 struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay; 107 108 enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval; 109 110 int TMH_strict_v19; 111 112 int TMH_auth_disabled; 113 114 int TMH_have_self_provisioning; 115 116 int TMH_password_change_mfa; 117 118 enum TEH_TanChannelSet TEH_mandatory_tan_channels; 119 120 struct GNUNET_TIME_Relative TMH_legal_expiration; 121 122 unsigned int TMH_num_cspecs; 123 124 json_t *TMH_global_spa_config_data; 125 126 struct TALER_CurrencySpecification *TMH_cspecs; 127 128 struct GNUNET_CURL_Context *TMH_curl_ctx; 129 130 /** 131 * Event handler for instance settings changes. 132 */ 133 static struct GNUNET_DB_EventHandler *instance_eh; 134 135 /** 136 * True if we started any HTTP daemon. 137 */ 138 static bool have_daemons; 139 140 /** 141 * Should a "Connection: close" header be added to each HTTP response? 142 */ 143 static int merchant_connection_close; 144 145 /** 146 * Should we enable HTTP/2 and HTTP/3 when talking to the exchange 147 * (and donau)? Those are not expected to be terribly beneficial for 148 * a server with stable connections to an exchange, but they could 149 * cause stability issues with libcurl. Per default, we *enforce* 150 * HTTP/1.x-only, as that is the conservative and most tested code 151 * path. 152 */ 153 static int enable_h3; 154 155 /** 156 * Context for integrating #TMH_curl_ctx with the 157 * GNUnet event loop. 158 */ 159 static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc; 160 161 /** 162 * Global return code 163 */ 164 static int global_ret; 165 166 /** 167 * Our configuration. 168 */ 169 const struct GNUNET_CONFIGURATION_Handle *TMH_cfg; 170 171 172 void 173 TMH_wire_method_free (struct TMH_WireMethod *wm) 174 { 175 GNUNET_free (wm->payto_uri.full_payto); 176 GNUNET_free (wm->wire_method); 177 GNUNET_free (wm->extra_wire_subject_metadata); 178 GNUNET_free (wm->credit_facade_url); 179 json_decref (wm->credit_facade_credentials); 180 GNUNET_free (wm); 181 } 182 183 184 void 185 TMH_instance_decref (struct TMH_MerchantInstance *mi) 186 { 187 struct TMH_WireMethod *wm; 188 189 mi->rc--; 190 if (0 != mi->rc) 191 return; 192 TMH_force_get_orders_resume (mi); 193 while (NULL != (wm = (mi->wm_head))) 194 { 195 GNUNET_CONTAINER_DLL_remove (mi->wm_head, 196 mi->wm_tail, 197 wm); 198 TMH_wire_method_free (wm); 199 } 200 201 GNUNET_free (mi->settings.id); 202 GNUNET_free (mi->settings.name); 203 GNUNET_free (mi->settings.email); 204 GNUNET_free (mi->settings.phone); 205 GNUNET_free (mi->settings.website); 206 GNUNET_free (mi->settings.logo); 207 json_decref (mi->settings.address); 208 json_decref (mi->settings.jurisdiction); 209 GNUNET_free (mi); 210 } 211 212 213 enum GNUNET_GenericReturnValue 214 TMH_instance_free_cb (void *cls, 215 const struct GNUNET_HashCode *key, 216 void *value) 217 { 218 struct TMH_MerchantInstance *mi = value; 219 220 (void) cls; 221 (void) key; 222 TMH_force_get_orders_resume (mi); 223 GNUNET_assert (GNUNET_OK == 224 GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map, 225 &mi->h_instance, 226 mi)); 227 TMH_instance_decref (mi); 228 return GNUNET_YES; 229 } 230 231 232 /** 233 * Shutdown task (invoked when the application is being 234 * terminated for any reason) 235 * 236 * @param cls NULL 237 */ 238 static void 239 do_shutdown (void *cls) 240 { 241 (void) cls; 242 TALER_MHD_daemons_halt (); 243 TMH_handler_statistic_report_transactions_cleanup (); 244 TMH_force_kac_resume (); 245 TMH_force_orders_resume (); 246 TMH_force_get_sessions_ID_resume (); 247 TMH_force_get_templates_ID_resume (); 248 TMH_force_get_orders_resume_typst (); 249 TMH_force_ac_resume (); 250 TMH_force_pc_resume (); 251 TMH_force_kyc_resume (); 252 TMH_force_gorc_resume (); 253 TMH_force_wallet_get_order_resume (); 254 TMH_force_wallet_refund_order_resume (); 255 TMH_force_post_donau_resume (); 256 TMH_challenge_done (); 257 if (NULL != instance_eh) 258 { 259 TALER_MERCHANTDB_event_listen_cancel (instance_eh); 260 instance_eh = NULL; 261 } 262 if (NULL != TMH_by_id_map) 263 { 264 GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map, 265 &TMH_instance_free_cb, 266 NULL); 267 GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map); 268 TMH_by_id_map = NULL; 269 } 270 TALER_MHD_daemons_destroy (); 271 TMH_EXCHANGES_done (); 272 if (NULL != TMH_db) 273 { 274 TALER_MERCHANTDB_disconnect (TMH_db); 275 TMH_db = NULL; 276 } 277 TALER_TEMPLATING_done (); 278 if (NULL != TMH_curl_ctx) 279 { 280 GNUNET_CURL_fini (TMH_curl_ctx); 281 TMH_curl_ctx = NULL; 282 } 283 if (NULL != merchant_curl_rc) 284 { 285 GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc); 286 merchant_curl_rc = NULL; 287 } 288 if (NULL != TMH_payment_target_regex) 289 { 290 regfree (&TMH_payment_target_re); 291 GNUNET_free (TMH_payment_target_regex); 292 } 293 } 294 295 296 /** 297 * Function called whenever MHD is done with a request. If the 298 * request was a POST, we may have stored a `struct Buffer *` in the 299 * @a con_cls that might still need to be cleaned up. Call the 300 * respective function to free the memory. 301 * 302 * @param cls client-defined closure 303 * @param connection connection handle 304 * @param con_cls value as set by the last call to 305 * the #MHD_AccessHandlerCallback 306 * @param toe reason for request termination 307 * @see #MHD_OPTION_NOTIFY_COMPLETED 308 * @ingroup request 309 */ 310 static void 311 handle_mhd_completion_callback (void *cls, 312 struct MHD_Connection *connection, 313 void **con_cls, 314 enum MHD_RequestTerminationCode toe) 315 { 316 struct TMH_HandlerContext *hc = *con_cls; 317 318 (void) cls; 319 if (NULL == hc) 320 return; 321 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 322 { 323 #if MHD_VERSION >= 0x00097304 324 const union MHD_ConnectionInfo *ci; 325 unsigned int http_status = 0; 326 327 ci = MHD_get_connection_info (connection, 328 MHD_CONNECTION_INFO_HTTP_STATUS); 329 if (NULL != ci) 330 http_status = ci->http_status; 331 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 332 "Request for `%s' completed with HTTP status %u (%d)\n", 333 hc->url, 334 http_status, 335 toe); 336 #else 337 (void) connection; 338 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 339 "Finished handling request for `%s' with MHD termination code %d\n", 340 hc->url, 341 (int) toe); 342 #endif 343 } 344 if (NULL != hc->cc) 345 hc->cc (hc->ctx); 346 TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context); 347 GNUNET_free (hc->infix); 348 GNUNET_free (hc->rewritten_url); 349 if (NULL != hc->request_body) 350 json_decref (hc->request_body); 351 if (NULL != hc->instance) 352 TMH_instance_decref (hc->instance); 353 TALER_MERCHANTDB_preflight (TMH_db); 354 GNUNET_free (hc->full_url); 355 GNUNET_free (hc); 356 *con_cls = NULL; 357 } 358 359 360 struct TMH_MerchantInstance * 361 TMH_lookup_instance (const char *instance_id) 362 { 363 struct GNUNET_HashCode h_instance; 364 char *id; 365 366 if (NULL == instance_id) 367 id = GNUNET_strdup ("admin"); 368 else 369 id = GNUNET_STRINGS_utf8_tolower (instance_id); 370 GNUNET_CRYPTO_hash (id, 371 strlen (id), 372 &h_instance); 373 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 374 "Looking for by-id key %s of '%s' in hashmap\n", 375 GNUNET_h2s (&h_instance), 376 id); 377 GNUNET_free (id); 378 /* We're fine if that returns NULL, the calling routine knows how 379 to handle that */ 380 return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map, 381 &h_instance); 382 } 383 384 385 /** 386 * Add instance definition to our active set of instances. 387 * 388 * @param[in,out] mi merchant instance details to define 389 * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already 390 */ 391 enum GNUNET_GenericReturnValue 392 TMH_add_instance (struct TMH_MerchantInstance *mi) 393 { 394 const char *id; 395 enum GNUNET_GenericReturnValue ret; 396 397 id = mi->settings.id; 398 if (NULL == id) 399 id = "admin"; 400 GNUNET_CRYPTO_hash (id, 401 strlen (id), 402 &mi->h_instance); 403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 404 "Looking for by-id key %s of `%s' in hashmap\n", 405 GNUNET_h2s (&mi->h_instance), 406 id); 407 ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map, 408 &mi->h_instance, 409 mi, 410 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); 411 if (GNUNET_OK == ret) 412 { 413 GNUNET_assert (mi->rc < UINT_MAX); 414 mi->rc++; 415 } 416 return ret; 417 } 418 419 420 /** 421 * Function called first by MHD with the full URL. 422 * 423 * @param cls NULL 424 * @param full_url the full URL 425 * @param con MHD connection object 426 * @return our handler context 427 */ 428 static void * 429 full_url_track_callback (void *cls, 430 const char *full_url, 431 struct MHD_Connection *con) 432 { 433 struct TMH_HandlerContext *hc; 434 435 hc = GNUNET_new (struct TMH_HandlerContext); 436 hc->connection = con; 437 GNUNET_async_scope_fresh (&hc->async_scope_id); 438 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 439 hc->full_url = GNUNET_strdup (full_url); 440 return hc; 441 } 442 443 444 /** 445 * The callback was called again by MHD, continue processing 446 * the request with the already identified handler. 447 * 448 * @param hc the handler context 449 * @param upload_data the data being uploaded (excluding HEADERS, 450 * for a POST that fits into memory and that is encoded 451 * with a supported encoding, the POST data will NOT be 452 * given in upload_data and is instead available as 453 * part of #MHD_get_connection_values; very large POST 454 * data *will* be made available incrementally in 455 * @a upload_data) 456 * @param upload_data_size set initially to the size of the 457 * @a upload_data provided; the method must update this 458 * value to the number of bytes NOT processed; 459 * @return #MHD_YES if the connection was handled successfully, 460 * #MHD_NO if the socket must be closed due to a serious 461 * error while handling the request 462 */ 463 static enum MHD_Result 464 process_upload_with_handler (struct TMH_HandlerContext *hc, 465 const char *upload_data, 466 size_t *upload_data_size) 467 { 468 GNUNET_assert (NULL != hc->rh); 469 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 470 if ( (hc->has_body) && 471 (NULL == hc->request_body) ) 472 { 473 size_t mul = hc->rh->max_upload; 474 enum GNUNET_GenericReturnValue res; 475 476 if (0 == mul) 477 mul = DEFAULT_MAX_UPLOAD_SIZE; 478 if ( (hc->total_upload + *upload_data_size < hc->total_upload) || 479 (hc->total_upload + *upload_data_size > mul) ) 480 { 481 /* Client exceeds upload limit. Should _usually_ be checked earlier 482 when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with 483 chunked encoding an uploader MAY have omitted this, and thus 484 not permitted us to check on time. In this case, we just close 485 the connection once it exceeds our limit (instead of waiting 486 for the upload to complete and then fail). This could theoretically 487 cause some clients to retry, alas broken or malicious clients 488 are likely to retry anyway, so little we can do about it, and 489 failing earlier seems the best option here. */ 490 GNUNET_break_op (0); 491 return MHD_NO; 492 } 493 hc->total_upload += *upload_data_size; 494 res = TALER_MHD_parse_post_json (hc->connection, 495 &hc->json_parse_context, 496 upload_data, 497 upload_data_size, 498 &hc->request_body); 499 if (GNUNET_SYSERR == res) 500 return MHD_NO; 501 /* A error response was already generated */ 502 if ( (GNUNET_NO == res) || 503 /* or, need more data to accomplish parsing */ 504 (NULL == hc->request_body) ) 505 return MHD_YES; /* let MHD call us *again* */ 506 } 507 /* Upload complete (if any), call handler to generate reply */ 508 return hc->rh->handler (hc->rh, 509 hc->connection, 510 hc); 511 } 512 513 514 /** 515 * Log information about the request being handled. 516 * 517 * @param hc handler context 518 * @param method HTTP method of the request 519 */ 520 static void 521 log_request (const struct TMH_HandlerContext *hc, 522 const char *method) 523 { 524 const char *correlation_id; 525 526 correlation_id = MHD_lookup_connection_value (hc->connection, 527 MHD_HEADER_KIND, 528 "Taler-Correlation-Id"); 529 if ( (NULL != correlation_id) && 530 (GNUNET_YES != 531 GNUNET_CURL_is_valid_scope_id (correlation_id)) ) 532 { 533 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 534 "Illegal incoming correlation ID\n"); 535 correlation_id = NULL; 536 } 537 if (NULL != correlation_id) 538 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 539 "Handling request for (%s) URL '%s', correlation_id=%s\n", 540 method, 541 hc->url, 542 correlation_id); 543 else 544 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 545 "Handling request (%s) for URL '%s'\n", 546 method, 547 hc->url); 548 } 549 550 551 /** 552 * Identify the instance of the request from the URL. 553 * 554 * @param[in,out] hc handler context 555 * @param[in,out] urlp URL path of the request, updated to point to the rest 556 * @param[out] use_admin set to true if we are using the admin instance 557 * @return #GNUNET_OK on success, 558 * #GNUNET_NO if an error was queued (return #MHD_YES) 559 * #GNUNET_SYSERR to close the connection (return #MHD_NO) 560 */ 561 static enum GNUNET_GenericReturnValue 562 identify_instance (struct TMH_HandlerContext *hc, 563 const char **urlp, 564 bool *use_admin) 565 { 566 const char *url = *urlp; 567 const char *instance_prefix = "/instances/"; 568 569 if (0 == strncmp (url, 570 instance_prefix, 571 strlen (instance_prefix))) 572 { 573 /* url starts with "/instances/" */ 574 const char *istart = url + strlen (instance_prefix); 575 const char *slash = strchr (istart, '/'); 576 char *raw_id; 577 char *instance_id; 578 579 if (NULL == slash) 580 raw_id = GNUNET_strdup (istart); 581 else 582 raw_id = GNUNET_strndup (istart, 583 slash - istart); 584 /* Instance IDs are case-insensitive, so fold the segment before comparing 585 it against "admin" below. Without this, '/instances/Admin/' misses the 586 redirect to the modern path, leaving use_admin false, and every 587 'default_only' handler that '/instances/admin/' reaches (all of 588 /management/) then replies 404. */ 589 instance_id = GNUNET_STRINGS_utf8_tolower (raw_id); 590 GNUNET_free (raw_id); 591 if (0 == strcmp (instance_id, 592 "admin")) 593 { 594 enum MHD_Result ret; 595 struct MHD_Response *response; 596 const char *rstart = hc->full_url + strlen (instance_prefix); 597 const char *rslash = strchr (rstart, '/'); 598 599 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 600 "Client used deprecated '/instances/admin/' path. Redirecting to modern path\n"); 601 602 response 603 = MHD_create_response_from_buffer (0, 604 NULL, 605 MHD_RESPMEM_PERSISTENT); 606 TALER_MHD_add_global_headers (response, 607 true); 608 if (MHD_NO == 609 MHD_add_response_header (response, 610 MHD_HTTP_HEADER_LOCATION, 611 NULL == rslash 612 ? "/" 613 : rslash)) 614 { 615 GNUNET_break (0); 616 MHD_destroy_response (response); 617 GNUNET_free (instance_id); 618 return GNUNET_SYSERR; 619 } 620 ret = MHD_queue_response (hc->connection, 621 MHD_HTTP_PERMANENT_REDIRECT, 622 response); 623 MHD_destroy_response (response); 624 GNUNET_free (instance_id); 625 return (MHD_YES == ret) ? GNUNET_NO : GNUNET_SYSERR; 626 } 627 hc->instance = TMH_lookup_instance (instance_id); 628 if ( (NULL == hc->instance) && 629 (0 == strcmp ("admin", 630 instance_id)) ) 631 hc->instance = TMH_lookup_instance (NULL); 632 GNUNET_free (instance_id); 633 if (NULL == slash) 634 *urlp = ""; 635 else 636 *urlp = slash; 637 } 638 else 639 { 640 /* use 'default' */ 641 *use_admin = true; 642 hc->instance = TMH_lookup_instance (NULL); 643 } 644 if (NULL != hc->instance) 645 { 646 GNUNET_assert (hc->instance->rc < UINT_MAX); 647 hc->instance->rc++; 648 } 649 return GNUNET_OK; 650 } 651 652 653 /** 654 * A client has requested the given url using the given method 655 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, 656 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback 657 * must call MHD callbacks to provide content to give back to the 658 * client and return an HTTP status code (i.e. #MHD_HTTP_OK, 659 * #MHD_HTTP_NOT_FOUND, etc.). 660 * 661 * @param cls argument given together with the function 662 * pointer when the handler was registered with MHD 663 * @param connection the MHD connection to handle 664 * @param url the requested url 665 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, 666 * #MHD_HTTP_METHOD_PUT, etc.) 667 * @param version the HTTP version string (i.e. 668 * #MHD_HTTP_VERSION_1_1) 669 * @param upload_data the data being uploaded (excluding HEADERS, 670 * for a POST that fits into memory and that is encoded 671 * with a supported encoding, the POST data will NOT be 672 * given in upload_data and is instead available as 673 * part of #MHD_get_connection_values; very large POST 674 * data *will* be made available incrementally in 675 * @a upload_data) 676 * @param upload_data_size set initially to the size of the 677 * @a upload_data provided; the method must update this 678 * value to the number of bytes NOT processed; 679 * @param con_cls pointer that the callback can set to some 680 * address and that will be preserved by MHD for future 681 * calls for this request; since the access handler may 682 * be called many times (i.e., for a PUT/POST operation 683 * with plenty of upload data) this allows the application 684 * to easily associate some request-specific state. 685 * If necessary, this state can be cleaned up in the 686 * global #MHD_RequestCompletedCallback (which 687 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). 688 * Initially, `*con_cls` will be set up by the 689 * full_url_track_callback(). 690 * @return #MHD_YES if the connection was handled successfully, 691 * #MHD_NO if the socket must be closed due to a serious 692 * error while handling the request 693 */ 694 static enum MHD_Result 695 url_handler (void *cls, 696 struct MHD_Connection *connection, 697 const char *url, 698 const char *method, 699 const char *version, 700 const char *upload_data, 701 size_t *upload_data_size, 702 void **con_cls) 703 { 704 struct TMH_HandlerContext *hc = *con_cls; 705 bool use_admin = false; 706 bool is_public = false; 707 708 (void) cls; 709 (void) version; 710 if (NULL == hc->url) 711 { 712 /* First time. 713 * Find out the merchant backend instance for the request. 714 * If there is an instance, remove the instance specification 715 * from the beginning of the request URL. */ 716 enum GNUNET_GenericReturnValue ret; 717 718 hc->url = url; 719 log_request (hc, 720 method); 721 ret = identify_instance (hc, 722 &url, 723 &use_admin); 724 if (GNUNET_OK != ret) 725 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 726 } 727 728 if (NULL != hc->instance) 729 { 730 /* Narrow DB interaction to selected instance */ 731 enum GNUNET_DB_QueryStatus qs; 732 733 qs = TALER_MERCHANTDB_set_instance (TMH_db, 734 hc->instance->settings.id); 735 switch (qs) 736 { 737 case GNUNET_DB_STATUS_HARD_ERROR: 738 GNUNET_break (0); 739 return TALER_MHD_reply_with_error ( 740 connection, 741 MHD_HTTP_INTERNAL_SERVER_ERROR, 742 TALER_EC_GENERIC_DB_SETUP_FAILED, 743 "set_instance"); 744 case GNUNET_DB_STATUS_SOFT_ERROR: 745 GNUNET_break (0); 746 return TALER_MHD_reply_with_error ( 747 connection, 748 MHD_HTTP_INTERNAL_SERVER_ERROR, 749 TALER_EC_GENERIC_DB_SETUP_FAILED, 750 "set_instance"); 751 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 752 return TALER_MHD_reply_with_error ( 753 connection, 754 MHD_HTTP_NOT_FOUND, 755 TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, 756 hc->url); 757 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 758 break; 759 } 760 } 761 762 if (NULL != hc->rh) 763 { 764 enum MHD_Result res; 765 766 /* MHD calls us again for a request, we already identified 767 the handler, just continue processing with the handler */ 768 res = process_upload_with_handler (hc, 769 upload_data, 770 upload_data_size); 771 if (NULL != hc->instance) 772 { 773 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 774 TALER_MERCHANTDB_set_instance (TMH_db, 775 NULL)); 776 } 777 return res; 778 } 779 780 /* First time, let's figure out the handler */ 781 { 782 enum GNUNET_GenericReturnValue ret; 783 784 ret = TMH_dispatch_request (hc, 785 url, 786 method, 787 use_admin, 788 &is_public); 789 if (GNUNET_OK != ret) 790 { 791 if (NULL != hc->instance) 792 { 793 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 794 TALER_MERCHANTDB_set_instance (TMH_db, 795 NULL)); 796 } 797 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 798 } 799 } 800 801 /* At this point, we must have found a handler */ 802 GNUNET_assert (NULL != hc->rh); 803 804 /* If an instance must be there, check one exists */ 805 if ( (NULL == hc->instance) && 806 (! hc->rh->skip_instance) ) 807 { 808 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 809 "Instance for `%s' not known\n", 810 hc->url); 811 return TALER_MHD_reply_with_error (connection, 812 MHD_HTTP_NOT_FOUND, 813 TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, 814 hc->url); 815 } 816 817 /* Perform access control for non-public handlers */ 818 if (! is_public) 819 { 820 enum GNUNET_GenericReturnValue ret; 821 822 ret = TMH_perform_access_control (hc); 823 if (GNUNET_OK != ret) 824 { 825 if (NULL != hc->instance) 826 { 827 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 828 TALER_MERCHANTDB_set_instance (TMH_db, 829 NULL)); 830 } 831 return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; 832 } 833 } 834 if (NULL != hc->instance) 835 { 836 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 837 TALER_MERCHANTDB_set_instance (TMH_db, 838 NULL)); 839 } 840 841 if ( (NULL != hc->instance) && /* make static analysis happy */ 842 (! hc->rh->skip_instance) && 843 (hc->instance->deleted) && 844 (! hc->rh->allow_deleted_instance) ) 845 { 846 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 847 "Instance `%s' was deleted\n", 848 hc->instance->settings.id); 849 return TALER_MHD_reply_with_error (connection, 850 MHD_HTTP_NOT_FOUND, 851 TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED, 852 hc->instance->settings.id); 853 } 854 855 /* Check upload constraints */ 856 hc->has_body = ( (0 == strcasecmp (method, 857 MHD_HTTP_METHOD_POST)) || 858 /* PUT is not yet used */ 859 (0 == strcasecmp (method, 860 MHD_HTTP_METHOD_PATCH)) ); 861 if (hc->has_body) 862 { 863 /* This is a macro: it will queue an error response and return 864 from this function if the upload would be too large. */ 865 TALER_MHD_check_content_length (connection, 866 0 == hc->rh->max_upload 867 ? DEFAULT_MAX_UPLOAD_SIZE 868 : hc->rh->max_upload); 869 GNUNET_break (NULL == hc->request_body); /* can't have it already */ 870 } 871 /* wait for MHD to call us again, this time hc->url will be non-NULL 872 and we should jump straight into process_upload_with_handler(). */ 873 return MHD_YES; 874 } 875 876 877 /** 878 * Callback invoked with information about a bank account. 879 * 880 * @param cls closure with a `struct TMH_MerchantInstance *` 881 * @param merchant_priv private key of the merchant instance 882 * @param acc details about the account 883 */ 884 static void 885 add_account_cb (void *cls, 886 const struct TALER_MerchantPrivateKeyP *merchant_priv, 887 const struct TALER_MERCHANTDB_AccountDetails *acc) 888 { 889 struct TMH_MerchantInstance *mi = cls; 890 struct TMH_WireMethod *wm; 891 892 (void) merchant_priv; 893 wm = GNUNET_new (struct TMH_WireMethod); 894 wm->h_wire = acc->h_wire; 895 wm->payto_uri.full_payto 896 = GNUNET_strdup (acc->payto_uri.full_payto); 897 if (NULL != acc->extra_wire_subject_metadata) 898 wm->extra_wire_subject_metadata 899 = GNUNET_strdup (acc->extra_wire_subject_metadata); 900 wm->wire_salt = acc->salt; 901 wm->wire_method 902 = TALER_payto_get_method (acc->payto_uri.full_payto); 903 wm->active = acc->active; 904 GNUNET_CONTAINER_DLL_insert (mi->wm_head, 905 mi->wm_tail, 906 wm); 907 } 908 909 910 /** 911 * Function called during startup to add all known instances to our 912 * hash map in memory for faster lookups when we receive requests. 913 * 914 * @param cls closure, NULL, unused 915 * @param merchant_pub public key of the instance 916 * @param merchant_priv private key of the instance, NULL if not available 917 * @param is detailed configuration settings for the instance 918 * @param ias authentication settings for the instance 919 */ 920 static void 921 add_instance_cb (void *cls, 922 const struct TALER_MerchantPublicKeyP *merchant_pub, 923 const struct TALER_MerchantPrivateKeyP *merchant_priv, 924 const struct TALER_MERCHANTDB_InstanceSettings *is, 925 const struct TALER_MERCHANTDB_InstanceAuthSettings *ias) 926 { 927 struct TMH_MerchantInstance *mi; 928 enum GNUNET_DB_QueryStatus qs; 929 930 (void) cls; 931 mi = TMH_lookup_instance (is->id); 932 if (NULL != mi) 933 { 934 /* (outdated) entry exists, remove old entry */ 935 (void) TMH_instance_free_cb (NULL, 936 &mi->h_instance, 937 mi); 938 } 939 mi = GNUNET_new (struct TMH_MerchantInstance); 940 mi->settings = *is; 941 mi->auth = *ias; 942 mi->settings.id = GNUNET_STRINGS_utf8_tolower (mi->settings.id); 943 mi->settings.name = GNUNET_strdup (mi->settings.name); 944 if (NULL != mi->settings.email) 945 mi->settings.email = GNUNET_strdup (mi->settings.email); 946 if (NULL != mi->settings.phone) 947 mi->settings.phone = GNUNET_strdup (mi->settings.phone); 948 if (NULL != mi->settings.website) 949 mi->settings.website = GNUNET_strdup (mi->settings.website); 950 if (NULL != mi->settings.logo) 951 mi->settings.logo = GNUNET_strdup (mi->settings.logo); 952 mi->settings.address = json_incref (mi->settings.address); 953 mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction); 954 if (NULL != merchant_priv) 955 mi->merchant_priv = *merchant_priv; 956 else 957 mi->deleted = true; 958 mi->merchant_pub = *merchant_pub; 959 qs = TALER_MERCHANTDB_set_instance (TMH_db, 960 mi->settings.id); 961 if (0 > qs) 962 { 963 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 964 "Error setting instance `%s'\n", 965 mi->settings.id); 966 GNUNET_SCHEDULER_shutdown (); 967 return; 968 } 969 qs = TALER_MERCHANTDB_iterate_accounts_by_instance ( 970 TMH_db, 971 mi->settings.id, 972 &add_account_cb, 973 mi); 974 if (0 > qs) 975 { 976 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 977 "Error loading accounts of `%s' from database\n", 978 mi->settings.id); 979 GNUNET_SCHEDULER_shutdown (); 980 return; 981 } 982 GNUNET_assert (GNUNET_OK == 983 TMH_add_instance (mi)); 984 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 985 TALER_MERCHANTDB_set_instance (TMH_db, 986 NULL)); 987 } 988 989 990 /** 991 * Trigger (re)loading of instance settings from DB. 992 * 993 * @param cls NULL 994 * @param extra ID of the instance that changed, NULL 995 * to load all instances (will not handle purges!) 996 * @param extra_len number of bytes in @a extra 997 */ 998 static void 999 load_instances (void *cls, 1000 const void *extra, 1001 size_t extra_len) 1002 { 1003 enum GNUNET_DB_QueryStatus qs; 1004 const char *id = extra; 1005 1006 (void) cls; 1007 if ( (NULL != extra) && 1008 ( (0 == extra_len) || 1009 ('\0' != id[extra_len - 1]) ) ) 1010 { 1011 GNUNET_break (0 == extra_len); 1012 extra = NULL; 1013 } 1014 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1015 "Received instance settings notification: reload `%s'\n", 1016 id); 1017 if (NULL == extra) 1018 { 1019 qs = TALER_MERCHANTDB_iterate_instances (TMH_db, 1020 false, 1021 &add_instance_cb, 1022 NULL); 1023 } 1024 else 1025 { 1026 struct TMH_MerchantInstance *mi; 1027 1028 /* This must be done here to handle instance 1029 purging, as for purged instances, the DB 1030 lookup below will otherwise do nothing */ 1031 mi = TMH_lookup_instance (id); 1032 if (NULL != mi) 1033 { 1034 (void) TMH_instance_free_cb (NULL, 1035 &mi->h_instance, 1036 mi); 1037 } 1038 qs = TALER_MERCHANTDB_iterate_instances_by_id (TMH_db, 1039 id, 1040 false, 1041 &add_instance_cb, 1042 NULL); 1043 } 1044 if (0 > qs) 1045 { 1046 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1047 "Failed initialization. Check database setup.\n"); 1048 global_ret = EXIT_FAILURE; 1049 GNUNET_SCHEDULER_shutdown (); 1050 return; 1051 } 1052 } 1053 1054 1055 /** 1056 * A transaction modified an instance setting (or created/deleted/purged 1057 * one). Notify all backends about the change. 1058 * 1059 * @param id ID of the instance that changed 1060 */ 1061 void 1062 TMH_reload_instances (const char *id) 1063 { 1064 struct GNUNET_DB_EventHeaderP es = { 1065 .size = htons (sizeof (es)), 1066 .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS) 1067 }; 1068 1069 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1070 "Generating instance settings notification: reload `%s'\n", 1071 id); 1072 TALER_MERCHANTDB_event_notify (TMH_db, 1073 &es, 1074 id, 1075 (NULL == id) 1076 ? 0 1077 : strlen (id) + 1); 1078 } 1079 1080 1081 /** 1082 * Callback invoked on every listen socket to start the 1083 * respective MHD HTTP daemon. 1084 * 1085 * @param cls unused 1086 * @param lsock the listen socket 1087 */ 1088 static void 1089 start_daemon (void *cls, 1090 int lsock) 1091 { 1092 struct MHD_Daemon *mhd; 1093 1094 (void) cls; 1095 GNUNET_assert (-1 != lsock); 1096 mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK 1097 | MHD_USE_AUTO, 1098 0 /* port */, 1099 NULL, NULL, 1100 &url_handler, NULL, 1101 MHD_OPTION_LISTEN_SOCKET, lsock, 1102 MHD_OPTION_URI_LOG_CALLBACK, 1103 &full_url_track_callback, NULL, 1104 MHD_OPTION_NOTIFY_COMPLETED, 1105 &handle_mhd_completion_callback, NULL, 1106 MHD_OPTION_CONNECTION_TIMEOUT, 1107 (unsigned int) 10 /* 10s */, 1108 MHD_OPTION_END); 1109 if (NULL == mhd) 1110 { 1111 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1112 "Failed to launch HTTP service.\n"); 1113 GNUNET_SCHEDULER_shutdown (); 1114 return; 1115 } 1116 have_daemons = true; 1117 TALER_MHD_daemon_start (mhd); 1118 } 1119 1120 1121 /** 1122 * Main function that will be run by the scheduler. 1123 * 1124 * @param cls closure 1125 * @param args remaining command-line arguments 1126 * @param cfgfile name of the configuration file used (for saving, can be 1127 * NULL!) 1128 * @param config configuration 1129 */ 1130 static void 1131 run (void *cls, 1132 char *const *args, 1133 const char *cfgfile, 1134 const struct GNUNET_CONFIGURATION_Handle *config) 1135 { 1136 enum TALER_MHD_GlobalOptions go; 1137 int elen; 1138 1139 (void) cls; 1140 (void) args; 1141 (void) cfgfile; 1142 TMH_cfg = config; 1143 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1144 "Starting taler-merchant-httpd\n"); 1145 go = TALER_MHD_GO_NONE; 1146 if (merchant_connection_close) 1147 go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; 1148 TALER_MHD_setup (go); 1149 TALER_EXCHANGE_setup (enable_h3 1150 ? TALER_EXCHANGE_GO_ENABLE_HTTP3 1151 : TALER_EXCHANGE_GO_FORCE_HTTP1_1); 1152 DONAU_setup (enable_h3 1153 ? DONAU_GO_ENABLE_HTTP3 1154 : DONAU_GO_FORCE_HTTP1_1); 1155 1156 global_ret = EXIT_SUCCESS; 1157 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1158 NULL); 1159 1160 TMH_curl_ctx 1161 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1162 &merchant_curl_rc); 1163 if (NULL == TMH_curl_ctx) 1164 { 1165 GNUNET_break (0); 1166 global_ret = EXIT_FAILURE; 1167 GNUNET_SCHEDULER_shutdown (); 1168 return; 1169 } 1170 merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx); 1171 /* Disable 100 continue processing */ 1172 GNUNET_break (GNUNET_OK == 1173 GNUNET_CURL_append_header (TMH_curl_ctx, 1174 MHD_HTTP_HEADER_EXPECT ":")); 1175 GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx, 1176 "Taler-Correlation-Id"); 1177 1178 if (GNUNET_SYSERR == 1179 TALER_config_get_currency (TMH_cfg, 1180 "merchant", 1181 &TMH_currency)) 1182 { 1183 global_ret = EXIT_NOTCONFIGURED; 1184 GNUNET_SCHEDULER_shutdown (); 1185 return; 1186 } 1187 if (GNUNET_OK != 1188 TALER_CONFIG_parse_currencies (TMH_cfg, 1189 TMH_currency, 1190 &TMH_num_cspecs, 1191 &TMH_cspecs)) 1192 { 1193 global_ret = EXIT_NOTCONFIGURED; 1194 GNUNET_SCHEDULER_shutdown (); 1195 return; 1196 } 1197 1198 { 1199 char *spa_data; 1200 1201 if (GNUNET_OK == 1202 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1203 "merchant", 1204 "GLOBAL_SPA_CONFIG_DATA", 1205 &spa_data)) 1206 { 1207 json_error_t err; 1208 1209 TMH_global_spa_config_data = json_loads (spa_data, 1210 JSON_REJECT_DUPLICATES, 1211 &err); 1212 GNUNET_free (spa_data); 1213 if (NULL == TMH_global_spa_config_data) 1214 { 1215 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1216 "merchant", 1217 "GLOBAL_SPA_CONFIG_DATA", 1218 err.text); 1219 global_ret = EXIT_NOTCONFIGURED; 1220 GNUNET_SCHEDULER_shutdown (); 1221 return; 1222 } 1223 } 1224 } 1225 1226 1227 if (GNUNET_SYSERR == 1228 (TMH_strict_v19 1229 = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1230 "merchant", 1231 "STRICT_PROTOCOL_V19"))) 1232 { 1233 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1234 "merchant", 1235 "STRICT_PROTOCOL_V19"); 1236 TMH_strict_v19 = GNUNET_NO; 1237 } 1238 if (GNUNET_SYSERR == 1239 (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1240 "merchant", 1241 "DISABLE_AUTHENTICATION"))) 1242 { 1243 TMH_auth_disabled = GNUNET_NO; 1244 } 1245 if (GNUNET_YES == TMH_auth_disabled) 1246 { 1247 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1248 "DANGEROUS: Endpoint Authentication disabled!"); 1249 } 1250 1251 if (GNUNET_SYSERR == 1252 (TMH_have_self_provisioning 1253 = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1254 "merchant", 1255 "ENABLE_SELF_PROVISIONING"))) 1256 { 1257 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1258 "merchant", 1259 "ENABLE_SELF_PROVISIONING"); 1260 TMH_have_self_provisioning = GNUNET_NO; 1261 } 1262 1263 if (GNUNET_SYSERR == 1264 (TMH_password_change_mfa 1265 = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1266 "merchant", 1267 "PASSWORD_CHANGE_MFA"))) 1268 { 1269 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1270 "merchant", 1271 "PASSWORD_CHANGE_MFA"); 1272 TMH_password_change_mfa = GNUNET_NO; 1273 } 1274 1275 if (GNUNET_OK != 1276 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1277 "merchant", 1278 "LEGAL_PRESERVATION", 1279 &TMH_legal_expiration)) 1280 { 1281 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1282 "merchant", 1283 "LEGAL_PRESERVATION"); 1284 global_ret = EXIT_NOTCONFIGURED; 1285 GNUNET_SCHEDULER_shutdown (); 1286 return; 1287 } 1288 1289 if (GNUNET_OK != 1290 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1291 "merchant", 1292 "DEFAULT_PAY_DELAY", 1293 &TMH_default_pay_delay)) 1294 { 1295 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1296 "merchant", 1297 "DEFAULT_PAY_DELAY"); 1298 TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS; 1299 } 1300 if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay)) 1301 { 1302 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 1303 "merchant", 1304 "DEFAULT_PAY_DELAY", 1305 "forever is not allowed"); 1306 global_ret = EXIT_NOTCONFIGURED; 1307 GNUNET_SCHEDULER_shutdown (); 1308 return; 1309 } 1310 if (GNUNET_OK != 1311 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1312 "merchant", 1313 "DEFAULT_REFUND_DELAY", 1314 &TMH_default_refund_delay)) 1315 { 1316 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1317 "merchant", 1318 "DEFAULT_REFUND_DELAY"); 1319 TMH_default_refund_delay = GNUNET_TIME_relative_multiply ( 1320 GNUNET_TIME_UNIT_DAYS, 1321 15); 1322 } 1323 if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay)) 1324 { 1325 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 1326 "merchant", 1327 "DEFAULT_REFUND_DELAY", 1328 "forever is not allowed"); 1329 global_ret = EXIT_NOTCONFIGURED; 1330 GNUNET_SCHEDULER_shutdown (); 1331 return; 1332 } 1333 1334 if (GNUNET_OK != 1335 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1336 "merchant", 1337 "DEFAULT_WIRE_TRANSFER_DELAY", 1338 &TMH_default_wire_transfer_delay)) 1339 { 1340 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1341 "merchant", 1342 "DEFAULT_WIRE_TRANSFER_DELAY"); 1343 TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS; 1344 } 1345 if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay)) 1346 { 1347 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 1348 "merchant", 1349 "DEFAULT_WIRE_TRANSFER_DELAY", 1350 "forever is not allowed"); 1351 global_ret = EXIT_NOTCONFIGURED; 1352 GNUNET_SCHEDULER_shutdown (); 1353 return; 1354 } 1355 1356 { 1357 char *dwtri; 1358 1359 if (GNUNET_OK != 1360 GNUNET_CONFIGURATION_get_value_string ( 1361 TMH_cfg, 1362 "merchant", 1363 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL", 1364 &dwtri)) 1365 { 1366 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1367 "merchant", 1368 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL"); 1369 TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE; 1370 } 1371 else 1372 { 1373 if (GNUNET_OK != 1374 GNUNET_TIME_string_to_round_interval ( 1375 dwtri, 1376 &TMH_default_wire_transfer_rounding_interval)) 1377 { 1378 GNUNET_log_config_invalid ( 1379 GNUNET_ERROR_TYPE_ERROR, 1380 "merchant", 1381 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL", 1382 "invalid time rounding interval"); 1383 global_ret = EXIT_NOTCONFIGURED; 1384 GNUNET_free (dwtri); 1385 GNUNET_SCHEDULER_shutdown (); 1386 return; 1387 } 1388 GNUNET_free (dwtri); 1389 } 1390 } 1391 1392 TMH_load_terms (TMH_cfg); 1393 1394 if (GNUNET_OK != 1395 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1396 "merchant", 1397 "PAYMENT_TARGET_TYPES", 1398 &TMH_allowed_payment_targets)) 1399 { 1400 TMH_allowed_payment_targets = GNUNET_strdup ("*"); 1401 } 1402 1403 if (GNUNET_OK != 1404 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1405 "merchant", 1406 "DEFAULT_PERSONA", 1407 &TMH_default_persona)) 1408 { 1409 TMH_default_persona = GNUNET_strdup ("expert"); 1410 } 1411 1412 if (GNUNET_OK != 1413 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1414 "merchant", 1415 "PAYMENT_TARGET_REGEX", 1416 &TMH_payment_target_regex)) 1417 { 1418 TMH_payment_target_regex = NULL; 1419 } 1420 else 1421 { 1422 if (0 == strlen (TMH_payment_target_regex)) 1423 { 1424 GNUNET_free (TMH_payment_target_regex); 1425 } 1426 else 1427 { 1428 if (0 != regcomp (&TMH_payment_target_re, 1429 TMH_payment_target_regex, 1430 REG_NOSUB | REG_EXTENDED)) 1431 { 1432 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1433 "merchant", 1434 "PAYMENT_TARGET_REGEX", 1435 "malformed regular expression"); 1436 global_ret = EXIT_NOTCONFIGURED; 1437 GNUNET_free (TMH_payment_target_regex); 1438 GNUNET_SCHEDULER_shutdown (); 1439 return; 1440 } 1441 } 1442 } 1443 if (GNUNET_OK != 1444 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1445 "merchant", 1446 "PHONE_REGEX", 1447 &TMH_phone_regex)) 1448 { 1449 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 1450 "merchant", 1451 "PHONE_REGEX", 1452 "no restrictions on phone number specified"); 1453 } 1454 else 1455 { 1456 if (0 != regcomp (&TMH_phone_rx, 1457 TMH_phone_regex, 1458 REG_EXTENDED)) 1459 { 1460 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1461 "merchant", 1462 "PHONE_REGEX", 1463 "Invalid regex specified"); 1464 global_ret = EXIT_NOTCONFIGURED; 1465 GNUNET_SCHEDULER_shutdown (); 1466 return; 1467 } 1468 } 1469 1470 if (GNUNET_OK != 1471 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1472 "merchant", 1473 "HELPER_SMS", 1474 &TMH_helper_sms)) 1475 { 1476 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 1477 "merchant", 1478 "HELPER_SMS", 1479 "no helper specified"); 1480 } 1481 1482 if (GNUNET_OK != 1483 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1484 "merchant", 1485 "HELPER_EMAIL", 1486 &TMH_helper_email)) 1487 { 1488 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 1489 "merchant", 1490 "HELPER_EMAIL", 1491 "no helper specified"); 1492 } 1493 1494 { 1495 char *tan_channels; 1496 1497 if (GNUNET_OK == 1498 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1499 "merchant", 1500 "MANDATORY_TAN_CHANNELS", 1501 &tan_channels)) 1502 { 1503 for (char *tok = strtok (tan_channels, 1504 " "); 1505 NULL != tok; 1506 tok = strtok (NULL, 1507 " ")) 1508 { 1509 if (0 == strcasecmp (tok, 1510 "sms")) 1511 { 1512 if (NULL == TMH_helper_sms) 1513 { 1514 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1515 "merchant", 1516 "MANDATORY_TAN_CHANNELS", 1517 "SMS mandatory, but no HELPER_SMS configured"); 1518 global_ret = EXIT_NOTCONFIGURED; 1519 GNUNET_SCHEDULER_shutdown (); 1520 GNUNET_free (tan_channels); 1521 return; 1522 } 1523 TEH_mandatory_tan_channels |= TMH_TCS_SMS; 1524 } 1525 else if (0 == strcasecmp (tok, 1526 "email")) 1527 { 1528 if (NULL == TMH_helper_email) 1529 { 1530 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1531 "merchant", 1532 "MANDATORY_TAN_CHANNELS", 1533 "EMAIL mandatory, but no HELPER_EMAIL configured"); 1534 global_ret = EXIT_NOTCONFIGURED; 1535 GNUNET_SCHEDULER_shutdown (); 1536 GNUNET_free (tan_channels); 1537 return; 1538 } 1539 TEH_mandatory_tan_channels |= TMH_TCS_EMAIL; 1540 } 1541 else 1542 { 1543 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1544 "merchant", 1545 "MANDATORY_TAN_CHANNELS", 1546 tok); 1547 global_ret = EXIT_NOTCONFIGURED; 1548 GNUNET_SCHEDULER_shutdown (); 1549 GNUNET_free (tan_channels); 1550 return; 1551 } 1552 } 1553 GNUNET_free (tan_channels); 1554 } 1555 } 1556 1557 if ( (GNUNET_YES == TMH_password_change_mfa) && 1558 (TMH_TCS_NONE == TEH_mandatory_tan_channels) ) 1559 { 1560 GNUNET_log_config_invalid ( 1561 GNUNET_ERROR_TYPE_ERROR, 1562 "merchant", 1563 "PASSWORD_CHANGE_MFA", 1564 "requires at least one MANDATORY_TAN_CHANNELS entry"); 1565 global_ret = EXIT_NOTCONFIGURED; 1566 GNUNET_SCHEDULER_shutdown (); 1567 return; 1568 } 1569 1570 if (GNUNET_OK == 1571 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1572 "merchant", 1573 "BASE_URL", 1574 &TMH_base_url)) 1575 { 1576 if (! TALER_is_web_url (TMH_base_url)) 1577 { 1578 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1579 "merchant", 1580 "BASE_URL", 1581 "Needs to start with 'http://' or 'https://'"); 1582 global_ret = EXIT_NOTCONFIGURED; 1583 GNUNET_SCHEDULER_shutdown (); 1584 return; 1585 } 1586 } 1587 if (GNUNET_OK == 1588 GNUNET_CONFIGURATION_get_value_filename (TMH_cfg, 1589 "merchant", 1590 "BACKOFFICE_SPA_DIR", 1591 &TMH_spa_dir)) 1592 { 1593 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1594 "Loading merchant SPA from %s\n", 1595 TMH_spa_dir); 1596 } 1597 else 1598 { 1599 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1600 "Loading merchant SPA from default location\n"); 1601 } 1602 1603 if (GNUNET_YES == 1604 GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1605 "merchant", 1606 "FORCE_AUDIT")) 1607 TMH_force_audit = GNUNET_YES; 1608 if (GNUNET_OK != 1609 TALER_TEMPLATING_init (TALER_MERCHANT_project_data ())) 1610 { 1611 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1612 "Failed to setup templates\n"); 1613 global_ret = EXIT_NO_RESTART; 1614 GNUNET_SCHEDULER_shutdown (); 1615 return; 1616 } 1617 if (GNUNET_OK != 1618 TMH_spa_init (TMH_spa_dir)) 1619 { 1620 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1621 "Failed to load single page app\n"); 1622 global_ret = EXIT_NO_RESTART; 1623 GNUNET_SCHEDULER_shutdown (); 1624 return; 1625 } 1626 /* /static/ is currently not used */ 1627 /* (void) TMH_statics_init (); */ 1628 if (NULL == 1629 (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4, 1630 GNUNET_YES))) 1631 { 1632 global_ret = EXIT_FAILURE; 1633 GNUNET_SCHEDULER_shutdown (); 1634 return; 1635 } 1636 if (NULL == 1637 (TMH_db = TALER_MERCHANTDB_connect (TMH_cfg))) 1638 { 1639 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1640 "Failed to connect to database. Consider running taler-merchant-dbinit!\n"); 1641 global_ret = EXIT_FAILURE; 1642 GNUNET_SCHEDULER_shutdown (); 1643 return; 1644 } 1645 elen = TMH_EXCHANGES_init (config); 1646 if (GNUNET_SYSERR == elen) 1647 { 1648 global_ret = EXIT_NOTCONFIGURED; 1649 GNUNET_SCHEDULER_shutdown (); 1650 return; 1651 } 1652 if (0 == elen) 1653 { 1654 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1655 "Fatal: no trusted exchanges configured. Exiting.\n"); 1656 global_ret = EXIT_NOTCONFIGURED; 1657 GNUNET_SCHEDULER_shutdown (); 1658 return; 1659 } 1660 1661 { 1662 struct GNUNET_DB_EventHeaderP es = { 1663 .size = htons (sizeof (es)), 1664 .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS) 1665 }; 1666 1667 instance_eh = TALER_MERCHANTDB_event_listen (TMH_db, 1668 &es, 1669 GNUNET_TIME_UNIT_FOREVER_REL, 1670 &load_instances, 1671 NULL); 1672 } 1673 load_instances (NULL, 1674 NULL, 1675 0); 1676 { 1677 enum GNUNET_GenericReturnValue ret; 1678 1679 ret = TALER_MHD_listen_bind (TMH_cfg, 1680 "merchant", 1681 &start_daemon, 1682 NULL); 1683 switch (ret) 1684 { 1685 case GNUNET_SYSERR: 1686 global_ret = EXIT_NOTCONFIGURED; 1687 GNUNET_SCHEDULER_shutdown (); 1688 return; 1689 case GNUNET_NO: 1690 if (! have_daemons) 1691 { 1692 global_ret = EXIT_FAILURE; 1693 GNUNET_SCHEDULER_shutdown (); 1694 return; 1695 } 1696 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1697 "Could not open all configured listen sockets\n"); 1698 break; 1699 case GNUNET_OK: 1700 break; 1701 } 1702 } 1703 global_ret = EXIT_SUCCESS; 1704 } 1705 1706 1707 /** 1708 * The main function of the serve tool 1709 * 1710 * @param argc number of arguments from the command line 1711 * @param argv command line arguments 1712 * @return 0 ok, non-zero on error 1713 */ 1714 int 1715 main (int argc, 1716 char *const *argv) 1717 { 1718 enum GNUNET_GenericReturnValue res; 1719 struct GNUNET_GETOPT_CommandLineOption options[] = { 1720 GNUNET_GETOPT_option_flag ('C', 1721 "connection-close", 1722 "force HTTP connections to be closed after each request", 1723 &merchant_connection_close), 1724 GNUNET_GETOPT_option_flag ('3', 1725 "http3", 1726 "enable support for HTTP/2 and HTTP/3", 1727 &enable_h3), 1728 GNUNET_GETOPT_option_timetravel ('T', 1729 "timetravel"), 1730 GNUNET_GETOPT_option_version (PACKAGE_VERSION), 1731 GNUNET_GETOPT_OPTION_END 1732 }; 1733 1734 res = GNUNET_PROGRAM_run ( 1735 TALER_MERCHANT_project_data (), 1736 argc, argv, 1737 "taler-merchant-httpd", 1738 "Taler merchant's HTTP backend interface", 1739 options, 1740 &run, NULL); 1741 if (GNUNET_SYSERR == res) 1742 return EXIT_NOTCONFIGURED; 1743 if (GNUNET_NO == res) 1744 return EXIT_SUCCESS; 1745 return global_ret; 1746 }