taler-merchant-httpd_post-private-orders.c (119082B)
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 6 it under the terms of the GNU Affero General Public License as 7 published by the Free Software Foundation; either version 3, 8 or (at your option) any later version. 9 10 TALER is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public 16 License along with TALER; see the file COPYING. If not, 17 see <http://www.gnu.org/licenses/> 18 */ 19 20 /** 21 * @file src/backend/taler-merchant-httpd_post-private-orders.c 22 * @brief the POST /orders handler 23 * @author Christian Grothoff 24 * @author Marcello Stanisci 25 * @author Christian Blättler 26 */ 27 #include "platform.h" 28 #include <gnunet/gnunet_common.h> 29 #include <gnunet/gnunet_db_lib.h> 30 #include <gnunet/gnunet_json_lib.h> 31 #include <gnunet/gnunet_time_lib.h> 32 #include <jansson.h> 33 #include <microhttpd.h> 34 #include <string.h> 35 #include <taler/taler_error_codes.h> 36 #include <taler/taler_signatures.h> 37 #include <taler/taler_json_lib.h> 38 #include <taler/taler_dbevents.h> 39 #include <taler/taler_util.h> 40 #include <taler/taler_merchant_util.h> 41 #include <time.h> 42 #include "taler-merchant-httpd.h" 43 #include "taler-merchant-httpd_exchanges.h" 44 #include "taler-merchant-httpd_post-private-orders.h" 45 #include "taler-merchant-httpd_token-keys.h" 46 #include "taler-merchant-httpd_get-exchanges.h" 47 #include "taler-merchant-httpd_contract.h" 48 #include "taler-merchant-httpd_helper.h" 49 #include "taler-merchant-httpd_get-private-orders.h" 50 #include "merchantdb_lib.h" 51 #include "merchant-database/start.h" 52 #include "merchant-database/event_listen.h" 53 #include "merchant-database/event_notify.h" 54 #include "merchant-database/preflight.h" 55 #include "merchant-database/do_expire_locks.h" 56 #include "merchant-database/get_missing_money_pot.h" 57 #include "merchant-database/insert_order.h" 58 #include "merchant-database/insert_order_lock.h" 59 #include "merchant-database/get_order.h" 60 #include "merchant-database/get_order_summary.h" 61 #include "merchant-database/get_product.h" 62 #include "merchant-database/iterate_token_family_keys.h" 63 #include "merchant-database/iterate_donau_instances_filtered.h" 64 #include "merchant-database/get_otp_device.h" 65 #include "merchant-database/delete_inventory_lock.h" 66 67 68 /** 69 * How often do we retry the simple INSERT database transaction? 70 */ 71 #define MAX_RETRIES 3 72 73 /** 74 * Maximum number of inventory products per order. 75 */ 76 #define MAX_PRODUCTS 1024 77 78 /** 79 * What is the label under which we find/place the merchant's 80 * jurisdiction in the locations list by default? 81 */ 82 #define STANDARD_LABEL_MERCHANT_JURISDICTION "_mj" 83 84 /** 85 * What is the label under which we find/place the merchant's 86 * address in the locations list by default? 87 */ 88 #define STANDARD_LABEL_MERCHANT_ADDRESS "_ma" 89 90 /** 91 * How long do we wait at most for /keys from the exchange(s)? 92 * Ensures that we do not block forever just because some exchange 93 * fails to respond *or* because our taler-merchant-keyscheck 94 * refuses a forced download. 95 */ 96 #define MAX_KEYS_WAIT \ 97 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 2500) 98 99 /** 100 * Generate the base URL for the given merchant instance. 101 * 102 * @param connection the MHD connection 103 * @param instance_id the merchant instance ID 104 * @returns the merchant instance's base URL 105 */ 106 static char * 107 make_merchant_base_url (struct MHD_Connection *connection, 108 const char *instance_id) 109 { 110 struct GNUNET_Buffer buf; 111 112 if (GNUNET_OK != 113 TMH_base_url_by_connection (connection, 114 instance_id, 115 &buf)) 116 return NULL; 117 GNUNET_buffer_write_path (&buf, 118 ""); 119 return GNUNET_buffer_reap_str (&buf); 120 } 121 122 123 /** 124 * Information about a product we are supposed to add to the order 125 * based on what we know it from our inventory. 126 */ 127 struct InventoryProduct 128 { 129 /** 130 * Identifier of the product in the inventory. 131 */ 132 const char *product_id; 133 134 /** 135 * Number of units of the product to add to the order (integer part). 136 */ 137 uint64_t quantity; 138 139 /** 140 * Fractional part of the quantity in units of 1/1000000 of the base value. 141 */ 142 uint32_t quantity_frac; 143 144 /** 145 * True if the integer quantity field was missing in the request. 146 */ 147 bool quantity_missing; 148 149 /** 150 * String representation of the quantity, if supplied. 151 */ 152 const char *unit_quantity; 153 154 /** 155 * True if the string quantity field was missing in the request. 156 */ 157 bool unit_quantity_missing; 158 159 /** 160 * Money pot associated with the product. 0 for none. 161 */ 162 uint64_t product_money_pot; 163 164 }; 165 166 167 /** 168 * Handle for a rekey operation where we (re)request 169 * the /keys from the exchange. 170 */ 171 struct RekeyExchange 172 { 173 /** 174 * Kept in a DLL. 175 */ 176 struct RekeyExchange *prev; 177 178 /** 179 * Kept in a DLL. 180 */ 181 struct RekeyExchange *next; 182 183 /** 184 * order this is for. 185 */ 186 struct OrderContext *oc; 187 188 /** 189 * Base URL of the exchange. 190 */ 191 char *url; 192 193 /** 194 * Request for keys. 195 */ 196 struct TMH_EXCHANGES_KeysOperation *fo; 197 198 }; 199 200 201 /** 202 * Data structure where we evaluate the viability of a given 203 * wire method for this order. 204 */ 205 struct WireMethodCandidate 206 { 207 /** 208 * Kept in a DLL. 209 */ 210 struct WireMethodCandidate *next; 211 212 /** 213 * Kept in a DLL. 214 */ 215 struct WireMethodCandidate *prev; 216 217 /** 218 * The wire method we are evaluating. 219 */ 220 const struct TMH_WireMethod *wm; 221 222 /** 223 * List of exchanges to use when we use this wire method. 224 */ 225 json_t *exchanges; 226 227 /** 228 * Set of maximum amounts that could be paid over all available exchanges 229 * for this @a wm. Used to determine if this order creation requests exceeds 230 * legal limits. 231 */ 232 struct TALER_AmountSet total_exchange_limits; 233 234 }; 235 236 237 /** 238 * Information we keep per order we are processing. 239 */ 240 struct OrderContext 241 { 242 /** 243 * Information set in the #ORDER_PHASE_PARSE_REQUEST phase. 244 */ 245 struct 246 { 247 /** 248 * Order field of the request 249 */ 250 json_t *order; 251 252 /** 253 * Set to how long refunds will be allowed. 254 */ 255 struct GNUNET_TIME_Relative refund_delay; 256 257 /** 258 * RFC8905 payment target type to find a matching merchant account 259 */ 260 const char *payment_target; 261 262 /** 263 * Shared key to use with @e pos_algorithm. 264 */ 265 char *pos_key; 266 267 /** 268 * Selected algorithm (by template) when we are to 269 * generate an OTP code for payment confirmation. 270 */ 271 enum TALER_MerchantConfirmationAlgorithm pos_algorithm; 272 273 /** 274 * Challenge from the offline verifier to sign once the order is 275 * paid. Only meaningful when @e pos_algorithm is a 276 * challenge-signature algorithm: the parser rejects a request 277 * that pairs a challenge with any other algorithm. 278 */ 279 struct TALER_PosChallengeP pos_challenge; 280 281 /** 282 * Hash of the POST request data, used to detect 283 * idempotent requests. 284 */ 285 struct TALER_MerchantPostDataHashP h_post_data; 286 287 /** 288 * Length of the @e inventory_products array. 289 */ 290 unsigned int inventory_products_length; 291 292 /** 293 * Specifies that some products are to be included in the 294 * order from the inventory. For these inventory management 295 * is performed (so the products must be in stock). 296 */ 297 struct InventoryProduct *inventory_products; 298 299 /** 300 * Length of the @e uuids array. 301 */ 302 unsigned int uuids_length; 303 304 /** 305 * array of UUIDs used to reserve products from @a inventory_products. 306 */ 307 struct GNUNET_Uuid *uuids; 308 309 /** 310 * Claim token for the request. 311 */ 312 struct TALER_ClaimTokenP claim_token; 313 314 /** 315 * Session ID (optional) to use for the order. 316 */ 317 const char *session_id; 318 319 } parse_request; 320 321 /** 322 * Information set in the #ORDER_PHASE_PARSE_ORDER phase. 323 */ 324 struct 325 { 326 327 /** 328 * The main order data as provided by the client. 329 */ 330 struct TALER_MERCHANT_Order *order; 331 332 /** 333 * Base URL of this merchant. 334 */ 335 char *merchant_base_url; 336 337 /** 338 * Wire transfer round-up interval to apply. 339 */ 340 enum GNUNET_TIME_RounderInterval wire_deadline_rounder; 341 342 } parse_order; 343 344 /** 345 * Information set in the #ORDER_PHASE_PARSE_CHOICES phase. 346 */ 347 struct 348 { 349 /** 350 * Array of possible specific contracts the wallet/customer may choose 351 * from by selecting the respective index when signing the deposit 352 * confirmation. 353 */ 354 struct TALER_MERCHANT_ContractChoice *choices; 355 356 /** 357 * Length of the @e choices array. 358 */ 359 unsigned int choices_len; 360 361 /** 362 * Array of token families referenced in the contract. 363 */ 364 struct TALER_MERCHANT_ContractTokenFamily *token_families; 365 366 /** 367 * Length of the @e token_families array. 368 */ 369 unsigned int token_families_len; 370 } parse_choices; 371 372 /** 373 * Information set in the #ORDER_PHASE_MERGE_INVENTORY phase. 374 */ 375 struct 376 { 377 /** 378 * Merged array of products in the @e order. 379 */ 380 json_t *products; 381 } merge_inventory; 382 383 /** 384 * Information set in the #ORDER_PHASE_ADD_PAYMENT_DETAILS phase. 385 */ 386 struct 387 { 388 389 /** 390 * DLL of wire methods under evaluation. 391 */ 392 struct WireMethodCandidate *wmc_head; 393 394 /** 395 * DLL of wire methods under evaluation. 396 */ 397 struct WireMethodCandidate *wmc_tail; 398 399 /** 400 * Array of maximum amounts that appear in the contract choices 401 * per currency. 402 * Determines the maximum amounts that a client could pay for this 403 * order and which we must thus make sure is acceptable for the 404 * selected wire method/account if possible. 405 */ 406 struct TALER_Amount *max_choice_limits; 407 408 /** 409 * Length of the @e max_choice_limits array. 410 */ 411 unsigned int num_max_choice_limits; 412 413 /** 414 * Set to true if we may need an exchange. True if any amount is non-zero. 415 */ 416 bool need_exchange; 417 418 } add_payment_details; 419 420 /** 421 * Information set in the #ORDER_PHASE_SELECT_WIRE_METHOD phase. 422 */ 423 struct 424 { 425 426 /** 427 * Array of exchanges we find acceptable for this order and wire method. 428 */ 429 json_t *exchanges; 430 431 /** 432 * Wire method (and our bank account) we have selected 433 * to be included for this order. 434 */ 435 const struct TMH_WireMethod *wm; 436 437 } select_wire_method; 438 439 /** 440 * Information set in the #ORDER_PHASE_SET_EXCHANGES phase. 441 */ 442 struct 443 { 444 445 /** 446 * Forced requests to /keys to update our exchange 447 * information. 448 */ 449 struct RekeyExchange *pending_reload_head; 450 451 /** 452 * Forced requests to /keys to update our exchange 453 * information. 454 */ 455 struct RekeyExchange *pending_reload_tail; 456 457 /** 458 * How long do we wait at most until giving up on getting keys? 459 */ 460 struct GNUNET_TIME_Absolute keys_timeout; 461 462 /** 463 * Task to wake us up on @e keys_timeout. 464 */ 465 struct GNUNET_SCHEDULER_Task *wakeup_task; 466 467 /** 468 * Array of reasons why a particular exchange may be 469 * limited or not be eligible. 470 */ 471 json_t *exchange_rejections; 472 473 /** 474 * Did we previously force reloading of /keys from 475 * all exchanges? Set to 'true' to prevent us from 476 * doing it again (and again...). 477 */ 478 bool forced_reload; 479 480 /** 481 * Did we find a working exchange? 482 */ 483 bool exchange_ok; 484 485 /** 486 * Did we find an exchange that justifies 487 * reloading keys? 488 */ 489 bool promising_exchange; 490 491 /** 492 * Set to true once we have attempted to load exchanges 493 * for the first time. 494 */ 495 bool exchanges_tried; 496 497 /** 498 * Details depending on the contract version. 499 */ 500 union 501 { 502 503 /** 504 * Details for contract v0. 505 */ 506 struct 507 { 508 /** 509 * Maximum fee for @e order based on STEFAN curves. 510 * Used to set @e max_fee if not provided as part of 511 * @e order. 512 */ 513 struct TALER_Amount max_stefan_fee; 514 515 } v0; 516 517 /** 518 * Details for contract v1. 519 */ 520 struct 521 { 522 /** 523 * Maximum fee for @e order based on STEFAN curves by 524 * contract choice. 525 * Used to set @e max_fee if not provided as part of 526 * @e order. 527 */ 528 struct TALER_Amount *max_stefan_fees; 529 530 } v1; 531 532 } details; 533 534 } set_exchanges; 535 536 /** 537 * Information set in the #ORDER_PHASE_SET_MAX_FEE phase. 538 */ 539 struct 540 { 541 542 /** 543 * Details depending on the contract version. 544 */ 545 union 546 { 547 548 /** 549 * Details for contract v0. 550 */ 551 struct 552 { 553 /** 554 * Maximum fee 555 */ 556 struct TALER_Amount max_fee; 557 } v0; 558 559 /** 560 * Details for contract v1. 561 */ 562 struct 563 { 564 /** 565 * Maximum fees by contract choice. 566 */ 567 struct TALER_Amount *max_fees; 568 569 } v1; 570 571 } details; 572 } set_max_fee; 573 574 /** 575 * Information set in the #ORDER_PHASE_EXECUTE_ORDER phase. 576 */ 577 struct 578 { 579 /** 580 * Which product (by offset) is out of stock, UINT_MAX if all were in-stock. 581 */ 582 unsigned int out_of_stock_index; 583 584 /** 585 * Set to a previous claim token *if* @e idempotent 586 * is also true. 587 */ 588 struct TALER_ClaimTokenP token; 589 590 /** 591 * Set to true if the order was idempotent and there 592 * was an equivalent one before. 593 */ 594 bool idempotent; 595 596 /** 597 * Set to true if the order is in conflict with a 598 * previous order with the same order ID. 599 */ 600 bool conflict; 601 } execute_order; 602 603 struct 604 { 605 /** 606 * Contract terms to store in the database. 607 */ 608 json_t *contract; 609 } serialize_order; 610 611 /** 612 * Connection of the request. 613 */ 614 struct MHD_Connection *connection; 615 616 /** 617 * Kept in a DLL while suspended. 618 */ 619 struct OrderContext *next; 620 621 /** 622 * Kept in a DLL while suspended. 623 */ 624 struct OrderContext *prev; 625 626 /** 627 * Handler context for the request. 628 */ 629 struct TMH_HandlerContext *hc; 630 631 /** 632 * #GNUNET_YES if suspended. 633 */ 634 enum GNUNET_GenericReturnValue suspended; 635 636 /** 637 * Current phase of setting up the order. 638 */ 639 enum 640 { 641 ORDER_PHASE_PARSE_REQUEST, 642 ORDER_PHASE_PARSE_ORDER, 643 ORDER_PHASE_PARSE_CHOICES, 644 ORDER_PHASE_MERGE_INVENTORY, 645 ORDER_PHASE_ADD_PAYMENT_DETAILS, 646 ORDER_PHASE_SET_EXCHANGES, 647 ORDER_PHASE_SELECT_WIRE_METHOD, 648 ORDER_PHASE_SET_MAX_FEE, 649 ORDER_PHASE_SERIALIZE_ORDER, 650 ORDER_PHASE_SALT_FORGETTABLE, 651 ORDER_PHASE_CHECK_CONTRACT, 652 ORDER_PHASE_EXECUTE_ORDER, 653 654 /** 655 * Processing is done, we should return #MHD_YES. 656 */ 657 ORDER_PHASE_FINISHED_MHD_YES, 658 659 /** 660 * Processing is done, we should return #MHD_NO. 661 */ 662 ORDER_PHASE_FINISHED_MHD_NO 663 } phase; 664 665 666 }; 667 668 669 /** 670 * Kept in a DLL while suspended. 671 */ 672 static struct OrderContext *oc_head; 673 674 /** 675 * Kept in a DLL while suspended. 676 */ 677 static struct OrderContext *oc_tail; 678 679 680 void 681 TMH_force_orders_resume () 682 { 683 struct OrderContext *oc; 684 685 while (NULL != (oc = oc_head)) 686 { 687 GNUNET_CONTAINER_DLL_remove (oc_head, 688 oc_tail, 689 oc); 690 oc->suspended = GNUNET_SYSERR; 691 MHD_resume_connection (oc->connection); 692 } 693 } 694 695 696 /** 697 * Update the phase of @a oc based on @a mret. 698 * 699 * @param[in,out] oc order to update phase for 700 * @param mret #MHD_NO to close with #MHD_NO 701 * #MHD_YES to close with #MHD_YES 702 */ 703 static void 704 finalize_order (struct OrderContext *oc, 705 enum MHD_Result mret) 706 { 707 oc->phase = (MHD_YES == mret) 708 ? ORDER_PHASE_FINISHED_MHD_YES 709 : ORDER_PHASE_FINISHED_MHD_NO; 710 } 711 712 713 /** 714 * Update the phase of @a oc based on @a ret. 715 * 716 * @param[in,out] oc order to update phase for 717 * @param ret #GNUNET_SYSERR to close with #MHD_NO 718 * #GNUNET_NO to close with #MHD_YES 719 * #GNUNET_OK is not allowed! 720 */ 721 static void 722 finalize_order2 (struct OrderContext *oc, 723 enum GNUNET_GenericReturnValue ret) 724 { 725 GNUNET_assert (GNUNET_OK != ret); 726 oc->phase = (GNUNET_NO == ret) 727 ? ORDER_PHASE_FINISHED_MHD_YES 728 : ORDER_PHASE_FINISHED_MHD_NO; 729 } 730 731 732 /** 733 * Generate an error response for @a oc. 734 * 735 * @param[in,out] oc order context to respond to 736 * @param http_status HTTP status code to set 737 * @param ec error code to set 738 * @param detail error message detail to set 739 */ 740 static void 741 reply_with_error (struct OrderContext *oc, 742 unsigned int http_status, 743 enum TALER_ErrorCode ec, 744 const char *detail) 745 { 746 enum MHD_Result mret; 747 748 mret = TALER_MHD_reply_with_error (oc->connection, 749 http_status, 750 ec, 751 detail); 752 finalize_order (oc, 753 mret); 754 } 755 756 757 /** 758 * Clean up memory used by @a wmc. 759 * 760 * @param[in,out] oc order context the WMC is part of 761 * @param[in] wmc wire method candidate to free 762 */ 763 static void 764 free_wmc (struct OrderContext *oc, 765 struct WireMethodCandidate *wmc) 766 { 767 GNUNET_CONTAINER_DLL_remove (oc->add_payment_details.wmc_head, 768 oc->add_payment_details.wmc_tail, 769 wmc); 770 TALER_amount_set_free (&wmc->total_exchange_limits); 771 json_decref (wmc->exchanges); 772 GNUNET_free (wmc); 773 } 774 775 776 /** 777 * Clean up memory used by @a cls. 778 * 779 * @param[in] cls the `struct OrderContext` to clean up 780 */ 781 static void 782 clean_order (void *cls) 783 { 784 struct OrderContext *oc = cls; 785 struct RekeyExchange *rx; 786 787 while (NULL != oc->add_payment_details.wmc_head) 788 free_wmc (oc, 789 oc->add_payment_details.wmc_head); 790 while (NULL != (rx = oc->set_exchanges.pending_reload_head)) 791 { 792 GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head, 793 oc->set_exchanges.pending_reload_tail, 794 rx); 795 TMH_EXCHANGES_keys4exchange_cancel (rx->fo); 796 GNUNET_free (rx->url); 797 GNUNET_free (rx); 798 } 799 GNUNET_array_grow (oc->add_payment_details.max_choice_limits, 800 oc->add_payment_details.num_max_choice_limits, 801 0); 802 if (NULL != oc->set_exchanges.wakeup_task) 803 { 804 GNUNET_SCHEDULER_cancel (oc->set_exchanges.wakeup_task); 805 oc->set_exchanges.wakeup_task = NULL; 806 } 807 if (NULL != oc->select_wire_method.exchanges) 808 { 809 json_decref (oc->select_wire_method.exchanges); 810 oc->select_wire_method.exchanges = NULL; 811 } 812 if (NULL != oc->set_exchanges.exchange_rejections) 813 { 814 json_decref (oc->set_exchanges.exchange_rejections); 815 oc->set_exchanges.exchange_rejections = NULL; 816 } 817 if (NULL != oc->parse_order.order) 818 { 819 switch (oc->parse_order.order->base->version) 820 { 821 case TALER_MERCHANT_CONTRACT_VERSION_0: 822 break; 823 case TALER_MERCHANT_CONTRACT_VERSION_1: 824 GNUNET_free (oc->set_max_fee.details.v1.max_fees); 825 GNUNET_free (oc->set_exchanges.details.v1.max_stefan_fees); 826 break; 827 } 828 TALER_MERCHANT_order_free (oc->parse_order.order); 829 oc->parse_order.order = NULL; 830 GNUNET_free (oc->parse_order.merchant_base_url); 831 } 832 if (NULL != oc->merge_inventory.products) 833 { 834 json_decref (oc->merge_inventory.products); 835 oc->merge_inventory.products = NULL; 836 } 837 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 838 { 839 TALER_MERCHANT_contract_choice_free (&oc->parse_choices.choices[i]); 840 } 841 GNUNET_array_grow (oc->parse_choices.choices, 842 oc->parse_choices.choices_len, 843 0); 844 for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++) 845 { 846 TALER_MERCHANT_contract_token_family_free ( 847 &oc->parse_choices.token_families[i]); 848 } 849 GNUNET_array_grow (oc->parse_choices.token_families, 850 oc->parse_choices.token_families_len, 851 0); 852 GNUNET_array_grow (oc->parse_request.inventory_products, 853 oc->parse_request.inventory_products_length, 854 0); 855 GNUNET_array_grow (oc->parse_request.uuids, 856 oc->parse_request.uuids_length, 857 0); 858 GNUNET_free (oc->parse_request.pos_key); 859 json_decref (oc->parse_request.order); 860 json_decref (oc->serialize_order.contract); 861 GNUNET_free (oc); 862 } 863 864 865 /* ***************** ORDER_PHASE_EXECUTE_ORDER **************** */ 866 867 /** 868 * Compute the quantity (integer and fractional parts) of a product that is 869 * actually available for a new order. This excludes units already sold, 870 * lost, or currently reserved by locks (shopping carts and unpaid orders). 871 * 872 * @param pd product details with current totals/sold/lost/locked 873 * @param[out] available_value remaining whole units (normalized, non-negative) 874 * @param[out] available_frac remaining fractional units (0..TALER_MERCHANT_UNIT_FRAC_BASE-1) 875 */ 876 static void 877 compute_available_quantity ( 878 const struct TALER_MERCHANTDB_ProductDetails *pd, 879 uint64_t *available_value, 880 uint32_t *available_frac) 881 { 882 int64_t value; 883 int64_t frac; 884 885 GNUNET_assert (NULL != available_value); 886 GNUNET_assert (NULL != available_frac); 887 888 if ( (INT64_MAX == pd->total_stock) && 889 (INT32_MAX == pd->total_stock_frac) ) 890 { 891 *available_value = pd->total_stock; 892 *available_frac = pd->total_stock_frac; 893 return; 894 } 895 896 value = (int64_t) pd->total_stock 897 - (int64_t) pd->total_sold 898 - (int64_t) pd->total_lost 899 - (int64_t) pd->total_locked; 900 frac = (int64_t) pd->total_stock_frac 901 - (int64_t) pd->total_sold_frac 902 - (int64_t) pd->total_lost_frac 903 - (int64_t) pd->total_locked_frac; 904 905 if (frac < 0) 906 { 907 int64_t borrow = ((-frac) + TALER_MERCHANT_UNIT_FRAC_BASE - 1) 908 / TALER_MERCHANT_UNIT_FRAC_BASE; 909 910 value -= borrow; 911 frac += borrow * (int64_t) TALER_MERCHANT_UNIT_FRAC_BASE; 912 } 913 else if (frac >= TALER_MERCHANT_UNIT_FRAC_BASE) 914 { 915 int64_t carry = frac / TALER_MERCHANT_UNIT_FRAC_BASE; 916 917 value += carry; 918 frac -= carry * (int64_t) TALER_MERCHANT_UNIT_FRAC_BASE; 919 } 920 921 if (value < 0) 922 { 923 GNUNET_break (0); 924 value = 0; 925 frac = 0; 926 } 927 928 *available_value = (uint64_t) value; 929 *available_frac = (uint32_t) frac; 930 } 931 932 933 /** 934 * Execute the database transaction to setup the order. 935 * 936 * @param[in,out] oc order context 937 * @return transaction status, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a uuids were insufficient to reserve required inventory 938 */ 939 static enum GNUNET_DB_QueryStatus 940 execute_transaction (struct OrderContext *oc) 941 { 942 enum GNUNET_DB_QueryStatus qs; 943 struct GNUNET_TIME_Timestamp timestamp; 944 uint64_t order_serial; 945 946 if (GNUNET_OK != 947 TALER_MERCHANTDB_start (TMH_db, 948 "insert_order")) 949 { 950 GNUNET_break (0); 951 return GNUNET_DB_STATUS_HARD_ERROR; 952 } 953 954 /* Test if we already have an order with this id */ 955 { 956 json_t *contract_terms; 957 struct TALER_MerchantPostDataHashP orig_post; 958 959 qs = TALER_MERCHANTDB_get_order (TMH_db, 960 oc->hc->instance->settings.id, 961 oc->parse_order.order->order_id, 962 &oc->execute_order.token, 963 &orig_post, 964 &contract_terms); 965 /* If yes, check for idempotency */ 966 if (0 > qs) 967 { 968 GNUNET_break (0); 969 TALER_MERCHANTDB_rollback (TMH_db); 970 return qs; 971 } 972 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) 973 { 974 TALER_MERCHANTDB_rollback (TMH_db); 975 json_decref (contract_terms); 976 /* Comparing the contract terms is sufficient because all the other 977 params get added to it at some point. */ 978 if (0 == GNUNET_memcmp (&orig_post, 979 &oc->parse_request.h_post_data)) 980 { 981 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 982 "Order creation idempotent\n"); 983 oc->execute_order.idempotent = true; 984 return qs; 985 } 986 GNUNET_break_op (0); 987 oc->execute_order.conflict = true; 988 return qs; 989 } 990 } 991 992 /* Setup order */ 993 qs = TALER_MERCHANTDB_insert_order (TMH_db, 994 oc->hc->instance->settings.id, 995 oc->parse_order.order->order_id, 996 oc->parse_request.session_id, 997 &oc->parse_request.h_post_data, 998 oc->parse_order.order->pay_deadline, 999 &oc->parse_request.claim_token, 1000 oc->serialize_order.contract, /* called 'contract terms' at database. */ 1001 oc->parse_request.pos_key, 1002 oc->parse_request.pos_algorithm, 1003 /* the parser only accepts a challenge 1004 together with these algorithms */ 1005 ( (TALER_MCA_ECDSA_CHALLENGE 1006 == oc->parse_request.pos_algorithm) || 1007 (TALER_MCA_EDDSA_CHALLENGE 1008 == oc->parse_request.pos_algorithm) ) 1009 ? &oc->parse_request.pos_challenge 1010 : NULL); 1011 if (qs <= 0) 1012 { 1013 /* qs == 0: probably instance does not exist (anymore) */ 1014 TALER_MERCHANTDB_rollback (TMH_db); 1015 return qs; 1016 } 1017 /* Migrate locks from UUIDs to new order: first release old locks */ 1018 for (unsigned int i = 0; i<oc->parse_request.uuids_length; i++) 1019 { 1020 qs = TALER_MERCHANTDB_delete_inventory_lock (TMH_db, 1021 &oc->parse_request.uuids[i]); 1022 if (qs < 0) 1023 { 1024 TALER_MERCHANTDB_rollback (TMH_db); 1025 return qs; 1026 } 1027 /* qs == 0 is OK here, that just means we did not HAVE any lock under this 1028 UUID */ 1029 } 1030 /* Migrate locks from UUIDs to new order: acquire new locks 1031 (note: this can basically ONLY fail on serializability OR 1032 because the UUID locks were insufficient for the desired 1033 quantities). */ 1034 for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++) 1035 { 1036 qs = TALER_MERCHANTDB_insert_order_lock ( 1037 TMH_db, 1038 oc->hc->instance->settings.id, 1039 oc->parse_order.order->order_id, 1040 oc->parse_request.inventory_products[i].product_id, 1041 oc->parse_request.inventory_products[i].quantity, 1042 oc->parse_request.inventory_products[i].quantity_frac); 1043 if (qs < 0) 1044 { 1045 TALER_MERCHANTDB_rollback (TMH_db); 1046 return qs; 1047 } 1048 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1049 { 1050 /* qs == 0: lock acquisition failed due to insufficient stocks */ 1051 TALER_MERCHANTDB_rollback (TMH_db); 1052 oc->execute_order.out_of_stock_index = i; /* indicate which product is causing the issue */ 1053 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 1054 } 1055 } 1056 oc->execute_order.out_of_stock_index = UINT_MAX; 1057 1058 /* Get the order serial and timestamp for the order we just created to 1059 update long-poll clients. */ 1060 qs = TALER_MERCHANTDB_get_order_summary ( 1061 TMH_db, 1062 oc->hc->instance->settings.id, 1063 oc->parse_order.order->order_id, 1064 ×tamp, 1065 &order_serial); 1066 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1067 { 1068 TALER_MERCHANTDB_rollback (TMH_db); 1069 return qs; 1070 } 1071 1072 { 1073 json_t *jhook; 1074 1075 jhook = GNUNET_JSON_PACK ( 1076 GNUNET_JSON_pack_string ("order_id", 1077 oc->parse_order.order->order_id), 1078 GNUNET_JSON_pack_object_incref ("contract", 1079 oc->serialize_order.contract), 1080 GNUNET_JSON_pack_string ("instance_id", 1081 oc->hc->instance->settings.id) 1082 ); 1083 GNUNET_assert (NULL != jhook); 1084 qs = TMH_trigger_webhook (oc->hc->instance->settings.id, 1085 "order_created", 1086 jhook); 1087 json_decref (jhook); 1088 if (0 > qs) 1089 { 1090 TALER_MERCHANTDB_rollback (TMH_db); 1091 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 1092 return qs; 1093 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1094 reply_with_error (oc, 1095 MHD_HTTP_INTERNAL_SERVER_ERROR, 1096 TALER_EC_GENERIC_DB_STORE_FAILED, 1097 "failed to trigger webhooks"); 1098 return qs; 1099 } 1100 } 1101 1102 TMH_notify_order_change (oc->hc->instance, 1103 TMH_OSF_NONE, 1104 timestamp, 1105 order_serial); 1106 /* finally, commit transaction (note: if it fails, we ALSO re-acquire 1107 the UUID locks, which is exactly what we want) */ 1108 qs = TALER_MERCHANTDB_commit (TMH_db); 1109 if (0 > qs) 1110 return qs; 1111 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* 1 == success! */ 1112 } 1113 1114 1115 /** 1116 * The request was successful, generate the #MHD_HTTP_OK response. 1117 * 1118 * @param[in,out] oc context to update 1119 * @param claim_token claim token to use, NULL if none 1120 */ 1121 static void 1122 yield_success_response (struct OrderContext *oc, 1123 const struct TALER_ClaimTokenP *claim_token) 1124 { 1125 enum MHD_Result ret; 1126 1127 ret = TALER_MHD_REPLY_JSON_PACK ( 1128 oc->connection, 1129 MHD_HTTP_OK, 1130 GNUNET_JSON_pack_string ("order_id", 1131 oc->parse_order.order->order_id), 1132 GNUNET_JSON_pack_timestamp ("pay_deadline", 1133 oc->parse_order.order->pay_deadline), 1134 GNUNET_JSON_pack_allow_null ( 1135 GNUNET_JSON_pack_data_auto ( 1136 "token", 1137 claim_token))); 1138 finalize_order (oc, 1139 ret); 1140 } 1141 1142 1143 /** 1144 * Transform an order into a proposal and store it in the 1145 * database. Write the resulting proposal or an error message 1146 * of a MHD connection. 1147 * 1148 * @param[in,out] oc order context 1149 */ 1150 static void 1151 phase_execute_order (struct OrderContext *oc) 1152 { 1153 const struct TALER_MERCHANTDB_InstanceSettings *settings = 1154 &oc->hc->instance->settings; 1155 enum GNUNET_DB_QueryStatus qs; 1156 1157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1158 "Executing database transaction to create order '%s' for instance '%s'\n", 1159 oc->parse_order.order->order_id, 1160 settings->id); 1161 for (unsigned int i = 0; i<MAX_RETRIES; i++) 1162 { 1163 TALER_MERCHANTDB_preflight (TMH_db); 1164 qs = execute_transaction (oc); 1165 if (GNUNET_DB_STATUS_SOFT_ERROR != qs) 1166 break; 1167 } 1168 if (0 >= qs) 1169 { 1170 /* Special report if retries insufficient */ 1171 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 1172 { 1173 GNUNET_break (0); 1174 reply_with_error (oc, 1175 MHD_HTTP_INTERNAL_SERVER_ERROR, 1176 TALER_EC_GENERIC_DB_SOFT_FAILURE, 1177 NULL); 1178 return; 1179 } 1180 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1181 { 1182 /* should be: contract (!) with same order ID 1183 already exists */ 1184 reply_with_error ( 1185 oc, 1186 MHD_HTTP_CONFLICT, 1187 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, 1188 oc->parse_order.order->order_id); 1189 return; 1190 } 1191 /* Other hard transaction error (disk full, etc.) */ 1192 GNUNET_break (0); 1193 reply_with_error ( 1194 oc, 1195 MHD_HTTP_INTERNAL_SERVER_ERROR, 1196 TALER_EC_GENERIC_DB_COMMIT_FAILED, 1197 NULL); 1198 return; 1199 } 1200 1201 /* DB transaction succeeded, check for idempotent */ 1202 if (oc->execute_order.idempotent) 1203 { 1204 yield_success_response (oc, 1205 GNUNET_is_zero (&oc->execute_order.token) 1206 ? NULL 1207 : &oc->execute_order.token); 1208 return; 1209 } 1210 if (oc->execute_order.conflict) 1211 { 1212 reply_with_error ( 1213 oc, 1214 MHD_HTTP_CONFLICT, 1215 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, 1216 oc->parse_order.order->order_id); 1217 return; 1218 } 1219 1220 /* DB transaction succeeded, check for out-of-stock */ 1221 if (oc->execute_order.out_of_stock_index < UINT_MAX) 1222 { 1223 /* We had a product that has insufficient quantities, 1224 generate the details for the response. */ 1225 struct TALER_MERCHANTDB_ProductDetails pd; 1226 enum MHD_Result ret; 1227 const struct InventoryProduct *ip; 1228 size_t num_categories = 0; 1229 uint64_t *categories = NULL; 1230 uint64_t available_quantity; 1231 uint32_t available_quantity_frac; 1232 char requested_quantity_buf[64]; 1233 char available_quantity_buf[64]; 1234 1235 ip = &oc->parse_request.inventory_products[ 1236 oc->execute_order.out_of_stock_index]; 1237 memset (&pd, 1238 0, 1239 sizeof (pd)); 1240 qs = TALER_MERCHANTDB_get_product ( 1241 TMH_db, 1242 oc->hc->instance->settings.id, 1243 ip->product_id, 1244 &pd, 1245 &num_categories, 1246 &categories); 1247 switch (qs) 1248 { 1249 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1250 GNUNET_free (categories); 1251 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1252 "Order creation failed: product out of stock\n"); 1253 1254 compute_available_quantity (&pd, 1255 &available_quantity, 1256 &available_quantity_frac); 1257 TALER_MERCHANT_vk_format_fractional_string ( 1258 TALER_MERCHANT_VK_QUANTITY, 1259 ip->quantity, 1260 ip->quantity_frac, 1261 sizeof (requested_quantity_buf), 1262 requested_quantity_buf); 1263 TALER_MERCHANT_vk_format_fractional_string ( 1264 TALER_MERCHANT_VK_QUANTITY, 1265 available_quantity, 1266 available_quantity_frac, 1267 sizeof (available_quantity_buf), 1268 available_quantity_buf); 1269 ret = TALER_MHD_REPLY_JSON_PACK ( 1270 oc->connection, 1271 MHD_HTTP_GONE, 1272 GNUNET_JSON_pack_string ( 1273 "product_id", 1274 ip->product_id), 1275 GNUNET_JSON_pack_uint64 ( 1276 "requested_quantity", 1277 ip->quantity), 1278 GNUNET_JSON_pack_string ( 1279 "unit_requested_quantity", 1280 requested_quantity_buf), 1281 GNUNET_JSON_pack_uint64 ( 1282 "available_quantity", 1283 available_quantity), 1284 GNUNET_JSON_pack_string ( 1285 "unit_available_quantity", 1286 available_quantity_buf), 1287 GNUNET_JSON_pack_allow_null ( 1288 GNUNET_JSON_pack_timestamp ( 1289 "restock_expected", 1290 pd.next_restock))); 1291 TALER_MERCHANTDB_product_details_free (&pd); 1292 finalize_order (oc, 1293 ret); 1294 return; 1295 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1296 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1297 "Order creation failed: unknown product out of stock\n"); 1298 finalize_order (oc, 1299 TALER_MHD_REPLY_JSON_PACK ( 1300 oc->connection, 1301 MHD_HTTP_GONE, 1302 GNUNET_JSON_pack_string ( 1303 "product_id", 1304 ip->product_id), 1305 GNUNET_JSON_pack_uint64 ( 1306 "requested_quantity", 1307 ip->quantity), 1308 GNUNET_JSON_pack_uint64 ( 1309 "available_quantity", 1310 0))); 1311 return; 1312 case GNUNET_DB_STATUS_SOFT_ERROR: 1313 GNUNET_break (0); 1314 reply_with_error ( 1315 oc, 1316 MHD_HTTP_INTERNAL_SERVER_ERROR, 1317 TALER_EC_GENERIC_DB_SOFT_FAILURE, 1318 NULL); 1319 return; 1320 case GNUNET_DB_STATUS_HARD_ERROR: 1321 GNUNET_break (0); 1322 reply_with_error ( 1323 oc, 1324 MHD_HTTP_INTERNAL_SERVER_ERROR, 1325 TALER_EC_GENERIC_DB_FETCH_FAILED, 1326 NULL); 1327 return; 1328 } 1329 GNUNET_break (0); 1330 oc->phase = ORDER_PHASE_FINISHED_MHD_NO; 1331 return; 1332 } /* end 'out of stock' case */ 1333 1334 /* Everything in-stock, generate positive response */ 1335 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1336 "Order creation succeeded\n"); 1337 yield_success_response (oc, 1338 GNUNET_is_zero (&oc->parse_request.claim_token) 1339 ? NULL 1340 : &oc->parse_request.claim_token); 1341 } 1342 1343 1344 /* ***************** ORDER_PHASE_CHECK_CONTRACT **************** */ 1345 1346 1347 /** 1348 * Check that the contract is now well-formed. Upon success, continue 1349 * processing with execute_order(). 1350 * 1351 * @param[in,out] oc order context 1352 */ 1353 static void 1354 phase_check_contract (struct OrderContext *oc) 1355 { 1356 struct TALER_PrivateContractHashP h_control; 1357 1358 switch (TALER_JSON_contract_hash (oc->serialize_order.contract, 1359 &h_control)) 1360 { 1361 case GNUNET_SYSERR: 1362 GNUNET_break (0); 1363 reply_with_error ( 1364 oc, 1365 MHD_HTTP_INTERNAL_SERVER_ERROR, 1366 TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, 1367 "could not compute hash of serialized order"); 1368 return; 1369 case GNUNET_NO: 1370 GNUNET_break_op (0); 1371 reply_with_error ( 1372 oc, 1373 MHD_HTTP_BAD_REQUEST, 1374 TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, 1375 "order contained unallowed values"); 1376 return; 1377 case GNUNET_OK: 1378 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1379 "Contract hash is %s\n", 1380 GNUNET_h2s (&h_control.hash)); 1381 oc->phase++; 1382 return; 1383 } 1384 GNUNET_assert (0); 1385 } 1386 1387 1388 /* ***************** ORDER_PHASE_SALT_FORGETTABLE **************** */ 1389 1390 1391 /** 1392 * Modify the final contract terms adding salts for 1393 * items that are forgettable. 1394 * 1395 * @param[in,out] oc order context 1396 */ 1397 static void 1398 phase_salt_forgettable (struct OrderContext *oc) 1399 { 1400 if (GNUNET_OK != 1401 TALER_JSON_contract_seed_forgettable (oc->parse_request.order, 1402 oc->serialize_order.contract)) 1403 { 1404 GNUNET_break_op (0); 1405 reply_with_error ( 1406 oc, 1407 MHD_HTTP_BAD_REQUEST, 1408 TALER_EC_GENERIC_JSON_INVALID, 1409 "could not compute hash of order due to bogus forgettable fields"); 1410 return; 1411 } 1412 oc->phase++; 1413 } 1414 1415 1416 /* ***************** ORDER_PHASE_SERIALIZE_ORDER **************** */ 1417 1418 /** 1419 * Find the family entry for the family of the given @a slug 1420 * in @a oc. 1421 * 1422 * @param[in] oc order context to search 1423 * @param slug slug to search for 1424 * @return NULL if @a slug was not found 1425 */ 1426 static struct TALER_MERCHANT_ContractTokenFamily * 1427 find_family (const struct OrderContext *oc, 1428 const char *slug) 1429 { 1430 for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++) 1431 { 1432 if (0 == strcmp (oc->parse_choices.token_families[i].slug, 1433 slug)) 1434 { 1435 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1436 "Token family %s already in order\n", 1437 slug); 1438 return &oc->parse_choices.token_families[i]; 1439 } 1440 } 1441 return NULL; 1442 } 1443 1444 1445 /** 1446 * Function called with each applicable family key that should 1447 * be added to the respective token family of the order. 1448 * 1449 * @param cls a `struct OrderContext *` to expand 1450 * @param tfkd token family key details to add to the contract 1451 */ 1452 static void 1453 add_family_key (void *cls, 1454 const struct TALER_MERCHANTDB_TokenFamilyKeyDetails *tfkd) 1455 { 1456 struct OrderContext *oc = cls; 1457 const struct TALER_MERCHANTDB_TokenFamilyDetails *tf = &tfkd->token_family; 1458 struct TALER_MERCHANT_ContractTokenFamily *family; 1459 1460 family = find_family (oc, 1461 tf->slug); 1462 if (NULL == family) 1463 { 1464 /* Family not yet in our contract terms, create new entry */ 1465 struct TALER_MERCHANT_ContractTokenFamily new_family; 1466 1467 TMH_token_family_to_contract (tf, 1468 &new_family); 1469 GNUNET_array_append (oc->parse_choices.token_families, 1470 oc->parse_choices.token_families_len, 1471 new_family); 1472 family = &oc->parse_choices.token_families[ 1473 oc->parse_choices.token_families_len - 1]; 1474 } 1475 if (NULL == tfkd->pub.public_key) 1476 return; 1477 for (unsigned int i = 0; i<family->keys_len; i++) 1478 { 1479 /* Note: cmp() returns 0 when the keys are EQUAL (memcmp-style). */ 1480 if (0 == TALER_token_issue_pub_cmp (&family->keys[i].pub, 1481 &tfkd->pub)) 1482 { 1483 /* A matching key is already in the list. */ 1484 return; 1485 } 1486 } 1487 1488 { 1489 struct TALER_MERCHANT_ContractTokenFamilyKey key; 1490 1491 TALER_token_issue_pub_copy (&key.pub, 1492 &tfkd->pub); 1493 key.valid_after = tfkd->signature_validity_start; 1494 key.valid_before = tfkd->signature_validity_end; 1495 GNUNET_array_append (family->keys, 1496 family->keys_len, 1497 key); 1498 } 1499 } 1500 1501 1502 /** 1503 * Check if the token family with the given @a slug is already present in the 1504 * list of token families for this order. If not, fetch its details and add it 1505 * to the list. 1506 * 1507 * @param[in,out] oc order context 1508 * @param slug slug of the token family 1509 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 1510 */ 1511 static enum GNUNET_GenericReturnValue 1512 add_input_token_family (struct OrderContext *oc, 1513 const char *slug) 1514 { 1515 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 1516 struct GNUNET_TIME_Timestamp end = oc->parse_order.order->pay_deadline; 1517 enum GNUNET_DB_QueryStatus qs; 1518 enum TALER_ErrorCode ec = TALER_EC_INVALID; /* make compiler happy */ 1519 unsigned int http_status = 0; /* make compiler happy */ 1520 1521 qs = TALER_MERCHANTDB_iterate_token_family_keys ( 1522 TMH_db, 1523 oc->hc->instance->settings.id, 1524 slug, 1525 now, 1526 end, 1527 &add_family_key, 1528 oc); 1529 switch (qs) 1530 { 1531 case GNUNET_DB_STATUS_HARD_ERROR: 1532 GNUNET_break (0); 1533 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 1534 ec = TALER_EC_GENERIC_DB_FETCH_FAILED; 1535 break; 1536 case GNUNET_DB_STATUS_SOFT_ERROR: 1537 GNUNET_break (0); 1538 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 1539 ec = TALER_EC_GENERIC_DB_SOFT_FAILURE; 1540 break; 1541 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1542 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1543 "Input token family slug %s unknown\n", 1544 slug); 1545 http_status = MHD_HTTP_NOT_FOUND; 1546 ec = TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN; 1547 break; 1548 default: /* one or more results are all OK */ 1549 return GNUNET_OK; 1550 } 1551 reply_with_error (oc, 1552 http_status, 1553 ec, 1554 slug); 1555 return GNUNET_SYSERR; 1556 } 1557 1558 1559 /** 1560 * Find the index of a key in the @a family that is valid at 1561 * the time @a valid_at. 1562 * 1563 * @param family to search 1564 * @param valid_at time when the key must be valid 1565 * @param[out] key_index index to initialize 1566 * @return #GNUNET_OK if a matching key was found 1567 */ 1568 static enum GNUNET_GenericReturnValue 1569 find_key_index (struct TALER_MERCHANT_ContractTokenFamily *family, 1570 struct GNUNET_TIME_Timestamp valid_at, 1571 unsigned int *key_index) 1572 { 1573 for (unsigned int i = 0; i<family->keys_len; i++) 1574 { 1575 if ( (GNUNET_TIME_timestamp_cmp (family->keys[i].valid_after, 1576 <=, 1577 valid_at)) && 1578 (GNUNET_TIME_timestamp_cmp (family->keys[i].valid_before, 1579 >=, 1580 valid_at)) ) 1581 { 1582 /* The token family and a matching key already exist. */ 1583 *key_index = i; 1584 return GNUNET_OK; 1585 } 1586 } 1587 return GNUNET_NO; 1588 } 1589 1590 1591 /** 1592 * Check if the token family with the given @a slug is already present in the 1593 * list of token families for this order. If not, fetch its details and add it 1594 * to the list. Also checks if there is a public key with that expires after 1595 * the payment deadline. If a key covering @a valid_at exists but its private 1596 * key would be deleted before the payment deadline, the lifetime of that 1597 * private key is extended; only if no key covers @a valid_at at all do we 1598 * generate a new key pair and store it in the database. 1599 * 1600 * @param[in,out] oc order context 1601 * @param slug slug of the token family 1602 * @param valid_at time when the token returned must be valid 1603 * @param[out] key_index set to the index of the respective public 1604 * key in the @a slug's token family keys array. 1605 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 1606 */ 1607 static enum GNUNET_GenericReturnValue 1608 add_output_token_family (struct OrderContext *oc, 1609 const char *slug, 1610 struct GNUNET_TIME_Timestamp valid_at, 1611 unsigned int *key_index) 1612 { 1613 struct TALER_MERCHANTDB_TokenFamilyKeyDetails key_details; 1614 struct TALER_MERCHANT_ContractTokenFamily *family; 1615 enum GNUNET_GenericReturnValue res; 1616 bool minted; 1617 1618 family = find_family (oc, 1619 slug); 1620 if ( (NULL != family) && 1621 (GNUNET_OK == 1622 find_key_index (family, 1623 valid_at, 1624 key_index)) ) 1625 { 1626 /* Even a key already in the contract must remain usable until 1627 this order's payment deadline. */ 1628 res = TMH_token_key_extend (oc->connection, 1629 oc->hc->instance->settings.id, 1630 slug, 1631 valid_at, 1632 oc->parse_order.order->pay_deadline); 1633 if (GNUNET_OK != res) 1634 { 1635 finalize_order2 (oc, 1636 res); 1637 return GNUNET_SYSERR; 1638 } 1639 return GNUNET_OK; 1640 } 1641 res = TMH_token_key_ensure (oc->connection, 1642 oc->hc->instance->settings.id, 1643 slug, 1644 valid_at, 1645 oc->parse_order.order->pay_deadline, 1646 &key_details, 1647 &minted); 1648 if (GNUNET_OK != res) 1649 { 1650 finalize_order2 (oc, 1651 res); 1652 return GNUNET_SYSERR; 1653 } 1654 /* Add the result even if the family is already in the contract: 1655 the database may have returned a key not yet listed there. */ 1656 add_family_key (oc, 1657 &key_details); 1658 TMH_token_key_details_free (&key_details); 1659 family = find_family (oc, 1660 slug); 1661 GNUNET_assert (NULL != family); 1662 if (minted) 1663 { 1664 /* Preserve the order path's choice of the newly appended key, 1665 even if an older key covers an overlapping validity period. */ 1666 *key_index = family->keys_len - 1; 1667 } 1668 else 1669 { 1670 GNUNET_assert (GNUNET_OK == 1671 find_key_index (family, 1672 valid_at, 1673 key_index)); 1674 } 1675 return GNUNET_OK; 1676 } 1677 1678 1679 /** 1680 * Build JSON array that represents all of the token families 1681 * in the contract. 1682 * 1683 * @param[in] oc v1-style order context 1684 * @return JSON array with token families for the contract 1685 */ 1686 static json_t * 1687 output_token_families (struct OrderContext *oc) 1688 { 1689 json_t *token_families = json_object (); 1690 1691 GNUNET_assert (NULL != token_families); 1692 for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++) 1693 { 1694 const struct TALER_MERCHANT_ContractTokenFamily *family 1695 = &oc->parse_choices.token_families[i]; 1696 json_t *jfamily; 1697 1698 jfamily = TALER_MERCHANT_json_from_token_family (family); 1699 1700 GNUNET_assert (jfamily != NULL); 1701 1702 GNUNET_assert (0 == 1703 json_object_set_new (token_families, 1704 family->slug, 1705 jfamily)); 1706 } 1707 return token_families; 1708 } 1709 1710 1711 /** 1712 * Build JSON array that represents all of the contract choices 1713 * in the contract. 1714 * 1715 * @param[in] oc v1-style order context 1716 * @return JSON array with token families for the contract 1717 */ 1718 static json_t * 1719 output_contract_choices (struct OrderContext *oc) 1720 { 1721 json_t *choices = json_array (); 1722 1723 GNUNET_assert (NULL != choices); 1724 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 1725 { 1726 oc->parse_choices.choices[i].max_fee = 1727 oc->set_max_fee.details.v1.max_fees[i]; 1728 GNUNET_assert (0 == json_array_append_new ( 1729 choices, 1730 TALER_MERCHANT_json_from_contract_choice ( 1731 &oc->parse_choices.choices[i]))); 1732 } 1733 return choices; 1734 } 1735 1736 1737 /** 1738 * Serialize order into @a oc->serialize_order.contract, 1739 * ready to be stored in the database. Upon success, continue 1740 * processing with check_contract(). 1741 * 1742 * @param[in,out] oc order context 1743 */ 1744 static void 1745 phase_serialize_order (struct OrderContext *oc) 1746 { 1747 json_t *merchant; 1748 1749 merchant = TMH_instance_metadata_to_json (oc->hc->instance); 1750 GNUNET_assert (NULL != merchant); 1751 1752 oc->serialize_order.contract = GNUNET_JSON_PACK ( 1753 GNUNET_JSON_pack_string ( 1754 "order_id", 1755 oc->parse_order.order->order_id), 1756 GNUNET_JSON_pack_object_steal ( 1757 NULL, 1758 TALER_MERCHANT_base_terms_serialize (oc->parse_order.order->base)), 1759 GNUNET_JSON_pack_array_incref ( 1760 "products", 1761 oc->merge_inventory.products), 1762 GNUNET_JSON_pack_data_auto ( 1763 "h_wire", 1764 &oc->select_wire_method.wm->h_wire), 1765 GNUNET_JSON_pack_string ( 1766 "wire_method", 1767 oc->select_wire_method.wm->wire_method), 1768 GNUNET_JSON_pack_timestamp ( 1769 "timestamp", 1770 oc->parse_order.order->timestamp), 1771 GNUNET_JSON_pack_timestamp ( 1772 "pay_deadline", 1773 oc->parse_order.order->pay_deadline), 1774 GNUNET_JSON_pack_timestamp ( 1775 "wire_transfer_deadline", 1776 oc->parse_order.order->wire_transfer_deadline), 1777 GNUNET_JSON_pack_string ( 1778 "merchant_base_url", 1779 oc->parse_order.merchant_base_url), 1780 GNUNET_JSON_pack_object_steal ( 1781 "merchant", 1782 merchant), 1783 GNUNET_JSON_pack_data_auto ( 1784 "merchant_pub", 1785 &oc->hc->instance->merchant_pub), 1786 GNUNET_JSON_pack_array_incref ( 1787 "exchanges", 1788 oc->select_wire_method.exchanges)); 1789 1790 { 1791 json_t *xtra; 1792 1793 switch (oc->parse_order.order->base->version) 1794 { 1795 case TALER_MERCHANT_CONTRACT_VERSION_0: 1796 xtra = GNUNET_JSON_PACK ( 1797 TALER_JSON_pack_amount ("max_fee", 1798 &oc->set_max_fee.details.v0.max_fee), 1799 GNUNET_JSON_pack_allow_null ( 1800 TALER_JSON_pack_amount ( 1801 "tip", 1802 oc->parse_order.order->details.v0.no_tip 1803 ? NULL 1804 : &oc->parse_order.order->details.v0.tip)), 1805 TALER_JSON_pack_amount ( 1806 "amount", 1807 &oc->parse_order.order->details.v0.brutto)); 1808 break; 1809 case TALER_MERCHANT_CONTRACT_VERSION_1: 1810 { 1811 json_t *token_families = output_token_families (oc); 1812 json_t *choices = output_contract_choices (oc); 1813 1814 if ( (NULL == token_families) || 1815 (NULL == choices) ) 1816 { 1817 GNUNET_break (0); 1818 return; 1819 } 1820 xtra = GNUNET_JSON_PACK ( 1821 GNUNET_JSON_pack_array_steal ("choices", 1822 choices), 1823 GNUNET_JSON_pack_object_steal ("token_families", 1824 token_families)); 1825 break; 1826 } 1827 default: 1828 GNUNET_assert (0); 1829 } 1830 GNUNET_assert (0 == 1831 json_object_update (oc->serialize_order.contract, 1832 xtra)); 1833 json_decref (xtra); 1834 } 1835 1836 1837 /* Pack does not work here, because it doesn't set zero-values for timestamps */ 1838 GNUNET_assert (0 == 1839 json_object_set_new ( 1840 oc->serialize_order.contract, 1841 "refund_deadline", 1842 GNUNET_JSON_from_timestamp ( 1843 oc->parse_order.order->refund_deadline))); 1844 /* auto_refund should only be set if it is not 0 */ 1845 if (! GNUNET_TIME_relative_is_zero ( 1846 oc->parse_order.order->base->auto_refund)) 1847 { 1848 /* Pack does not work here, because it sets zero-values for relative times */ 1849 GNUNET_assert (0 == 1850 json_object_set_new ( 1851 oc->serialize_order.contract, 1852 "auto_refund", 1853 GNUNET_JSON_from_time_rel ( 1854 oc->parse_order.order->base->auto_refund))); 1855 } 1856 1857 oc->phase++; 1858 } 1859 1860 1861 /* ***************** ORDER_PHASE_SET_MAX_FEE **************** */ 1862 1863 1864 /** 1865 * Set @a max_fee in @a oc based on @a max_stefan_fee value if not overridden 1866 * by @a client_fee. If neither is set, set the fee to zero using currency 1867 * from @a brutto. 1868 * 1869 * @param[in,out] oc order context 1870 * @param brutto brutto amount to compute fee for 1871 * @param client_fee client-given fee override (or invalid) 1872 * @param max_stefan_fee maximum STEFAN fee of any exchange 1873 * @param max_fee set to the maximum stefan fee 1874 */ 1875 static void 1876 compute_fee (struct OrderContext *oc, 1877 const struct TALER_Amount *brutto, 1878 const struct TALER_Amount *client_fee, 1879 const struct TALER_Amount *max_stefan_fee, 1880 struct TALER_Amount *max_fee) 1881 { 1882 const struct TALER_MERCHANTDB_InstanceSettings *settings 1883 = &oc->hc->instance->settings; 1884 1885 if (GNUNET_OK == 1886 TALER_amount_is_valid (client_fee)) 1887 { 1888 *max_fee = *client_fee; 1889 return; 1890 } 1891 if ( (settings->use_stefan) && 1892 (NULL != max_stefan_fee) && 1893 (GNUNET_OK == 1894 TALER_amount_is_valid (max_stefan_fee)) ) 1895 { 1896 *max_fee = *max_stefan_fee; 1897 return; 1898 } 1899 GNUNET_assert ( 1900 GNUNET_OK == 1901 TALER_amount_set_zero (brutto->currency, 1902 max_fee)); 1903 } 1904 1905 1906 /** 1907 * Initialize "set_max_fee" in @a oc based on STEFAN value or client 1908 * preference. Upon success, continue processing in next phase. 1909 * 1910 * @param[in,out] oc order context 1911 */ 1912 static void 1913 phase_set_max_fee (struct OrderContext *oc) 1914 { 1915 switch (oc->parse_order.order->base->version) 1916 { 1917 case TALER_MERCHANT_CONTRACT_VERSION_0: 1918 compute_fee (oc, 1919 &oc->parse_order.order->details.v0.brutto, 1920 &oc->parse_order.order->details.v0.max_fee, 1921 &oc->set_exchanges.details.v0.max_stefan_fee, 1922 &oc->set_max_fee.details.v0.max_fee); 1923 break; 1924 case TALER_MERCHANT_CONTRACT_VERSION_1: 1925 oc->set_max_fee.details.v1.max_fees 1926 = GNUNET_new_array (oc->parse_choices.choices_len, 1927 struct TALER_Amount); 1928 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 1929 compute_fee (oc, 1930 &oc->parse_choices.choices[i].amount, 1931 &oc->parse_choices.choices[i].max_fee, 1932 NULL != oc->set_exchanges.details.v1.max_stefan_fees 1933 ? &oc->set_exchanges.details.v1.max_stefan_fees[i] 1934 : NULL, 1935 &oc->set_max_fee.details.v1.max_fees[i]); 1936 break; 1937 default: 1938 GNUNET_break (0); 1939 break; 1940 } 1941 oc->phase++; 1942 } 1943 1944 1945 /* ***************** ORDER_PHASE_SELECT_WIRE_METHOD **************** */ 1946 1947 /** 1948 * Phase to select a wire method that will be acceptable for the order. 1949 * If none is "perfect" (allows all choices), might jump back to the 1950 * previous phase to force "/keys" downloads to see if that helps. 1951 * 1952 * @param[in,out] oc order context 1953 */ 1954 static void 1955 phase_select_wire_method (struct OrderContext *oc) 1956 { 1957 const struct TALER_Amount *ea; 1958 struct WireMethodCandidate *best = NULL; 1959 unsigned int max_choices = 0; 1960 unsigned int want_choices = 0; 1961 bool zero_amount = false; 1962 1963 switch (oc->parse_order.order->base->version) 1964 { 1965 case TALER_MERCHANT_CONTRACT_VERSION_0: 1966 ea = &oc->parse_order.order->details.v0.brutto; 1967 if (TALER_amount_is_zero (ea)) 1968 zero_amount = true; 1969 break; 1970 case TALER_MERCHANT_CONTRACT_VERSION_1: 1971 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 1972 { 1973 ea = &oc->parse_choices.choices[i].amount; 1974 if (TALER_amount_is_zero (ea)) 1975 zero_amount = true; 1976 } 1977 break; 1978 default: 1979 GNUNET_assert (0); 1980 } 1981 1982 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 1983 NULL != wmc; 1984 wmc = wmc->next) 1985 { 1986 unsigned int num_choices = 0; 1987 1988 switch (oc->parse_order.order->base->version) 1989 { 1990 case TALER_MERCHANT_CONTRACT_VERSION_0: 1991 want_choices = 1; 1992 ea = &oc->parse_order.order->details.v0.brutto; 1993 if (TALER_amount_is_zero (ea) || 1994 TALER_amount_set_test_above (&wmc->total_exchange_limits, 1995 ea)) 1996 num_choices++; 1997 break; 1998 case TALER_MERCHANT_CONTRACT_VERSION_1: 1999 want_choices = oc->parse_choices.choices_len; 2000 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2001 { 2002 ea = &oc->parse_choices.choices[i].amount; 2003 if (TALER_amount_is_zero (ea) || 2004 TALER_amount_set_test_above (&wmc->total_exchange_limits, 2005 ea)) 2006 num_choices++; 2007 } 2008 break; 2009 default: 2010 GNUNET_assert (0); 2011 } 2012 if (num_choices > max_choices) 2013 { 2014 best = wmc; 2015 max_choices = num_choices; 2016 } 2017 } 2018 2019 if ( (want_choices > max_choices) && 2020 (oc->set_exchanges.promising_exchange) && 2021 (! oc->set_exchanges.forced_reload) ) 2022 { 2023 oc->set_exchanges.exchange_ok = false; 2024 /* Not all choices in the contract can work with these 2025 exchanges, try again with forcing /keys download */ 2026 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 2027 NULL != wmc; 2028 wmc = wmc->next) 2029 { 2030 json_array_clear (wmc->exchanges); 2031 TALER_amount_set_free (&wmc->total_exchange_limits); 2032 } 2033 oc->phase = ORDER_PHASE_SET_EXCHANGES; 2034 return; 2035 } 2036 2037 if ( (NULL == best) && 2038 (! zero_amount) && 2039 (NULL != oc->parse_request.payment_target) ) 2040 { 2041 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2042 "Cannot create order: lacking suitable exchanges for payment target `%s'\n", 2043 oc->parse_request.payment_target); 2044 reply_with_error ( 2045 oc, 2046 MHD_HTTP_CONFLICT, 2047 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, 2048 oc->parse_request.payment_target); 2049 return; 2050 } 2051 2052 if ( (NULL == best) && 2053 (! zero_amount) ) 2054 { 2055 enum MHD_Result mret; 2056 2057 /* We actually do not have ANY workable exchange(s) */ 2058 mret = TALER_MHD_reply_json_steal ( 2059 oc->connection, 2060 GNUNET_JSON_PACK ( 2061 TALER_JSON_pack_ec ( 2062 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS), 2063 GNUNET_JSON_pack_allow_null ( 2064 GNUNET_JSON_pack_array_incref ( 2065 "exchange_rejections", 2066 oc->set_exchanges.exchange_rejections))), 2067 MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS); 2068 finalize_order (oc, 2069 mret); 2070 return; 2071 } 2072 2073 if (want_choices > max_choices) 2074 { 2075 /* Some choices are unpayable */ 2076 GNUNET_log ( 2077 GNUNET_ERROR_TYPE_WARNING, 2078 "Creating order, but some choices do not work with the selected wire method\n"); 2079 } 2080 if ( (0 == json_array_size (best->exchanges)) && 2081 (oc->add_payment_details.need_exchange) ) 2082 { 2083 /* We did not find any reasonable exchange */ 2084 GNUNET_log ( 2085 GNUNET_ERROR_TYPE_WARNING, 2086 "Creating order, but only for choices without payment\n"); 2087 } 2088 2089 oc->select_wire_method.wm 2090 = best->wm; 2091 oc->select_wire_method.exchanges 2092 = json_incref (best->exchanges); 2093 oc->phase++; 2094 } 2095 2096 2097 /* ***************** ORDER_PHASE_SET_EXCHANGES **************** */ 2098 2099 /** 2100 * Exchange `/keys` processing is done, resume handling 2101 * the order. 2102 * 2103 * @param[in,out] oc context to resume 2104 */ 2105 static void 2106 resume_with_keys (struct OrderContext *oc) 2107 { 2108 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2109 "Resuming order processing after /keys downloads\n"); 2110 GNUNET_assert (GNUNET_YES == oc->suspended); 2111 GNUNET_CONTAINER_DLL_remove (oc_head, 2112 oc_tail, 2113 oc); 2114 oc->suspended = GNUNET_NO; 2115 MHD_resume_connection (oc->connection); 2116 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 2117 } 2118 2119 2120 /** 2121 * Given a @a brutto amount for exchange with @a keys, set the 2122 * @a stefan_fee. Note that @a stefan_fee is updated to the maximum 2123 * of the input and the computed fee. 2124 * 2125 * @param[in,out] keys exchange keys 2126 * @param brutto some brutto amount the client is to pay 2127 * @param[in,out] stefan_fee set to STEFAN fee to be paid by the merchant 2128 */ 2129 static void 2130 compute_stefan_fee (const struct TALER_EXCHANGE_Keys *keys, 2131 const struct TALER_Amount *brutto, 2132 struct TALER_Amount *stefan_fee) 2133 { 2134 struct TALER_Amount net; 2135 2136 if (GNUNET_SYSERR != 2137 TALER_EXCHANGE_keys_stefan_b2n (keys, 2138 brutto, 2139 &net)) 2140 { 2141 struct TALER_Amount fee; 2142 2143 TALER_EXCHANGE_keys_stefan_round (keys, 2144 &net); 2145 if (-1 == TALER_amount_cmp (brutto, 2146 &net)) 2147 { 2148 /* brutto < netto! */ 2149 /* => after rounding, there is no real difference */ 2150 net = *brutto; 2151 } 2152 GNUNET_assert (0 <= 2153 TALER_amount_subtract (&fee, 2154 brutto, 2155 &net)); 2156 if ( (GNUNET_OK != 2157 TALER_amount_is_valid (stefan_fee)) || 2158 (-1 == TALER_amount_cmp (stefan_fee, 2159 &fee)) ) 2160 { 2161 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2162 "Updated STEFAN-based fee to %s\n", 2163 TALER_amount2s (&fee)); 2164 *stefan_fee = fee; 2165 } 2166 } 2167 } 2168 2169 2170 /** 2171 * Update MAX STEFAN fees based on @a keys. 2172 * 2173 * @param[in,out] oc order context to update 2174 * @param keys keys to derive STEFAN fees from 2175 */ 2176 static void 2177 update_stefan (struct OrderContext *oc, 2178 const struct TALER_EXCHANGE_Keys *keys) 2179 { 2180 switch (oc->parse_order.order->base->version) 2181 { 2182 case TALER_MERCHANT_CONTRACT_VERSION_0: 2183 compute_stefan_fee (keys, 2184 &oc->parse_order.order->details.v0.brutto, 2185 &oc->set_exchanges.details.v0.max_stefan_fee); 2186 break; 2187 case TALER_MERCHANT_CONTRACT_VERSION_1: 2188 oc->set_exchanges.details.v1.max_stefan_fees 2189 = GNUNET_new_array (oc->parse_choices.choices_len, 2190 struct TALER_Amount); 2191 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2192 if (0 == strcasecmp (keys->currency, 2193 oc->parse_choices.choices[i].amount.currency)) 2194 compute_stefan_fee (keys, 2195 &oc->parse_choices.choices[i].amount, 2196 &oc->set_exchanges.details.v1.max_stefan_fees[i]); 2197 break; 2198 default: 2199 GNUNET_assert (0); 2200 } 2201 } 2202 2203 2204 /** 2205 * Check our KYC status at all exchanges as our current limit is 2206 * too low and we failed to create an order. 2207 * 2208 * @param oc order context 2209 * @param wmc wire method candidate to notify for 2210 * @param exchange_url exchange to notify about 2211 */ 2212 static void 2213 notify_kyc_required (const struct OrderContext *oc, 2214 const struct WireMethodCandidate *wmc, 2215 const char *exchange_url) 2216 { 2217 struct GNUNET_DB_EventHeaderP es = { 2218 .size = htons (sizeof (es)), 2219 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED) 2220 }; 2221 char *hws; 2222 char *extra; 2223 2224 hws = GNUNET_STRINGS_data_to_string_alloc ( 2225 &wmc->wm->h_wire, 2226 sizeof (wmc->wm->h_wire)); 2227 2228 GNUNET_asprintf (&extra, 2229 "%s %s", 2230 hws, 2231 exchange_url); 2232 TALER_MERCHANTDB_event_notify (TMH_db, 2233 &es, 2234 extra, 2235 strlen (extra) + 1); 2236 GNUNET_free (extra); 2237 GNUNET_free (hws); 2238 } 2239 2240 2241 /** 2242 * Add a reason why a particular exchange was rejected to our 2243 * response data. 2244 * 2245 * @param[in,out] oc order context to update 2246 * @param exchange_url exchange this is about 2247 * @param ec error code to set for the exchange 2248 */ 2249 static void 2250 add_rejection (struct OrderContext *oc, 2251 const char *exchange_url, 2252 enum TALER_ErrorCode ec) 2253 { 2254 if (NULL == oc->set_exchanges.exchange_rejections) 2255 { 2256 oc->set_exchanges.exchange_rejections = json_array (); 2257 GNUNET_assert (NULL != oc->set_exchanges.exchange_rejections); 2258 } 2259 GNUNET_assert (0 == 2260 json_array_append_new ( 2261 oc->set_exchanges.exchange_rejections, 2262 GNUNET_JSON_PACK ( 2263 GNUNET_JSON_pack_string ("exchange_url", 2264 exchange_url), 2265 TALER_JSON_pack_ec (ec)))); 2266 } 2267 2268 2269 /** 2270 * Checks the limits that apply for this @a exchange and 2271 * the @a wmc and if the exchange is acceptable at all, adds it 2272 * to the list of exchanges for the @a wmc. 2273 * 2274 * @param oc context of the order 2275 * @param exchange internal handle for the exchange 2276 * @param exchange_url base URL of this exchange 2277 * @param wmc wire method to evaluate this exchange for 2278 * @return true if the exchange is acceptable for the contract 2279 */ 2280 static bool 2281 get_acceptable (struct OrderContext *oc, 2282 const struct TMH_Exchange *exchange, 2283 const char *exchange_url, 2284 struct WireMethodCandidate *wmc) 2285 { 2286 const struct TALER_Amount *max_needed = NULL; 2287 unsigned int priority = 42; /* make compiler happy */ 2288 json_t *j_exchange; 2289 enum TMH_ExchangeStatus res; 2290 struct TALER_Amount max_amount; 2291 2292 for (unsigned int i = 0; 2293 i<oc->add_payment_details.num_max_choice_limits; 2294 i++) 2295 { 2296 const struct TALER_Amount *val 2297 = &oc->add_payment_details.max_choice_limits[i]; 2298 2299 if (0 == strcasecmp (val->currency, 2300 TMH_EXCHANGES_get_currency (exchange))) 2301 { 2302 max_needed = val; 2303 break; 2304 } 2305 } 2306 if (NULL == max_needed) 2307 { 2308 /* exchange currency not relevant for any of our choices, skip it */ 2309 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2310 "Exchange %s with currency `%s' is not applicable to this order\n", 2311 exchange_url, 2312 TMH_EXCHANGES_get_currency (exchange)); 2313 add_rejection (oc, 2314 exchange_url, 2315 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH); 2316 return false; 2317 } 2318 2319 max_amount = *max_needed; 2320 res = TMH_exchange_check_debit ( 2321 oc->hc->instance->settings.id, 2322 exchange, 2323 wmc->wm, 2324 &max_amount); 2325 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2326 "Exchange %s evaluated at %d with max %s\n", 2327 exchange_url, 2328 res, 2329 TALER_amount2s (&max_amount)); 2330 if (TALER_amount_is_zero (&max_amount)) 2331 { 2332 if (! TALER_amount_is_zero (max_needed)) 2333 { 2334 /* Trigger re-checking the current deposit limit when 2335 * paying non-zero amount with zero deposit limit */ 2336 notify_kyc_required (oc, 2337 wmc, 2338 exchange_url); 2339 } 2340 /* If deposit is impossible, we don't list the 2341 * exchange in the contract terms. */ 2342 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2343 "Exchange %s deposit limit is zero, skipping it\n", 2344 exchange_url); 2345 add_rejection (oc, 2346 exchange_url, 2347 TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED); 2348 return false; 2349 } 2350 switch (res) 2351 { 2352 case TMH_ES_OK: 2353 case TMH_ES_RETRY_OK: 2354 priority = 1024; /* high */ 2355 oc->set_exchanges.exchange_ok = true; 2356 break; 2357 case TMH_ES_NO_ACC: 2358 if (oc->set_exchanges.forced_reload) 2359 priority = 0; /* fresh negative response */ 2360 else 2361 priority = 512; /* stale negative response */ 2362 break; 2363 case TMH_ES_NO_CURR: 2364 if (oc->set_exchanges.forced_reload) 2365 priority = 0; /* fresh negative response */ 2366 else 2367 priority = 512; /* stale negative response */ 2368 break; 2369 case TMH_ES_NO_KEYS: 2370 if (oc->set_exchanges.forced_reload) 2371 priority = 256; /* fresh, no accounts yet */ 2372 else 2373 priority = 768; /* stale, no accounts yet */ 2374 break; 2375 case TMH_ES_NO_ACC_RETRY_OK: 2376 if (oc->set_exchanges.forced_reload) 2377 { 2378 priority = 0; /* fresh negative response */ 2379 } 2380 else 2381 { 2382 oc->set_exchanges.promising_exchange = true; 2383 priority = 512; /* stale negative response */ 2384 } 2385 break; 2386 case TMH_ES_NO_CURR_RETRY_OK: 2387 if (oc->set_exchanges.forced_reload) 2388 priority = 0; /* fresh negative response */ 2389 else 2390 priority = 512; /* stale negative response */ 2391 break; 2392 case TMH_ES_NO_KEYS_RETRY_OK: 2393 if (oc->set_exchanges.forced_reload) 2394 { 2395 priority = 256; /* fresh, no accounts yet */ 2396 } 2397 else 2398 { 2399 oc->set_exchanges.promising_exchange = true; 2400 priority = 768; /* stale, no accounts yet */ 2401 } 2402 break; 2403 } 2404 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2405 "Exchange %s deposit limit is %s, adding it!\n", 2406 exchange_url, 2407 TALER_amount2s (&max_amount)); 2408 2409 j_exchange = GNUNET_JSON_PACK ( 2410 GNUNET_JSON_pack_string ("url", 2411 exchange_url), 2412 GNUNET_JSON_pack_uint64 ("priority", 2413 priority), 2414 TALER_JSON_pack_amount ("max_contribution", 2415 &max_amount), 2416 GNUNET_JSON_pack_data_auto ("master_pub", 2417 TMH_EXCHANGES_get_master_pub (exchange))); 2418 GNUNET_assert (NULL != j_exchange); 2419 /* Add exchange to list of exchanges for this wire method 2420 candidate */ 2421 GNUNET_assert (0 == 2422 json_array_append_new (wmc->exchanges, 2423 j_exchange)); 2424 GNUNET_assert (0 <= 2425 TALER_amount_set_add (&wmc->total_exchange_limits, 2426 &max_amount, 2427 max_needed)); 2428 return true; 2429 } 2430 2431 2432 /** 2433 * Function called with the result of a #TMH_EXCHANGES_keys4exchange() 2434 * operation. 2435 * 2436 * @param cls closure with our `struct RekeyExchange *` 2437 * @param keys the keys of the exchange 2438 * @param exchange representation of the exchange 2439 */ 2440 static void 2441 keys_cb ( 2442 void *cls, 2443 struct TALER_EXCHANGE_Keys *keys, 2444 struct TMH_Exchange *exchange) 2445 { 2446 struct RekeyExchange *rx = cls; 2447 struct OrderContext *oc = rx->oc; 2448 const struct TALER_MERCHANTDB_InstanceSettings *settings = 2449 &oc->hc->instance->settings; 2450 bool applicable = false; 2451 2452 rx->fo = NULL; 2453 GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head, 2454 oc->set_exchanges.pending_reload_tail, 2455 rx); 2456 if (NULL == keys) 2457 { 2458 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2459 "Failed to download %skeys\n", 2460 rx->url); 2461 oc->set_exchanges.promising_exchange = true; 2462 add_rejection (oc, 2463 rx->url, 2464 TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE); 2465 goto cleanup; 2466 } 2467 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2468 "Got response for %skeys\n", 2469 rx->url); 2470 2471 /* Evaluate the use of this exchange for each wire method candidate. 2472 Note that the exchange may have *several* accounts sharing the same 2473 wire method; as get_acceptable() judges the exchange as a whole (and 2474 not the individual account), it must be called at most once per wire 2475 method candidate, or we would list the exchange more than once in the 2476 contract terms and count its deposit limit more than once. */ 2477 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 2478 NULL != wmc; 2479 wmc = wmc->next) 2480 { 2481 bool matches = false; 2482 2483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2484 "Order could use wire method `%s'\n", 2485 wmc->wm->wire_method); 2486 for (unsigned int j = 0; j<keys->accounts_len; j++) 2487 { 2488 struct TALER_FullPayto full_payto = keys->accounts[j].fpayto_uri; 2489 char *wire_method = TALER_payto_get_method (full_payto.full_payto); 2490 2491 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2492 "Exchange `%s' has wire method `%s'\n", 2493 rx->url, 2494 wire_method); 2495 matches = (0 == strcmp (wmc->wm->wire_method, 2496 wire_method)); 2497 GNUNET_free (wire_method); 2498 if (matches) 2499 break; 2500 } 2501 if (matches) 2502 applicable |= get_acceptable (oc, 2503 exchange, 2504 rx->url, 2505 wmc); 2506 } 2507 if ( (! applicable) && 2508 (! oc->set_exchanges.forced_reload) ) 2509 { 2510 /* Checks for 'forced_reload' to not log the error *again* 2511 if we forced a re-load and are encountering the 2512 applicability error a 2nd time */ 2513 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2514 "Exchange `%s' %u wire methods are not applicable to this order\n", 2515 rx->url, 2516 keys->accounts_len); 2517 add_rejection (oc, 2518 rx->url, 2519 TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED); 2520 } 2521 if (applicable && 2522 settings->use_stefan) 2523 update_stefan (oc, 2524 keys); 2525 cleanup: 2526 GNUNET_free (rx->url); 2527 GNUNET_free (rx); 2528 if (NULL != oc->set_exchanges.pending_reload_head) 2529 return; 2530 resume_with_keys (oc); 2531 } 2532 2533 2534 /** 2535 * Force re-downloading of /keys from @a exchange, 2536 * we currently have no acceptable exchange, so we 2537 * should try to get one. 2538 * 2539 * @param cls closure with our `struct OrderContext` 2540 * @param url base URL of the exchange 2541 * @param exchange internal handle for the exchange 2542 */ 2543 static void 2544 get_exchange_keys (void *cls, 2545 const char *url, 2546 const struct TMH_Exchange *exchange) 2547 { 2548 struct OrderContext *oc = cls; 2549 struct RekeyExchange *rx; 2550 2551 rx = GNUNET_new (struct RekeyExchange); 2552 rx->oc = oc; 2553 rx->url = GNUNET_strdup (url); 2554 GNUNET_CONTAINER_DLL_insert (oc->set_exchanges.pending_reload_head, 2555 oc->set_exchanges.pending_reload_tail, 2556 rx); 2557 if (oc->set_exchanges.forced_reload) 2558 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2559 "Forcing download of %skeys\n", 2560 url); 2561 rx->fo = TMH_EXCHANGES_keys4exchange (url, 2562 oc->set_exchanges.forced_reload, 2563 &keys_cb, 2564 rx); 2565 } 2566 2567 2568 /** 2569 * Task run when we are timing out on /keys and will just 2570 * proceed with what we got. 2571 * 2572 * @param cls our `struct OrderContext *` to resume 2573 */ 2574 static void 2575 wakeup_timeout (void *cls) 2576 { 2577 struct OrderContext *oc = cls; 2578 2579 oc->set_exchanges.wakeup_task = NULL; 2580 GNUNET_assert (GNUNET_YES == oc->suspended); 2581 GNUNET_CONTAINER_DLL_remove (oc_head, 2582 oc_tail, 2583 oc); 2584 MHD_resume_connection (oc->connection); 2585 oc->suspended = GNUNET_NO; 2586 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 2587 } 2588 2589 2590 /** 2591 * Set list of acceptable exchanges in @a oc. Upon success, continues 2592 * processing with add_payment_details(). 2593 * 2594 * @param[in,out] oc order context 2595 * @return true to suspend execution 2596 */ 2597 static bool 2598 phase_set_exchanges (struct OrderContext *oc) 2599 { 2600 if (NULL != oc->set_exchanges.wakeup_task) 2601 { 2602 GNUNET_SCHEDULER_cancel (oc->set_exchanges.wakeup_task); 2603 oc->set_exchanges.wakeup_task = NULL; 2604 } 2605 2606 if (! oc->add_payment_details.need_exchange) 2607 { 2608 /* Total amount is zero, so we don't actually need exchanges! */ 2609 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2610 "Order total is zero, no need for exchanges\n"); 2611 oc->select_wire_method.exchanges = json_array (); 2612 GNUNET_assert (NULL != oc->select_wire_method.exchanges); 2613 /* Pick first one, doesn't matter as the amount is zero */ 2614 oc->select_wire_method.wm = oc->hc->instance->wm_head; 2615 oc->phase = ORDER_PHASE_SET_MAX_FEE; 2616 return false; 2617 } 2618 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2619 "Trying to find exchanges\n"); 2620 if (NULL == oc->set_exchanges.pending_reload_head) 2621 { 2622 if (! oc->set_exchanges.exchanges_tried) 2623 { 2624 oc->set_exchanges.exchanges_tried = true; 2625 oc->set_exchanges.keys_timeout 2626 = GNUNET_TIME_relative_to_absolute (MAX_KEYS_WAIT); 2627 TMH_exchange_get_trusted (&get_exchange_keys, 2628 oc); 2629 } 2630 else if ( (! oc->set_exchanges.forced_reload) && 2631 (oc->set_exchanges.promising_exchange) && 2632 (! oc->set_exchanges.exchange_ok) ) 2633 { 2634 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 2635 NULL != wmc; 2636 wmc = wmc->next) 2637 GNUNET_break (0 == 2638 json_array_clear (wmc->exchanges)); 2639 /* Try one more time with forcing /keys download */ 2640 oc->set_exchanges.forced_reload = true; 2641 TMH_exchange_get_trusted (&get_exchange_keys, 2642 oc); 2643 } 2644 } 2645 if (GNUNET_TIME_absolute_is_past (oc->set_exchanges.keys_timeout)) 2646 { 2647 struct RekeyExchange *rx; 2648 2649 while (NULL != (rx = oc->set_exchanges.pending_reload_head)) 2650 { 2651 GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head, 2652 oc->set_exchanges.pending_reload_tail, 2653 rx); 2654 TMH_EXCHANGES_keys4exchange_cancel (rx->fo); 2655 GNUNET_free (rx->url); 2656 GNUNET_free (rx); 2657 } 2658 } 2659 if (NULL != oc->set_exchanges.pending_reload_head) 2660 { 2661 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2662 "Still trying to (re)load %skeys\n", 2663 oc->set_exchanges.pending_reload_head->url); 2664 oc->set_exchanges.wakeup_task 2665 = GNUNET_SCHEDULER_add_at (oc->set_exchanges.keys_timeout, 2666 &wakeup_timeout, 2667 oc); 2668 MHD_suspend_connection (oc->connection); 2669 oc->suspended = GNUNET_YES; 2670 GNUNET_CONTAINER_DLL_insert (oc_head, 2671 oc_tail, 2672 oc); 2673 return true; /* reloads pending */ 2674 } 2675 oc->phase++; 2676 return false; 2677 } 2678 2679 2680 /* ***************** ORDER_PHASE_ADD_PAYMENT_DETAILS **************** */ 2681 2682 /** 2683 * Process the @a payment_target and add the details of how the 2684 * order could be paid to @a order. On success, continue 2685 * processing with add_payment_fees(). 2686 * 2687 * @param[in,out] oc order context 2688 */ 2689 static void 2690 phase_add_payment_details (struct OrderContext *oc) 2691 { 2692 /* First, determine the maximum amounts that could be paid per currency */ 2693 switch (oc->parse_order.order->base->version) 2694 { 2695 case TALER_MERCHANT_CONTRACT_VERSION_0: 2696 GNUNET_array_append (oc->add_payment_details.max_choice_limits, 2697 oc->add_payment_details.num_max_choice_limits, 2698 oc->parse_order.order->details.v0.brutto); 2699 if (! TALER_amount_is_zero ( 2700 &oc->parse_order.order->details.v0.brutto)) 2701 { 2702 oc->add_payment_details.need_exchange = true; 2703 } 2704 break; 2705 case TALER_MERCHANT_CONTRACT_VERSION_1: 2706 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2707 { 2708 const struct TALER_Amount *amount 2709 = &oc->parse_choices.choices[i].amount; 2710 bool found = false; 2711 2712 if (! TALER_amount_is_zero (amount)) 2713 { 2714 oc->add_payment_details.need_exchange = true; 2715 } 2716 for (unsigned int j = 0; 2717 j<oc->add_payment_details.num_max_choice_limits; 2718 j++) 2719 { 2720 struct TALER_Amount *mx = &oc->add_payment_details.max_choice_limits[j]; 2721 if (GNUNET_YES == 2722 TALER_amount_cmp_currency (mx, 2723 amount)) 2724 { 2725 TALER_amount_max (mx, 2726 mx, 2727 amount); 2728 found = true; 2729 break; 2730 } 2731 } 2732 if (! found) 2733 { 2734 GNUNET_array_append (oc->add_payment_details.max_choice_limits, 2735 oc->add_payment_details.num_max_choice_limits, 2736 *amount); 2737 } 2738 } 2739 break; 2740 default: 2741 GNUNET_assert (0); 2742 } 2743 2744 /* Then, create a candidate for each available wire method */ 2745 for (struct TMH_WireMethod *wm = oc->hc->instance->wm_head; 2746 NULL != wm; 2747 wm = wm->next) 2748 { 2749 struct WireMethodCandidate *wmc; 2750 2751 /* Locate wire method that has a matching payment target */ 2752 if (! wm->active) 2753 continue; /* ignore inactive methods */ 2754 if ( (NULL != oc->parse_request.payment_target) && 2755 (0 != strcasecmp (oc->parse_request.payment_target, 2756 wm->wire_method) ) ) 2757 continue; /* honor client preference */ 2758 wmc = GNUNET_new (struct WireMethodCandidate); 2759 wmc->wm = wm; 2760 wmc->exchanges = json_array (); 2761 GNUNET_assert (NULL != wmc->exchanges); 2762 GNUNET_CONTAINER_DLL_insert (oc->add_payment_details.wmc_head, 2763 oc->add_payment_details.wmc_tail, 2764 wmc); 2765 } 2766 2767 if (NULL == oc->add_payment_details.wmc_head) 2768 { 2769 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2770 "No wire method available for instance '%s'\n", 2771 oc->hc->instance->settings.id); 2772 reply_with_error (oc, 2773 MHD_HTTP_NOT_FOUND, 2774 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE, 2775 oc->parse_request.payment_target); 2776 return; 2777 } 2778 2779 /* next, we'll evaluate available exchanges */ 2780 oc->phase++; 2781 } 2782 2783 2784 /* ***************** ORDER_PHASE_MERGE_INVENTORY **************** */ 2785 2786 2787 /** 2788 * Helper function to sort uint64_t array with qsort(). 2789 * 2790 * @param a pointer to element to compare 2791 * @param b pointer to element to compare 2792 * @return 0 on equal, -1 on smaller, 1 on larger 2793 */ 2794 static int 2795 uint64_cmp (const void *a, 2796 const void *b) 2797 { 2798 uint64_t ua = *(const uint64_t *) a; 2799 uint64_t ub = *(const uint64_t *) b; 2800 2801 if (ua < ub) 2802 return -1; 2803 if (ua > ub) 2804 return 1; 2805 return 0; 2806 } 2807 2808 2809 /** 2810 * Merge the inventory products into products, querying the 2811 * database about the details of those products. Upon success, 2812 * continue processing by calling add_payment_details(). 2813 * 2814 * @param[in,out] oc order context to process 2815 */ 2816 static void 2817 phase_merge_inventory (struct OrderContext *oc) 2818 { 2819 uint64_t pots[oc->parse_order.order->products_len + 1]; 2820 size_t pots_off = 0; 2821 2822 if (0 != oc->parse_order.order->base->default_money_pot) 2823 pots[pots_off++] = oc->parse_order.order->base->default_money_pot; 2824 /** 2825 * parse_request.inventory_products => instructions to add products to contract terms 2826 * parse_order.products => contains products that are not from the backend-managed inventory. 2827 */ 2828 oc->merge_inventory.products = json_array (); 2829 for (size_t i = 0; i<oc->parse_order.order->products_len; i++) 2830 { 2831 GNUNET_assert ( 2832 0 == 2833 json_array_append_new ( 2834 oc->merge_inventory.products, 2835 TALER_MERCHANT_product_sold_serialize ( 2836 &oc->parse_order.order->products[i]))); 2837 if (0 != oc->parse_order.order->products[i].product_money_pot) 2838 pots[pots_off++] = oc->parse_order.order->products[i].product_money_pot; 2839 } 2840 2841 /* make sure pots array only has distinct elements */ 2842 qsort (pots, 2843 pots_off, 2844 sizeof (uint64_t), 2845 &uint64_cmp); 2846 { 2847 size_t e = 0; 2848 2849 for (size_t i = 1; i<pots_off; i++) 2850 { 2851 if (pots[e] != pots[i]) 2852 pots[++e] = pots[i]; 2853 } 2854 if (pots_off > 0) 2855 e++; 2856 pots_off = e; 2857 } 2858 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2859 "Found %u unique money pots in order\n", 2860 (unsigned int) pots_off); 2861 2862 /* check if all money pots exist; note that we do NOT treat 2863 the inventory products to this check, as (1) the foreign key 2864 constraint should ensure this, and (2) if the money pot 2865 were deleted (concurrently), the value is specified to be 2866 considered 0 (aka none) and so we can proceed anyway. */ 2867 if (pots_off > 0) 2868 { 2869 enum GNUNET_DB_QueryStatus qs; 2870 uint64_t pot_missing; 2871 2872 qs = TALER_MERCHANTDB_get_missing_money_pot (TMH_db, 2873 oc->hc->instance->settings.id, 2874 pots_off, 2875 pots, 2876 &pot_missing); 2877 switch (qs) 2878 { 2879 case GNUNET_DB_STATUS_HARD_ERROR: 2880 case GNUNET_DB_STATUS_SOFT_ERROR: 2881 GNUNET_break (0); 2882 reply_with_error (oc, 2883 MHD_HTTP_INTERNAL_SERVER_ERROR, 2884 TALER_EC_GENERIC_DB_FETCH_FAILED, 2885 "get_missing_money_pot"); 2886 return; 2887 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 2888 /* great, good case! */ 2889 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2890 "All money pots exist\n"); 2891 break; 2892 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 2893 { 2894 char mstr[32]; 2895 2896 GNUNET_snprintf (mstr, 2897 sizeof (mstr), 2898 "%llu", 2899 (unsigned long long) pot_missing); 2900 reply_with_error (oc, 2901 MHD_HTTP_NOT_FOUND, 2902 TALER_EC_MERCHANT_GENERIC_MONEY_POT_UNKNOWN, 2903 mstr); 2904 return; 2905 } 2906 } 2907 } 2908 2909 /* Populate products from inventory product array and database */ 2910 { 2911 GNUNET_assert (NULL != oc->merge_inventory.products); 2912 for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++) 2913 { 2914 struct InventoryProduct *ip 2915 = &oc->parse_request.inventory_products[i]; 2916 struct TALER_MERCHANTDB_ProductDetails pd; 2917 enum GNUNET_DB_QueryStatus qs; 2918 size_t num_categories = 0; 2919 uint64_t *categories = NULL; 2920 2921 qs = TALER_MERCHANTDB_get_product (TMH_db, 2922 oc->hc->instance->settings.id, 2923 ip->product_id, 2924 &pd, 2925 &num_categories, 2926 &categories); 2927 if (qs <= 0) 2928 { 2929 enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 2930 unsigned int http_status = 0; 2931 2932 switch (qs) 2933 { 2934 case GNUNET_DB_STATUS_HARD_ERROR: 2935 GNUNET_break (0); 2936 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 2937 ec = TALER_EC_GENERIC_DB_FETCH_FAILED; 2938 break; 2939 case GNUNET_DB_STATUS_SOFT_ERROR: 2940 GNUNET_break (0); 2941 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 2942 ec = TALER_EC_GENERIC_DB_SOFT_FAILURE; 2943 break; 2944 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 2945 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2946 "Product %s from order unknown\n", 2947 ip->product_id); 2948 http_status = MHD_HTTP_NOT_FOUND; 2949 ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN; 2950 break; 2951 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 2952 /* case listed to make compilers happy */ 2953 GNUNET_assert (0); 2954 } 2955 reply_with_error (oc, 2956 http_status, 2957 ec, 2958 ip->product_id); 2959 return; 2960 } 2961 GNUNET_free (categories); 2962 oc->parse_order.order->base->minimum_age 2963 = GNUNET_MAX (oc->parse_order.order->base->minimum_age, 2964 pd.minimum_age); 2965 { 2966 const char *eparam; 2967 2968 if ( (! ip->quantity_missing) && 2969 (ip->quantity > (uint64_t) INT64_MAX) ) 2970 { 2971 GNUNET_break_op (0); 2972 reply_with_error (oc, 2973 MHD_HTTP_BAD_REQUEST, 2974 TALER_EC_GENERIC_PARAMETER_MALFORMED, 2975 "quantity"); 2976 TALER_MERCHANTDB_product_details_free (&pd); 2977 return; 2978 } 2979 if (GNUNET_OK != 2980 TALER_MERCHANT_vk_process_quantity_inputs ( 2981 TALER_MERCHANT_VK_QUANTITY, 2982 pd.allow_fractional_quantity, 2983 ip->quantity_missing, 2984 (int64_t) ip->quantity, 2985 ip->unit_quantity_missing, 2986 ip->unit_quantity, 2987 &ip->quantity, 2988 &ip->quantity_frac, 2989 &eparam)) 2990 { 2991 GNUNET_break_op (0); 2992 reply_with_error (oc, 2993 MHD_HTTP_BAD_REQUEST, 2994 TALER_EC_GENERIC_PARAMETER_MALFORMED, 2995 eparam); 2996 TALER_MERCHANTDB_product_details_free (&pd); 2997 return; 2998 } 2999 } 3000 { 3001 struct TALER_MERCHANT_ProductSold ps = { 3002 .product_id = (char *) ip->product_id, 3003 .product_name = pd.product_name, 3004 .description = pd.description, 3005 .description_i18n = pd.description_i18n, 3006 .unit_quantity.integer = ip->quantity, 3007 .unit_quantity.fractional = ip->quantity_frac, 3008 .prices_length = pd.price_array_length, 3009 .prices = GNUNET_new_array (pd.price_array_length, 3010 struct TALER_Amount), 3011 .prices_are_net = pd.price_is_net, 3012 .image = pd.image, 3013 .taxes = pd.taxes, 3014 .delivery_date = oc->parse_order.order->base->delivery_date, 3015 .product_money_pot = pd.money_pot_id, 3016 .unit = pd.unit, 3017 3018 }; 3019 json_t *p; 3020 char unit_quantity_buf[64]; 3021 3022 for (size_t j = 0; j<pd.price_array_length; j++) 3023 { 3024 struct TALER_Amount atomic_amount; 3025 3026 GNUNET_assert ( 3027 GNUNET_OK == 3028 TALER_amount_set_zero (pd.price_array[j].currency, 3029 &atomic_amount)); 3030 atomic_amount.fraction = 1; 3031 GNUNET_assert ( 3032 GNUNET_OK == 3033 TALER_MERCHANT_amount_multiply_by_quantity ( 3034 &ps.prices[j], 3035 &pd.price_array[j], 3036 &ps.unit_quantity, 3037 TALER_MERCHANT_ROUND_UP, 3038 &atomic_amount)); 3039 } 3040 3041 TALER_MERCHANT_vk_format_fractional_string ( 3042 TALER_MERCHANT_VK_QUANTITY, 3043 ip->quantity, 3044 ip->quantity_frac, 3045 sizeof (unit_quantity_buf), 3046 unit_quantity_buf); 3047 if (0 != pd.money_pot_id) 3048 pots[pots_off++] = pd.money_pot_id; 3049 p = TALER_MERCHANT_product_sold_serialize (&ps); 3050 GNUNET_assert (NULL != p); 3051 GNUNET_free (ps.prices); 3052 GNUNET_assert (0 == 3053 json_array_append_new (oc->merge_inventory.products, 3054 p)); 3055 } 3056 TALER_MERCHANTDB_product_details_free (&pd); 3057 } 3058 } 3059 3060 /* check if final product list is well-formed */ 3061 if (! TMH_products_array_valid (oc->merge_inventory.products)) 3062 { 3063 GNUNET_break_op (0); 3064 reply_with_error (oc, 3065 MHD_HTTP_BAD_REQUEST, 3066 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3067 "order:products"); 3068 return; 3069 } 3070 oc->phase++; 3071 } 3072 3073 3074 /* ***************** ORDER_PHASE_PARSE_CHOICES **************** */ 3075 3076 /** 3077 * Callback function that is called for each donau instance. 3078 * It simply adds the provided donau_url to the json. 3079 * 3080 * @param cls closure with our `struct TALER_MERCHANT_ContractOutput *` 3081 * @param donau_url the URL of the donau instance 3082 */ 3083 static void 3084 add_donau_url (void *cls, 3085 const char *donau_url) 3086 { 3087 struct TALER_MERCHANT_ContractOutput *output = cls; 3088 3089 GNUNET_array_append (output->details.donation_receipt.donau_urls, 3090 output->details.donation_receipt.donau_urls_len, 3091 GNUNET_strdup (donau_url)); 3092 } 3093 3094 3095 /** 3096 * Add the donau output to the contract output. 3097 * 3098 * @param oc order context 3099 * @param output contract output to add donau URLs to 3100 */ 3101 static bool 3102 add_donau_output (struct OrderContext *oc, 3103 struct TALER_MERCHANT_ContractOutput *output) 3104 { 3105 enum GNUNET_DB_QueryStatus qs; 3106 3107 qs = TALER_MERCHANTDB_iterate_donau_instances_filtered ( 3108 TMH_db, 3109 output->details.donation_receipt.amount.currency, 3110 &add_donau_url, 3111 output); 3112 if (qs < 0) 3113 { 3114 GNUNET_break (0); 3115 reply_with_error (oc, 3116 MHD_HTTP_INTERNAL_SERVER_ERROR, 3117 TALER_EC_GENERIC_DB_FETCH_FAILED, 3118 "donau url parsing db call"); 3119 for (unsigned int i = 0; 3120 i < output->details.donation_receipt.donau_urls_len; 3121 i++) 3122 GNUNET_free (output->details.donation_receipt.donau_urls[i]); 3123 GNUNET_array_grow (output->details.donation_receipt.donau_urls, 3124 output->details.donation_receipt.donau_urls_len, 3125 0); 3126 return false; 3127 } 3128 return true; 3129 } 3130 3131 3132 /** 3133 * Parse contract choices. Upon success, continue 3134 * processing with merge_inventory(). 3135 * 3136 * @param[in,out] oc order context 3137 */ 3138 static void 3139 phase_parse_choices (struct OrderContext *oc) 3140 { 3141 switch (oc->parse_order.order->base->version) 3142 { 3143 case TALER_MERCHANT_CONTRACT_VERSION_0: 3144 oc->phase++; 3145 return; 3146 case TALER_MERCHANT_CONTRACT_VERSION_1: 3147 /* handle below */ 3148 break; 3149 default: 3150 GNUNET_assert (0); 3151 } 3152 3153 /* Convert order choices to contract choices */ 3154 GNUNET_array_grow (oc->parse_choices.choices, 3155 oc->parse_choices.choices_len, 3156 oc->parse_order.order->details.v1.choices_len); 3157 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 3158 { 3159 const struct TALER_MERCHANT_OrderChoice *ochoice 3160 = &oc->parse_order.order->details.v1.choices[i]; 3161 struct TALER_MERCHANT_ContractChoice *cchoice 3162 = &oc->parse_choices.choices[i]; 3163 unsigned int off; 3164 3165 if (! TMH_test_exchange_configured_for_currency ( 3166 ochoice->amount.currency)) 3167 { 3168 GNUNET_break_op (0); 3169 reply_with_error (oc, 3170 MHD_HTTP_CONFLICT, 3171 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, 3172 ochoice->amount.currency); 3173 return; 3174 } 3175 cchoice->amount = ochoice->amount; 3176 cchoice->tip = ochoice->tip; 3177 cchoice->no_tip = ochoice->no_tip; 3178 if (NULL != ochoice->description) 3179 cchoice->description = GNUNET_strdup (ochoice->description); 3180 if (NULL != ochoice->description_i18n) 3181 cchoice->description_i18n = json_incref (ochoice->description_i18n); 3182 cchoice->max_fee = ochoice->max_fee; 3183 3184 /* convert inputs */ 3185 GNUNET_array_grow (cchoice->inputs, 3186 cchoice->inputs_len, 3187 ochoice->inputs_len); 3188 off = 0; 3189 for (unsigned int j = 0; j < ochoice->inputs_len; j++) 3190 { 3191 const struct TALER_MERCHANT_OrderInput *order_input 3192 = &ochoice->inputs[j]; 3193 struct TALER_MERCHANT_ContractInput *contract_input 3194 = &cchoice->inputs[off]; 3195 3196 contract_input->type = order_input->type; 3197 switch (order_input->type) 3198 { 3199 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 3200 GNUNET_assert (0); 3201 break; 3202 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 3203 /* Ignore inputs tokens with 'count' field set to 0 */ 3204 if (0 == order_input->details.token.count) 3205 continue; 3206 contract_input->details.token.count 3207 = order_input->details.token.count; 3208 contract_input->details.token.token_family_slug 3209 = order_input->details.token.token_family_slug; 3210 if (GNUNET_OK != 3211 add_input_token_family (oc, 3212 contract_input->details.token.token_family_slug)) 3213 { 3214 GNUNET_break_op (0); 3215 return; 3216 } 3217 off++; 3218 continue; 3219 } /* switch input type */ 3220 GNUNET_assert (0); 3221 } /* for all inputs */ 3222 GNUNET_array_grow (cchoice->inputs, 3223 cchoice->inputs_len, 3224 off); 3225 3226 /* convert outputs */ 3227 GNUNET_array_grow (cchoice->outputs, 3228 cchoice->outputs_len, 3229 ochoice->outputs_len); 3230 off = 0; 3231 for (unsigned int j = 0; j < ochoice->outputs_len; j++) 3232 { 3233 const struct TALER_MERCHANT_OrderOutput *order_output 3234 = &ochoice->outputs[j]; 3235 struct TALER_MERCHANT_ContractOutput *contract_output 3236 = &cchoice->outputs[off]; 3237 3238 contract_output->type = order_output->type; 3239 switch (order_output->type) 3240 { 3241 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 3242 GNUNET_assert (0); 3243 break; 3244 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 3245 if (order_output->details.donation_receipt.no_amount) 3246 { 3247 contract_output->details.donation_receipt.amount 3248 = ochoice->amount; 3249 } 3250 else 3251 { 3252 contract_output->details.donation_receipt.amount 3253 = order_output->details.donation_receipt.amount; 3254 } 3255 if (! add_donau_output (oc, 3256 contract_output)) 3257 { 3258 GNUNET_break (0); 3259 return; 3260 } 3261 off++; 3262 continue; 3263 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 3264 /* Ignore inputs tokens with 'count' field set to 0 */ 3265 if (0 == order_output->details.token.count) 3266 continue; 3267 3268 contract_output->details.token.token_family_slug 3269 = order_output->details.token.token_family_slug; 3270 contract_output->details.token.count 3271 = order_output->details.token.count; 3272 if (0 == order_output->details.token.valid_at.abs_time.abs_value_us) 3273 contract_output->details.token.valid_at 3274 = GNUNET_TIME_timestamp_get (); 3275 else 3276 contract_output->details.token.valid_at 3277 = order_output->details.token.valid_at; 3278 if (GNUNET_OK != 3279 add_output_token_family ( 3280 oc, 3281 contract_output->details.token.token_family_slug, 3282 contract_output->details.token.valid_at, 3283 &contract_output->details.token.key_index)) 3284 3285 { 3286 /* note: reply_with_error() was already called */ 3287 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3288 "Could not handle output token family `%s'\n", 3289 contract_output->details.token.token_family_slug); 3290 return; 3291 } 3292 off++; 3293 continue; 3294 } /* end switch */ 3295 GNUNET_assert (0); 3296 } /* for outputs */ 3297 GNUNET_array_grow (cchoice->outputs, 3298 cchoice->outputs_len, 3299 off); 3300 } /* for all choices */ 3301 oc->phase++; 3302 } 3303 3304 3305 /* ***************** ORDER_PHASE_PARSE_ORDER **************** */ 3306 3307 3308 /** 3309 * Parse the order field of the request. Upon success, continue 3310 * processing with parse_choices(). 3311 * 3312 * @param[in,out] oc order context 3313 */ 3314 static void 3315 phase_parse_order (struct OrderContext *oc) 3316 { 3317 const struct TALER_MERCHANTDB_InstanceSettings *settings = 3318 &oc->hc->instance->settings; 3319 bool computed_refund_deadline = false; 3320 3321 oc->parse_order.order 3322 = TALER_MERCHANT_order_parse ( 3323 oc->parse_request.order); 3324 if (NULL == oc->parse_order.order) 3325 { 3326 GNUNET_break_op (0); 3327 reply_with_error (oc, 3328 MHD_HTTP_BAD_REQUEST, 3329 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3330 "order"); 3331 return; 3332 } 3333 3334 switch (oc->parse_order.order->base->version) 3335 { 3336 case TALER_MERCHANT_CONTRACT_VERSION_0: 3337 if (! TMH_test_exchange_configured_for_currency ( 3338 oc->parse_order.order->details.v0.brutto.currency)) 3339 { 3340 GNUNET_break_op (0); 3341 reply_with_error ( 3342 oc, 3343 MHD_HTTP_CONFLICT, 3344 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, 3345 oc->parse_order.order->details.v0.brutto.currency); 3346 return; 3347 } 3348 break; 3349 case TALER_MERCHANT_CONTRACT_VERSION_1: 3350 break; 3351 default: 3352 GNUNET_break_op (0); 3353 reply_with_error (oc, 3354 MHD_HTTP_BAD_REQUEST, 3355 TALER_EC_GENERIC_VERSION_MALFORMED, 3356 "invalid version specified in order, supported are null, '0' or '1'"); 3357 return; 3358 } 3359 3360 /* Add order_id if it doesn't exist. */ 3361 if (NULL == oc->parse_order.order->order_id) 3362 { 3363 char buf[256]; 3364 time_t timer; 3365 struct tm *tm_info; 3366 size_t off; 3367 uint64_t rand; 3368 char *last; 3369 3370 time (&timer); 3371 tm_info = localtime (&timer); 3372 if (NULL == tm_info) 3373 { 3374 reply_with_error ( 3375 oc, 3376 MHD_HTTP_INTERNAL_SERVER_ERROR, 3377 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME, 3378 NULL); 3379 return; 3380 } 3381 off = strftime (buf, 3382 sizeof (buf) - 1, 3383 "%Y.%j", 3384 tm_info); 3385 /* Check for error state of strftime */ 3386 GNUNET_assert (0 != off); 3387 buf[off++] = '-'; 3388 /* The encoded suffix is raw identifier entropy, not a bounded number. */ 3389 GNUNET_CRYPTO_random_block (&rand, 3390 sizeof (rand)); 3391 last = GNUNET_STRINGS_data_to_string (&rand, 3392 sizeof (uint64_t), 3393 &buf[off], 3394 sizeof (buf) - off); 3395 GNUNET_assert (NULL != last); 3396 *last = '\0'; 3397 3398 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3399 "Assigning order ID `%s' server-side\n", 3400 buf); 3401 oc->parse_order.order->order_id = GNUNET_strdup (buf); 3402 } 3403 3404 /* Patch fulfillment URL with order_id (implements #6467). */ 3405 if (NULL != oc->parse_order.order->base->fulfillment_url) 3406 { 3407 const char *pos; 3408 3409 pos = strstr (oc->parse_order.order->base->fulfillment_url, 3410 "${ORDER_ID}"); 3411 if (NULL != pos) 3412 { 3413 /* replace ${ORDER_ID} with the real order_id */ 3414 char *nurl; 3415 3416 /* We only allow one placeholder */ 3417 if (strstr (pos + strlen ("${ORDER_ID}"), 3418 "${ORDER_ID}")) 3419 { 3420 GNUNET_break_op (0); 3421 reply_with_error (oc, 3422 MHD_HTTP_BAD_REQUEST, 3423 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3424 "fulfillment_url"); 3425 return; 3426 } 3427 3428 GNUNET_asprintf ( 3429 &nurl, 3430 "%.*s%s%s", 3431 /* first output URL until ${ORDER_ID} */ 3432 (int) (pos - oc->parse_order.order->base->fulfillment_url), 3433 oc->parse_order.order->base->fulfillment_url, 3434 /* replace ${ORDER_ID} with the right order_id */ 3435 oc->parse_order.order->order_id, 3436 /* append rest of original URL */ 3437 pos + strlen ("${ORDER_ID}")); 3438 oc->parse_order.order->base->fulfillment_url = GNUNET_strdup (nurl); 3439 GNUNET_free (nurl); 3440 } 3441 } 3442 3443 if ( (GNUNET_TIME_absolute_is_zero ( 3444 oc->parse_order.order->pay_deadline.abs_time)) || 3445 (GNUNET_TIME_absolute_is_never ( 3446 oc->parse_order.order->pay_deadline.abs_time)) ) 3447 { 3448 oc->parse_order.order->pay_deadline 3449 = GNUNET_TIME_relative_to_timestamp ( 3450 settings->default_pay_delay); 3451 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3452 "Pay deadline was zero (or never), setting to %s\n", 3453 GNUNET_TIME_timestamp2s ( 3454 oc->parse_order.order->pay_deadline)); 3455 } 3456 else if (GNUNET_TIME_absolute_is_past ( 3457 oc->parse_order.order->pay_deadline.abs_time)) 3458 { 3459 GNUNET_break_op (0); 3460 reply_with_error ( 3461 oc, 3462 MHD_HTTP_BAD_REQUEST, 3463 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PAY_DEADLINE_IN_PAST, 3464 NULL); 3465 return; 3466 } 3467 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3468 "Pay deadline is %s\n", 3469 GNUNET_TIME_timestamp2s ( 3470 oc->parse_order.order->pay_deadline)); 3471 3472 /* Check soundness of refund deadline, and that a timestamp 3473 * is actually present. */ 3474 { 3475 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 3476 3477 /* Add timestamp if it doesn't exist (or is zero) */ 3478 if (GNUNET_TIME_absolute_is_zero ( 3479 oc->parse_order.order->timestamp.abs_time)) 3480 { 3481 oc->parse_order.order->timestamp = now; 3482 } 3483 3484 /* If no refund_deadline given, set one based on refund_delay. */ 3485 if (GNUNET_TIME_absolute_is_never ( 3486 oc->parse_order.order->refund_deadline.abs_time)) 3487 { 3488 if (GNUNET_TIME_relative_is_zero ( 3489 oc->parse_request.refund_delay)) 3490 { 3491 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3492 "Refund delay is zero, no refunds are possible for this order\n"); 3493 oc->parse_order.order->refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; 3494 } 3495 else 3496 { 3497 computed_refund_deadline = true; 3498 oc->parse_order.order->refund_deadline 3499 = GNUNET_TIME_absolute_to_timestamp ( 3500 GNUNET_TIME_absolute_add ( 3501 oc->parse_order.order->pay_deadline.abs_time, 3502 oc->parse_request.refund_delay)); 3503 } 3504 } 3505 3506 if ( (! GNUNET_TIME_absolute_is_zero ( 3507 oc->parse_order.order->base->delivery_date.abs_time)) && 3508 (GNUNET_TIME_absolute_is_past ( 3509 oc->parse_order.order->base->delivery_date.abs_time)) ) 3510 { 3511 GNUNET_break_op (0); 3512 reply_with_error ( 3513 oc, 3514 MHD_HTTP_BAD_REQUEST, 3515 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST, 3516 NULL); 3517 return; 3518 } 3519 } 3520 3521 if ( (! GNUNET_TIME_absolute_is_zero ( 3522 oc->parse_order.order->refund_deadline.abs_time)) && 3523 (GNUNET_TIME_absolute_is_past ( 3524 oc->parse_order.order->refund_deadline.abs_time)) ) 3525 { 3526 GNUNET_break_op (0); 3527 reply_with_error ( 3528 oc, 3529 MHD_HTTP_BAD_REQUEST, 3530 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_DEADLINE_IN_PAST, 3531 NULL); 3532 return; 3533 } 3534 3535 if (GNUNET_TIME_absolute_is_never ( 3536 oc->parse_order.order->wire_transfer_deadline.abs_time)) 3537 { 3538 struct GNUNET_TIME_Absolute start; 3539 3540 start = GNUNET_TIME_absolute_max ( 3541 oc->parse_order.order->refund_deadline.abs_time, 3542 oc->parse_order.order->pay_deadline.abs_time); 3543 oc->parse_order.order->wire_transfer_deadline 3544 = GNUNET_TIME_absolute_to_timestamp ( 3545 GNUNET_TIME_round_up ( 3546 GNUNET_TIME_absolute_add ( 3547 start, 3548 settings->default_wire_transfer_delay), 3549 settings->default_wire_transfer_rounding_interval)); 3550 if (GNUNET_TIME_absolute_is_never ( 3551 oc->parse_order.order->wire_transfer_deadline.abs_time)) 3552 { 3553 GNUNET_break_op (0); 3554 reply_with_error ( 3555 oc, 3556 MHD_HTTP_BAD_REQUEST, 3557 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER, 3558 "order:wire_transfer_deadline"); 3559 return; 3560 } 3561 } 3562 else if (computed_refund_deadline) 3563 { 3564 /* if we computed the refund_deadline from default settings 3565 and did have a configured wire_deadline, make sure that 3566 the refund_deadline is at or below the wire_deadline. */ 3567 oc->parse_order.order->refund_deadline 3568 = GNUNET_TIME_timestamp_min ( 3569 oc->parse_order.order->refund_deadline, 3570 oc->parse_order.order->wire_transfer_deadline); 3571 } 3572 if (GNUNET_TIME_timestamp_cmp ( 3573 oc->parse_order.order->wire_transfer_deadline, 3574 <, 3575 oc->parse_order.order->refund_deadline)) 3576 { 3577 GNUNET_break_op (0); 3578 reply_with_error ( 3579 oc, 3580 MHD_HTTP_BAD_REQUEST, 3581 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE, 3582 "order:wire_transfer_deadline;order:refund_deadline"); 3583 return; 3584 } 3585 3586 { 3587 char *url; 3588 3589 url = make_merchant_base_url (oc->connection, 3590 settings->id); 3591 if (NULL == url) 3592 { 3593 GNUNET_break_op (0); 3594 reply_with_error ( 3595 oc, 3596 MHD_HTTP_BAD_REQUEST, 3597 TALER_EC_GENERIC_PARAMETER_MISSING, 3598 "order:merchant_base_url"); 3599 return; 3600 } 3601 oc->parse_order.merchant_base_url = url; 3602 } 3603 3604 // FIXME: move to util during parsing! 3605 if ( (NULL != oc->parse_order.order->base->delivery_location) && 3606 (! TMH_location_object_valid (oc->parse_order.order->base->delivery_location)) ) 3607 { 3608 GNUNET_break_op (0); 3609 reply_with_error (oc, 3610 MHD_HTTP_BAD_REQUEST, 3611 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3612 "delivery_location"); 3613 return; 3614 } 3615 3616 oc->phase++; 3617 } 3618 3619 3620 /* ***************** ORDER_PHASE_PARSE_REQUEST **************** */ 3621 3622 /** 3623 * Parse the client request. Upon success, 3624 * continue processing by calling parse_order(). 3625 * 3626 * @param[in,out] oc order context to process 3627 */ 3628 static void 3629 phase_parse_request (struct OrderContext *oc) 3630 { 3631 const json_t *ip = NULL; 3632 const json_t *uuid = NULL; 3633 const char *otp_id = NULL; 3634 bool create_token = true; /* default */ 3635 bool no_challenge = true; 3636 struct GNUNET_JSON_Specification spec[] = { 3637 GNUNET_JSON_spec_json ("order", 3638 &oc->parse_request.order), 3639 GNUNET_JSON_spec_mark_optional ( 3640 GNUNET_JSON_spec_relative_time ("refund_delay", 3641 &oc->parse_request.refund_delay), 3642 NULL), 3643 GNUNET_JSON_spec_mark_optional ( 3644 GNUNET_JSON_spec_string ("payment_target", 3645 &oc->parse_request.payment_target), 3646 NULL), 3647 GNUNET_JSON_spec_mark_optional ( 3648 GNUNET_JSON_spec_array_const ("inventory_products", 3649 &ip), 3650 NULL), 3651 GNUNET_JSON_spec_mark_optional ( 3652 TALER_JSON_spec_session_id ("session_id", 3653 &oc->parse_request.session_id), 3654 NULL), 3655 GNUNET_JSON_spec_mark_optional ( 3656 GNUNET_JSON_spec_array_const ("lock_uuids", 3657 &uuid), 3658 NULL), 3659 GNUNET_JSON_spec_mark_optional ( 3660 GNUNET_JSON_spec_bool ("create_token", 3661 &create_token), 3662 NULL), 3663 GNUNET_JSON_spec_mark_optional ( 3664 TALER_JSON_spec_slug ("otp_id", 3665 &otp_id), 3666 NULL), 3667 GNUNET_JSON_spec_mark_optional ( 3668 GNUNET_JSON_spec_fixed_auto ("challenge", 3669 &oc->parse_request.pos_challenge), 3670 &no_challenge), 3671 GNUNET_JSON_spec_end () 3672 }; 3673 enum GNUNET_GenericReturnValue ret; 3674 3675 oc->parse_request.refund_delay 3676 = oc->hc->instance->settings.default_refund_delay; 3677 ret = TALER_MHD_parse_json_data (oc->connection, 3678 oc->hc->request_body, 3679 spec); 3680 if (GNUNET_OK != ret) 3681 { 3682 GNUNET_break_op (0); 3683 finalize_order2 (oc, 3684 ret); 3685 return; 3686 } 3687 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3688 "Refund delay is %s\n", 3689 GNUNET_TIME_relative2s (oc->parse_request.refund_delay, 3690 false)); 3691 TALER_MERCHANTDB_do_expire_locks (TMH_db); 3692 if (NULL != otp_id) 3693 { 3694 struct TALER_MERCHANTDB_OtpDeviceDetails td; 3695 enum GNUNET_DB_QueryStatus qs; 3696 3697 memset (&td, 3698 0, 3699 sizeof (td)); 3700 qs = TALER_MERCHANTDB_get_otp_device (TMH_db, 3701 oc->hc->instance->settings.id, 3702 otp_id, 3703 &td); 3704 switch (qs) 3705 { 3706 case GNUNET_DB_STATUS_HARD_ERROR: 3707 GNUNET_break (0); 3708 reply_with_error (oc, 3709 MHD_HTTP_INTERNAL_SERVER_ERROR, 3710 TALER_EC_GENERIC_DB_FETCH_FAILED, 3711 "get_otp_device"); 3712 return; 3713 case GNUNET_DB_STATUS_SOFT_ERROR: 3714 GNUNET_break (0); 3715 reply_with_error (oc, 3716 MHD_HTTP_INTERNAL_SERVER_ERROR, 3717 TALER_EC_GENERIC_DB_SOFT_FAILURE, 3718 "get_otp_device"); 3719 return; 3720 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 3721 reply_with_error (oc, 3722 MHD_HTTP_NOT_FOUND, 3723 TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, 3724 otp_id); 3725 return; 3726 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 3727 break; 3728 } 3729 oc->parse_request.pos_key = td.otp_key; 3730 oc->parse_request.pos_algorithm = td.otp_algorithm; 3731 GNUNET_free (td.otp_description); 3732 GNUNET_free (td.otp_device_pub); 3733 } 3734 /* pos_algorithm is TALER_MCA_NONE when the order references no OTP 3735 device, so this also rejects a challenge sent on its own */ 3736 { 3737 switch (oc->parse_request.pos_algorithm) 3738 { 3739 case TALER_MCA_ECDSA_CHALLENGE: 3740 case TALER_MCA_EDDSA_CHALLENGE: 3741 /* the confirmation is a signature over the challenge, so 3742 without one there would be nothing to sign at payment time */ 3743 if (no_challenge) 3744 { 3745 GNUNET_break_op (0); 3746 reply_with_error (oc, 3747 MHD_HTTP_BAD_REQUEST, 3748 TALER_EC_GENERIC_PARAMETER_MISSING, 3749 "challenge"); 3750 return; 3751 } 3752 break; 3753 case TALER_MCA_NONE: 3754 case TALER_MCA_WITHOUT_PRICE: 3755 case TALER_MCA_WITH_PRICE: 3756 /* these are time-based; a challenge would never be looked at */ 3757 if (! no_challenge) 3758 { 3759 GNUNET_break_op (0); 3760 reply_with_error (oc, 3761 MHD_HTTP_BAD_REQUEST, 3762 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3763 "challenge"); 3764 return; 3765 } 3766 break; 3767 } 3768 } 3769 if (create_token) 3770 { 3771 GNUNET_CRYPTO_random_block (&oc->parse_request.claim_token, 3772 sizeof (oc->parse_request.claim_token)); 3773 } 3774 /* Compute h_post_data (for idempotency check) */ 3775 { 3776 char *req_body_enc; 3777 3778 /* Dump normalized JSON to string. */ 3779 if (NULL == (req_body_enc 3780 = json_dumps (oc->hc->request_body, 3781 JSON_ENCODE_ANY 3782 | JSON_COMPACT 3783 | JSON_SORT_KEYS))) 3784 { 3785 GNUNET_break (0); 3786 GNUNET_JSON_parse_free (spec); 3787 reply_with_error (oc, 3788 MHD_HTTP_INTERNAL_SERVER_ERROR, 3789 TALER_EC_GENERIC_ALLOCATION_FAILURE, 3790 "request body normalization for hashing"); 3791 return; 3792 } 3793 GNUNET_CRYPTO_hash (req_body_enc, 3794 strlen (req_body_enc), 3795 &oc->parse_request.h_post_data.hash); 3796 GNUNET_free (req_body_enc); 3797 } 3798 3799 /* parse the inventory_products (optionally given) */ 3800 if (NULL != ip) 3801 { 3802 unsigned int ipl = (unsigned int) json_array_size (ip); 3803 3804 if ( (json_array_size (ip) != (size_t) ipl) || 3805 (ipl > MAX_PRODUCTS) ) 3806 { 3807 GNUNET_break_op (0); 3808 GNUNET_JSON_parse_free (spec); 3809 reply_with_error (oc, 3810 MHD_HTTP_BAD_REQUEST, 3811 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3812 "inventory_products (too many)"); 3813 return; 3814 } 3815 GNUNET_array_grow (oc->parse_request.inventory_products, 3816 oc->parse_request.inventory_products_length, 3817 (unsigned int) json_array_size (ip)); 3818 for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++) 3819 { 3820 struct InventoryProduct *ipr = &oc->parse_request.inventory_products[i]; 3821 const char *error_name; 3822 unsigned int error_line; 3823 struct GNUNET_JSON_Specification ispec[] = { 3824 TALER_JSON_spec_slug ("product_id", 3825 &ipr->product_id), 3826 GNUNET_JSON_spec_mark_optional ( 3827 GNUNET_JSON_spec_uint64 ("quantity", 3828 &ipr->quantity), 3829 &ipr->quantity_missing), 3830 GNUNET_JSON_spec_mark_optional ( 3831 GNUNET_JSON_spec_string ("unit_quantity", 3832 &ipr->unit_quantity), 3833 &ipr->unit_quantity_missing), 3834 GNUNET_JSON_spec_mark_optional ( 3835 GNUNET_JSON_spec_uint64 ("product_money_pot", 3836 &ipr->product_money_pot), 3837 NULL), 3838 GNUNET_JSON_spec_end () 3839 }; 3840 3841 ret = GNUNET_JSON_parse (json_array_get (ip, 3842 i), 3843 ispec, 3844 &error_name, 3845 &error_line); 3846 if (GNUNET_OK != ret) 3847 { 3848 GNUNET_break_op (0); 3849 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3850 "Product parsing failed at #%u: %s:%u\n", 3851 i, 3852 error_name, 3853 error_line); 3854 reply_with_error (oc, 3855 MHD_HTTP_BAD_REQUEST, 3856 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3857 "inventory_products"); 3858 return; 3859 } 3860 if (ipr->quantity_missing && ipr->unit_quantity_missing) 3861 { 3862 ipr->quantity = 1; 3863 ipr->quantity_missing = false; 3864 } 3865 } 3866 } 3867 3868 /* parse the lock_uuids (optionally given) */ 3869 if (NULL != uuid) 3870 { 3871 GNUNET_array_grow (oc->parse_request.uuids, 3872 oc->parse_request.uuids_length, 3873 json_array_size (uuid)); 3874 for (unsigned int i = 0; i<oc->parse_request.uuids_length; i++) 3875 { 3876 json_t *ui = json_array_get (uuid, 3877 i); 3878 3879 if (! json_is_string (ui)) 3880 { 3881 GNUNET_break_op (0); 3882 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3883 "UUID parsing failed at #%u\n", 3884 i); 3885 reply_with_error (oc, 3886 MHD_HTTP_BAD_REQUEST, 3887 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3888 "lock_uuids"); 3889 return; 3890 } 3891 TMH_uuid_from_string (json_string_value (ui), 3892 &oc->parse_request.uuids[i]); 3893 } 3894 } 3895 oc->phase++; 3896 } 3897 3898 3899 /* ***************** Main handler **************** */ 3900 3901 3902 enum MHD_Result 3903 TMH_private_post_orders ( 3904 const struct TMH_RequestHandler *rh, 3905 struct MHD_Connection *connection, 3906 struct TMH_HandlerContext *hc) 3907 { 3908 struct OrderContext *oc = hc->ctx; 3909 3910 if (NULL == oc) 3911 { 3912 oc = GNUNET_new (struct OrderContext); 3913 hc->ctx = oc; 3914 hc->cc = &clean_order; 3915 oc->connection = connection; 3916 oc->hc = hc; 3917 } 3918 while (1) 3919 { 3920 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3921 "Processing order in phase %d\n", 3922 oc->phase); 3923 switch (oc->phase) 3924 { 3925 case ORDER_PHASE_PARSE_REQUEST: 3926 phase_parse_request (oc); 3927 break; 3928 case ORDER_PHASE_PARSE_ORDER: 3929 phase_parse_order (oc); 3930 break; 3931 case ORDER_PHASE_PARSE_CHOICES: 3932 phase_parse_choices (oc); 3933 break; 3934 case ORDER_PHASE_MERGE_INVENTORY: 3935 phase_merge_inventory (oc); 3936 break; 3937 case ORDER_PHASE_ADD_PAYMENT_DETAILS: 3938 phase_add_payment_details (oc); 3939 break; 3940 case ORDER_PHASE_SET_EXCHANGES: 3941 if (phase_set_exchanges (oc)) 3942 return MHD_YES; 3943 break; 3944 case ORDER_PHASE_SELECT_WIRE_METHOD: 3945 phase_select_wire_method (oc); 3946 break; 3947 case ORDER_PHASE_SET_MAX_FEE: 3948 phase_set_max_fee (oc); 3949 break; 3950 case ORDER_PHASE_SERIALIZE_ORDER: 3951 phase_serialize_order (oc); 3952 break; 3953 case ORDER_PHASE_CHECK_CONTRACT: 3954 phase_check_contract (oc); 3955 break; 3956 case ORDER_PHASE_SALT_FORGETTABLE: 3957 phase_salt_forgettable (oc); 3958 break; 3959 case ORDER_PHASE_EXECUTE_ORDER: 3960 phase_execute_order (oc); 3961 break; 3962 case ORDER_PHASE_FINISHED_MHD_YES: 3963 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3964 "Finished processing order (1)\n"); 3965 return MHD_YES; 3966 case ORDER_PHASE_FINISHED_MHD_NO: 3967 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3968 "Finished processing order (0)\n"); 3969 return MHD_NO; 3970 } 3971 } 3972 } 3973 3974 3975 /* end of taler-merchant-httpd_post-private-orders.c */