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