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