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