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