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