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