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