taler-merchant-httpd.c (56726B)
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 switch (qs) 962 { 963 case GNUNET_DB_STATUS_SOFT_ERROR: 964 case GNUNET_DB_STATUS_HARD_ERROR: 965 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 966 "Error setting instance `%s'\n", 967 mi->settings.id); 968 GNUNET_SCHEDULER_shutdown (); 969 return; 970 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 971 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 972 "Instance `%s' not found in the database (legacy case mismatch not migrated?)\n", 973 mi->settings.id); 974 mi->rc = 1; 975 TMH_instance_decref (mi); 976 return; 977 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 978 break; 979 } 980 qs = TALER_MERCHANTDB_iterate_accounts_by_instance ( 981 TMH_db, 982 mi->settings.id, 983 &add_account_cb, 984 mi); 985 if (0 > qs) 986 { 987 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 988 "Error loading accounts of `%s' from database\n", 989 mi->settings.id); 990 GNUNET_SCHEDULER_shutdown (); 991 return; 992 } 993 GNUNET_assert (GNUNET_OK == 994 TMH_add_instance (mi)); 995 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 996 TALER_MERCHANTDB_set_instance (TMH_db, 997 NULL)); 998 } 999 1000 1001 /** 1002 * Trigger (re)loading of instance settings from DB. 1003 * 1004 * @param cls NULL 1005 * @param extra ID of the instance that changed, NULL 1006 * to load all instances (will not handle purges!) 1007 * @param extra_len number of bytes in @a extra 1008 */ 1009 static void 1010 load_instances (void *cls, 1011 const void *extra, 1012 size_t extra_len) 1013 { 1014 enum GNUNET_DB_QueryStatus qs; 1015 const char *id = extra; 1016 1017 (void) cls; 1018 if ( (NULL != extra) && 1019 ( (0 == extra_len) || 1020 ('\0' != id[extra_len - 1]) ) ) 1021 { 1022 GNUNET_break (0 == extra_len); 1023 extra = NULL; 1024 } 1025 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1026 "Received instance settings notification: reload `%s'\n", 1027 id); 1028 if (NULL == extra) 1029 { 1030 qs = TALER_MERCHANTDB_iterate_instances (TMH_db, 1031 false, 1032 &add_instance_cb, 1033 NULL); 1034 } 1035 else 1036 { 1037 struct TMH_MerchantInstance *mi; 1038 1039 /* This must be done here to handle instance 1040 purging, as for purged instances, the DB 1041 lookup below will otherwise do nothing */ 1042 mi = TMH_lookup_instance (id); 1043 if (NULL != mi) 1044 { 1045 (void) TMH_instance_free_cb (NULL, 1046 &mi->h_instance, 1047 mi); 1048 } 1049 qs = TALER_MERCHANTDB_iterate_instances_by_id (TMH_db, 1050 id, 1051 false, 1052 &add_instance_cb, 1053 NULL); 1054 } 1055 if (0 > qs) 1056 { 1057 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1058 "Failed initialization. Check database setup.\n"); 1059 global_ret = EXIT_FAILURE; 1060 GNUNET_SCHEDULER_shutdown (); 1061 return; 1062 } 1063 } 1064 1065 1066 /** 1067 * A transaction modified an instance setting (or created/deleted/purged 1068 * one). Notify all backends about the change. 1069 * 1070 * @param id ID of the instance that changed 1071 */ 1072 void 1073 TMH_reload_instances (const char *id) 1074 { 1075 struct GNUNET_DB_EventHeaderP es = { 1076 .size = htons (sizeof (es)), 1077 .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS) 1078 }; 1079 1080 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1081 "Generating instance settings notification: reload `%s'\n", 1082 id); 1083 TALER_MERCHANTDB_event_notify (TMH_db, 1084 &es, 1085 id, 1086 (NULL == id) 1087 ? 0 1088 : strlen (id) + 1); 1089 } 1090 1091 1092 /** 1093 * Callback invoked on every listen socket to start the 1094 * respective MHD HTTP daemon. 1095 * 1096 * @param cls unused 1097 * @param lsock the listen socket 1098 */ 1099 static void 1100 start_daemon (void *cls, 1101 int lsock) 1102 { 1103 struct MHD_Daemon *mhd; 1104 1105 (void) cls; 1106 GNUNET_assert (-1 != lsock); 1107 mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK 1108 | MHD_USE_AUTO, 1109 0 /* port */, 1110 NULL, NULL, 1111 &url_handler, NULL, 1112 MHD_OPTION_LISTEN_SOCKET, lsock, 1113 MHD_OPTION_URI_LOG_CALLBACK, 1114 &full_url_track_callback, NULL, 1115 MHD_OPTION_NOTIFY_COMPLETED, 1116 &handle_mhd_completion_callback, NULL, 1117 MHD_OPTION_CONNECTION_TIMEOUT, 1118 (unsigned int) 10 /* 10s */, 1119 MHD_OPTION_END); 1120 if (NULL == mhd) 1121 { 1122 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1123 "Failed to launch HTTP service.\n"); 1124 GNUNET_SCHEDULER_shutdown (); 1125 return; 1126 } 1127 have_daemons = true; 1128 TALER_MHD_daemon_start (mhd); 1129 } 1130 1131 1132 /** 1133 * Main function that will be run by the scheduler. 1134 * 1135 * @param cls closure 1136 * @param args remaining command-line arguments 1137 * @param cfgfile name of the configuration file used (for saving, can be 1138 * NULL!) 1139 * @param config configuration 1140 */ 1141 static void 1142 run (void *cls, 1143 char *const *args, 1144 const char *cfgfile, 1145 const struct GNUNET_CONFIGURATION_Handle *config) 1146 { 1147 enum TALER_MHD_GlobalOptions go; 1148 int elen; 1149 1150 (void) cls; 1151 (void) args; 1152 (void) cfgfile; 1153 TMH_cfg = config; 1154 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1155 "Starting taler-merchant-httpd\n"); 1156 go = TALER_MHD_GO_NONE; 1157 if (merchant_connection_close) 1158 go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; 1159 TALER_MHD_setup (go); 1160 TALER_EXCHANGE_setup (enable_h3 1161 ? TALER_EXCHANGE_GO_ENABLE_HTTP3 1162 : TALER_EXCHANGE_GO_FORCE_HTTP1_1); 1163 DONAU_setup (enable_h3 1164 ? DONAU_GO_ENABLE_HTTP3 1165 : DONAU_GO_FORCE_HTTP1_1); 1166 1167 global_ret = EXIT_SUCCESS; 1168 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1169 NULL); 1170 1171 TMH_curl_ctx 1172 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1173 &merchant_curl_rc); 1174 if (NULL == TMH_curl_ctx) 1175 { 1176 GNUNET_break (0); 1177 global_ret = EXIT_FAILURE; 1178 GNUNET_SCHEDULER_shutdown (); 1179 return; 1180 } 1181 merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx); 1182 /* Disable 100 continue processing */ 1183 GNUNET_break (GNUNET_OK == 1184 GNUNET_CURL_append_header (TMH_curl_ctx, 1185 MHD_HTTP_HEADER_EXPECT ":")); 1186 GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx, 1187 "Taler-Correlation-Id"); 1188 1189 if (GNUNET_SYSERR == 1190 TALER_config_get_currency (TMH_cfg, 1191 "merchant", 1192 &TMH_currency)) 1193 { 1194 global_ret = EXIT_NOTCONFIGURED; 1195 GNUNET_SCHEDULER_shutdown (); 1196 return; 1197 } 1198 if (GNUNET_OK != 1199 TALER_CONFIG_parse_currencies (TMH_cfg, 1200 TMH_currency, 1201 &TMH_num_cspecs, 1202 &TMH_cspecs)) 1203 { 1204 global_ret = EXIT_NOTCONFIGURED; 1205 GNUNET_SCHEDULER_shutdown (); 1206 return; 1207 } 1208 1209 { 1210 char *spa_data; 1211 1212 if (GNUNET_OK == 1213 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1214 "merchant", 1215 "GLOBAL_SPA_CONFIG_DATA", 1216 &spa_data)) 1217 { 1218 json_error_t err; 1219 1220 TMH_global_spa_config_data = json_loads (spa_data, 1221 JSON_REJECT_DUPLICATES, 1222 &err); 1223 GNUNET_free (spa_data); 1224 if (NULL == TMH_global_spa_config_data) 1225 { 1226 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1227 "merchant", 1228 "GLOBAL_SPA_CONFIG_DATA", 1229 err.text); 1230 global_ret = EXIT_NOTCONFIGURED; 1231 GNUNET_SCHEDULER_shutdown (); 1232 return; 1233 } 1234 } 1235 } 1236 1237 1238 if (GNUNET_SYSERR == 1239 (TMH_strict_v19 1240 = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1241 "merchant", 1242 "STRICT_PROTOCOL_V19"))) 1243 { 1244 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1245 "merchant", 1246 "STRICT_PROTOCOL_V19"); 1247 TMH_strict_v19 = GNUNET_NO; 1248 } 1249 if (GNUNET_SYSERR == 1250 (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1251 "merchant", 1252 "DISABLE_AUTHENTICATION"))) 1253 { 1254 TMH_auth_disabled = GNUNET_NO; 1255 } 1256 if (GNUNET_YES == TMH_auth_disabled) 1257 { 1258 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1259 "DANGEROUS: Endpoint Authentication disabled!"); 1260 } 1261 1262 if (GNUNET_SYSERR == 1263 (TMH_have_self_provisioning 1264 = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1265 "merchant", 1266 "ENABLE_SELF_PROVISIONING"))) 1267 { 1268 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1269 "merchant", 1270 "ENABLE_SELF_PROVISIONING"); 1271 TMH_have_self_provisioning = GNUNET_NO; 1272 } 1273 1274 if (GNUNET_SYSERR == 1275 (TMH_password_change_mfa 1276 = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1277 "merchant", 1278 "PASSWORD_CHANGE_MFA"))) 1279 { 1280 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1281 "merchant", 1282 "PASSWORD_CHANGE_MFA"); 1283 TMH_password_change_mfa = GNUNET_NO; 1284 } 1285 1286 if (GNUNET_OK != 1287 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1288 "merchant", 1289 "LEGAL_PRESERVATION", 1290 &TMH_legal_expiration)) 1291 { 1292 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1293 "merchant", 1294 "LEGAL_PRESERVATION"); 1295 global_ret = EXIT_NOTCONFIGURED; 1296 GNUNET_SCHEDULER_shutdown (); 1297 return; 1298 } 1299 1300 if (GNUNET_OK != 1301 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1302 "merchant", 1303 "DEFAULT_PAY_DELAY", 1304 &TMH_default_pay_delay)) 1305 { 1306 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1307 "merchant", 1308 "DEFAULT_PAY_DELAY"); 1309 TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS; 1310 } 1311 if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay)) 1312 { 1313 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 1314 "merchant", 1315 "DEFAULT_PAY_DELAY", 1316 "forever is not allowed"); 1317 global_ret = EXIT_NOTCONFIGURED; 1318 GNUNET_SCHEDULER_shutdown (); 1319 return; 1320 } 1321 if (GNUNET_OK != 1322 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1323 "merchant", 1324 "DEFAULT_REFUND_DELAY", 1325 &TMH_default_refund_delay)) 1326 { 1327 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1328 "merchant", 1329 "DEFAULT_REFUND_DELAY"); 1330 TMH_default_refund_delay = GNUNET_TIME_relative_multiply ( 1331 GNUNET_TIME_UNIT_DAYS, 1332 15); 1333 } 1334 if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay)) 1335 { 1336 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 1337 "merchant", 1338 "DEFAULT_REFUND_DELAY", 1339 "forever is not allowed"); 1340 global_ret = EXIT_NOTCONFIGURED; 1341 GNUNET_SCHEDULER_shutdown (); 1342 return; 1343 } 1344 1345 if (GNUNET_OK != 1346 GNUNET_CONFIGURATION_get_value_time (TMH_cfg, 1347 "merchant", 1348 "DEFAULT_WIRE_TRANSFER_DELAY", 1349 &TMH_default_wire_transfer_delay)) 1350 { 1351 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1352 "merchant", 1353 "DEFAULT_WIRE_TRANSFER_DELAY"); 1354 TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS; 1355 } 1356 if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay)) 1357 { 1358 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 1359 "merchant", 1360 "DEFAULT_WIRE_TRANSFER_DELAY", 1361 "forever is not allowed"); 1362 global_ret = EXIT_NOTCONFIGURED; 1363 GNUNET_SCHEDULER_shutdown (); 1364 return; 1365 } 1366 1367 { 1368 char *dwtri; 1369 1370 if (GNUNET_OK != 1371 GNUNET_CONFIGURATION_get_value_string ( 1372 TMH_cfg, 1373 "merchant", 1374 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL", 1375 &dwtri)) 1376 { 1377 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 1378 "merchant", 1379 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL"); 1380 TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE; 1381 } 1382 else 1383 { 1384 if (GNUNET_OK != 1385 GNUNET_TIME_string_to_round_interval ( 1386 dwtri, 1387 &TMH_default_wire_transfer_rounding_interval)) 1388 { 1389 GNUNET_log_config_invalid ( 1390 GNUNET_ERROR_TYPE_ERROR, 1391 "merchant", 1392 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL", 1393 "invalid time rounding interval"); 1394 global_ret = EXIT_NOTCONFIGURED; 1395 GNUNET_free (dwtri); 1396 GNUNET_SCHEDULER_shutdown (); 1397 return; 1398 } 1399 GNUNET_free (dwtri); 1400 } 1401 } 1402 1403 TMH_load_terms (TMH_cfg); 1404 1405 if (GNUNET_OK != 1406 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1407 "merchant", 1408 "PAYMENT_TARGET_TYPES", 1409 &TMH_allowed_payment_targets)) 1410 { 1411 TMH_allowed_payment_targets = GNUNET_strdup ("*"); 1412 } 1413 1414 if (GNUNET_OK != 1415 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1416 "merchant", 1417 "DEFAULT_PERSONA", 1418 &TMH_default_persona)) 1419 { 1420 TMH_default_persona = GNUNET_strdup ("expert"); 1421 } 1422 1423 if (GNUNET_OK != 1424 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1425 "merchant", 1426 "PAYMENT_TARGET_REGEX", 1427 &TMH_payment_target_regex)) 1428 { 1429 TMH_payment_target_regex = NULL; 1430 } 1431 else 1432 { 1433 if (0 == strlen (TMH_payment_target_regex)) 1434 { 1435 GNUNET_free (TMH_payment_target_regex); 1436 } 1437 else 1438 { 1439 if (0 != regcomp (&TMH_payment_target_re, 1440 TMH_payment_target_regex, 1441 REG_NOSUB | REG_EXTENDED)) 1442 { 1443 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1444 "merchant", 1445 "PAYMENT_TARGET_REGEX", 1446 "malformed regular expression"); 1447 global_ret = EXIT_NOTCONFIGURED; 1448 GNUNET_free (TMH_payment_target_regex); 1449 GNUNET_SCHEDULER_shutdown (); 1450 return; 1451 } 1452 } 1453 } 1454 if (GNUNET_OK != 1455 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1456 "merchant", 1457 "PHONE_REGEX", 1458 &TMH_phone_regex)) 1459 { 1460 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 1461 "merchant", 1462 "PHONE_REGEX", 1463 "no restrictions on phone number specified"); 1464 } 1465 else 1466 { 1467 if (0 != regcomp (&TMH_phone_rx, 1468 TMH_phone_regex, 1469 REG_EXTENDED)) 1470 { 1471 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1472 "merchant", 1473 "PHONE_REGEX", 1474 "Invalid regex specified"); 1475 global_ret = EXIT_NOTCONFIGURED; 1476 GNUNET_SCHEDULER_shutdown (); 1477 return; 1478 } 1479 } 1480 1481 if (GNUNET_OK != 1482 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1483 "merchant", 1484 "HELPER_SMS", 1485 &TMH_helper_sms)) 1486 { 1487 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 1488 "merchant", 1489 "HELPER_SMS", 1490 "no helper specified"); 1491 } 1492 1493 if (GNUNET_OK != 1494 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1495 "merchant", 1496 "HELPER_EMAIL", 1497 &TMH_helper_email)) 1498 { 1499 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 1500 "merchant", 1501 "HELPER_EMAIL", 1502 "no helper specified"); 1503 } 1504 1505 { 1506 char *tan_channels; 1507 1508 if (GNUNET_OK == 1509 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1510 "merchant", 1511 "MANDATORY_TAN_CHANNELS", 1512 &tan_channels)) 1513 { 1514 for (char *tok = strtok (tan_channels, 1515 " "); 1516 NULL != tok; 1517 tok = strtok (NULL, 1518 " ")) 1519 { 1520 if (0 == strcasecmp (tok, 1521 "sms")) 1522 { 1523 if (NULL == TMH_helper_sms) 1524 { 1525 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1526 "merchant", 1527 "MANDATORY_TAN_CHANNELS", 1528 "SMS mandatory, but no HELPER_SMS configured"); 1529 global_ret = EXIT_NOTCONFIGURED; 1530 GNUNET_SCHEDULER_shutdown (); 1531 GNUNET_free (tan_channels); 1532 return; 1533 } 1534 TEH_mandatory_tan_channels |= TMH_TCS_SMS; 1535 } 1536 else if (0 == strcasecmp (tok, 1537 "email")) 1538 { 1539 if (NULL == TMH_helper_email) 1540 { 1541 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1542 "merchant", 1543 "MANDATORY_TAN_CHANNELS", 1544 "EMAIL mandatory, but no HELPER_EMAIL configured"); 1545 global_ret = EXIT_NOTCONFIGURED; 1546 GNUNET_SCHEDULER_shutdown (); 1547 GNUNET_free (tan_channels); 1548 return; 1549 } 1550 TEH_mandatory_tan_channels |= TMH_TCS_EMAIL; 1551 } 1552 else 1553 { 1554 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1555 "merchant", 1556 "MANDATORY_TAN_CHANNELS", 1557 tok); 1558 global_ret = EXIT_NOTCONFIGURED; 1559 GNUNET_SCHEDULER_shutdown (); 1560 GNUNET_free (tan_channels); 1561 return; 1562 } 1563 } 1564 GNUNET_free (tan_channels); 1565 } 1566 } 1567 1568 if ( (GNUNET_YES == TMH_password_change_mfa) && 1569 (TMH_TCS_NONE == TEH_mandatory_tan_channels) ) 1570 { 1571 GNUNET_log_config_invalid ( 1572 GNUNET_ERROR_TYPE_ERROR, 1573 "merchant", 1574 "PASSWORD_CHANGE_MFA", 1575 "requires at least one MANDATORY_TAN_CHANNELS entry"); 1576 global_ret = EXIT_NOTCONFIGURED; 1577 GNUNET_SCHEDULER_shutdown (); 1578 return; 1579 } 1580 1581 if (GNUNET_OK == 1582 GNUNET_CONFIGURATION_get_value_string (TMH_cfg, 1583 "merchant", 1584 "BASE_URL", 1585 &TMH_base_url)) 1586 { 1587 if (! TALER_is_web_url (TMH_base_url)) 1588 { 1589 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 1590 "merchant", 1591 "BASE_URL", 1592 "Needs to start with 'http://' or 'https://'"); 1593 global_ret = EXIT_NOTCONFIGURED; 1594 GNUNET_SCHEDULER_shutdown (); 1595 return; 1596 } 1597 } 1598 if (GNUNET_OK == 1599 GNUNET_CONFIGURATION_get_value_filename (TMH_cfg, 1600 "merchant", 1601 "BACKOFFICE_SPA_DIR", 1602 &TMH_spa_dir)) 1603 { 1604 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1605 "Loading merchant SPA from %s\n", 1606 TMH_spa_dir); 1607 } 1608 else 1609 { 1610 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1611 "Loading merchant SPA from default location\n"); 1612 } 1613 1614 if (GNUNET_YES == 1615 GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg, 1616 "merchant", 1617 "FORCE_AUDIT")) 1618 TMH_force_audit = GNUNET_YES; 1619 if (GNUNET_OK != 1620 TALER_TEMPLATING_init (TALER_MERCHANT_project_data ())) 1621 { 1622 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1623 "Failed to setup templates\n"); 1624 global_ret = EXIT_NO_RESTART; 1625 GNUNET_SCHEDULER_shutdown (); 1626 return; 1627 } 1628 if (GNUNET_OK != 1629 TMH_spa_init (TMH_spa_dir)) 1630 { 1631 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1632 "Failed to load single page app\n"); 1633 global_ret = EXIT_NO_RESTART; 1634 GNUNET_SCHEDULER_shutdown (); 1635 return; 1636 } 1637 /* /static/ is currently not used */ 1638 /* (void) TMH_statics_init (); */ 1639 if (NULL == 1640 (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4, 1641 GNUNET_YES))) 1642 { 1643 global_ret = EXIT_FAILURE; 1644 GNUNET_SCHEDULER_shutdown (); 1645 return; 1646 } 1647 if (NULL == 1648 (TMH_db = TALER_MERCHANTDB_connect (TMH_cfg))) 1649 { 1650 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1651 "Failed to connect to database. Consider running taler-merchant-dbinit!\n"); 1652 global_ret = EXIT_FAILURE; 1653 GNUNET_SCHEDULER_shutdown (); 1654 return; 1655 } 1656 elen = TMH_EXCHANGES_init (config); 1657 if (GNUNET_SYSERR == elen) 1658 { 1659 global_ret = EXIT_NOTCONFIGURED; 1660 GNUNET_SCHEDULER_shutdown (); 1661 return; 1662 } 1663 if (0 == elen) 1664 { 1665 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1666 "Fatal: no trusted exchanges configured. Exiting.\n"); 1667 global_ret = EXIT_NOTCONFIGURED; 1668 GNUNET_SCHEDULER_shutdown (); 1669 return; 1670 } 1671 1672 { 1673 struct GNUNET_DB_EventHeaderP es = { 1674 .size = htons (sizeof (es)), 1675 .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS) 1676 }; 1677 1678 instance_eh = TALER_MERCHANTDB_event_listen (TMH_db, 1679 &es, 1680 GNUNET_TIME_UNIT_FOREVER_REL, 1681 &load_instances, 1682 NULL); 1683 } 1684 load_instances (NULL, 1685 NULL, 1686 0); 1687 { 1688 enum GNUNET_GenericReturnValue ret; 1689 1690 ret = TALER_MHD_listen_bind (TMH_cfg, 1691 "merchant", 1692 &start_daemon, 1693 NULL); 1694 switch (ret) 1695 { 1696 case GNUNET_SYSERR: 1697 global_ret = EXIT_NOTCONFIGURED; 1698 GNUNET_SCHEDULER_shutdown (); 1699 return; 1700 case GNUNET_NO: 1701 if (! have_daemons) 1702 { 1703 global_ret = EXIT_FAILURE; 1704 GNUNET_SCHEDULER_shutdown (); 1705 return; 1706 } 1707 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1708 "Could not open all configured listen sockets\n"); 1709 break; 1710 case GNUNET_OK: 1711 break; 1712 } 1713 } 1714 global_ret = EXIT_SUCCESS; 1715 } 1716 1717 1718 /** 1719 * The main function of the serve tool 1720 * 1721 * @param argc number of arguments from the command line 1722 * @param argv command line arguments 1723 * @return 0 ok, non-zero on error 1724 */ 1725 int 1726 main (int argc, 1727 char *const *argv) 1728 { 1729 enum GNUNET_GenericReturnValue res; 1730 struct GNUNET_GETOPT_CommandLineOption options[] = { 1731 GNUNET_GETOPT_option_flag ('C', 1732 "connection-close", 1733 "force HTTP connections to be closed after each request", 1734 &merchant_connection_close), 1735 GNUNET_GETOPT_option_flag ('3', 1736 "http3", 1737 "enable support for HTTP/2 and HTTP/3", 1738 &enable_h3), 1739 GNUNET_GETOPT_option_timetravel ('T', 1740 "timetravel"), 1741 GNUNET_GETOPT_option_version (PACKAGE_VERSION), 1742 GNUNET_GETOPT_OPTION_END 1743 }; 1744 1745 res = GNUNET_PROGRAM_run ( 1746 TALER_MERCHANT_project_data (), 1747 argc, argv, 1748 "taler-merchant-httpd", 1749 "Taler merchant's HTTP backend interface", 1750 options, 1751 &run, NULL); 1752 if (GNUNET_SYSERR == res) 1753 return EXIT_NOTCONFIGURED; 1754 if (GNUNET_NO == res) 1755 return EXIT_SUCCESS; 1756 return global_ret; 1757 }