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