taler-merchant-httpd_post-private-orders.c (146119B)
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 #ifdef HAVE_DONAU_DONAU_SERVICE_H 3582 /** 3583 * Callback function that is called for each donau instance. 3584 * It simply adds the provided donau_url to the json. 3585 * 3586 * @param cls closure with our `struct TALER_MERCHANT_ContractOutput *` 3587 * @param donau_url the URL of the donau instance 3588 */ 3589 static void 3590 add_donau_url (void *cls, 3591 const char *donau_url) 3592 { 3593 struct TALER_MERCHANT_ContractOutput *output = cls; 3594 3595 GNUNET_array_append (output->details.donation_receipt.donau_urls, 3596 output->details.donation_receipt.donau_urls_len, 3597 GNUNET_strdup (donau_url)); 3598 } 3599 3600 3601 /** 3602 * Add the donau output to the contract output. 3603 * 3604 * @param oc order context 3605 * @param output contract output to add donau URLs to 3606 */ 3607 static bool 3608 add_donau_output (struct OrderContext *oc, 3609 struct TALER_MERCHANT_ContractOutput *output) 3610 { 3611 enum GNUNET_DB_QueryStatus qs; 3612 3613 qs = TMH_db->select_donau_instances_filtered ( 3614 TMH_db->cls, 3615 output->details.donation_receipt.amount.currency, 3616 &add_donau_url, 3617 output); 3618 if (qs < 0) 3619 { 3620 GNUNET_break (0); 3621 reply_with_error (oc, 3622 MHD_HTTP_INTERNAL_SERVER_ERROR, 3623 TALER_EC_GENERIC_DB_FETCH_FAILED, 3624 "donau url parsing db call"); 3625 for (unsigned int i = 0; 3626 i < output->details.donation_receipt.donau_urls_len; 3627 i++) 3628 GNUNET_free (output->details.donation_receipt.donau_urls[i]); 3629 GNUNET_array_grow (output->details.donation_receipt.donau_urls, 3630 output->details.donation_receipt.donau_urls_len, 3631 0); 3632 return false; 3633 } 3634 return true; 3635 } 3636 3637 3638 #endif 3639 3640 /** 3641 * Parse contract choices. Upon success, continue 3642 * processing with merge_inventory(). 3643 * 3644 * @param[in,out] oc order context 3645 */ 3646 static void 3647 phase_parse_choices (struct OrderContext *oc) 3648 { 3649 const json_t *jchoices; 3650 3651 switch (oc->parse_order.version) 3652 { 3653 case TALER_MERCHANT_CONTRACT_VERSION_0: 3654 oc->phase++; 3655 return; 3656 case TALER_MERCHANT_CONTRACT_VERSION_1: 3657 /* handle below */ 3658 break; 3659 default: 3660 GNUNET_assert (0); 3661 } 3662 3663 jchoices = oc->parse_order.details.v1.choices; 3664 3665 if (! json_is_array (jchoices)) 3666 GNUNET_assert (0); 3667 if (0 == json_array_size (jchoices)) 3668 { 3669 reply_with_error (oc, 3670 MHD_HTTP_BAD_REQUEST, 3671 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3672 "choices"); 3673 return; 3674 } 3675 GNUNET_array_grow (oc->parse_choices.choices, 3676 oc->parse_choices.choices_len, 3677 json_array_size (jchoices)); 3678 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 3679 { 3680 struct TALER_MERCHANT_ContractChoice *choice 3681 = &oc->parse_choices.choices[i]; 3682 const char *error_name; 3683 unsigned int error_line; 3684 const json_t *jinputs; 3685 const json_t *joutputs; 3686 bool no_fee; 3687 struct GNUNET_JSON_Specification spec[] = { 3688 TALER_JSON_spec_amount_any ("amount", 3689 &choice->amount), 3690 GNUNET_JSON_spec_mark_optional ( 3691 TALER_JSON_spec_amount_any ("tip", 3692 &choice->tip), 3693 &choice->no_tip), 3694 GNUNET_JSON_spec_mark_optional ( 3695 TALER_JSON_spec_amount_any ("max_fee", 3696 &choice->max_fee), 3697 &no_fee), 3698 GNUNET_JSON_spec_mark_optional ( 3699 GNUNET_JSON_spec_string_copy ("description", 3700 &choice->description), 3701 NULL), 3702 GNUNET_JSON_spec_mark_optional ( 3703 GNUNET_JSON_spec_object_copy ("description_i18n", 3704 &choice->description_i18n), 3705 NULL), 3706 GNUNET_JSON_spec_mark_optional ( 3707 GNUNET_JSON_spec_array_const ("inputs", 3708 &jinputs), 3709 NULL), 3710 GNUNET_JSON_spec_mark_optional ( 3711 GNUNET_JSON_spec_array_const ("outputs", 3712 &joutputs), 3713 NULL), 3714 GNUNET_JSON_spec_end () 3715 }; 3716 enum GNUNET_GenericReturnValue ret; 3717 3718 ret = GNUNET_JSON_parse (json_array_get (jchoices, 3719 i), 3720 spec, 3721 &error_name, 3722 &error_line); 3723 if (GNUNET_OK != ret) 3724 { 3725 GNUNET_break_op (0); 3726 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3727 "Choice parsing failed: %s:%u\n", 3728 error_name, 3729 error_line); 3730 reply_with_error (oc, 3731 MHD_HTTP_BAD_REQUEST, 3732 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3733 "choice"); 3734 return; 3735 } 3736 if ( (! no_fee) && 3737 (GNUNET_OK != 3738 TALER_amount_cmp_currency (&choice->amount, 3739 &choice->max_fee)) ) 3740 { 3741 GNUNET_break_op (0); 3742 GNUNET_JSON_parse_free (spec); 3743 reply_with_error (oc, 3744 MHD_HTTP_BAD_REQUEST, 3745 TALER_EC_GENERIC_CURRENCY_MISMATCH, 3746 "different currencies used for 'max_fee' and 'amount' currency"); 3747 return; 3748 } 3749 if ( (! choice->no_tip) && 3750 (GNUNET_OK != 3751 TALER_amount_cmp_currency (&choice->amount, 3752 &choice->tip)) ) 3753 { 3754 GNUNET_break_op (0); 3755 GNUNET_JSON_parse_free (spec); 3756 reply_with_error (oc, 3757 MHD_HTTP_BAD_REQUEST, 3758 TALER_EC_GENERIC_CURRENCY_MISMATCH, 3759 "tip and amount"); 3760 return; 3761 } 3762 3763 if (! TMH_test_exchange_configured_for_currency ( 3764 choice->amount.currency)) 3765 { 3766 GNUNET_break_op (0); 3767 GNUNET_JSON_parse_free (spec); 3768 reply_with_error (oc, 3769 MHD_HTTP_CONFLICT, 3770 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, 3771 choice->amount.currency); 3772 return; 3773 } 3774 3775 if (NULL != jinputs) 3776 { 3777 const json_t *jinput; 3778 size_t idx; 3779 json_array_foreach ((json_t *) jinputs, idx, jinput) 3780 { 3781 struct TALER_MERCHANT_ContractInput input = { 3782 .details.token.count = 1 3783 }; 3784 3785 if (GNUNET_OK != 3786 TALER_MERCHANT_parse_choice_input ((json_t *) jinput, 3787 &input, 3788 idx, 3789 true)) 3790 { 3791 GNUNET_break_op (0); 3792 reply_with_error (oc, 3793 MHD_HTTP_BAD_REQUEST, 3794 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3795 "input"); 3796 return; 3797 } 3798 3799 switch (input.type) 3800 { 3801 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 3802 GNUNET_assert (0); 3803 break; 3804 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 3805 /* Ignore inputs tokens with 'count' field set to 0 */ 3806 if (0 == input.details.token.count) 3807 continue; 3808 3809 if (GNUNET_OK != 3810 add_input_token_family (oc, 3811 input.details.token.token_family_slug)) 3812 3813 { 3814 GNUNET_break_op (0); 3815 reply_with_error (oc, 3816 MHD_HTTP_BAD_REQUEST, 3817 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN, 3818 input.details.token.token_family_slug); 3819 return; 3820 } 3821 3822 GNUNET_array_append (choice->inputs, 3823 choice->inputs_len, 3824 input); 3825 continue; 3826 } 3827 GNUNET_assert (0); 3828 } 3829 } 3830 3831 if (NULL != joutputs) 3832 { 3833 const json_t *joutput; 3834 size_t idx; 3835 json_array_foreach ((json_t *) joutputs, idx, joutput) 3836 { 3837 struct TALER_MERCHANT_ContractOutput output = { 3838 .details.token.count = 1 3839 }; 3840 3841 if (GNUNET_OK != 3842 TALER_MERCHANT_parse_choice_output ((json_t *) joutput, 3843 &output, 3844 idx, 3845 true)) 3846 { 3847 reply_with_error (oc, 3848 MHD_HTTP_BAD_REQUEST, 3849 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3850 "output"); 3851 return; 3852 } 3853 3854 switch (output.type) 3855 { 3856 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 3857 GNUNET_assert (0); 3858 break; 3859 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 3860 #ifdef HAVE_DONAU_DONAU_SERVICE_H 3861 output.details.donation_receipt.amount = choice->amount; 3862 if (! add_donau_output (oc, 3863 &output)) 3864 { 3865 GNUNET_break (0); 3866 return; 3867 } 3868 GNUNET_array_append (choice->outputs, 3869 choice->outputs_len, 3870 output); 3871 #endif 3872 continue; 3873 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 3874 /* Ignore inputs tokens with 'count' field set to 0 */ 3875 if (0 == output.details.token.count) 3876 continue; 3877 3878 if (0 == output.details.token.valid_at.abs_time.abs_value_us) 3879 output.details.token.valid_at 3880 = GNUNET_TIME_timestamp_get (); 3881 if (GNUNET_OK != 3882 add_output_token_family (oc, 3883 output.details.token.token_family_slug, 3884 output.details.token.valid_at, 3885 &output.details.token.key_index)) 3886 3887 { 3888 /* note: reply_with_error() was already called */ 3889 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3890 "Could not handle output token family `%s'\n", 3891 output.details.token.token_family_slug); 3892 return; 3893 } 3894 3895 GNUNET_array_append (choice->outputs, 3896 choice->outputs_len, 3897 output); 3898 continue; 3899 } 3900 GNUNET_assert (0); 3901 } 3902 } 3903 } 3904 oc->phase++; 3905 } 3906 3907 3908 /* ***************** ORDER_PHASE_PARSE_ORDER **************** */ 3909 3910 3911 /** 3912 * Parse the order field of the request. Upon success, continue 3913 * processing with parse_choices(). 3914 * 3915 * @param[in,out] oc order context 3916 */ 3917 static void 3918 phase_parse_order (struct OrderContext *oc) 3919 { 3920 const struct TALER_MERCHANTDB_InstanceSettings *settings = 3921 &oc->hc->instance->settings; 3922 const char *merchant_base_url = NULL; 3923 uint64_t version = 0; 3924 const json_t *jmerchant = NULL; 3925 const json_t *products = NULL; 3926 const char *order_id = NULL; 3927 struct GNUNET_JSON_Specification spec[] = { 3928 GNUNET_JSON_spec_mark_optional ( 3929 GNUNET_JSON_spec_uint64 ("version", 3930 &version), 3931 NULL), 3932 GNUNET_JSON_spec_string ("summary", 3933 &oc->parse_order.summary), 3934 GNUNET_JSON_spec_mark_optional ( 3935 GNUNET_JSON_spec_array_const ("products", 3936 &products), 3937 NULL), 3938 GNUNET_JSON_spec_mark_optional ( 3939 GNUNET_JSON_spec_object_const ("summary_i18n", 3940 &oc->parse_order.summary_i18n), 3941 NULL), 3942 GNUNET_JSON_spec_mark_optional ( 3943 GNUNET_JSON_spec_string ("order_id", 3944 &order_id), 3945 NULL), 3946 GNUNET_JSON_spec_mark_optional ( 3947 GNUNET_JSON_spec_string ("fulfillment_message", 3948 &oc->parse_order.fulfillment_message), 3949 NULL), 3950 GNUNET_JSON_spec_mark_optional ( 3951 GNUNET_JSON_spec_object_const ("fulfillment_message_i18n", 3952 &oc->parse_order.fulfillment_message_i18n), 3953 NULL), 3954 GNUNET_JSON_spec_mark_optional ( 3955 GNUNET_JSON_spec_string ("fulfillment_url", 3956 &oc->parse_order.fulfillment_url), 3957 NULL), 3958 GNUNET_JSON_spec_mark_optional ( 3959 GNUNET_JSON_spec_string ("public_reorder_url", 3960 &oc->parse_order.public_reorder_url), 3961 NULL), 3962 GNUNET_JSON_spec_mark_optional ( 3963 TALER_JSON_spec_web_url ("merchant_base_url", 3964 &merchant_base_url), 3965 NULL), 3966 /* For sanity check, this field must NOT be present */ 3967 GNUNET_JSON_spec_mark_optional ( 3968 GNUNET_JSON_spec_object_const ("merchant", 3969 &jmerchant), 3970 NULL), 3971 GNUNET_JSON_spec_mark_optional ( 3972 GNUNET_JSON_spec_timestamp ("timestamp", 3973 &oc->parse_order.timestamp), 3974 NULL), 3975 GNUNET_JSON_spec_mark_optional ( 3976 GNUNET_JSON_spec_timestamp ("refund_deadline", 3977 &oc->parse_order.refund_deadline), 3978 NULL), 3979 GNUNET_JSON_spec_mark_optional ( 3980 GNUNET_JSON_spec_timestamp ("pay_deadline", 3981 &oc->parse_order.pay_deadline), 3982 NULL), 3983 GNUNET_JSON_spec_mark_optional ( 3984 GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", 3985 &oc->parse_order.wire_deadline), 3986 NULL), 3987 GNUNET_JSON_spec_mark_optional ( 3988 GNUNET_JSON_spec_object_const ("delivery_location", 3989 &oc->parse_order.delivery_location), 3990 NULL), 3991 GNUNET_JSON_spec_mark_optional ( 3992 GNUNET_JSON_spec_timestamp ("delivery_date", 3993 &oc->parse_order.delivery_date), 3994 NULL), 3995 GNUNET_JSON_spec_mark_optional ( 3996 GNUNET_JSON_spec_uint32 ("minimum_age", 3997 &oc->parse_order.minimum_age), 3998 NULL), 3999 GNUNET_JSON_spec_mark_optional ( 4000 GNUNET_JSON_spec_relative_time ("auto_refund", 4001 &oc->parse_order.auto_refund), 4002 NULL), 4003 GNUNET_JSON_spec_mark_optional ( 4004 GNUNET_JSON_spec_object_const ("extra", 4005 &oc->parse_order.extra), 4006 NULL), 4007 GNUNET_JSON_spec_mark_optional ( 4008 GNUNET_JSON_spec_uint64 ("order_default_money_pot", 4009 &oc->parse_order.order_default_money_pot), 4010 NULL), 4011 GNUNET_JSON_spec_end () 4012 }; 4013 enum GNUNET_GenericReturnValue ret; 4014 bool computed_refund_deadline; 4015 4016 oc->parse_order.refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS; 4017 oc->parse_order.wire_deadline = GNUNET_TIME_UNIT_FOREVER_TS; 4018 ret = TALER_MHD_parse_json_data (oc->connection, 4019 oc->parse_request.order, 4020 spec); 4021 if (GNUNET_OK != ret) 4022 { 4023 GNUNET_break_op (0); 4024 finalize_order2 (oc, 4025 ret); 4026 return; 4027 } 4028 if ( (NULL != products) && 4029 (0 != (oc->parse_order.products_len = json_array_size (products))) ) 4030 { 4031 size_t i; 4032 json_t *p; 4033 4034 oc->parse_order.products 4035 = GNUNET_new_array (oc->parse_order.products_len, 4036 struct TALER_MERCHANT_ProductSold); 4037 json_array_foreach (products, i, p) 4038 { 4039 if (GNUNET_OK != 4040 TALER_MERCHANT_parse_product_sold (p, 4041 &oc->parse_order.products[i])) 4042 { 4043 GNUNET_break_op (0); 4044 reply_with_error (oc, 4045 MHD_HTTP_BAD_REQUEST, 4046 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4047 "order.products"); 4048 return; 4049 } 4050 } 4051 } 4052 if (NULL != order_id) 4053 { 4054 size_t len = strlen (order_id); 4055 4056 for (size_t i = 0; i<len; i++) 4057 { 4058 char c = order_id[i]; 4059 4060 if (! ( ( (c >= 'A') && 4061 (c <= 'Z') ) || 4062 ( (c >= 'a') && 4063 (c <= 'z') ) || 4064 ( (c >= '0') && 4065 (c <= '9') ) || 4066 (c == '-') || 4067 (c == '_') || 4068 (c == '.') || 4069 (c == ':') ) ) 4070 { 4071 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 4072 "Invalid character `%c' in order ID `%s'\n", 4073 c, 4074 order_id); 4075 reply_with_error (oc, 4076 MHD_HTTP_BAD_REQUEST, 4077 TALER_EC_GENERIC_CURRENCY_MISMATCH, 4078 "Invalid character in order_id"); 4079 return; 4080 } 4081 } 4082 } 4083 switch (version) 4084 { 4085 case 0: 4086 { 4087 bool no_fee; 4088 const json_t *choices = NULL; 4089 struct GNUNET_JSON_Specification specv0[] = { 4090 TALER_JSON_spec_amount_any ( 4091 "amount", 4092 &oc->parse_order.details.v0.brutto), 4093 GNUNET_JSON_spec_mark_optional ( 4094 TALER_JSON_spec_amount_any ( 4095 "tip", 4096 &oc->parse_order.details.v0.tip), 4097 &oc->parse_order.details.v0.no_tip), 4098 GNUNET_JSON_spec_mark_optional ( 4099 TALER_JSON_spec_amount_any ( 4100 "max_fee", 4101 &oc->parse_order.details.v0.max_fee), 4102 &no_fee), 4103 /* for sanity check, must be *absent*! */ 4104 GNUNET_JSON_spec_mark_optional ( 4105 GNUNET_JSON_spec_array_const ("choices", 4106 &choices), 4107 NULL), 4108 GNUNET_JSON_spec_end () 4109 }; 4110 4111 ret = TALER_MHD_parse_json_data (oc->connection, 4112 oc->parse_request.order, 4113 specv0); 4114 if (GNUNET_OK != ret) 4115 { 4116 GNUNET_break_op (0); 4117 finalize_order2 (oc, 4118 ret); 4119 return; 4120 } 4121 if ( (! no_fee) && 4122 (GNUNET_OK != 4123 TALER_amount_cmp_currency (&oc->parse_order.details.v0.brutto, 4124 &oc->parse_order.details.v0.max_fee)) ) 4125 { 4126 GNUNET_break_op (0); 4127 GNUNET_JSON_parse_free (spec); 4128 reply_with_error (oc, 4129 MHD_HTTP_BAD_REQUEST, 4130 TALER_EC_GENERIC_CURRENCY_MISMATCH, 4131 "different currencies used for 'max_fee' and 'amount' currency"); 4132 return; 4133 } 4134 if ( (! oc->parse_order.details.v0.no_tip) && 4135 (GNUNET_OK != 4136 TALER_amount_cmp_currency (&oc->parse_order.details.v0.brutto, 4137 &oc->parse_order.details.v0.tip)) ) 4138 { 4139 GNUNET_break_op (0); 4140 GNUNET_JSON_parse_free (spec); 4141 reply_with_error (oc, 4142 MHD_HTTP_BAD_REQUEST, 4143 TALER_EC_GENERIC_CURRENCY_MISMATCH, 4144 "tip and amount"); 4145 return; 4146 } 4147 if (! TMH_test_exchange_configured_for_currency ( 4148 oc->parse_order.details.v0.brutto.currency)) 4149 { 4150 GNUNET_break_op (0); 4151 GNUNET_JSON_parse_free (spec); 4152 reply_with_error (oc, 4153 MHD_HTTP_CONFLICT, 4154 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, 4155 oc->parse_order.details.v0.brutto.currency); 4156 return; 4157 } 4158 if (NULL != choices) 4159 { 4160 GNUNET_break_op (0); 4161 GNUNET_JSON_parse_free (spec); 4162 reply_with_error (oc, 4163 MHD_HTTP_BAD_REQUEST, 4164 TALER_EC_GENERIC_UNEXPECTED_REQUEST_ERROR, 4165 "choices array must be null for v0 contracts"); 4166 return; 4167 } 4168 oc->parse_order.version = TALER_MERCHANT_CONTRACT_VERSION_0; 4169 break; 4170 } 4171 case 1: 4172 { 4173 struct GNUNET_JSON_Specification specv1[] = { 4174 GNUNET_JSON_spec_array_const ( 4175 "choices", 4176 &oc->parse_order.details.v1.choices), 4177 GNUNET_JSON_spec_end () 4178 }; 4179 4180 ret = TALER_MHD_parse_json_data (oc->connection, 4181 oc->parse_request.order, 4182 specv1); 4183 if (GNUNET_OK != ret) 4184 { 4185 GNUNET_break_op (0); 4186 finalize_order2 (oc, 4187 ret); 4188 return; 4189 } 4190 oc->parse_order.version = TALER_MERCHANT_CONTRACT_VERSION_1; 4191 break; 4192 } 4193 default: 4194 GNUNET_break_op (0); 4195 GNUNET_JSON_parse_free (spec); 4196 reply_with_error (oc, 4197 MHD_HTTP_BAD_REQUEST, 4198 TALER_EC_GENERIC_VERSION_MALFORMED, 4199 "invalid version specified in order, supported are null, '0' or '1'"); 4200 return; 4201 } 4202 4203 /* Add order_id if it doesn't exist. */ 4204 if (NULL != order_id) 4205 { 4206 oc->parse_order.order_id = GNUNET_strdup (order_id); 4207 } 4208 else 4209 { 4210 char buf[256]; 4211 time_t timer; 4212 struct tm *tm_info; 4213 size_t off; 4214 uint64_t rand; 4215 char *last; 4216 4217 time (&timer); 4218 tm_info = localtime (&timer); 4219 if (NULL == tm_info) 4220 { 4221 GNUNET_JSON_parse_free (spec); 4222 reply_with_error ( 4223 oc, 4224 MHD_HTTP_INTERNAL_SERVER_ERROR, 4225 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME, 4226 NULL); 4227 return; 4228 } 4229 off = strftime (buf, 4230 sizeof (buf) - 1, 4231 "%Y.%j", 4232 tm_info); 4233 /* Check for error state of strftime */ 4234 GNUNET_assert (0 != off); 4235 buf[off++] = '-'; 4236 rand = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 4237 UINT64_MAX); 4238 last = GNUNET_STRINGS_data_to_string (&rand, 4239 sizeof (uint64_t), 4240 &buf[off], 4241 sizeof (buf) - off); 4242 GNUNET_assert (NULL != last); 4243 *last = '\0'; 4244 4245 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4246 "Assigning order ID `%s' server-side\n", 4247 buf); 4248 4249 oc->parse_order.order_id = GNUNET_strdup (buf); 4250 GNUNET_assert (NULL != oc->parse_order.order_id); 4251 } 4252 4253 /* Patch fulfillment URL with order_id (implements #6467). */ 4254 if (NULL != oc->parse_order.fulfillment_url) 4255 { 4256 const char *pos; 4257 4258 pos = strstr (oc->parse_order.fulfillment_url, 4259 "${ORDER_ID}"); 4260 if (NULL != pos) 4261 { 4262 /* replace ${ORDER_ID} with the real order_id */ 4263 char *nurl; 4264 4265 /* We only allow one placeholder */ 4266 if (strstr (pos + strlen ("${ORDER_ID}"), 4267 "${ORDER_ID}")) 4268 { 4269 GNUNET_break_op (0); 4270 reply_with_error (oc, 4271 MHD_HTTP_BAD_REQUEST, 4272 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4273 "fulfillment_url"); 4274 return; 4275 } 4276 4277 GNUNET_asprintf (&nurl, 4278 "%.*s%s%s", 4279 /* first output URL until ${ORDER_ID} */ 4280 (int) (pos - oc->parse_order.fulfillment_url), 4281 oc->parse_order.fulfillment_url, 4282 /* replace ${ORDER_ID} with the right order_id */ 4283 oc->parse_order.order_id, 4284 /* append rest of original URL */ 4285 pos + strlen ("${ORDER_ID}")); 4286 4287 oc->parse_order.fulfillment_url = GNUNET_strdup (nurl); 4288 4289 GNUNET_free (nurl); 4290 } 4291 } 4292 4293 if ( (GNUNET_TIME_absolute_is_zero (oc->parse_order.pay_deadline.abs_time)) || 4294 (GNUNET_TIME_absolute_is_never (oc->parse_order.pay_deadline.abs_time)) ) 4295 { 4296 oc->parse_order.pay_deadline = GNUNET_TIME_relative_to_timestamp ( 4297 settings->default_pay_delay); 4298 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4299 "Pay deadline was zero (or never), setting to %s\n", 4300 GNUNET_TIME_timestamp2s (oc->parse_order.pay_deadline)); 4301 } 4302 else if (GNUNET_TIME_absolute_is_past (oc->parse_order.pay_deadline.abs_time)) 4303 { 4304 GNUNET_break_op (0); 4305 reply_with_error ( 4306 oc, 4307 MHD_HTTP_BAD_REQUEST, 4308 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PAY_DEADLINE_IN_PAST, 4309 NULL); 4310 return; 4311 } 4312 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4313 "Pay deadline is %s\n", 4314 GNUNET_TIME_timestamp2s (oc->parse_order.pay_deadline)); 4315 4316 /* Check soundness of refund deadline, and that a timestamp 4317 * is actually present. */ 4318 { 4319 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 4320 4321 /* Add timestamp if it doesn't exist (or is zero) */ 4322 if (GNUNET_TIME_absolute_is_zero (oc->parse_order.timestamp.abs_time)) 4323 { 4324 oc->parse_order.timestamp = now; 4325 } 4326 4327 /* If no refund_deadline given, set one based on refund_delay. */ 4328 if (GNUNET_TIME_absolute_is_never ( 4329 oc->parse_order.refund_deadline.abs_time)) 4330 { 4331 if (GNUNET_TIME_relative_is_zero (oc->parse_request.refund_delay)) 4332 { 4333 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4334 "Refund delay is zero, no refunds are possible for this order\n"); 4335 oc->parse_order.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; 4336 } 4337 else 4338 { 4339 computed_refund_deadline = true; 4340 oc->parse_order.refund_deadline 4341 = GNUNET_TIME_absolute_to_timestamp ( 4342 GNUNET_TIME_absolute_add (oc->parse_order.pay_deadline.abs_time, 4343 oc->parse_request.refund_delay)); 4344 } 4345 } 4346 4347 if ( (! GNUNET_TIME_absolute_is_zero ( 4348 oc->parse_order.delivery_date.abs_time)) && 4349 (GNUNET_TIME_absolute_is_past ( 4350 oc->parse_order.delivery_date.abs_time)) ) 4351 { 4352 GNUNET_break_op (0); 4353 reply_with_error ( 4354 oc, 4355 MHD_HTTP_BAD_REQUEST, 4356 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST, 4357 NULL); 4358 return; 4359 } 4360 } 4361 4362 if ( (! GNUNET_TIME_absolute_is_zero ( 4363 oc->parse_order.refund_deadline.abs_time)) && 4364 (GNUNET_TIME_absolute_is_past ( 4365 oc->parse_order.refund_deadline.abs_time)) ) 4366 { 4367 GNUNET_break_op (0); 4368 reply_with_error ( 4369 oc, 4370 MHD_HTTP_BAD_REQUEST, 4371 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_DEADLINE_IN_PAST, 4372 NULL); 4373 return; 4374 } 4375 4376 if (GNUNET_TIME_absolute_is_never (oc->parse_order.wire_deadline.abs_time)) 4377 { 4378 struct GNUNET_TIME_Absolute start; 4379 4380 start = GNUNET_TIME_absolute_max ( 4381 oc->parse_order.refund_deadline.abs_time, 4382 oc->parse_order.pay_deadline.abs_time); 4383 oc->parse_order.wire_deadline 4384 = GNUNET_TIME_absolute_to_timestamp ( 4385 GNUNET_TIME_round_up ( 4386 GNUNET_TIME_absolute_add ( 4387 start, 4388 settings->default_wire_transfer_delay), 4389 settings->default_wire_transfer_rounding_interval)); 4390 if (GNUNET_TIME_absolute_is_never ( 4391 oc->parse_order.wire_deadline.abs_time)) 4392 { 4393 GNUNET_break_op (0); 4394 reply_with_error ( 4395 oc, 4396 MHD_HTTP_BAD_REQUEST, 4397 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER, 4398 "order:wire_transfer_deadline"); 4399 return; 4400 } 4401 } 4402 else if (computed_refund_deadline) 4403 { 4404 /* if we computed the refund_deadline from default settings 4405 and did have a configured wire_deadline, make sure that 4406 the refund_deadline is at or below the wire_deadline. */ 4407 oc->parse_order.refund_deadline 4408 = GNUNET_TIME_timestamp_min (oc->parse_order.refund_deadline, 4409 oc->parse_order.wire_deadline); 4410 } 4411 if (GNUNET_TIME_timestamp_cmp (oc->parse_order.wire_deadline, 4412 <, 4413 oc->parse_order.refund_deadline)) 4414 { 4415 GNUNET_break_op (0); 4416 reply_with_error ( 4417 oc, 4418 MHD_HTTP_BAD_REQUEST, 4419 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE, 4420 "order:wire_transfer_deadline;order:refund_deadline"); 4421 return; 4422 } 4423 4424 if (NULL != merchant_base_url) 4425 { 4426 if (('\0' == *merchant_base_url) || 4427 ('/' != merchant_base_url[strlen (merchant_base_url) - 1])) 4428 { 4429 GNUNET_break_op (0); 4430 reply_with_error ( 4431 oc, 4432 MHD_HTTP_BAD_REQUEST, 4433 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR, 4434 "merchant_base_url is not valid"); 4435 return; 4436 } 4437 oc->parse_order.merchant_base_url 4438 = GNUNET_strdup (merchant_base_url); 4439 } 4440 else 4441 { 4442 char *url; 4443 4444 url = make_merchant_base_url (oc->connection, 4445 settings->id); 4446 if (NULL == url) 4447 { 4448 GNUNET_break_op (0); 4449 reply_with_error ( 4450 oc, 4451 MHD_HTTP_BAD_REQUEST, 4452 TALER_EC_GENERIC_PARAMETER_MISSING, 4453 "order:merchant_base_url"); 4454 return; 4455 } 4456 oc->parse_order.merchant_base_url = url; 4457 } 4458 4459 /* Merchant information must not already be present */ 4460 if (NULL != jmerchant) 4461 { 4462 GNUNET_break_op (0); 4463 reply_with_error ( 4464 oc, 4465 MHD_HTTP_BAD_REQUEST, 4466 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR, 4467 "'merchant' field already set, but must be provided by backend"); 4468 return; 4469 } 4470 4471 if ( (NULL != oc->parse_order.delivery_location) && 4472 (! TMH_location_object_valid (oc->parse_order.delivery_location)) ) 4473 { 4474 GNUNET_break_op (0); 4475 reply_with_error (oc, 4476 MHD_HTTP_BAD_REQUEST, 4477 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4478 "delivery_location"); 4479 return; 4480 } 4481 4482 oc->phase++; 4483 } 4484 4485 4486 /* ***************** ORDER_PHASE_PARSE_REQUEST **************** */ 4487 4488 /** 4489 * Parse the client request. Upon success, 4490 * continue processing by calling parse_order(). 4491 * 4492 * @param[in,out] oc order context to process 4493 */ 4494 static void 4495 phase_parse_request (struct OrderContext *oc) 4496 { 4497 const json_t *ip = NULL; 4498 const json_t *uuid = NULL; 4499 const char *otp_id = NULL; 4500 bool create_token = true; /* default */ 4501 struct GNUNET_JSON_Specification spec[] = { 4502 GNUNET_JSON_spec_json ("order", 4503 &oc->parse_request.order), 4504 GNUNET_JSON_spec_mark_optional ( 4505 GNUNET_JSON_spec_relative_time ("refund_delay", 4506 &oc->parse_request.refund_delay), 4507 NULL), 4508 GNUNET_JSON_spec_mark_optional ( 4509 GNUNET_JSON_spec_string ("payment_target", 4510 &oc->parse_request.payment_target), 4511 NULL), 4512 GNUNET_JSON_spec_mark_optional ( 4513 GNUNET_JSON_spec_array_const ("inventory_products", 4514 &ip), 4515 NULL), 4516 GNUNET_JSON_spec_mark_optional ( 4517 GNUNET_JSON_spec_string ("session_id", 4518 &oc->parse_request.session_id), 4519 NULL), 4520 GNUNET_JSON_spec_mark_optional ( 4521 GNUNET_JSON_spec_array_const ("lock_uuids", 4522 &uuid), 4523 NULL), 4524 GNUNET_JSON_spec_mark_optional ( 4525 GNUNET_JSON_spec_bool ("create_token", 4526 &create_token), 4527 NULL), 4528 GNUNET_JSON_spec_mark_optional ( 4529 GNUNET_JSON_spec_string ("otp_id", 4530 &otp_id), 4531 NULL), 4532 GNUNET_JSON_spec_end () 4533 }; 4534 enum GNUNET_GenericReturnValue ret; 4535 4536 oc->parse_request.refund_delay 4537 = oc->hc->instance->settings.default_refund_delay; 4538 ret = TALER_MHD_parse_json_data (oc->connection, 4539 oc->hc->request_body, 4540 spec); 4541 if (GNUNET_OK != ret) 4542 { 4543 GNUNET_break_op (0); 4544 finalize_order2 (oc, 4545 ret); 4546 return; 4547 } 4548 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 4549 "Refund delay is %s\n", 4550 GNUNET_TIME_relative2s (oc->parse_request.refund_delay, 4551 false)); 4552 TMH_db->expire_locks (TMH_db->cls); 4553 if (NULL != otp_id) 4554 { 4555 struct TALER_MERCHANTDB_OtpDeviceDetails td; 4556 enum GNUNET_DB_QueryStatus qs; 4557 4558 memset (&td, 4559 0, 4560 sizeof (td)); 4561 qs = TMH_db->select_otp (TMH_db->cls, 4562 oc->hc->instance->settings.id, 4563 otp_id, 4564 &td); 4565 switch (qs) 4566 { 4567 case GNUNET_DB_STATUS_HARD_ERROR: 4568 GNUNET_break (0); 4569 reply_with_error (oc, 4570 MHD_HTTP_INTERNAL_SERVER_ERROR, 4571 TALER_EC_GENERIC_DB_FETCH_FAILED, 4572 "select_otp"); 4573 return; 4574 case GNUNET_DB_STATUS_SOFT_ERROR: 4575 GNUNET_break (0); 4576 reply_with_error (oc, 4577 MHD_HTTP_INTERNAL_SERVER_ERROR, 4578 TALER_EC_GENERIC_DB_SOFT_FAILURE, 4579 "select_otp"); 4580 return; 4581 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 4582 reply_with_error (oc, 4583 MHD_HTTP_NOT_FOUND, 4584 TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, 4585 otp_id); 4586 return; 4587 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 4588 break; 4589 } 4590 oc->parse_request.pos_key = td.otp_key; 4591 oc->parse_request.pos_algorithm = td.otp_algorithm; 4592 GNUNET_free (td.otp_description); 4593 } 4594 if (create_token) 4595 { 4596 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 4597 &oc->parse_request.claim_token, 4598 sizeof (oc->parse_request.claim_token)); 4599 } 4600 /* Compute h_post_data (for idempotency check) */ 4601 { 4602 char *req_body_enc; 4603 4604 /* Dump normalized JSON to string. */ 4605 if (NULL == (req_body_enc 4606 = json_dumps (oc->hc->request_body, 4607 JSON_ENCODE_ANY 4608 | JSON_COMPACT 4609 | JSON_SORT_KEYS))) 4610 { 4611 GNUNET_break (0); 4612 GNUNET_JSON_parse_free (spec); 4613 reply_with_error (oc, 4614 MHD_HTTP_INTERNAL_SERVER_ERROR, 4615 TALER_EC_GENERIC_ALLOCATION_FAILURE, 4616 "request body normalization for hashing"); 4617 return; 4618 } 4619 GNUNET_CRYPTO_hash (req_body_enc, 4620 strlen (req_body_enc), 4621 &oc->parse_request.h_post_data.hash); 4622 GNUNET_free (req_body_enc); 4623 } 4624 4625 /* parse the inventory_products (optionally given) */ 4626 if (NULL != ip) 4627 { 4628 unsigned int ipl = (unsigned int) json_array_size (ip); 4629 4630 if ( (json_array_size (ip) != (size_t) ipl) || 4631 (ipl > MAX_PRODUCTS) ) 4632 { 4633 GNUNET_break_op (0); 4634 GNUNET_JSON_parse_free (spec); 4635 reply_with_error (oc, 4636 MHD_HTTP_BAD_REQUEST, 4637 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4638 "inventory_products (too many)"); 4639 return; 4640 } 4641 GNUNET_array_grow (oc->parse_request.inventory_products, 4642 oc->parse_request.inventory_products_length, 4643 (unsigned int) json_array_size (ip)); 4644 for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++) 4645 { 4646 struct InventoryProduct *ipr = &oc->parse_request.inventory_products[i]; 4647 const char *error_name; 4648 unsigned int error_line; 4649 struct GNUNET_JSON_Specification ispec[] = { 4650 GNUNET_JSON_spec_string ("product_id", 4651 &ipr->product_id), 4652 GNUNET_JSON_spec_mark_optional ( 4653 GNUNET_JSON_spec_uint64 ("quantity", 4654 &ipr->quantity), 4655 &ipr->quantity_missing), 4656 GNUNET_JSON_spec_mark_optional ( 4657 GNUNET_JSON_spec_string ("unit_quantity", 4658 &ipr->unit_quantity), 4659 &ipr->unit_quantity_missing), 4660 GNUNET_JSON_spec_mark_optional ( 4661 GNUNET_JSON_spec_uint64 ("product_money_pot", 4662 &ipr->product_money_pot), 4663 NULL), 4664 GNUNET_JSON_spec_end () 4665 }; 4666 4667 ret = GNUNET_JSON_parse (json_array_get (ip, 4668 i), 4669 ispec, 4670 &error_name, 4671 &error_line); 4672 if (GNUNET_OK != ret) 4673 { 4674 GNUNET_break_op (0); 4675 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 4676 "Product parsing failed at #%u: %s:%u\n", 4677 i, 4678 error_name, 4679 error_line); 4680 reply_with_error (oc, 4681 MHD_HTTP_BAD_REQUEST, 4682 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4683 "inventory_products"); 4684 return; 4685 } 4686 if (ipr->quantity_missing && ipr->unit_quantity_missing) 4687 { 4688 ipr->quantity = 1; 4689 ipr->quantity_missing = false; 4690 } 4691 } 4692 } 4693 4694 /* parse the lock_uuids (optionally given) */ 4695 if (NULL != uuid) 4696 { 4697 GNUNET_array_grow (oc->parse_request.uuids, 4698 oc->parse_request.uuids_length, 4699 json_array_size (uuid)); 4700 for (unsigned int i = 0; i<oc->parse_request.uuids_length; i++) 4701 { 4702 json_t *ui = json_array_get (uuid, 4703 i); 4704 4705 if (! json_is_string (ui)) 4706 { 4707 GNUNET_break_op (0); 4708 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 4709 "UUID parsing failed at #%u\n", 4710 i); 4711 reply_with_error (oc, 4712 MHD_HTTP_BAD_REQUEST, 4713 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4714 "lock_uuids"); 4715 return; 4716 } 4717 TMH_uuid_from_string (json_string_value (ui), 4718 &oc->parse_request.uuids[i]); 4719 } 4720 } 4721 oc->phase++; 4722 } 4723 4724 4725 /* ***************** Main handler **************** */ 4726 4727 4728 MHD_RESULT 4729 TMH_private_post_orders ( 4730 const struct TMH_RequestHandler *rh, 4731 struct MHD_Connection *connection, 4732 struct TMH_HandlerContext *hc) 4733 { 4734 struct OrderContext *oc = hc->ctx; 4735 4736 if (NULL == oc) 4737 { 4738 oc = GNUNET_new (struct OrderContext); 4739 hc->ctx = oc; 4740 hc->cc = &clean_order; 4741 oc->connection = connection; 4742 oc->hc = hc; 4743 } 4744 while (1) 4745 { 4746 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4747 "Processing order in phase %d\n", 4748 oc->phase); 4749 switch (oc->phase) 4750 { 4751 case ORDER_PHASE_PARSE_REQUEST: 4752 phase_parse_request (oc); 4753 break; 4754 case ORDER_PHASE_PARSE_ORDER: 4755 phase_parse_order (oc); 4756 break; 4757 case ORDER_PHASE_PARSE_CHOICES: 4758 phase_parse_choices (oc); 4759 break; 4760 case ORDER_PHASE_MERGE_INVENTORY: 4761 phase_merge_inventory (oc); 4762 break; 4763 case ORDER_PHASE_ADD_PAYMENT_DETAILS: 4764 phase_add_payment_details (oc); 4765 break; 4766 case ORDER_PHASE_SET_EXCHANGES: 4767 if (phase_set_exchanges (oc)) 4768 return MHD_YES; 4769 break; 4770 case ORDER_PHASE_SELECT_WIRE_METHOD: 4771 phase_select_wire_method (oc); 4772 break; 4773 case ORDER_PHASE_SET_MAX_FEE: 4774 phase_set_max_fee (oc); 4775 break; 4776 case ORDER_PHASE_SERIALIZE_ORDER: 4777 phase_serialize_order (oc); 4778 break; 4779 case ORDER_PHASE_CHECK_CONTRACT: 4780 phase_check_contract (oc); 4781 break; 4782 case ORDER_PHASE_SALT_FORGETTABLE: 4783 phase_salt_forgettable (oc); 4784 break; 4785 case ORDER_PHASE_EXECUTE_ORDER: 4786 phase_execute_order (oc); 4787 break; 4788 case ORDER_PHASE_FINISHED_MHD_YES: 4789 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4790 "Finished processing order (1)\n"); 4791 return MHD_YES; 4792 case ORDER_PHASE_FINISHED_MHD_NO: 4793 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4794 "Finished processing order (0)\n"); 4795 return MHD_NO; 4796 } 4797 } 4798 } 4799 4800 4801 /* end of taler-merchant-httpd_post-private-orders.c */