taler-merchant-httpd.c (107096B)
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 "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_merchant_util.h" 32 #include "taler-merchant-httpd_config.h" 33 #include "taler-merchant-httpd_exchanges.h" 34 #include "taler-merchant-httpd_get-orders-ID.h" 35 #include "taler-merchant-httpd_get-products-image.h" 36 #include "taler-merchant-httpd_get-templates-ID.h" 37 #include "taler-merchant-httpd_helper.h" 38 #include "taler-merchant-httpd_mhd.h" 39 #include "taler-merchant-httpd_mfa.h" 40 #include "taler-merchant-httpd_private-delete-account-ID.h" 41 #include "taler-merchant-httpd_private-delete-categories-ID.h" 42 #include "taler-merchant-httpd_private-delete-units-ID.h" 43 #include "taler-merchant-httpd_private-delete-instances-ID.h" 44 #include "taler-merchant-httpd_private-delete-instances-ID-token.h" 45 #include "taler-merchant-httpd_private-delete-products-ID.h" 46 #include "taler-merchant-httpd_private-delete-orders-ID.h" 47 #include "taler-merchant-httpd_private-delete-otp-devices-ID.h" 48 #include "taler-merchant-httpd_private-delete-templates-ID.h" 49 #include "taler-merchant-httpd_private-delete-token-families-SLUG.h" 50 #include "taler-merchant-httpd_private-delete-transfers-ID.h" 51 #include "taler-merchant-httpd_private-delete-webhooks-ID.h" 52 #include "taler-merchant-httpd_private-get-accounts.h" 53 #include "taler-merchant-httpd_private-get-accounts-ID.h" 54 #include "taler-merchant-httpd_private-get-categories.h" 55 #include "taler-merchant-httpd_private-get-categories-ID.h" 56 #include "taler-merchant-httpd_private-get-units.h" 57 #include "taler-merchant-httpd_private-get-units-ID.h" 58 #include "taler-merchant-httpd_private-get-incoming.h" 59 #include "taler-merchant-httpd_private-get-instances.h" 60 #include "taler-merchant-httpd_private-get-instances-ID.h" 61 #include "taler-merchant-httpd_private-get-instances-ID-kyc.h" 62 #include "taler-merchant-httpd_private-get-instances-ID-tokens.h" 63 #include "taler-merchant-httpd_private-get-pos.h" 64 #include "taler-merchant-httpd_private-get-products.h" 65 #include "taler-merchant-httpd_private-get-products-ID.h" 66 #include "taler-merchant-httpd_private-get-orders.h" 67 #include "taler-merchant-httpd_private-get-orders-ID.h" 68 #include "taler-merchant-httpd_private-get-otp-devices.h" 69 #include "taler-merchant-httpd_private-get-otp-devices-ID.h" 70 #include "taler-merchant-httpd_private-get-statistics-amount-SLUG.h" 71 #include "taler-merchant-httpd_private-get-statistics-counter-SLUG.h" 72 #include "taler-merchant-httpd_private-get-templates.h" 73 #include "taler-merchant-httpd_private-get-templates-ID.h" 74 #include "taler-merchant-httpd_private-get-token-families.h" 75 #include "taler-merchant-httpd_private-get-token-families-SLUG.h" 76 #include "taler-merchant-httpd_private-get-transfers.h" 77 #include "taler-merchant-httpd_private-get-webhooks.h" 78 #include "taler-merchant-httpd_private-get-webhooks-ID.h" 79 #include "taler-merchant-httpd_private-patch-accounts-ID.h" 80 #include "taler-merchant-httpd_private-patch-categories-ID.h" 81 #include "taler-merchant-httpd_private-patch-units-ID.h" 82 #include "taler-merchant-httpd_private-patch-instances-ID.h" 83 #include "taler-merchant-httpd_private-patch-orders-ID-forget.h" 84 #include "taler-merchant-httpd_private-patch-otp-devices-ID.h" 85 #include "taler-merchant-httpd_private-patch-products-ID.h" 86 #include "taler-merchant-httpd_private-patch-templates-ID.h" 87 #include "taler-merchant-httpd_private-patch-token-families-SLUG.h" 88 #include "taler-merchant-httpd_private-patch-webhooks-ID.h" 89 #include "taler-merchant-httpd_private-post-account.h" 90 #include "taler-merchant-httpd_private-post-categories.h" 91 #include "taler-merchant-httpd_private-post-units.h" 92 #include "taler-merchant-httpd_private-post-instances.h" 93 #include "taler-merchant-httpd_private-post-instances-ID-auth.h" 94 #include "taler-merchant-httpd_private-post-instances-ID-token.h" 95 #include "taler-merchant-httpd_private-post-otp-devices.h" 96 #include "taler-merchant-httpd_private-post-orders.h" 97 #include "taler-merchant-httpd_private-post-orders-ID-refund.h" 98 #include "taler-merchant-httpd_private-post-products.h" 99 #include "taler-merchant-httpd_private-post-products-ID-lock.h" 100 #include "taler-merchant-httpd_private-post-templates.h" 101 #include "taler-merchant-httpd_private-post-token-families.h" 102 #include "taler-merchant-httpd_private-post-transfers.h" 103 #include "taler-merchant-httpd_private-post-webhooks.h" 104 #include "taler-merchant-httpd_post-challenge-ID.h" 105 #include "taler-merchant-httpd_post-challenge-ID-confirm.h" 106 #include "taler-merchant-httpd_post-orders-ID-abort.h" 107 #include "taler-merchant-httpd_post-orders-ID-claim.h" 108 #include "taler-merchant-httpd_post-orders-ID-paid.h" 109 #include "taler-merchant-httpd_post-orders-ID-pay.h" 110 #include "taler-merchant-httpd_post-using-templates.h" 111 #include "taler-merchant-httpd_post-orders-ID-refund.h" 112 #include "taler-merchant-httpd_spa.h" 113 #include "taler-merchant-httpd_statics.h" 114 #include "taler-merchant-httpd_terms.h" 115 116 #ifdef HAVE_DONAU_DONAU_SERVICE_H 117 #include "taler-merchant-httpd_private-get-donau-instances.h" 118 #include "taler-merchant-httpd_private-post-donau-instance.h" 119 #include "taler-merchant-httpd_private-delete-donau-instance-ID.h" 120 #endif 121 122 /** 123 * Backlog for listen operation on unix-domain sockets. 124 */ 125 #define UNIX_BACKLOG 500 126 127 /** 128 * Default maximum upload size permitted. Can be overridden 129 * per handler. 130 */ 131 #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024) 132 133 char *TMH_currency; 134 135 char *TMH_base_url; 136 137 char *TMH_spa_dir; 138 139 char *TMH_helper_email; 140 141 char *TMH_helper_sms; 142 143 char *TMH_allowed_payment_targets; 144 145 char *TMH_default_persona; 146 147 char *TMH_payment_target_regex; 148 149 regex_t TMH_payment_target_re; 150 151 int TMH_force_audit; 152 153 struct TALER_MERCHANTDB_Plugin *TMH_db; 154 155 struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map; 156 157 struct GNUNET_TIME_Relative TMH_default_pay_delay; 158 159 struct GNUNET_TIME_Relative TMH_default_refund_delay; 160 161 struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay; 162 163 enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval; 164 165 int TMH_strict_v19; 166 167 int TMH_auth_disabled; 168 169 int TMH_have_self_provisioning; 170 171 enum TEH_TanChannelSet TEH_mandatory_tan_channels; 172 173 struct GNUNET_TIME_Relative TMH_legal_expiration; 174 175 unsigned int TMH_num_cspecs; 176 177 struct TALER_CurrencySpecification *TMH_cspecs; 178 179 struct GNUNET_CURL_Context *TMH_curl_ctx; 180 181 /** 182 * Event handler for instance settings changes. 183 */ 184 static struct GNUNET_DB_EventHandler *instance_eh; 185 186 /** 187 * True if we started any HTTP daemon. 188 */ 189 static bool have_daemons; 190 191 /** 192 * Should a "Connection: close" header be added to each HTTP response? 193 */ 194 static int merchant_connection_close; 195 196 /** 197 * Context for integrating #TMH_curl_ctx with the 198 * GNUnet event loop. 199 */ 200 static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc; 201 202 /** 203 * Global return code 204 */ 205 static int global_ret; 206 207 /** 208 * Our configuration. 209 */ 210 static const struct GNUNET_CONFIGURATION_Handle *cfg; 211 212 /** 213 * Maximum length of a permissions string of a scope 214 */ 215 #define TMH_MAX_SCOPE_PERMISSIONS_LEN 4096 216 217 /** 218 * Maximum length of a name of a scope 219 */ 220 #define TMH_MAX_NAME_LEN 255 221 222 /** 223 * Represents a hard-coded set of default scopes with their 224 * permissions and names 225 */ 226 struct ScopePermissionMap 227 { 228 /** 229 * The scope enum value 230 */ 231 enum TMH_AuthScope as; 232 233 /** 234 * The scope name 235 */ 236 char name[TMH_MAX_NAME_LEN]; 237 238 /** 239 * The scope permissions string. 240 * Comma-separated. 241 */ 242 char permissions[TMH_MAX_SCOPE_PERMISSIONS_LEN]; 243 }; 244 245 /** 246 * The default scopes array for merchant 247 */ 248 struct ScopePermissionMap scope_permissions[] = { 249 /* Deprecated since v19 */ 250 { 251 .as = TMH_AS_ALL, 252 .name = "write", 253 .permissions = "*" 254 }, 255 /* Full access for SPA */ 256 { 257 .as = TMH_AS_ALL, 258 .name = "all", 259 .permissions = "*" 260 }, 261 /* Full access for SPA */ 262 { 263 .as = TMH_AS_SPA, 264 .name = "spa", 265 .permissions = "*" 266 }, 267 /* Read-only access */ 268 { 269 .as = TMH_AS_READ_ONLY, 270 .name = "readonly", 271 .permissions = "*-read" 272 }, 273 /* Simple order management */ 274 { 275 .as = TMH_AS_ORDER_SIMPLE, 276 .name = "order-simple", 277 .permissions = "orders-read,orders-write" 278 }, 279 /* Simple order management for PoS, also allows inventory locking */ 280 { 281 .as = TMH_AS_ORDER_POS, 282 .name = "order-pos", 283 .permissions = "orders-read,orders-write,inventory-lock" 284 }, 285 /* Simple order management, also allows refunding */ 286 { 287 .as = TMH_AS_ORDER_MGMT, 288 .name = "order-mgmt", 289 .permissions = "orders-read,orders-write,orders-refund" 290 }, 291 /* Full order management, allows inventory locking and refunds */ 292 { 293 .as = TMH_AS_ORDER_FULL, 294 .name = "order-full", 295 .permissions = "orders-read,orders-write,inventory-lock,orders-refund" 296 }, 297 /* No permissions, dummy scope */ 298 { 299 .as = TMH_AS_NONE, 300 } 301 }; 302 303 304 /** 305 * Get permissions string for scope. 306 * Also extracts the leftmost bit into the @a refreshable 307 * output parameter. 308 * 309 * @param as the scope to get the permissions string from 310 * @param[out] refreshable true if the token associated with this scope is refreshable. 311 * @return the permissions string, or NULL if no such scope found 312 */ 313 static const char* 314 get_scope_permissions (enum TMH_AuthScope as, 315 bool *refreshable) 316 { 317 *refreshable = as & TMH_AS_REFRESHABLE; 318 for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++) 319 { 320 /* We ignore the TMH_AS_REFRESHABLE bit */ 321 if ( (as & ~TMH_AS_REFRESHABLE) == 322 (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) ) 323 return scope_permissions[i].permissions; 324 } 325 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 326 "Failed to find required permissions for scope %d\n", 327 as); 328 return NULL; 329 } 330 331 332 /** 333 * Checks if @a permission_required is in permissions of 334 * @a scope. 335 * 336 * @param permission_required the permission to check. 337 * @param scope the scope to check. 338 * @return true if @a permission_required is in the permissions set of @a scope. 339 */ 340 static bool 341 permission_in_scope (const char *permission_required, 342 enum TMH_AuthScope scope) 343 { 344 char *permissions; 345 const char *perms_tmp; 346 bool is_read_perm = false; 347 bool is_write_perm = false; 348 bool refreshable; 349 const char *last_dash; 350 351 perms_tmp = get_scope_permissions (scope, 352 &refreshable); 353 if (NULL == perms_tmp) 354 { 355 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 356 "Permission check failed: scope %d not understood\n", 357 (int) scope); 358 return false; 359 } 360 last_dash = strrchr (permission_required, 361 '-'); 362 if (NULL != last_dash) 363 { 364 is_write_perm = (0 == strcmp (last_dash, 365 "-write")); 366 is_read_perm = (0 == strcmp (last_dash, 367 "-read")); 368 } 369 370 if (0 == strcmp ("token-refresh", 371 permission_required)) 372 { 373 if (! refreshable) 374 { 375 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 376 "Permission check failed: token not refreshable\n"); 377 } 378 return refreshable; 379 } 380 permissions = GNUNET_strdup (perms_tmp); 381 { 382 const char *perm = strtok (permissions, 383 ","); 384 385 if (NULL == perm) 386 { 387 GNUNET_free (permissions); 388 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 389 "Permission check failed: empty permission set\n"); 390 return false; 391 } 392 while (NULL != perm) 393 { 394 if (0 == strcmp ("*", 395 perm)) 396 { 397 GNUNET_free (permissions); 398 return true; 399 } 400 if ( (0 == strcmp ("*-write", 401 perm)) && 402 (is_write_perm) ) 403 { 404 GNUNET_free (permissions); 405 return true; 406 } 407 if ( (0 == strcmp ("*-read", 408 perm)) && 409 (is_read_perm) ) 410 { 411 GNUNET_free (permissions); 412 return true; 413 } 414 if (0 == strcmp (permission_required, 415 perm)) 416 { 417 GNUNET_free (permissions); 418 return true; 419 } 420 perm = strtok (NULL, 421 ","); 422 } 423 } 424 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 425 "Permission check failed: %s not found in %s\n", 426 permission_required, 427 permissions); 428 GNUNET_free (permissions); 429 return false; 430 } 431 432 433 bool 434 TMH_scope_is_subset (enum TMH_AuthScope as, 435 enum TMH_AuthScope candidate) 436 { 437 const char *as_perms; 438 const char *candidate_perms; 439 char *permissions; 440 bool as_refreshable; 441 bool cand_refreshable; 442 443 as_perms = get_scope_permissions (as, 444 &as_refreshable); 445 candidate_perms = get_scope_permissions (candidate, 446 &cand_refreshable); 447 if (! as_refreshable && cand_refreshable) 448 return false; 449 if ( (NULL == as_perms) && 450 (NULL != candidate_perms) ) 451 return false; 452 if ( (NULL == candidate_perms) || 453 (0 == strcmp ("*", 454 as_perms))) 455 return true; 456 permissions = GNUNET_strdup (candidate_perms); 457 { 458 const char *perm; 459 460 perm = strtok (permissions, 461 ","); 462 if (NULL == perm) 463 { 464 GNUNET_free (permissions); 465 return true; 466 } 467 while (NULL != perm) 468 { 469 if (! permission_in_scope (perm, 470 as)) 471 { 472 GNUNET_free (permissions); 473 return false; 474 } 475 perm = strtok (NULL, 476 ","); 477 } 478 } 479 GNUNET_free (permissions); 480 return true; 481 } 482 483 484 enum TMH_AuthScope 485 TMH_get_scope_by_name (const char *name) 486 { 487 for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++) 488 { 489 if (0 == strcasecmp (scope_permissions[i].name, 490 name)) 491 return scope_permissions[i].as; 492 } 493 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 494 "Name `%s' does not match any scope we understand\n", 495 name); 496 return TMH_AS_NONE; 497 } 498 499 500 const char* 501 TMH_get_name_by_scope (enum TMH_AuthScope scope, 502 bool *refreshable) 503 { 504 *refreshable = scope & TMH_AS_REFRESHABLE; 505 for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++) 506 { 507 /* We ignore the TMH_AS_REFRESHABLE bit */ 508 if ( (scope & ~TMH_AS_REFRESHABLE) == 509 (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) ) 510 return scope_permissions[i].name; 511 } 512 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 513 "Scope #%d does not match any scope we understand\n", 514 (int) scope); 515 return NULL; 516 } 517 518 519 enum GNUNET_GenericReturnValue 520 TMH_check_auth (const char *password, 521 struct TALER_MerchantAuthenticationSaltP *salt, 522 struct TALER_MerchantAuthenticationHashP *hash) 523 { 524 struct TALER_MerchantAuthenticationHashP val; 525 526 if (GNUNET_is_zero (hash)) 527 return GNUNET_OK; 528 if (NULL == password) 529 { 530 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 531 "Denying access: empty password provided\n"); 532 return GNUNET_SYSERR; 533 } 534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 535 "Checking against token with salt %s\n", 536 TALER_B2S (salt)); 537 TALER_merchant_instance_auth_hash_with_salt (&val, 538 salt, 539 password); 540 if (0 != 541 GNUNET_memcmp (&val, 542 hash)) 543 { 544 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 545 "Access denied: password does not match\n"); 546 return GNUNET_SYSERR; 547 } 548 return GNUNET_OK; 549 } 550 551 552 /** 553 * Check if @a userpass grants access to @a instance. 554 * 555 * @param userpass base64 encoded "$USERNAME:$PASSWORD" value 556 * from HTTP Basic "Authentication" header 557 * @param instance the access controlled instance 558 */ 559 static enum GNUNET_GenericReturnValue 560 check_auth_instance (const char *userpass, 561 struct TMH_MerchantInstance *instance) 562 { 563 char *tmp; 564 char *colon; 565 const char *instance_name; 566 const char *password; 567 const char *target_instance = "admin"; 568 enum GNUNET_GenericReturnValue ret; 569 570 /* implicitly a zeroed out hash means no authentication */ 571 if (GNUNET_is_zero (&instance->auth.auth_hash)) 572 return GNUNET_OK; 573 if (NULL == userpass) 574 { 575 GNUNET_break_op (0); 576 return GNUNET_SYSERR; 577 } 578 if (0 == 579 GNUNET_STRINGS_base64_decode (userpass, 580 strlen (userpass), 581 (void**) &tmp)) 582 { 583 GNUNET_break_op (0); 584 return GNUNET_SYSERR; 585 } 586 colon = strchr (tmp, 587 ':'); 588 if (NULL == colon) 589 { 590 GNUNET_break_op (0); 591 GNUNET_free (tmp); 592 return GNUNET_SYSERR; 593 } 594 *colon = '\0'; 595 instance_name = tmp; 596 password = colon + 1; 597 /* instance->settings.id can be NULL if there is no instance yet */ 598 if (NULL != instance->settings.id) 599 target_instance = instance->settings.id; 600 if (0 != strcmp (instance_name, 601 target_instance)) 602 { 603 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 604 "Somebody tried to login to instance %s with username %s (login failed).\n", 605 target_instance, 606 instance_name); 607 GNUNET_free (tmp); 608 return GNUNET_SYSERR; 609 } 610 ret = TMH_check_auth (password, 611 &instance->auth.auth_salt, 612 &instance->auth.auth_hash); 613 GNUNET_free (tmp); 614 if (GNUNET_OK != ret) 615 { 616 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 617 "Password provided does not match credentials for %s\n", 618 target_instance); 619 } 620 return ret; 621 } 622 623 624 void 625 TMH_compute_auth (const char *token, 626 struct TALER_MerchantAuthenticationSaltP *salt, 627 struct TALER_MerchantAuthenticationHashP *hash) 628 { 629 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 630 salt, 631 sizeof (*salt)); 632 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 633 "Computing initial auth using token with salt %s\n", 634 TALER_B2S (salt)); 635 TALER_merchant_instance_auth_hash_with_salt (hash, 636 salt, 637 token); 638 } 639 640 641 void 642 TMH_wire_method_free (struct TMH_WireMethod *wm) 643 { 644 GNUNET_free (wm->payto_uri.full_payto); 645 GNUNET_free (wm->wire_method); 646 GNUNET_free (wm->credit_facade_url); 647 json_decref (wm->credit_facade_credentials); 648 GNUNET_free (wm); 649 } 650 651 652 void 653 TMH_instance_decref (struct TMH_MerchantInstance *mi) 654 { 655 struct TMH_WireMethod *wm; 656 657 mi->rc--; 658 if (0 != mi->rc) 659 return; 660 TMH_force_get_orders_resume (mi); 661 while (NULL != (wm = (mi->wm_head))) 662 { 663 GNUNET_CONTAINER_DLL_remove (mi->wm_head, 664 mi->wm_tail, 665 wm); 666 TMH_wire_method_free (wm); 667 } 668 669 GNUNET_free (mi->settings.id); 670 GNUNET_free (mi->settings.name); 671 GNUNET_free (mi->settings.email); 672 GNUNET_free (mi->settings.phone); 673 GNUNET_free (mi->settings.website); 674 GNUNET_free (mi->settings.logo); 675 json_decref (mi->settings.address); 676 json_decref (mi->settings.jurisdiction); 677 GNUNET_free (mi); 678 } 679 680 681 enum GNUNET_GenericReturnValue 682 TMH_instance_free_cb (void *cls, 683 const struct GNUNET_HashCode *key, 684 void *value) 685 { 686 struct TMH_MerchantInstance *mi = value; 687 688 (void) cls; 689 (void) key; 690 GNUNET_assert (GNUNET_OK == 691 GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map, 692 &mi->h_instance, 693 mi)); 694 TMH_instance_decref (mi); 695 return GNUNET_YES; 696 } 697 698 699 /** 700 * Shutdown task (invoked when the application is being 701 * terminated for any reason) 702 * 703 * @param cls NULL 704 */ 705 static void 706 do_shutdown (void *cls) 707 { 708 (void) cls; 709 TALER_MHD_daemons_halt (); 710 TMH_force_orders_resume (); 711 TMH_force_ac_resume (); 712 TMH_force_pc_resume (); 713 TMH_force_kyc_resume (); 714 TMH_force_gorc_resume (); 715 TMH_force_wallet_get_order_resume (); 716 TMH_force_wallet_refund_order_resume (); 717 TMH_challenge_done (); 718 TALER_MHD_daemons_destroy (); 719 if (NULL != instance_eh) 720 { 721 TMH_db->event_listen_cancel (instance_eh); 722 instance_eh = NULL; 723 } 724 TMH_EXCHANGES_done (); 725 if (NULL != TMH_db) 726 { 727 TALER_MERCHANTDB_plugin_unload (TMH_db); 728 TMH_db = NULL; 729 } 730 if (NULL != TMH_by_id_map) 731 { 732 GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map, 733 &TMH_instance_free_cb, 734 NULL); 735 GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map); 736 TMH_by_id_map = NULL; 737 } 738 TALER_TEMPLATING_done (); 739 if (NULL != TMH_curl_ctx) 740 { 741 GNUNET_CURL_fini (TMH_curl_ctx); 742 TMH_curl_ctx = NULL; 743 } 744 if (NULL != merchant_curl_rc) 745 { 746 GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc); 747 merchant_curl_rc = NULL; 748 } 749 if (NULL != TMH_payment_target_regex) 750 { 751 regfree (&TMH_payment_target_re); 752 GNUNET_free (TMH_payment_target_regex); 753 } 754 } 755 756 757 /** 758 * Function called whenever MHD is done with a request. If the 759 * request was a POST, we may have stored a `struct Buffer *` in the 760 * @a con_cls that might still need to be cleaned up. Call the 761 * respective function to free the memory. 762 * 763 * @param cls client-defined closure 764 * @param connection connection handle 765 * @param con_cls value as set by the last call to 766 * the #MHD_AccessHandlerCallback 767 * @param toe reason for request termination 768 * @see #MHD_OPTION_NOTIFY_COMPLETED 769 * @ingroup request 770 */ 771 static void 772 handle_mhd_completion_callback (void *cls, 773 struct MHD_Connection *connection, 774 void **con_cls, 775 enum MHD_RequestTerminationCode toe) 776 { 777 struct TMH_HandlerContext *hc = *con_cls; 778 779 (void) cls; 780 if (NULL == hc) 781 return; 782 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 783 { 784 #if MHD_VERSION >= 0x00097304 785 const union MHD_ConnectionInfo *ci; 786 unsigned int http_status = 0; 787 788 ci = MHD_get_connection_info (connection, 789 MHD_CONNECTION_INFO_HTTP_STATUS); 790 if (NULL != ci) 791 http_status = ci->http_status; 792 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 793 "Request for `%s' completed with HTTP status %u (%d)\n", 794 hc->url, 795 http_status, 796 toe); 797 #else 798 (void) connection; 799 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 800 "Finished handling request for `%s' with MHD termination code %d\n", 801 hc->url, 802 (int) toe); 803 #endif 804 } 805 if (NULL != hc->cc) 806 hc->cc (hc->ctx); 807 TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context); 808 GNUNET_free (hc->infix); 809 if (NULL != hc->request_body) 810 json_decref (hc->request_body); 811 if (NULL != hc->instance) 812 TMH_instance_decref (hc->instance); 813 TMH_db->preflight (TMH_db->cls); 814 GNUNET_free (hc->full_url); 815 GNUNET_free (hc); 816 *con_cls = NULL; 817 } 818 819 820 struct TMH_MerchantInstance * 821 TMH_lookup_instance (const char *instance_id) 822 { 823 struct GNUNET_HashCode h_instance; 824 825 if (NULL == instance_id) 826 instance_id = "admin"; 827 GNUNET_CRYPTO_hash (instance_id, 828 strlen (instance_id), 829 &h_instance); 830 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 831 "Looking for by-id key %s of '%s' in hashmap\n", 832 GNUNET_h2s (&h_instance), 833 instance_id); 834 /* We're fine if that returns NULL, the calling routine knows how 835 to handle that */ 836 return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map, 837 &h_instance); 838 } 839 840 841 /** 842 * Add instance definition to our active set of instances. 843 * 844 * @param[in,out] mi merchant instance details to define 845 * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already 846 */ 847 enum GNUNET_GenericReturnValue 848 TMH_add_instance (struct TMH_MerchantInstance *mi) 849 { 850 const char *id; 851 int ret; 852 853 id = mi->settings.id; 854 if (NULL == id) 855 id = "admin"; 856 GNUNET_CRYPTO_hash (id, 857 strlen (id), 858 &mi->h_instance); 859 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 860 "Looking for by-id key %s of `%s' in hashmap\n", 861 GNUNET_h2s (&mi->h_instance), 862 id); 863 ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map, 864 &mi->h_instance, 865 mi, 866 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); 867 if (GNUNET_OK == ret) 868 { 869 GNUNET_assert (mi->rc < UINT_MAX); 870 mi->rc++; 871 } 872 return ret; 873 } 874 875 876 /** 877 * Handle a OPTIONS "*" request. 878 * 879 * @param rh context of the handler 880 * @param connection the MHD connection to handle 881 * @param[in,out] hc context with further information about the request 882 * @return MHD result code 883 */ 884 static MHD_RESULT 885 handle_server_options (const struct TMH_RequestHandler *rh, 886 struct MHD_Connection *connection, 887 struct TMH_HandlerContext *hc) 888 { 889 (void) rh; 890 (void) hc; 891 return TALER_MHD_reply_cors_preflight (connection); 892 } 893 894 895 /** 896 * Generates the response for "/", redirecting the 897 * client to the "/webui/" from where we serve the SPA. 898 * 899 * @param rh request handler 900 * @param connection MHD connection 901 * @param hc handler context 902 * @return MHD result code 903 */ 904 static MHD_RESULT 905 spa_redirect (const struct TMH_RequestHandler *rh, 906 struct MHD_Connection *connection, 907 struct TMH_HandlerContext *hc) 908 { 909 const char *text = "Redirecting to /webui/"; 910 struct MHD_Response *response; 911 char *dst; 912 913 response = MHD_create_response_from_buffer (strlen (text), 914 (void *) text, 915 MHD_RESPMEM_PERSISTENT); 916 if (NULL == response) 917 { 918 GNUNET_break (0); 919 return MHD_NO; 920 } 921 TALER_MHD_add_global_headers (response, 922 true); 923 GNUNET_break (MHD_YES == 924 MHD_add_response_header (response, 925 MHD_HTTP_HEADER_CONTENT_TYPE, 926 "text/plain")); 927 if ( (NULL == hc->instance) || 928 (0 == strcmp ("admin", 929 hc->instance->settings.id)) ) 930 dst = GNUNET_strdup ("/webui/"); 931 else 932 GNUNET_asprintf (&dst, 933 "/instances/%s/webui/", 934 hc->instance->settings.id); 935 if (MHD_NO == 936 MHD_add_response_header (response, 937 MHD_HTTP_HEADER_LOCATION, 938 dst)) 939 { 940 GNUNET_break (0); 941 MHD_destroy_response (response); 942 GNUNET_free (dst); 943 return MHD_NO; 944 } 945 GNUNET_free (dst); 946 947 { 948 MHD_RESULT ret; 949 950 ret = MHD_queue_response (connection, 951 MHD_HTTP_FOUND, 952 response); 953 MHD_destroy_response (response); 954 return ret; 955 } 956 } 957 958 959 /** 960 * Extract the token from authorization header value @a auth. 961 * The @a auth value can be a bearer token or a Basic 962 * authentication header. In both cases, this function 963 * updates @a auth to point to the actual credential, 964 * skipping spaces. 965 * 966 * NOTE: We probably want to replace this function with MHD2 967 * API calls in the future that are more robust. 968 * 969 * @param[in,out] auth pointer to authorization header value, 970 * will be updated to point to the start of the token 971 * or set to NULL if header value is invalid 972 * @param[out] is_basic_auth will be set to true if the 973 * authorization header uses basic authentication, 974 * otherwise to false 975 */ 976 static void 977 extract_auth (const char **auth, 978 bool *is_basic_auth) 979 { 980 const char *bearer = "Bearer "; 981 const char *basic = "Basic "; 982 const char *tok = *auth; 983 size_t offset = 0; 984 bool is_bearer = false; 985 986 *is_basic_auth = false; 987 if (0 == strncmp (tok, 988 bearer, 989 strlen (bearer))) 990 { 991 offset = strlen (bearer); 992 is_bearer = true; 993 } 994 else if (0 == strncmp (tok, 995 basic, 996 strlen (basic))) 997 { 998 offset = strlen (basic); 999 *is_basic_auth = true; 1000 } 1001 else 1002 { 1003 *auth = NULL; 1004 return; 1005 } 1006 tok += offset; 1007 while (' ' == *tok) 1008 tok++; 1009 if ( (is_bearer) && 1010 (0 != strncasecmp (tok, 1011 RFC_8959_PREFIX, 1012 strlen (RFC_8959_PREFIX))) ) 1013 { 1014 *auth = NULL; 1015 return; 1016 } 1017 *auth = tok; 1018 } 1019 1020 1021 /** 1022 * Checks if the @a rh matches the given (parsed) URL. 1023 * 1024 * @param rh handler to compare against 1025 * @param url the main URL (without "/private/" prefix, if any) 1026 * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config' 1027 * @param infix_url infix text, i.e. "$ORDER_ID". 1028 * @param infix_strlen length of the string in @a infix_url 1029 * @param suffix_url suffix, i.e. "/refund", including the "/" 1030 * @param suffix_strlen number of characters in @a suffix_url 1031 * @return true if @a rh matches this request 1032 */ 1033 static bool 1034 prefix_match (const struct TMH_RequestHandler *rh, 1035 const char *url, 1036 size_t prefix_strlen, 1037 const char *infix_url, 1038 size_t infix_strlen, 1039 const char *suffix_url, 1040 size_t suffix_strlen) 1041 { 1042 if ( (prefix_strlen != strlen (rh->url_prefix)) || 1043 (0 != memcmp (url, 1044 rh->url_prefix, 1045 prefix_strlen)) ) 1046 return false; 1047 if (! rh->have_id_segment) 1048 { 1049 /* Require /$PREFIX/$SUFFIX or /$PREFIX */ 1050 if (NULL != suffix_url) 1051 return false; /* too many segments to match */ 1052 if ( (NULL == infix_url) /* either or */ 1053 ^ (NULL == rh->url_suffix) ) 1054 return false; /* suffix existence mismatch */ 1055 /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */ 1056 if ( (NULL != infix_url) && 1057 ( (infix_strlen != strlen (rh->url_suffix)) || 1058 (0 != memcmp (infix_url, 1059 rh->url_suffix, 1060 infix_strlen)) ) ) 1061 return false; /* cannot use infix as suffix: content mismatch */ 1062 } 1063 else 1064 { 1065 /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */ 1066 if (NULL == infix_url) 1067 return false; /* infix existence mismatch */ 1068 if ( ( (NULL == suffix_url) 1069 ^ (NULL == rh->url_suffix) ) ) 1070 return false; /* suffix existence mismatch */ 1071 if ( (NULL != suffix_url) && 1072 ( (suffix_strlen != strlen (rh->url_suffix)) || 1073 (0 != memcmp (suffix_url, 1074 rh->url_suffix, 1075 suffix_strlen)) ) ) 1076 return false; /* suffix content mismatch */ 1077 } 1078 return true; 1079 } 1080 1081 1082 /** 1083 * Function called first by MHD with the full URL. 1084 * 1085 * @param cls NULL 1086 * @param full_url the full URL 1087 * @param con MHD connection object 1088 * @return our handler context 1089 */ 1090 static void * 1091 full_url_track_callback (void *cls, 1092 const char *full_url, 1093 struct MHD_Connection *con) 1094 { 1095 struct TMH_HandlerContext *hc; 1096 1097 hc = GNUNET_new (struct TMH_HandlerContext); 1098 hc->connection = con; 1099 GNUNET_async_scope_fresh (&hc->async_scope_id); 1100 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 1101 hc->full_url = GNUNET_strdup (full_url); 1102 return hc; 1103 } 1104 1105 1106 /** 1107 * Function used to process Basic authorization header value. 1108 * Sets correct scope in the auth_scope parameter of the 1109 * #TMH_HandlerContext. 1110 * 1111 * @param hc the handler context 1112 * @param authn_s the value of the authorization header 1113 */ 1114 static void 1115 process_basic_auth (struct TMH_HandlerContext *hc, 1116 const char *authn_s) 1117 { 1118 /* Handle token endpoint slightly differently: Only allow 1119 * instance password (Basic auth) to retrieve access token. 1120 * We need to handle authorization with Basic auth here first 1121 * The only time we need to handle authentication like this is 1122 * for the token endpoint! 1123 */ 1124 if ( (0 != strncmp (hc->rh->url_prefix, 1125 "/token", 1126 strlen ("/token"))) || 1127 (0 != strncmp (MHD_HTTP_METHOD_POST, 1128 hc->rh->method, 1129 strlen (MHD_HTTP_METHOD_POST))) || 1130 (NULL == hc->instance)) 1131 { 1132 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1133 "Called endpoint `%s' with Basic authentication. Rejecting...\n", 1134 hc->rh->url_prefix); 1135 hc->auth_scope = TMH_AS_NONE; 1136 return; 1137 } 1138 if (GNUNET_OK == 1139 check_auth_instance (authn_s, 1140 hc->instance)) 1141 { 1142 hc->auth_scope = TMH_AS_ALL; 1143 } 1144 else 1145 { 1146 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1147 "Basic authentication failed!\n"); 1148 hc->auth_scope = TMH_AS_NONE; 1149 } 1150 } 1151 1152 1153 /** 1154 * Function used to process Bearer authorization header value. 1155 * Sets correct scope in the auth_scope parameter of the 1156 * #TMH_HandlerContext.. 1157 * 1158 * @param hc the handler context 1159 * @param authn_s the value of the authorization header 1160 * @return TALER_EC_NONE on success. 1161 */ 1162 static enum TALER_ErrorCode 1163 process_bearer_auth (struct TMH_HandlerContext *hc, 1164 const char *authn_s) 1165 { 1166 if (NULL == hc->instance) 1167 { 1168 hc->auth_scope = TMH_AS_NONE; 1169 return TALER_EC_NONE; 1170 } 1171 if (GNUNET_is_zero (&hc->instance->auth.auth_hash)) 1172 { 1173 /* hash zero means no authentication for instance */ 1174 hc->auth_scope = TMH_AS_ALL; 1175 return TALER_EC_NONE; 1176 } 1177 { 1178 enum TALER_ErrorCode ec; 1179 1180 ec = TMH_check_token (authn_s, 1181 hc->instance->settings.id, 1182 &hc->auth_scope); 1183 if (TALER_EC_NONE != ec) 1184 { 1185 char *dec; 1186 size_t dec_len; 1187 const char *token; 1188 1189 /* NOTE: Deprecated, remove sometime after v1.1 */ 1190 if (0 != strncasecmp (authn_s, 1191 RFC_8959_PREFIX, 1192 strlen (RFC_8959_PREFIX))) 1193 { 1194 GNUNET_break_op (0); 1195 hc->auth_scope = TMH_AS_NONE; 1196 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1197 "Authentication token invalid: %d\n", 1198 (int) ec); 1199 return ec; 1200 } 1201 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1202 "Trying deprecated secret-token:password API authN\n"); 1203 token = authn_s + strlen (RFC_8959_PREFIX); 1204 dec_len = GNUNET_STRINGS_urldecode (token, 1205 strlen (token), 1206 &dec); 1207 if ( (0 == dec_len) || 1208 (GNUNET_OK != 1209 TMH_check_auth (dec, 1210 &hc->instance->auth.auth_salt, 1211 &hc->instance->auth.auth_hash)) ) 1212 { 1213 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1214 "Login failed\n"); 1215 hc->auth_scope = TMH_AS_NONE; 1216 GNUNET_free (dec); 1217 return TALER_EC_NONE; 1218 } 1219 hc->auth_scope = TMH_AS_ALL; 1220 GNUNET_free (dec); 1221 } 1222 } 1223 return TALER_EC_NONE; 1224 } 1225 1226 1227 /** 1228 * A client has requested the given url using the given method 1229 * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, 1230 * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback 1231 * must call MHD callbacks to provide content to give back to the 1232 * client and return an HTTP status code (i.e. #MHD_HTTP_OK, 1233 * #MHD_HTTP_NOT_FOUND, etc.). 1234 * 1235 * @param cls argument given together with the function 1236 * pointer when the handler was registered with MHD 1237 * @param connection the MHD connection to handle 1238 * @param url the requested url 1239 * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, 1240 * #MHD_HTTP_METHOD_PUT, etc.) 1241 * @param version the HTTP version string (i.e. 1242 * #MHD_HTTP_VERSION_1_1) 1243 * @param upload_data the data being uploaded (excluding HEADERS, 1244 * for a POST that fits into memory and that is encoded 1245 * with a supported encoding, the POST data will NOT be 1246 * given in upload_data and is instead available as 1247 * part of #MHD_get_connection_values; very large POST 1248 * data *will* be made available incrementally in 1249 * @a upload_data) 1250 * @param upload_data_size set initially to the size of the 1251 * @a upload_data provided; the method must update this 1252 * value to the number of bytes NOT processed; 1253 * @param con_cls pointer that the callback can set to some 1254 * address and that will be preserved by MHD for future 1255 * calls for this request; since the access handler may 1256 * be called many times (i.e., for a PUT/POST operation 1257 * with plenty of upload data) this allows the application 1258 * to easily associate some request-specific state. 1259 * If necessary, this state can be cleaned up in the 1260 * global #MHD_RequestCompletedCallback (which 1261 * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). 1262 * Initially, `*con_cls` will be set up by the 1263 * full_url_track_callback(). 1264 * @return #MHD_YES if the connection was handled successfully, 1265 * #MHD_NO if the socket must be closed due to a serious 1266 * error while handling the request 1267 */ 1268 static MHD_RESULT 1269 url_handler (void *cls, 1270 struct MHD_Connection *connection, 1271 const char *url, 1272 const char *method, 1273 const char *version, 1274 const char *upload_data, 1275 size_t *upload_data_size, 1276 void **con_cls) 1277 { 1278 static struct TMH_RequestHandler management_handlers[] = { 1279 /* GET /instances */ 1280 { 1281 .url_prefix = "/instances", 1282 .method = MHD_HTTP_METHOD_GET, 1283 .permission = "instances-write", 1284 .skip_instance = true, 1285 .default_only = true, 1286 .handler = &TMH_private_get_instances 1287 }, 1288 /* POST /instances */ 1289 { 1290 .url_prefix = "/instances", 1291 .method = MHD_HTTP_METHOD_POST, 1292 .permission = "instances-write", 1293 .skip_instance = true, 1294 .default_only = true, 1295 .handler = &TMH_private_post_instances, 1296 /* allow instance data of up to 8 MB, that should be plenty; 1297 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1298 would require further changes to the allocation logic 1299 in the code... */ 1300 .max_upload = 1024 * 1024 * 8 1301 }, 1302 /* GET /instances/$ID/ */ 1303 { 1304 .url_prefix = "/instances/", 1305 .method = MHD_HTTP_METHOD_GET, 1306 .permission = "instances-write", 1307 .skip_instance = true, 1308 .default_only = true, 1309 .have_id_segment = true, 1310 .handler = &TMH_private_get_instances_default_ID 1311 }, 1312 /* DELETE /instances/$ID */ 1313 { 1314 .url_prefix = "/instances/", 1315 .method = MHD_HTTP_METHOD_DELETE, 1316 .permission = "instances-write", 1317 .skip_instance = true, 1318 .default_only = true, 1319 .have_id_segment = true, 1320 .handler = &TMH_private_delete_instances_default_ID 1321 }, 1322 /* PATCH /instances/$ID */ 1323 { 1324 .url_prefix = "/instances/", 1325 .method = MHD_HTTP_METHOD_PATCH, 1326 .permission = "instances-write", 1327 .skip_instance = true, 1328 .default_only = true, 1329 .have_id_segment = true, 1330 .handler = &TMH_private_patch_instances_default_ID, 1331 /* allow instance data of up to 8 MB, that should be plenty; 1332 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1333 would require further changes to the allocation logic 1334 in the code... */ 1335 .max_upload = 1024 * 1024 * 8 1336 }, 1337 /* POST /auth: */ 1338 { 1339 .url_prefix = "/instances/", 1340 .url_suffix = "auth", 1341 .method = MHD_HTTP_METHOD_POST, 1342 .permission = "instances-auth-write", 1343 .skip_instance = true, 1344 .default_only = true, 1345 .have_id_segment = true, 1346 .handler = &TMH_private_post_instances_default_ID_auth, 1347 /* Body should be pretty small. */ 1348 .max_upload = 1024 * 1024 1349 }, 1350 /* GET /kyc: */ 1351 { 1352 .url_prefix = "/instances/", 1353 .url_suffix = "kyc", 1354 .method = MHD_HTTP_METHOD_GET, 1355 .permission = "instances-kyc-read", 1356 .skip_instance = true, 1357 .default_only = true, 1358 .have_id_segment = true, 1359 .handler = &TMH_private_get_instances_default_ID_kyc, 1360 }, 1361 { 1362 .url_prefix = NULL 1363 } 1364 }; 1365 1366 static struct TMH_RequestHandler private_handlers[] = { 1367 /* GET /instances/$ID/: */ 1368 { 1369 .url_prefix = "/", 1370 .method = MHD_HTTP_METHOD_GET, 1371 .permission = "instances-read", 1372 .handler = &TMH_private_get_instances_ID 1373 }, 1374 /* DELETE /instances/$ID/: */ 1375 { 1376 .url_prefix = "/", 1377 .method = MHD_HTTP_METHOD_DELETE, 1378 .permission = "instances-write", 1379 .allow_deleted_instance = true, 1380 .handler = &TMH_private_delete_instances_ID 1381 }, 1382 /* PATCH /instances/$ID/: */ 1383 { 1384 .url_prefix = "/", 1385 .method = MHD_HTTP_METHOD_PATCH, 1386 .handler = &TMH_private_patch_instances_ID, 1387 .permission = "instances-write", 1388 .allow_deleted_instance = true, 1389 /* allow instance data of up to 8 MB, that should be plenty; 1390 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1391 would require further changes to the allocation logic 1392 in the code... */ 1393 .max_upload = 1024 * 1024 * 8 1394 }, 1395 /* POST /auth: */ 1396 { 1397 .url_prefix = "/auth", 1398 .method = MHD_HTTP_METHOD_POST, 1399 .handler = &TMH_private_post_instances_ID_auth, 1400 .permission = "auth-write", 1401 /* Body should be pretty small. */ 1402 .max_upload = 1024 * 1024, 1403 }, 1404 /* GET /kyc: */ 1405 { 1406 .url_prefix = "/kyc", 1407 .method = MHD_HTTP_METHOD_GET, 1408 .permission = "kyc-read", 1409 .handler = &TMH_private_get_instances_ID_kyc, 1410 }, 1411 /* GET /pos: */ 1412 { 1413 .url_prefix = "/pos", 1414 .method = MHD_HTTP_METHOD_GET, 1415 .permission = "pos-read", 1416 .handler = &TMH_private_get_pos 1417 }, 1418 /* GET /categories: */ 1419 { 1420 .url_prefix = "/categories", 1421 .method = MHD_HTTP_METHOD_GET, 1422 .permission = "categories-read", 1423 .handler = &TMH_private_get_categories 1424 }, 1425 /* POST /categories: */ 1426 { 1427 .url_prefix = "/categories", 1428 .method = MHD_HTTP_METHOD_POST, 1429 .permission = "categories-write", 1430 .handler = &TMH_private_post_categories, 1431 /* allow category data of up to 8 kb, that should be plenty */ 1432 .max_upload = 1024 * 8 1433 }, 1434 /* GET /categories/$ID: */ 1435 { 1436 .url_prefix = "/categories/", 1437 .method = MHD_HTTP_METHOD_GET, 1438 .permission = "categories-read", 1439 .have_id_segment = true, 1440 .allow_deleted_instance = true, 1441 .handler = &TMH_private_get_categories_ID 1442 }, 1443 /* DELETE /categories/$ID: */ 1444 { 1445 .url_prefix = "/categories/", 1446 .method = MHD_HTTP_METHOD_DELETE, 1447 .permission = "categories-write", 1448 .have_id_segment = true, 1449 .allow_deleted_instance = true, 1450 .handler = &TMH_private_delete_categories_ID 1451 }, 1452 /* PATCH /categories/$ID/: */ 1453 { 1454 .url_prefix = "/categories/", 1455 .method = MHD_HTTP_METHOD_PATCH, 1456 .permission = "categories-write", 1457 .have_id_segment = true, 1458 .allow_deleted_instance = true, 1459 .handler = &TMH_private_patch_categories_ID, 1460 /* allow category data of up to 8 kb, that should be plenty */ 1461 .max_upload = 1024 * 8 1462 }, 1463 /* GET /units: */ 1464 { 1465 .url_prefix = "/units", 1466 .method = MHD_HTTP_METHOD_GET, 1467 .handler = &TMH_private_get_units 1468 }, 1469 /* POST /units: */ 1470 { 1471 .url_prefix = "/units", 1472 .method = MHD_HTTP_METHOD_POST, 1473 .permission = "units-write", 1474 .handler = &TMH_private_post_units, 1475 .max_upload = 1024 * 8 1476 }, 1477 /* GET /units/$UNIT: */ 1478 { 1479 .url_prefix = "/units/", 1480 .method = MHD_HTTP_METHOD_GET, 1481 .have_id_segment = true, 1482 .allow_deleted_instance = true, 1483 .handler = &TMH_private_get_units_ID 1484 }, 1485 /* DELETE /units/$UNIT: */ 1486 { 1487 .url_prefix = "/units/", 1488 .method = MHD_HTTP_METHOD_DELETE, 1489 .permission = "units-write", 1490 .have_id_segment = true, 1491 .allow_deleted_instance = true, 1492 .handler = &TMH_private_delete_units_ID 1493 }, 1494 /* PATCH /units/$UNIT: */ 1495 { 1496 .url_prefix = "/units/", 1497 .method = MHD_HTTP_METHOD_PATCH, 1498 .permission = "units-write", 1499 .have_id_segment = true, 1500 .allow_deleted_instance = true, 1501 .handler = &TMH_private_patch_units_ID, 1502 .max_upload = 1024 * 8 1503 }, 1504 /* GET /products: */ 1505 { 1506 .url_prefix = "/products", 1507 .permission = "products-read", 1508 .method = MHD_HTTP_METHOD_GET, 1509 .handler = &TMH_private_get_products 1510 }, 1511 /* POST /products: */ 1512 { 1513 .url_prefix = "/products", 1514 .method = MHD_HTTP_METHOD_POST, 1515 .permission = "products-write", 1516 .handler = &TMH_private_post_products, 1517 /* allow product data of up to 8 MB, that should be plenty; 1518 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1519 would require further changes to the allocation logic 1520 in the code... */ 1521 .max_upload = 1024 * 1024 * 8 1522 }, 1523 /* GET /products/$ID: */ 1524 { 1525 .url_prefix = "/products/", 1526 .method = MHD_HTTP_METHOD_GET, 1527 .have_id_segment = true, 1528 .permission = "products-read", 1529 .allow_deleted_instance = true, 1530 .handler = &TMH_private_get_products_ID 1531 }, 1532 /* DELETE /products/$ID/: */ 1533 { 1534 .url_prefix = "/products/", 1535 .method = MHD_HTTP_METHOD_DELETE, 1536 .have_id_segment = true, 1537 .permission = "products-write", 1538 .allow_deleted_instance = true, 1539 .handler = &TMH_private_delete_products_ID 1540 }, 1541 /* PATCH /products/$ID/: */ 1542 { 1543 .url_prefix = "/products/", 1544 .method = MHD_HTTP_METHOD_PATCH, 1545 .have_id_segment = true, 1546 .allow_deleted_instance = true, 1547 .permission = "products-write", 1548 .handler = &TMH_private_patch_products_ID, 1549 /* allow product data of up to 8 MB, that should be plenty; 1550 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1551 would require further changes to the allocation logic 1552 in the code... */ 1553 .max_upload = 1024 * 1024 * 8 1554 }, 1555 /* POST /products/$ID/lock: */ 1556 { 1557 .url_prefix = "/products/", 1558 .url_suffix = "lock", 1559 .method = MHD_HTTP_METHOD_POST, 1560 .have_id_segment = true, 1561 .permission = "products-lock", 1562 .handler = &TMH_private_post_products_ID_lock, 1563 /* the body should be pretty small, allow 1 MB of upload 1564 to set a conservative bound for sane wallets */ 1565 .max_upload = 1024 * 1024 1566 }, 1567 /* POST /orders: */ 1568 { 1569 .url_prefix = "/orders", 1570 .method = MHD_HTTP_METHOD_POST, 1571 .permission = "orders-write", 1572 .handler = &TMH_private_post_orders, 1573 /* allow contracts of up to 8 MB, that should be plenty; 1574 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1575 would require further changes to the allocation logic 1576 in the code... */ 1577 .max_upload = 1024 * 1024 * 8 1578 }, 1579 /* GET /orders/$ID: */ 1580 { 1581 .url_prefix = "/orders/", 1582 .method = MHD_HTTP_METHOD_GET, 1583 .permission = "orders-read", 1584 .have_id_segment = true, 1585 .allow_deleted_instance = true, 1586 .handler = &TMH_private_get_orders_ID 1587 }, 1588 /* GET /orders: */ 1589 { 1590 .url_prefix = "/orders", 1591 .method = MHD_HTTP_METHOD_GET, 1592 .permission = "orders-read", 1593 .allow_deleted_instance = true, 1594 .handler = &TMH_private_get_orders 1595 }, 1596 /* POST /orders/$ID/refund: */ 1597 { 1598 .url_prefix = "/orders/", 1599 .url_suffix = "refund", 1600 .method = MHD_HTTP_METHOD_POST, 1601 .have_id_segment = true, 1602 .permission = "orders-refund", 1603 .handler = &TMH_private_post_orders_ID_refund, 1604 /* the body should be pretty small, allow 1 MB of upload 1605 to set a conservative bound for sane wallets */ 1606 .max_upload = 1024 * 1024 1607 }, 1608 /* PATCH /orders/$ID/forget: */ 1609 { 1610 .url_prefix = "/orders/", 1611 .url_suffix = "forget", 1612 .method = MHD_HTTP_METHOD_PATCH, 1613 .permission = "orders-write", 1614 .have_id_segment = true, 1615 .allow_deleted_instance = true, 1616 .handler = &TMH_private_patch_orders_ID_forget, 1617 /* the body should be pretty small, allow 1 MB of upload 1618 to set a conservative bound for sane wallets */ 1619 .max_upload = 1024 * 1024 1620 }, 1621 /* DELETE /orders/$ID: */ 1622 { 1623 .url_prefix = "/orders/", 1624 .method = MHD_HTTP_METHOD_DELETE, 1625 .permission = "orders-write", 1626 .have_id_segment = true, 1627 .allow_deleted_instance = true, 1628 .handler = &TMH_private_delete_orders_ID 1629 }, 1630 /* POST /transfers: */ 1631 { 1632 .url_prefix = "/transfers", 1633 .method = MHD_HTTP_METHOD_POST, 1634 .allow_deleted_instance = true, 1635 .handler = &TMH_private_post_transfers, 1636 .permission = "transfers-write", 1637 /* the body should be pretty small, allow 1 MB of upload 1638 to set a conservative bound for sane wallets */ 1639 .max_upload = 1024 * 1024 1640 }, 1641 /* DELETE /transfers/$ID: */ 1642 { 1643 .url_prefix = "/transfers/", 1644 .method = MHD_HTTP_METHOD_DELETE, 1645 .permission = "transfers-write", 1646 .allow_deleted_instance = true, 1647 .handler = &TMH_private_delete_transfers_ID, 1648 .have_id_segment = true, 1649 /* the body should be pretty small, allow 1 MB of upload 1650 to set a conservative bound for sane wallets */ 1651 .max_upload = 1024 * 1024 1652 }, 1653 /* GET /transfers: */ 1654 { 1655 .url_prefix = "/transfers", 1656 .permission = "transfers-read", 1657 .method = MHD_HTTP_METHOD_GET, 1658 .allow_deleted_instance = true, 1659 .handler = &TMH_private_get_transfers 1660 }, 1661 /* GET /incoming: */ 1662 { 1663 .url_prefix = "/incoming", 1664 .permission = "transfers-read", 1665 .method = MHD_HTTP_METHOD_GET, 1666 .allow_deleted_instance = true, 1667 .handler = &TMH_private_get_incoming 1668 }, 1669 /* POST /otp-devices: */ 1670 { 1671 .url_prefix = "/otp-devices", 1672 .permission = "otp-devices-write", 1673 .method = MHD_HTTP_METHOD_POST, 1674 .handler = &TMH_private_post_otp_devices 1675 }, 1676 /* GET /otp-devices: */ 1677 { 1678 .url_prefix = "/otp-devices", 1679 .permission = "opt-devices-read", 1680 .method = MHD_HTTP_METHOD_GET, 1681 .handler = &TMH_private_get_otp_devices 1682 }, 1683 /* GET /otp-devices/$ID/: */ 1684 { 1685 .url_prefix = "/otp-devices/", 1686 .method = MHD_HTTP_METHOD_GET, 1687 .permission = "otp-devices-read", 1688 .have_id_segment = true, 1689 .handler = &TMH_private_get_otp_devices_ID 1690 }, 1691 /* DELETE /otp-devices/$ID/: */ 1692 { 1693 .url_prefix = "/otp-devices/", 1694 .method = MHD_HTTP_METHOD_DELETE, 1695 .permission = "otp-devices-write", 1696 .have_id_segment = true, 1697 .handler = &TMH_private_delete_otp_devices_ID 1698 }, 1699 /* PATCH /otp-devices/$ID/: */ 1700 { 1701 .url_prefix = "/otp-devices/", 1702 .method = MHD_HTTP_METHOD_PATCH, 1703 .permission = "otp-devices-write", 1704 .have_id_segment = true, 1705 .handler = &TMH_private_patch_otp_devices_ID 1706 }, 1707 /* POST /templates: */ 1708 { 1709 .url_prefix = "/templates", 1710 .method = MHD_HTTP_METHOD_POST, 1711 .permission = "templates-write", 1712 .handler = &TMH_private_post_templates, 1713 /* allow template data of up to 8 MB, that should be plenty; 1714 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1715 would require further changes to the allocation logic 1716 in the code... */ 1717 .max_upload = 1024 * 1024 * 8 1718 }, 1719 /* GET /templates: */ 1720 { 1721 .url_prefix = "/templates", 1722 .permission = "templates-read", 1723 .method = MHD_HTTP_METHOD_GET, 1724 .handler = &TMH_private_get_templates 1725 }, 1726 /* GET /templates/$ID/: */ 1727 { 1728 .url_prefix = "/templates/", 1729 .method = MHD_HTTP_METHOD_GET, 1730 .permission = "templates-read", 1731 .have_id_segment = true, 1732 .allow_deleted_instance = true, 1733 .handler = &TMH_private_get_templates_ID 1734 }, 1735 /* DELETE /templates/$ID/: */ 1736 { 1737 .url_prefix = "/templates/", 1738 .method = MHD_HTTP_METHOD_DELETE, 1739 .permission = "templates-write", 1740 .have_id_segment = true, 1741 .allow_deleted_instance = true, 1742 .handler = &TMH_private_delete_templates_ID 1743 }, 1744 /* PATCH /templates/$ID/: */ 1745 { 1746 .url_prefix = "/templates/", 1747 .method = MHD_HTTP_METHOD_PATCH, 1748 .permission = "templates-write", 1749 .have_id_segment = true, 1750 .allow_deleted_instance = true, 1751 .handler = &TMH_private_patch_templates_ID, 1752 /* allow template data of up to 8 MB, that should be plenty; 1753 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1754 would require further changes to the allocation logic 1755 in the code... */ 1756 .max_upload = 1024 * 1024 * 8 1757 }, 1758 /* GET /webhooks: */ 1759 { 1760 .url_prefix = "/webhooks", 1761 .permission = "webhooks-read", 1762 .method = MHD_HTTP_METHOD_GET, 1763 .handler = &TMH_private_get_webhooks 1764 }, 1765 /* POST /webhooks: */ 1766 { 1767 .url_prefix = "/webhooks", 1768 .method = MHD_HTTP_METHOD_POST, 1769 .permission = "webhooks-write", 1770 .handler = &TMH_private_post_webhooks, 1771 /* allow webhook data of up to 8 MB, that should be plenty; 1772 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1773 would require further changes to the allocation logic 1774 in the code... */ 1775 .max_upload = 1024 * 1024 * 8 1776 }, 1777 /* GET /webhooks/$ID/: */ 1778 { 1779 .url_prefix = "/webhooks/", 1780 .method = MHD_HTTP_METHOD_GET, 1781 .permission = "webhooks-read", 1782 .have_id_segment = true, 1783 .allow_deleted_instance = true, 1784 .handler = &TMH_private_get_webhooks_ID 1785 }, 1786 /* DELETE /webhooks/$ID/: */ 1787 { 1788 .url_prefix = "/webhooks/", 1789 .permission = "webhooks-write", 1790 .method = MHD_HTTP_METHOD_DELETE, 1791 .have_id_segment = true, 1792 .allow_deleted_instance = true, 1793 .handler = &TMH_private_delete_webhooks_ID 1794 }, 1795 /* PATCH /webhooks/$ID/: */ 1796 { 1797 .url_prefix = "/webhooks/", 1798 .method = MHD_HTTP_METHOD_PATCH, 1799 .permission = "webhooks-write", 1800 .have_id_segment = true, 1801 .allow_deleted_instance = true, 1802 .handler = &TMH_private_patch_webhooks_ID, 1803 /* allow webhook data of up to 8 MB, that should be plenty; 1804 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 1805 would require further changes to the allocation logic 1806 in the code... */ 1807 .max_upload = 1024 * 1024 * 8 1808 }, 1809 /* POST /accounts: */ 1810 { 1811 .url_prefix = "/accounts", 1812 .method = MHD_HTTP_METHOD_POST, 1813 .permission = "accounts-write", 1814 .handler = &TMH_private_post_account, 1815 /* allow account details of up to 8 kb, that should be plenty */ 1816 .max_upload = 1024 * 8 1817 }, 1818 /* PATCH /accounts/$H_WIRE: */ 1819 { 1820 .url_prefix = "/accounts/", 1821 .method = MHD_HTTP_METHOD_PATCH, 1822 .permission = "accounts-write", 1823 .handler = &TMH_private_patch_accounts_ID, 1824 .have_id_segment = true, 1825 /* allow account details of up to 8 kb, that should be plenty */ 1826 .max_upload = 1024 * 8 1827 }, 1828 /* GET /accounts: */ 1829 { 1830 .url_prefix = "/accounts", 1831 .permission = "accounts-read", 1832 .method = MHD_HTTP_METHOD_GET, 1833 .handler = &TMH_private_get_accounts 1834 }, 1835 /* GET /accounts/$H_WIRE: */ 1836 { 1837 .url_prefix = "/accounts/", 1838 .permission = "accounts-read", 1839 .method = MHD_HTTP_METHOD_GET, 1840 .have_id_segment = true, 1841 .handler = &TMH_private_get_accounts_ID 1842 }, 1843 /* DELETE /accounts/$H_WIRE: */ 1844 { 1845 .url_prefix = "/accounts/", 1846 .permission = "accounts-write", 1847 .method = MHD_HTTP_METHOD_DELETE, 1848 .handler = &TMH_private_delete_account_ID, 1849 .have_id_segment = true 1850 }, 1851 /* GET /tokens: */ 1852 { 1853 .url_prefix = "/tokens", 1854 .permission = "tokens-read", 1855 .method = MHD_HTTP_METHOD_GET, 1856 .handler = &TMH_private_get_instances_ID_tokens, 1857 }, 1858 /* POST /token: */ 1859 { 1860 .url_prefix = "/token", 1861 .permission = "token-refresh", 1862 .method = MHD_HTTP_METHOD_POST, 1863 .handler = &TMH_private_post_instances_ID_token, 1864 /* Body should be tiny. */ 1865 .max_upload = 1024 1866 }, 1867 /* DELETE /tokens/$SERIAL: */ 1868 { 1869 .url_prefix = "/tokens/", 1870 .permission = "tokens-write", 1871 .method = MHD_HTTP_METHOD_DELETE, 1872 .handler = &TMH_private_delete_instances_ID_token_SERIAL, 1873 .have_id_segment = true 1874 }, 1875 /* DELETE /token: */ 1876 { 1877 .url_prefix = "/token", 1878 .method = MHD_HTTP_METHOD_DELETE, 1879 .handler = &TMH_private_delete_instances_ID_token, 1880 }, 1881 /* GET /tokenfamilies: */ 1882 { 1883 .url_prefix = "/tokenfamilies", 1884 .permission = "tokenfamilies-read", 1885 .method = MHD_HTTP_METHOD_GET, 1886 .handler = &TMH_private_get_tokenfamilies 1887 }, 1888 /* POST /tokenfamilies: */ 1889 { 1890 .url_prefix = "/tokenfamilies", 1891 .permission = "tokenfamilies-write", 1892 .method = MHD_HTTP_METHOD_POST, 1893 .handler = &TMH_private_post_token_families 1894 }, 1895 /* GET /tokenfamilies/$SLUG/: */ 1896 { 1897 .url_prefix = "/tokenfamilies/", 1898 .method = MHD_HTTP_METHOD_GET, 1899 .permission = "tokenfamilies-read", 1900 .have_id_segment = true, 1901 .handler = &TMH_private_get_tokenfamilies_SLUG 1902 }, 1903 /* DELETE /tokenfamilies/$SLUG/: */ 1904 { 1905 .url_prefix = "/tokenfamilies/", 1906 .method = MHD_HTTP_METHOD_DELETE, 1907 .permission = "tokenfamilies-write", 1908 .have_id_segment = true, 1909 .handler = &TMH_private_delete_token_families_SLUG 1910 }, 1911 /* PATCH /tokenfamilies/$SLUG/: */ 1912 { 1913 .url_prefix = "/tokenfamilies/", 1914 .method = MHD_HTTP_METHOD_PATCH, 1915 .permission = "tokenfamilies-write", 1916 .have_id_segment = true, 1917 .handler = &TMH_private_patch_token_family_SLUG, 1918 }, 1919 #ifdef HAVE_DONAU_DONAU_SERVICE_H 1920 /* GET /donau */ 1921 { 1922 .url_prefix = "/donau", 1923 .method = MHD_HTTP_METHOD_GET, 1924 .handler = &TMH_private_get_donau_instances 1925 }, 1926 /* POST /donau */ 1927 { 1928 .url_prefix = "/donau", 1929 .method = MHD_HTTP_METHOD_POST, 1930 .handler = &TMH_private_post_donau_instance 1931 }, 1932 /* DELETE /donau/$charity-id */ 1933 { 1934 .url_prefix = "/donau/", 1935 .method = MHD_HTTP_METHOD_DELETE, 1936 .have_id_segment = true, 1937 .handler = &TMH_private_delete_donau_instance_ID 1938 }, 1939 #endif 1940 /* GET /statistics-counter/$SLUG: */ 1941 { 1942 .url_prefix = "/statistics-counter/", 1943 .method = MHD_HTTP_METHOD_GET, 1944 .permission = "statistics-read", 1945 .have_id_segment = true, 1946 .handler = &TMH_private_get_statistics_counter_SLUG, 1947 }, 1948 /* GET /statistics-amount/$SLUG: */ 1949 { 1950 .url_prefix = "/statistics-amount/", 1951 .method = MHD_HTTP_METHOD_GET, 1952 .permission = "statistics-read", 1953 .have_id_segment = true, 1954 .handler = &TMH_private_get_statistics_amount_SLUG, 1955 }, 1956 { 1957 .url_prefix = NULL 1958 } 1959 }; 1960 static struct TMH_RequestHandler public_handlers[] = { 1961 { 1962 /* for "admin" instance, it does not even 1963 have to exist before we give the WebUI */ 1964 .url_prefix = "/", 1965 .method = MHD_HTTP_METHOD_GET, 1966 .mime_type = "text/html", 1967 .skip_instance = true, 1968 .default_only = true, 1969 .handler = &spa_redirect, 1970 .response_code = MHD_HTTP_FOUND 1971 }, 1972 { 1973 .url_prefix = "/config", 1974 .method = MHD_HTTP_METHOD_GET, 1975 .skip_instance = true, 1976 .default_only = true, 1977 .handler = &MH_handler_config 1978 }, 1979 { 1980 /* for "normal" instance,s they must exist 1981 before we give the WebUI */ 1982 .url_prefix = "/", 1983 .method = MHD_HTTP_METHOD_GET, 1984 .mime_type = "text/html", 1985 .handler = &spa_redirect, 1986 .response_code = MHD_HTTP_FOUND 1987 }, 1988 { 1989 .url_prefix = "/webui/", 1990 .method = MHD_HTTP_METHOD_GET, 1991 .mime_type = "text/html", 1992 .skip_instance = true, 1993 .have_id_segment = true, 1994 .handler = &TMH_return_spa, 1995 .response_code = MHD_HTTP_OK 1996 }, 1997 { 1998 .url_prefix = "/agpl", 1999 .method = MHD_HTTP_METHOD_GET, 2000 .skip_instance = true, 2001 .handler = &TMH_MHD_handler_agpl_redirect 2002 }, 2003 { 2004 .url_prefix = "/agpl", 2005 .method = MHD_HTTP_METHOD_GET, 2006 .skip_instance = true, 2007 .handler = &TMH_MHD_handler_agpl_redirect 2008 }, 2009 { 2010 .url_prefix = "/terms", 2011 .method = MHD_HTTP_METHOD_GET, 2012 .skip_instance = true, 2013 .handler = &TMH_handler_terms 2014 }, 2015 { 2016 .url_prefix = "/privacy", 2017 .method = MHD_HTTP_METHOD_GET, 2018 .skip_instance = true, 2019 .handler = &TMH_handler_privacy 2020 }, 2021 /* Also serve the same /config per instance */ 2022 { 2023 .url_prefix = "/config", 2024 .method = MHD_HTTP_METHOD_GET, 2025 .handler = &MH_handler_config 2026 }, 2027 /* POST /orders/$ID/abort: */ 2028 { 2029 .url_prefix = "/orders/", 2030 .have_id_segment = true, 2031 .url_suffix = "abort", 2032 .method = MHD_HTTP_METHOD_POST, 2033 .handler = &TMH_post_orders_ID_abort, 2034 /* wallet may give us many coins to sign, allow 1 MB of upload 2035 to set a conservative bound for sane wallets */ 2036 .max_upload = 1024 * 1024 2037 }, 2038 /* POST /orders/$ID/claim: */ 2039 { 2040 .url_prefix = "/orders/", 2041 .have_id_segment = true, 2042 .url_suffix = "claim", 2043 .method = MHD_HTTP_METHOD_POST, 2044 .handler = &TMH_post_orders_ID_claim, 2045 /* the body should be pretty small, allow 1 MB of upload 2046 to set a conservative bound for sane wallets */ 2047 .max_upload = 1024 * 1024 2048 }, 2049 /* POST /orders/$ID/pay: */ 2050 { 2051 .url_prefix = "/orders/", 2052 .have_id_segment = true, 2053 .url_suffix = "pay", 2054 .method = MHD_HTTP_METHOD_POST, 2055 .handler = &TMH_post_orders_ID_pay, 2056 /* wallet may give us many coins to sign, allow 1 MB of upload 2057 to set a conservative bound for sane wallets */ 2058 .max_upload = 1024 * 1024 2059 }, 2060 /* POST /orders/$ID/paid: */ 2061 { 2062 .url_prefix = "/orders/", 2063 .have_id_segment = true, 2064 .allow_deleted_instance = true, 2065 .url_suffix = "paid", 2066 .method = MHD_HTTP_METHOD_POST, 2067 .handler = &TMH_post_orders_ID_paid, 2068 /* the body should be pretty small, allow 1 MB of upload 2069 to set a conservative bound for sane wallets */ 2070 .max_upload = 1024 * 1024 2071 }, 2072 /* POST /orders/$ID/refund: */ 2073 { 2074 .url_prefix = "/orders/", 2075 .have_id_segment = true, 2076 .allow_deleted_instance = true, 2077 .url_suffix = "refund", 2078 .method = MHD_HTTP_METHOD_POST, 2079 .handler = &TMH_post_orders_ID_refund, 2080 /* the body should be pretty small, allow 1 MB of upload 2081 to set a conservative bound for sane wallets */ 2082 .max_upload = 1024 * 1024 2083 }, 2084 /* GET /orders/$ID: */ 2085 { 2086 .url_prefix = "/orders/", 2087 .method = MHD_HTTP_METHOD_GET, 2088 .allow_deleted_instance = true, 2089 .have_id_segment = true, 2090 .handler = &TMH_get_orders_ID 2091 }, 2092 /* GET /static/ *: */ 2093 { 2094 .url_prefix = "/static/", 2095 .method = MHD_HTTP_METHOD_GET, 2096 .have_id_segment = true, 2097 .handler = &TMH_return_static 2098 }, 2099 /* GET /templates/$ID/: */ 2100 { 2101 .url_prefix = "/templates/", 2102 .method = MHD_HTTP_METHOD_GET, 2103 .have_id_segment = true, 2104 .handler = &TMH_get_templates_ID 2105 }, 2106 /* GET /products/$HASH/image: */ 2107 { 2108 .url_prefix = "/products/", 2109 .method = MHD_HTTP_METHOD_GET, 2110 .have_id_segment = true, 2111 .allow_deleted_instance = true, 2112 .url_suffix = "image", 2113 .handler = &TMH_get_products_image 2114 }, 2115 /* POST /templates/$ID: */ 2116 { 2117 .url_prefix = "/templates/", 2118 .method = MHD_HTTP_METHOD_POST, 2119 .have_id_segment = true, 2120 .handler = &TMH_post_using_templates_ID, 2121 .max_upload = 1024 * 1024 2122 }, 2123 /* POST /challenge/$ID: */ 2124 { 2125 .url_prefix = "/challenge/", 2126 .method = MHD_HTTP_METHOD_POST, 2127 .have_id_segment = true, 2128 .handler = &TMH_post_challenge_ID, 2129 .max_upload = 1024 2130 }, 2131 /* POST /challenge/$ID/confirm: */ 2132 { 2133 .url_prefix = "/challenge/", 2134 .method = MHD_HTTP_METHOD_POST, 2135 .have_id_segment = true, 2136 .url_suffix = "confirm", 2137 .handler = &TMH_post_challenge_ID_confirm, 2138 .max_upload = 1024 2139 }, 2140 /* POST /instances */ 2141 { 2142 .url_prefix = "/instances", 2143 .method = MHD_HTTP_METHOD_POST, 2144 .skip_instance = true, 2145 .default_only = true, 2146 .handler = &TMH_public_post_instances, 2147 /* allow instance data of up to 8 MB, that should be plenty; 2148 note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB) 2149 would require further changes to the allocation logic 2150 in the code... */ 2151 .max_upload = 1024 * 1024 * 8 2152 }, 2153 /* POST /forgot-password: */ 2154 { 2155 .url_prefix = "/forgot-password", 2156 .method = MHD_HTTP_METHOD_POST, 2157 .handler = &TMH_public_post_instances_ID_auth, 2158 /* Body should be pretty small. */ 2159 .max_upload = 1024 * 1024 2160 }, 2161 { 2162 .url_prefix = "*", 2163 .method = MHD_HTTP_METHOD_OPTIONS, 2164 .handler = &handle_server_options 2165 }, 2166 { 2167 .url_prefix = NULL 2168 } 2169 }; 2170 struct TMH_HandlerContext *hc = *con_cls; 2171 struct TMH_RequestHandler *handlers; 2172 bool use_default = false; 2173 2174 (void) cls; 2175 (void) version; 2176 if (NULL != hc->url) 2177 { 2178 /* MHD calls us again for a request, for first call 2179 see 'else' case below */ 2180 GNUNET_assert (NULL != hc->rh); 2181 GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); 2182 if ( (hc->has_body) && 2183 (NULL == hc->request_body) ) 2184 { 2185 size_t mul = hc->rh->max_upload; 2186 enum GNUNET_GenericReturnValue res; 2187 2188 if (0 == mul) 2189 mul = DEFAULT_MAX_UPLOAD_SIZE; 2190 if ( (hc->total_upload + *upload_data_size < hc->total_upload) || 2191 (hc->total_upload + *upload_data_size > mul) ) 2192 { 2193 /* Client exceeds upload limit. Should _usually_ be checked earlier 2194 when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with 2195 chunked encoding an uploader MAY have omitted this, and thus 2196 not permitted us to check on time. In this case, we just close 2197 the connection once it exceeds our limit (instead of waiting 2198 for the upload to complete and then fail). This could theoretically 2199 cause some clients to retry, alas broken or malicious clients 2200 are likely to retry anyway, so little we can do about it, and 2201 failing earlier seems the best option here. */ 2202 GNUNET_break_op (0); 2203 return MHD_NO; 2204 } 2205 hc->total_upload += *upload_data_size; 2206 res = TALER_MHD_parse_post_json (connection, 2207 &hc->json_parse_context, 2208 upload_data, 2209 upload_data_size, 2210 &hc->request_body); 2211 if (GNUNET_SYSERR == res) 2212 return MHD_NO; 2213 /* A error response was already generated */ 2214 if ( (GNUNET_NO == res) || 2215 /* or, need more data to accomplish parsing */ 2216 (NULL == hc->request_body) ) 2217 return MHD_YES; /* let MHD call us *again* */ 2218 } 2219 /* Upload complete (if any), call handler to generate reply */ 2220 return hc->rh->handler (hc->rh, 2221 connection, 2222 hc); 2223 } 2224 hc->url = url; 2225 { 2226 const char *correlation_id; 2227 2228 correlation_id = MHD_lookup_connection_value (connection, 2229 MHD_HEADER_KIND, 2230 "Taler-Correlation-Id"); 2231 if ( (NULL != correlation_id) && 2232 (GNUNET_YES != 2233 GNUNET_CURL_is_valid_scope_id (correlation_id)) ) 2234 { 2235 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2236 "Illegal incoming correlation ID\n"); 2237 correlation_id = NULL; 2238 } 2239 if (NULL != correlation_id) 2240 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2241 "Handling request for (%s) URL '%s', correlation_id=%s\n", 2242 method, 2243 url, 2244 correlation_id); 2245 else 2246 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2247 "Handling request (%s) for URL '%s'\n", 2248 method, 2249 url); 2250 } 2251 2252 if (0 == strcasecmp (method, 2253 MHD_HTTP_METHOD_HEAD)) 2254 method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */ 2255 2256 2257 /* Find out the merchant backend instance for the request. 2258 * If there is an instance, remove the instance specification 2259 * from the beginning of the request URL. */ 2260 { 2261 const char *instance_prefix = "/instances/"; 2262 2263 if (0 == strncmp (url, 2264 instance_prefix, 2265 strlen (instance_prefix))) 2266 { 2267 /* url starts with "/instances/" */ 2268 const char *istart = url + strlen (instance_prefix); 2269 const char *slash = strchr (istart, '/'); 2270 char *instance_id; 2271 2272 if (NULL == slash) 2273 instance_id = GNUNET_strdup (istart); 2274 else 2275 instance_id = GNUNET_strndup (istart, 2276 slash - istart); 2277 if (0 == strcmp (instance_id, 2278 "admin")) 2279 { 2280 MHD_RESULT ret; 2281 struct MHD_Response *response; 2282 const char *rstart = hc->full_url + strlen (instance_prefix); 2283 const char *rslash = strchr (rstart, '/'); 2284 2285 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2286 "Client used deprecated '/instances/default/' path. Redirecting to modern path\n"); 2287 2288 response 2289 = MHD_create_response_from_buffer (0, 2290 NULL, 2291 MHD_RESPMEM_PERSISTENT); 2292 TALER_MHD_add_global_headers (response, 2293 true); 2294 if (MHD_NO == 2295 MHD_add_response_header (response, 2296 MHD_HTTP_HEADER_LOCATION, 2297 NULL == rslash 2298 ? "/" 2299 : rslash)) 2300 { 2301 GNUNET_break (0); 2302 MHD_destroy_response (response); 2303 GNUNET_free (instance_id); 2304 return MHD_NO; 2305 } 2306 ret = MHD_queue_response (connection, 2307 MHD_HTTP_PERMANENT_REDIRECT, 2308 response); 2309 MHD_destroy_response (response); 2310 GNUNET_free (instance_id); 2311 return ret; 2312 } 2313 hc->instance = TMH_lookup_instance (instance_id); 2314 if ( (NULL == hc->instance) && 2315 (0 == strcmp ("admin", 2316 instance_id)) ) 2317 hc->instance = TMH_lookup_instance (NULL); 2318 GNUNET_free (instance_id); 2319 if (NULL == slash) 2320 url = ""; 2321 else 2322 url = slash; 2323 } 2324 else 2325 { 2326 /* use 'default' */ 2327 use_default = true; 2328 hc->instance = TMH_lookup_instance (NULL); 2329 } 2330 if (NULL != hc->instance) 2331 { 2332 GNUNET_assert (hc->instance->rc < UINT_MAX); 2333 hc->instance->rc++; 2334 } 2335 } 2336 2337 { 2338 const char *management_prefix = "/management/"; 2339 const char *private_prefix = "/private/"; 2340 2341 if ( (0 == strncmp (url, 2342 management_prefix, 2343 strlen (management_prefix))) ) 2344 { 2345 handlers = management_handlers; 2346 url += strlen (management_prefix) - 1; 2347 } 2348 else if ( (0 == strncmp (url, 2349 private_prefix, 2350 strlen (private_prefix))) || 2351 (0 == strcmp (url, 2352 "/private")) ) 2353 { 2354 handlers = private_handlers; 2355 if (0 == strcmp (url, 2356 "/private")) 2357 url = "/"; 2358 else 2359 url += strlen (private_prefix) - 1; 2360 } 2361 else 2362 { 2363 handlers = public_handlers; 2364 } 2365 } 2366 2367 if (0 == strcmp (url, 2368 "")) 2369 url = "/"; /* code below does not like empty string */ 2370 2371 { 2372 /* Matching URL found, but maybe method doesn't match */ 2373 size_t prefix_strlen; /* i.e. 8 for "/orders/", or 7 for "/config" */ 2374 const char *infix_url = NULL; /* i.e. "$ORDER_ID", no '/'-es */ 2375 size_t infix_strlen = 0; /* number of characters in infix_url */ 2376 const char *suffix_url = NULL; /* i.e. "refund", excludes '/' at the beginning */ 2377 size_t suffix_strlen = 0; /* number of characters in suffix_url */ 2378 2379 /* parse the URL into the three different components */ 2380 { 2381 const char *slash; 2382 2383 slash = strchr (&url[1], '/'); 2384 if (NULL == slash) 2385 { 2386 /* the prefix was everything */ 2387 prefix_strlen = strlen (url); 2388 } 2389 else 2390 { 2391 prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */ 2392 infix_url = slash + 1; 2393 slash = strchr (infix_url, '/'); 2394 if (NULL == slash) 2395 { 2396 /* the infix was the rest */ 2397 infix_strlen = strlen (infix_url); 2398 } 2399 else 2400 { 2401 infix_strlen = slash - infix_url; /* excludes both '/'-es */ 2402 suffix_url = slash + 1; /* skip the '/' */ 2403 suffix_strlen = strlen (suffix_url); 2404 } 2405 hc->infix = GNUNET_strndup (infix_url, 2406 infix_strlen); 2407 } 2408 } 2409 2410 /* find matching handler */ 2411 { 2412 bool url_found = false; 2413 2414 for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++) 2415 { 2416 struct TMH_RequestHandler *rh = &handlers[i]; 2417 2418 if (rh->default_only && (! use_default)) 2419 continue; 2420 if (! prefix_match (rh, 2421 url, 2422 prefix_strlen, 2423 infix_url, 2424 infix_strlen, 2425 suffix_url, 2426 suffix_strlen)) 2427 continue; 2428 url_found = true; 2429 if (0 == strcasecmp (method, 2430 MHD_HTTP_METHOD_OPTIONS)) 2431 { 2432 return TALER_MHD_reply_cors_preflight (connection); 2433 } 2434 if ( (rh->method != NULL) && 2435 (0 != strcasecmp (method, 2436 rh->method)) ) 2437 continue; 2438 hc->rh = rh; 2439 break; 2440 } 2441 /* Handle HTTP 405: METHOD NOT ALLOWED case */ 2442 if ( (NULL == hc->rh) && 2443 (url_found) ) 2444 { 2445 struct MHD_Response *reply; 2446 MHD_RESULT ret; 2447 char *allowed = NULL; 2448 2449 GNUNET_break_op (0); 2450 /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */ 2451 for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++) 2452 { 2453 struct TMH_RequestHandler *rh = &handlers[i]; 2454 2455 if (rh->default_only && (! use_default)) 2456 continue; 2457 if (! prefix_match (rh, 2458 url, 2459 prefix_strlen, 2460 infix_url, 2461 infix_strlen, 2462 suffix_url, 2463 suffix_strlen)) 2464 continue; 2465 if (NULL == allowed) 2466 { 2467 allowed = GNUNET_strdup (rh->method); 2468 } 2469 else 2470 { 2471 char *tmp; 2472 2473 GNUNET_asprintf (&tmp, 2474 "%s, %s", 2475 allowed, 2476 rh->method); 2477 GNUNET_free (allowed); 2478 allowed = tmp; 2479 } 2480 if (0 == strcasecmp (rh->method, 2481 MHD_HTTP_METHOD_GET)) 2482 { 2483 char *tmp; 2484 2485 GNUNET_asprintf (&tmp, 2486 "%s, %s", 2487 allowed, 2488 MHD_HTTP_METHOD_HEAD); 2489 GNUNET_free (allowed); 2490 allowed = tmp; 2491 } 2492 } 2493 reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID, 2494 method); 2495 GNUNET_break (MHD_YES == 2496 MHD_add_response_header (reply, 2497 MHD_HTTP_HEADER_ALLOW, 2498 allowed)); 2499 GNUNET_free (allowed); 2500 ret = MHD_queue_response (connection, 2501 MHD_HTTP_METHOD_NOT_ALLOWED, 2502 reply); 2503 MHD_destroy_response (reply); 2504 return ret; 2505 } 2506 if (NULL == hc->rh) 2507 { 2508 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2509 "Endpoint `%s' not known\n", 2510 hc->url); 2511 return TALER_MHD_reply_with_error (connection, 2512 MHD_HTTP_NOT_FOUND, 2513 TALER_EC_GENERIC_ENDPOINT_UNKNOWN, 2514 hc->url); 2515 } 2516 } 2517 } 2518 /* At this point, we must have found a handler */ 2519 GNUNET_assert (NULL != hc->rh); 2520 2521 /* If an instance should be there, check one exists */ 2522 if ( (NULL == hc->instance) && 2523 (! hc->rh->skip_instance) ) 2524 { 2525 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2526 "Instance for `%s' not known\n", 2527 hc->url); 2528 return TALER_MHD_reply_with_error (connection, 2529 MHD_HTTP_NOT_FOUND, 2530 TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, 2531 hc->infix); 2532 } 2533 2534 /* Access control for non-public handlers */ 2535 if (public_handlers != handlers) 2536 { 2537 const char *auth; 2538 bool is_basic_auth = false; 2539 bool auth_malformed = false; 2540 2541 auth = MHD_lookup_connection_value (connection, 2542 MHD_HEADER_KIND, 2543 MHD_HTTP_HEADER_AUTHORIZATION); 2544 2545 if (NULL != auth) 2546 { 2547 extract_auth (&auth, 2548 &is_basic_auth); 2549 if (NULL == auth) 2550 auth_malformed = true; 2551 hc->auth_token = auth; 2552 } 2553 2554 /* If we have zero configured instances (not even ones that have been 2555 purged) or explicitly disabled authentication, THEN we accept anything 2556 (no access control), as we then also have no data to protect. */ 2557 if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) || 2558 (GNUNET_YES == TMH_auth_disabled)) 2559 { 2560 hc->auth_scope = TMH_AS_ALL; 2561 } 2562 else if (is_basic_auth) 2563 { 2564 process_basic_auth (hc, auth); 2565 } 2566 else /* Check bearer token */ 2567 { 2568 enum TALER_ErrorCode ec; 2569 2570 ec = process_bearer_auth (hc, 2571 auth); 2572 if (TALER_EC_NONE != ec) 2573 { 2574 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2575 "Bearer authentication failed: %d\n", 2576 (int) ec); 2577 return TALER_MHD_reply_with_ec (connection, 2578 ec, 2579 NULL); 2580 } 2581 } 2582 /* We grant access if: 2583 - Endpoint does not require permissions 2584 - Authorization scope of bearer token contains permissions 2585 required by endpoint. 2586 */ 2587 if ( (NULL != hc->rh->permission) && 2588 (! permission_in_scope (hc->rh->permission, 2589 hc->auth_scope))) 2590 { 2591 if (auth_malformed && 2592 (TMH_AS_NONE == hc->auth_scope) ) 2593 { 2594 GNUNET_break_op (0); 2595 return TALER_MHD_reply_with_error ( 2596 connection, 2597 MHD_HTTP_UNAUTHORIZED, 2598 TALER_EC_GENERIC_PARAMETER_MALFORMED, 2599 "'" RFC_8959_PREFIX 2600 "' prefix or 'Bearer' missing in 'Authorization' header"); 2601 } 2602 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2603 "Credentials provided are %d which are insufficient for access to `%s'\n", 2604 (int) hc->auth_scope, 2605 hc->rh->permission); 2606 return TALER_MHD_reply_with_error (connection, 2607 MHD_HTTP_UNAUTHORIZED, 2608 TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED, 2609 "Check credentials in 'Authorization' header"); 2610 } 2611 } /* if (use_private) */ 2612 2613 2614 if ( (NULL == hc->instance) && 2615 (! hc->rh->skip_instance) ) 2616 { 2617 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2618 "Instance for URL `%s' not known\n", 2619 url); 2620 return TALER_MHD_reply_with_error (connection, 2621 MHD_HTTP_NOT_FOUND, 2622 TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, 2623 url); 2624 } 2625 if ( (NULL != hc->instance) && /* make static analysis happy */ 2626 (! hc->rh->skip_instance) && 2627 (hc->instance->deleted) && 2628 (! hc->rh->allow_deleted_instance) ) 2629 { 2630 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2631 "Instance `%s' was deleted\n", 2632 hc->instance->settings.id); 2633 return TALER_MHD_reply_with_error (connection, 2634 MHD_HTTP_NOT_FOUND, 2635 TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED, 2636 hc->instance->settings.id); 2637 } 2638 /* parse request body */ 2639 hc->has_body = ( (0 == strcasecmp (method, 2640 MHD_HTTP_METHOD_POST)) || 2641 /* PUT is not yet used */ 2642 (0 == strcasecmp (method, 2643 MHD_HTTP_METHOD_PATCH)) ); 2644 if (hc->has_body) 2645 { 2646 TALER_MHD_check_content_length (connection, 2647 0 == hc->rh->max_upload 2648 ? DEFAULT_MAX_UPLOAD_SIZE 2649 : hc->rh->max_upload); 2650 GNUNET_break (NULL == hc->request_body); /* can't have it already */ 2651 } 2652 return MHD_YES; /* wait for MHD to call us again */ 2653 } 2654 2655 2656 /** 2657 * Callback invoked with information about a bank account. 2658 * 2659 * @param cls closure with a `struct TMH_MerchantInstance *` 2660 * @param merchant_priv private key of the merchant instance 2661 * @param acc details about the account 2662 */ 2663 static void 2664 add_account_cb (void *cls, 2665 const struct TALER_MerchantPrivateKeyP *merchant_priv, 2666 const struct TALER_MERCHANTDB_AccountDetails *acc) 2667 { 2668 struct TMH_MerchantInstance *mi = cls; 2669 struct TMH_WireMethod *wm; 2670 2671 (void) merchant_priv; 2672 wm = GNUNET_new (struct TMH_WireMethod); 2673 wm->h_wire = acc->h_wire; 2674 wm->payto_uri.full_payto 2675 = GNUNET_strdup (acc->payto_uri.full_payto); 2676 wm->wire_salt = acc->salt; 2677 wm->wire_method 2678 = TALER_payto_get_method (acc->payto_uri.full_payto); 2679 wm->active = acc->active; 2680 GNUNET_CONTAINER_DLL_insert (mi->wm_head, 2681 mi->wm_tail, 2682 wm); 2683 } 2684 2685 2686 /** 2687 * Function called during startup to add all known instances to our 2688 * hash map in memory for faster lookups when we receive requests. 2689 * 2690 * @param cls closure, NULL, unused 2691 * @param merchant_pub public key of the instance 2692 * @param merchant_priv private key of the instance, NULL if not available 2693 * @param is detailed configuration settings for the instance 2694 * @param ias authentication settings for the instance 2695 */ 2696 static void 2697 add_instance_cb (void *cls, 2698 const struct TALER_MerchantPublicKeyP *merchant_pub, 2699 const struct TALER_MerchantPrivateKeyP *merchant_priv, 2700 const struct TALER_MERCHANTDB_InstanceSettings *is, 2701 const struct TALER_MERCHANTDB_InstanceAuthSettings *ias) 2702 { 2703 struct TMH_MerchantInstance *mi; 2704 enum GNUNET_DB_QueryStatus qs; 2705 2706 (void) cls; 2707 mi = TMH_lookup_instance (is->id); 2708 if (NULL != mi) 2709 { 2710 /* (outdated) entry exists, remove old entry */ 2711 (void) TMH_instance_free_cb (NULL, 2712 &mi->h_instance, 2713 mi); 2714 } 2715 mi = GNUNET_new (struct TMH_MerchantInstance); 2716 mi->settings = *is; 2717 mi->auth = *ias; 2718 mi->settings.id = GNUNET_strdup (mi->settings.id); 2719 mi->settings.name = GNUNET_strdup (mi->settings.name); 2720 if (NULL != mi->settings.email) 2721 mi->settings.email = GNUNET_strdup (mi->settings.email); 2722 if (NULL != mi->settings.phone) 2723 mi->settings.phone = GNUNET_strdup (mi->settings.phone); 2724 if (NULL != mi->settings.website) 2725 mi->settings.website = GNUNET_strdup (mi->settings.website); 2726 if (NULL != mi->settings.logo) 2727 mi->settings.logo = GNUNET_strdup (mi->settings.logo); 2728 mi->settings.address = json_incref (mi->settings.address); 2729 mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction); 2730 if (NULL != merchant_priv) 2731 mi->merchant_priv = *merchant_priv; 2732 else 2733 mi->deleted = true; 2734 mi->merchant_pub = *merchant_pub; 2735 qs = TMH_db->select_accounts (TMH_db->cls, 2736 mi->settings.id, 2737 &add_account_cb, 2738 mi); 2739 if (0 > qs) 2740 { 2741 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2742 "Error loading accounts of `%s' from database\n", 2743 mi->settings.id); 2744 } 2745 GNUNET_assert (GNUNET_OK == 2746 TMH_add_instance (mi)); 2747 } 2748 2749 2750 /** 2751 * Trigger (re)loading of instance settings from DB. 2752 * 2753 * @param cls NULL 2754 * @param extra ID of the instance that changed, NULL 2755 * to load all instances (will not handle purges!) 2756 * @param extra_len number of bytes in @a extra 2757 */ 2758 static void 2759 load_instances (void *cls, 2760 const void *extra, 2761 size_t extra_len) 2762 { 2763 enum GNUNET_DB_QueryStatus qs; 2764 const char *id = extra; 2765 2766 (void) cls; 2767 if ( (NULL != extra) && 2768 ( (0 == extra_len) || 2769 ('\0' != id[extra_len - 1]) ) ) 2770 { 2771 GNUNET_break (0 == extra_len); 2772 extra = NULL; 2773 } 2774 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2775 "Received instance settings notification: reload `%s'\n", 2776 id); 2777 if (NULL == extra) 2778 { 2779 qs = TMH_db->lookup_instances (TMH_db->cls, 2780 false, 2781 &add_instance_cb, 2782 NULL); 2783 } 2784 else 2785 { 2786 struct TMH_MerchantInstance *mi; 2787 2788 /* This must be done here to handle instance 2789 purging, as for purged instances, the DB 2790 lookup below will otherwise do nothing */ 2791 mi = TMH_lookup_instance (id); 2792 if (NULL != mi) 2793 { 2794 (void) TMH_instance_free_cb (NULL, 2795 &mi->h_instance, 2796 mi); 2797 } 2798 qs = TMH_db->lookup_instance (TMH_db->cls, 2799 id, 2800 false, 2801 &add_instance_cb, 2802 NULL); 2803 } 2804 if (0 > qs) 2805 { 2806 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2807 "Failed initialization. Check database setup.\n"); 2808 global_ret = EXIT_NOPERMISSION; 2809 GNUNET_SCHEDULER_shutdown (); 2810 return; 2811 } 2812 } 2813 2814 2815 /** 2816 * A transaction modified an instance setting (or created/deleted/purged 2817 * one). Notify all backends about the change. 2818 * 2819 * @param id ID of the instance that changed 2820 */ 2821 void 2822 TMH_reload_instances (const char *id) 2823 { 2824 struct GNUNET_DB_EventHeaderP es = { 2825 .size = ntohs (sizeof (es)), 2826 .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS) 2827 }; 2828 2829 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2830 "Generating instance settings notification: reload `%s'\n", 2831 id); 2832 TMH_db->event_notify (TMH_db->cls, 2833 &es, 2834 id, 2835 (NULL == id) 2836 ? 0 2837 : strlen (id) + 1); 2838 } 2839 2840 2841 /** 2842 * Callback invoked on every listen socket to start the 2843 * respective MHD HTTP daemon. 2844 * 2845 * @param cls unused 2846 * @param lsock the listen socket 2847 */ 2848 static void 2849 start_daemon (void *cls, 2850 int lsock) 2851 { 2852 struct MHD_Daemon *mhd; 2853 2854 (void) cls; 2855 GNUNET_assert (-1 != lsock); 2856 mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK 2857 | MHD_USE_AUTO, 2858 0 /* port */, 2859 NULL, NULL, 2860 &url_handler, NULL, 2861 MHD_OPTION_LISTEN_SOCKET, lsock, 2862 MHD_OPTION_URI_LOG_CALLBACK, 2863 &full_url_track_callback, NULL, 2864 MHD_OPTION_NOTIFY_COMPLETED, 2865 &handle_mhd_completion_callback, NULL, 2866 MHD_OPTION_CONNECTION_TIMEOUT, 2867 (unsigned int) 10 /* 10s */, 2868 MHD_OPTION_END); 2869 if (NULL == mhd) 2870 { 2871 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2872 "Failed to launch HTTP service.\n"); 2873 GNUNET_SCHEDULER_shutdown (); 2874 return; 2875 } 2876 have_daemons = true; 2877 TALER_MHD_daemon_start (mhd); 2878 } 2879 2880 2881 /** 2882 * Main function that will be run by the scheduler. 2883 * 2884 * @param cls closure 2885 * @param args remaining command-line arguments 2886 * @param cfgfile name of the configuration file used (for saving, can be 2887 * NULL!) 2888 * @param config configuration 2889 */ 2890 static void 2891 run (void *cls, 2892 char *const *args, 2893 const char *cfgfile, 2894 const struct GNUNET_CONFIGURATION_Handle *config) 2895 { 2896 enum TALER_MHD_GlobalOptions go; 2897 int elen; 2898 2899 (void) cls; 2900 (void) args; 2901 (void) cfgfile; 2902 cfg = config; 2903 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2904 "Starting taler-merchant-httpd\n"); 2905 go = TALER_MHD_GO_NONE; 2906 if (merchant_connection_close) 2907 go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE; 2908 TALER_MHD_setup (go); 2909 2910 global_ret = EXIT_SUCCESS; 2911 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 2912 NULL); 2913 2914 TMH_curl_ctx 2915 = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 2916 &merchant_curl_rc); 2917 if (NULL == TMH_curl_ctx) 2918 { 2919 GNUNET_break (0); 2920 global_ret = EXIT_NO_RESTART; 2921 GNUNET_SCHEDULER_shutdown (); 2922 return; 2923 } 2924 merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx); 2925 /* Disable 100 continue processing */ 2926 GNUNET_break (GNUNET_OK == 2927 GNUNET_CURL_append_header (TMH_curl_ctx, 2928 MHD_HTTP_HEADER_EXPECT ":")); 2929 GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx, 2930 "Taler-Correlation-Id"); 2931 2932 if (GNUNET_SYSERR == 2933 TALER_config_get_currency (cfg, 2934 "merchant", 2935 &TMH_currency)) 2936 { 2937 global_ret = EXIT_NOTCONFIGURED; 2938 GNUNET_SCHEDULER_shutdown (); 2939 return; 2940 } 2941 if (GNUNET_OK != 2942 TALER_CONFIG_parse_currencies (cfg, 2943 TMH_currency, 2944 &TMH_num_cspecs, 2945 &TMH_cspecs)) 2946 { 2947 global_ret = EXIT_NOTCONFIGURED; 2948 GNUNET_SCHEDULER_shutdown (); 2949 return; 2950 } 2951 2952 if (GNUNET_SYSERR == 2953 (TMH_strict_v19 2954 = GNUNET_CONFIGURATION_get_value_yesno (cfg, 2955 "merchant", 2956 "STRICT_PROTOCOL_V19"))) 2957 { 2958 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 2959 "merchant", 2960 "STRICT_PROTOCOL_V19"); 2961 TMH_strict_v19 = GNUNET_NO; 2962 } 2963 if (GNUNET_SYSERR == 2964 (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (cfg, 2965 "merchant", 2966 "DISABLE_AUTHENTICATION"))) 2967 { 2968 TMH_auth_disabled = GNUNET_NO; 2969 } 2970 if (GNUNET_YES == TMH_auth_disabled) 2971 { 2972 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2973 "DANGEROUS: Endpoint Authentication disabled!"); 2974 } 2975 2976 if (GNUNET_SYSERR == 2977 (TMH_have_self_provisioning 2978 = GNUNET_CONFIGURATION_get_value_yesno (cfg, 2979 "merchant", 2980 "ENABLE_SELF_PROVISIONING"))) 2981 { 2982 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 2983 "merchant", 2984 "ENABLE_SELF_PROVISIONING"); 2985 TMH_have_self_provisioning = GNUNET_NO; 2986 } 2987 2988 if (GNUNET_OK != 2989 GNUNET_CONFIGURATION_get_value_time (cfg, 2990 "merchant", 2991 "LEGAL_PRESERVATION", 2992 &TMH_legal_expiration)) 2993 { 2994 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2995 "merchant", 2996 "LEGAL_PRESERVATION"); 2997 global_ret = EXIT_NOTCONFIGURED; 2998 GNUNET_SCHEDULER_shutdown (); 2999 return; 3000 } 3001 3002 if (GNUNET_OK != 3003 GNUNET_CONFIGURATION_get_value_time (cfg, 3004 "merchant", 3005 "DEFAULT_PAY_DELAY", 3006 &TMH_default_pay_delay)) 3007 { 3008 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 3009 "merchant", 3010 "DEFAULT_PAY_DELAY"); 3011 TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS; 3012 } 3013 if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay)) 3014 { 3015 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 3016 "merchant", 3017 "DEFAULT_PAY_DELAY", 3018 "forever is not allowed"); 3019 global_ret = EXIT_NOTCONFIGURED; 3020 GNUNET_SCHEDULER_shutdown (); 3021 return; 3022 } 3023 if (GNUNET_OK != 3024 GNUNET_CONFIGURATION_get_value_time (cfg, 3025 "merchant", 3026 "DEFAULT_REFUND_DELAY", 3027 &TMH_default_refund_delay)) 3028 { 3029 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 3030 "merchant", 3031 "DEFAULT_REFUND_DELAY"); 3032 TMH_default_refund_delay = GNUNET_TIME_relative_multiply ( 3033 GNUNET_TIME_UNIT_DAYS, 3034 15); 3035 } 3036 if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay)) 3037 { 3038 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 3039 "merchant", 3040 "DEFAULT_REFUND_DELAY", 3041 "forever is not allowed"); 3042 global_ret = EXIT_NOTCONFIGURED; 3043 GNUNET_SCHEDULER_shutdown (); 3044 return; 3045 } 3046 3047 if (GNUNET_OK != 3048 GNUNET_CONFIGURATION_get_value_time (cfg, 3049 "merchant", 3050 "DEFAULT_WIRE_TRANSFER_DELAY", 3051 &TMH_default_wire_transfer_delay)) 3052 { 3053 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 3054 "merchant", 3055 "DEFAULT_WIRE_TRANSFER_DELAY"); 3056 TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS; 3057 } 3058 if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay)) 3059 { 3060 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO, 3061 "merchant", 3062 "DEFAULT_WIRE_TRANSFER_DELAY", 3063 "forever is not allowed"); 3064 global_ret = EXIT_NOTCONFIGURED; 3065 GNUNET_SCHEDULER_shutdown (); 3066 return; 3067 } 3068 3069 { 3070 char *dwtri; 3071 3072 if (GNUNET_OK != 3073 GNUNET_CONFIGURATION_get_value_string ( 3074 cfg, 3075 "merchant", 3076 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL", 3077 &dwtri)) 3078 { 3079 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO, 3080 "merchant", 3081 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL"); 3082 TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE; 3083 } 3084 else 3085 { 3086 if (GNUNET_OK != 3087 GNUNET_TIME_string_to_round_interval ( 3088 dwtri, 3089 &TMH_default_wire_transfer_rounding_interval)) 3090 { 3091 GNUNET_log_config_invalid ( 3092 GNUNET_ERROR_TYPE_ERROR, 3093 "merchant", 3094 "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL", 3095 "invalid time rounding interval"); 3096 global_ret = EXIT_NOTCONFIGURED; 3097 GNUNET_free (dwtri); 3098 GNUNET_SCHEDULER_shutdown (); 3099 return; 3100 } 3101 GNUNET_free (dwtri); 3102 } 3103 } 3104 3105 TMH_load_terms (cfg); 3106 3107 if (GNUNET_OK != 3108 GNUNET_CONFIGURATION_get_value_string (cfg, 3109 "merchant", 3110 "PAYMENT_TARGET_TYPES", 3111 &TMH_allowed_payment_targets)) 3112 { 3113 TMH_allowed_payment_targets = GNUNET_strdup ("*"); 3114 } 3115 3116 if (GNUNET_OK != 3117 GNUNET_CONFIGURATION_get_value_string (cfg, 3118 "merchant", 3119 "DEFAULT_PERSONA", 3120 &TMH_default_persona)) 3121 { 3122 TMH_default_persona = GNUNET_strdup ("expert"); 3123 } 3124 3125 if (GNUNET_OK != 3126 GNUNET_CONFIGURATION_get_value_string (cfg, 3127 "merchant", 3128 "PAYMENT_TARGET_REGEX", 3129 &TMH_payment_target_regex)) 3130 { 3131 TMH_payment_target_regex = NULL; 3132 } 3133 else 3134 { 3135 if (0 == strlen (TMH_payment_target_regex)) 3136 { 3137 GNUNET_free (TMH_payment_target_regex); 3138 } 3139 else 3140 { 3141 if (0 != regcomp (&TMH_payment_target_re, 3142 TMH_payment_target_regex, 3143 REG_NOSUB | REG_EXTENDED)) 3144 { 3145 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 3146 "merchant", 3147 "PAYMENT_TARGET_REGEX", 3148 "malformed regular expression"); 3149 global_ret = EXIT_NOTCONFIGURED; 3150 GNUNET_free (TMH_payment_target_regex); 3151 GNUNET_SCHEDULER_shutdown (); 3152 return; 3153 } 3154 } 3155 } 3156 3157 if (GNUNET_OK != 3158 GNUNET_CONFIGURATION_get_value_string (cfg, 3159 "merchant", 3160 "HELPER_SMS", 3161 &TMH_helper_sms)) 3162 { 3163 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 3164 "merchant", 3165 "HELPER_SMS", 3166 "no helper specified"); 3167 } 3168 3169 if (GNUNET_OK != 3170 GNUNET_CONFIGURATION_get_value_string (cfg, 3171 "merchant", 3172 "HELPER_EMAIL", 3173 &TMH_helper_email)) 3174 { 3175 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, 3176 "merchant", 3177 "HELPER_EMAIL", 3178 "no helper specified"); 3179 } 3180 3181 { 3182 char *tan_channels; 3183 3184 if (GNUNET_OK == 3185 GNUNET_CONFIGURATION_get_value_string (cfg, 3186 "merchant", 3187 "MANDATORY_TAN_CHANNELS", 3188 &tan_channels)) 3189 { 3190 for (char *tok = strtok (tan_channels, 3191 " "); 3192 NULL != tok; 3193 tok = strtok (NULL, 3194 " ")) 3195 { 3196 if (0 == strcasecmp (tok, 3197 "sms")) 3198 { 3199 if (NULL == TMH_helper_sms) 3200 { 3201 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 3202 "merchant", 3203 "MANDATORY_TAN_CHANNELS", 3204 "SMS mandatory, but no HELPER_SMS configured"); 3205 global_ret = EXIT_NOTCONFIGURED; 3206 GNUNET_SCHEDULER_shutdown (); 3207 GNUNET_free (tan_channels); 3208 return; 3209 } 3210 TEH_mandatory_tan_channels |= TEH_TCS_SMS; 3211 } 3212 else if (0 == strcasecmp (tok, 3213 "email")) 3214 { 3215 if (NULL == TMH_helper_email) 3216 { 3217 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 3218 "merchant", 3219 "MANDATORY_TAN_CHANNELS", 3220 "EMAIL mandatory, but no HELPER_EMAIL configured"); 3221 global_ret = EXIT_NOTCONFIGURED; 3222 GNUNET_SCHEDULER_shutdown (); 3223 GNUNET_free (tan_channels); 3224 return; 3225 } 3226 TEH_mandatory_tan_channels |= TEH_TCS_EMAIL; 3227 } 3228 else 3229 { 3230 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 3231 "merchant", 3232 "MANDATORY_TAN_CHANNELS", 3233 tok); 3234 global_ret = EXIT_NOTCONFIGURED; 3235 GNUNET_SCHEDULER_shutdown (); 3236 GNUNET_free (tan_channels); 3237 return; 3238 } 3239 } 3240 GNUNET_free (tan_channels); 3241 } 3242 } 3243 3244 if (GNUNET_OK == 3245 GNUNET_CONFIGURATION_get_value_string (cfg, 3246 "merchant", 3247 "BASE_URL", 3248 &TMH_base_url)) 3249 { 3250 if (! TALER_is_web_url (TMH_base_url)) 3251 { 3252 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 3253 "merchant", 3254 "BASE_URL", 3255 "Needs to start with 'http://' or 'https://'"); 3256 global_ret = EXIT_NOTCONFIGURED; 3257 GNUNET_SCHEDULER_shutdown (); 3258 return; 3259 } 3260 } 3261 if (GNUNET_OK == 3262 GNUNET_CONFIGURATION_get_value_string (cfg, 3263 "merchant", 3264 "BACKOFFICE_SPA_DIR", 3265 &TMH_spa_dir)) 3266 { 3267 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3268 "Loading merchant SPA from %s\n", 3269 TMH_spa_dir); 3270 } 3271 else 3272 { 3273 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3274 "Loading merchant SPA from default location\n"); 3275 } 3276 3277 if (GNUNET_YES == 3278 GNUNET_CONFIGURATION_get_value_yesno (cfg, 3279 "merchant", 3280 "FORCE_AUDIT")) 3281 TMH_force_audit = GNUNET_YES; 3282 if (GNUNET_OK != 3283 TALER_TEMPLATING_init (TALER_MERCHANT_project_data ())) 3284 { 3285 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3286 "Failed to setup templates\n"); 3287 global_ret = EXIT_NOTINSTALLED; 3288 GNUNET_SCHEDULER_shutdown (); 3289 return; 3290 } 3291 if (GNUNET_OK != 3292 TMH_spa_init (TMH_spa_dir)) 3293 { 3294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3295 "Failed to load single page app\n"); 3296 global_ret = EXIT_NOTINSTALLED; 3297 GNUNET_SCHEDULER_shutdown (); 3298 return; 3299 } 3300 /* /static/ is currently not used */ 3301 /* (void) TMH_statics_init (); */ 3302 if (NULL == 3303 (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4, 3304 GNUNET_YES))) 3305 { 3306 global_ret = EXIT_FAILURE; 3307 GNUNET_SCHEDULER_shutdown (); 3308 return; 3309 } 3310 if (NULL == 3311 (TMH_db = TALER_MERCHANTDB_plugin_load (cfg))) 3312 { 3313 global_ret = EXIT_NOTINSTALLED; 3314 GNUNET_SCHEDULER_shutdown (); 3315 return; 3316 } 3317 if (GNUNET_OK != 3318 TMH_db->connect (TMH_db->cls)) 3319 { 3320 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3321 "Failed to connect to database. Consider running taler-merchant-dbinit!\n"); 3322 global_ret = EXIT_FAILURE; 3323 GNUNET_SCHEDULER_shutdown (); 3324 return; 3325 } 3326 elen = TMH_EXCHANGES_init (config); 3327 if (GNUNET_SYSERR == elen) 3328 { 3329 global_ret = EXIT_NOTCONFIGURED; 3330 GNUNET_SCHEDULER_shutdown (); 3331 return; 3332 } 3333 if (0 == elen) 3334 { 3335 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3336 "Fatal: no trusted exchanges configured. Exiting.\n"); 3337 global_ret = EXIT_NOTCONFIGURED; 3338 GNUNET_SCHEDULER_shutdown (); 3339 return; 3340 } 3341 3342 { 3343 struct GNUNET_DB_EventHeaderP es = { 3344 .size = ntohs (sizeof (es)), 3345 .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS) 3346 }; 3347 3348 instance_eh = TMH_db->event_listen (TMH_db->cls, 3349 &es, 3350 GNUNET_TIME_UNIT_FOREVER_REL, 3351 &load_instances, 3352 NULL); 3353 } 3354 load_instances (NULL, 3355 NULL, 3356 0); 3357 { 3358 enum GNUNET_GenericReturnValue ret; 3359 3360 ret = TALER_MHD_listen_bind (cfg, 3361 "merchant", 3362 &start_daemon, 3363 NULL); 3364 switch (ret) 3365 { 3366 case GNUNET_SYSERR: 3367 global_ret = EXIT_NOTCONFIGURED; 3368 GNUNET_SCHEDULER_shutdown (); 3369 return; 3370 case GNUNET_NO: 3371 if (! have_daemons) 3372 { 3373 global_ret = EXIT_NOTCONFIGURED; 3374 GNUNET_SCHEDULER_shutdown (); 3375 return; 3376 } 3377 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3378 "Could not open all configured listen sockets\n"); 3379 break; 3380 case GNUNET_OK: 3381 break; 3382 } 3383 } 3384 global_ret = EXIT_SUCCESS; 3385 } 3386 3387 3388 /** 3389 * The main function of the serve tool 3390 * 3391 * @param argc number of arguments from the command line 3392 * @param argv command line arguments 3393 * @return 0 ok, non-zero on error 3394 */ 3395 int 3396 main (int argc, 3397 char *const *argv) 3398 { 3399 enum GNUNET_GenericReturnValue res; 3400 struct GNUNET_GETOPT_CommandLineOption options[] = { 3401 GNUNET_GETOPT_option_flag ('C', 3402 "connection-close", 3403 "force HTTP connections to be closed after each request", 3404 &merchant_connection_close), 3405 GNUNET_GETOPT_option_timetravel ('T', 3406 "timetravel"), 3407 GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION), 3408 GNUNET_GETOPT_OPTION_END 3409 }; 3410 3411 res = GNUNET_PROGRAM_run ( 3412 TALER_MERCHANT_project_data (), 3413 argc, argv, 3414 "taler-merchant-httpd", 3415 "Taler merchant's HTTP backend interface", 3416 options, 3417 &run, NULL); 3418 if (GNUNET_SYSERR == res) 3419 return EXIT_INVALIDARGUMENT; 3420 if (GNUNET_NO == res) 3421 return EXIT_SUCCESS; 3422 return global_ret; 3423 }