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