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