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