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