taler-merchant-httpd_post-private-orders.c (130127B)
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 if (TALER_token_issue_pub_cmp (&family->keys[i].pub, 1591 &tfkd->pub)) 1592 { 1593 /* A matching key is already in the list. */ 1594 return; 1595 } 1596 } 1597 1598 { 1599 struct TALER_MERCHANT_ContractTokenFamilyKey key; 1600 1601 TALER_token_issue_pub_copy (&key.pub, 1602 &tfkd->pub); 1603 key.valid_after = tfkd->signature_validity_start; 1604 key.valid_before = tfkd->signature_validity_end; 1605 GNUNET_array_append (family->keys, 1606 family->keys_len, 1607 key); 1608 } 1609 } 1610 1611 1612 /** 1613 * Check if the token family with the given @a slug is already present in the 1614 * list of token families for this order. If not, fetch its details and add it 1615 * to the list. 1616 * 1617 * @param[in,out] oc order context 1618 * @param slug slug of the token family 1619 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 1620 */ 1621 static enum GNUNET_GenericReturnValue 1622 add_input_token_family (struct OrderContext *oc, 1623 const char *slug) 1624 { 1625 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 1626 struct GNUNET_TIME_Timestamp end = oc->parse_order.order->pay_deadline; 1627 enum GNUNET_DB_QueryStatus qs; 1628 enum TALER_ErrorCode ec = TALER_EC_INVALID; /* make compiler happy */ 1629 unsigned int http_status = 0; /* make compiler happy */ 1630 1631 qs = TALER_MERCHANTDB_lookup_token_family_keys ( 1632 TMH_db, 1633 oc->hc->instance->settings.id, 1634 slug, 1635 now, 1636 end, 1637 &add_family_key, 1638 oc); 1639 switch (qs) 1640 { 1641 case GNUNET_DB_STATUS_HARD_ERROR: 1642 GNUNET_break (0); 1643 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 1644 ec = TALER_EC_GENERIC_DB_FETCH_FAILED; 1645 break; 1646 case GNUNET_DB_STATUS_SOFT_ERROR: 1647 GNUNET_break (0); 1648 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 1649 ec = TALER_EC_GENERIC_DB_SOFT_FAILURE; 1650 break; 1651 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1652 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1653 "Input token family slug %s unknown\n", 1654 slug); 1655 http_status = MHD_HTTP_NOT_FOUND; 1656 ec = TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN; 1657 break; 1658 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1659 return GNUNET_OK; 1660 } 1661 reply_with_error (oc, 1662 http_status, 1663 ec, 1664 slug); 1665 return GNUNET_SYSERR; 1666 } 1667 1668 1669 /** 1670 * Find the index of a key in the @a family that is valid at 1671 * the time @a valid_at. 1672 * 1673 * @param family to search 1674 * @param valid_at time when the key must be valid 1675 * @param[out] key_index index to initialize 1676 * @return #GNUNET_OK if a matching key was found 1677 */ 1678 static enum GNUNET_GenericReturnValue 1679 find_key_index (struct TALER_MERCHANT_ContractTokenFamily *family, 1680 struct GNUNET_TIME_Timestamp valid_at, 1681 unsigned int *key_index) 1682 { 1683 for (unsigned int i = 0; i<family->keys_len; i++) 1684 { 1685 if ( (GNUNET_TIME_timestamp_cmp (family->keys[i].valid_after, 1686 <=, 1687 valid_at)) && 1688 (GNUNET_TIME_timestamp_cmp (family->keys[i].valid_before, 1689 >=, 1690 valid_at)) ) 1691 { 1692 /* The token family and a matching key already exist. */ 1693 *key_index = i; 1694 return GNUNET_OK; 1695 } 1696 } 1697 return GNUNET_NO; 1698 } 1699 1700 1701 /** 1702 * Create fresh key pair based on @a cipher_spec. 1703 * 1704 * @param cipher_spec which kind of key pair should we generate 1705 * @param[out] priv set to new private key 1706 * @param[out] pub set to new public key 1707 * @return #GNUNET_OK on success 1708 */ 1709 static enum GNUNET_GenericReturnValue 1710 create_key (const char *cipher_spec, 1711 struct TALER_TokenIssuePrivateKey *priv, 1712 struct TALER_TokenIssuePublicKey *pub) 1713 { 1714 unsigned int len; 1715 char dummy; 1716 1717 if (0 == strcmp ("cs", 1718 cipher_spec)) 1719 { 1720 GNUNET_CRYPTO_blind_sign_keys_create ( 1721 &priv->private_key, 1722 &pub->public_key, 1723 GNUNET_CRYPTO_BSA_CS); 1724 return GNUNET_OK; 1725 } 1726 if (1 == 1727 sscanf (cipher_spec, 1728 "rsa(%u)%c", 1729 &len, 1730 &dummy)) 1731 { 1732 GNUNET_CRYPTO_blind_sign_keys_create ( 1733 &priv->private_key, 1734 &pub->public_key, 1735 GNUNET_CRYPTO_BSA_RSA, 1736 len); 1737 return GNUNET_OK; 1738 } 1739 return GNUNET_SYSERR; 1740 } 1741 1742 1743 /** 1744 * Check if the token family with the given @a slug is already present in the 1745 * list of token families for this order. If not, fetch its details and add it 1746 * to the list. Also checks if there is a public key with that expires after 1747 * the payment deadline. If not, generates a new key pair and stores it in 1748 * the database. 1749 * 1750 * @param[in,out] oc order context 1751 * @param slug slug of the token family 1752 * @param valid_at time when the token returned must be valid 1753 * @param[out] key_index set to the index of the respective public 1754 * key in the @a slug's token family keys array. 1755 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 1756 */ 1757 static enum GNUNET_GenericReturnValue 1758 add_output_token_family (struct OrderContext *oc, 1759 const char *slug, 1760 struct GNUNET_TIME_Timestamp valid_at, 1761 unsigned int *key_index) 1762 { 1763 struct TALER_MERCHANTDB_TokenFamilyKeyDetails key_details; 1764 struct TALER_MERCHANT_ContractTokenFamily *family; 1765 enum GNUNET_DB_QueryStatus qs; 1766 1767 family = find_family (oc, 1768 slug); 1769 if ( (NULL != family) && 1770 (GNUNET_OK == 1771 find_key_index (family, 1772 valid_at, 1773 key_index)) ) 1774 return GNUNET_OK; 1775 qs = TALER_MERCHANTDB_lookup_token_family_key ( 1776 TMH_db, 1777 oc->hc->instance->settings.id, 1778 slug, 1779 valid_at, 1780 oc->parse_order.order->pay_deadline, 1781 &key_details); 1782 switch (qs) 1783 { 1784 case GNUNET_DB_STATUS_HARD_ERROR: 1785 GNUNET_break (0); 1786 reply_with_error (oc, 1787 MHD_HTTP_INTERNAL_SERVER_ERROR, 1788 TALER_EC_GENERIC_DB_FETCH_FAILED, 1789 "lookup_token_family_key"); 1790 return GNUNET_SYSERR; 1791 case GNUNET_DB_STATUS_SOFT_ERROR: 1792 /* Single-statement transaction shouldn't possibly cause serialization errors. 1793 Thus treating like a hard error. */ 1794 GNUNET_break (0); 1795 reply_with_error (oc, 1796 MHD_HTTP_INTERNAL_SERVER_ERROR, 1797 TALER_EC_GENERIC_DB_SOFT_FAILURE, 1798 "lookup_token_family_key"); 1799 return GNUNET_SYSERR; 1800 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1801 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1802 "Output token family slug %s unknown at %llu for %llu for instance %s\n", 1803 slug, 1804 (unsigned long long) valid_at.abs_time.abs_value_us, 1805 (unsigned long long) oc->parse_order.order->pay_deadline.abs_time.abs_value_us, 1806 oc->hc->instance->settings.id); 1807 reply_with_error (oc, 1808 MHD_HTTP_NOT_FOUND, 1809 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_TOKEN_FAMILY_SLUG_UNKNOWN, 1810 slug); 1811 return GNUNET_SYSERR; 1812 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1813 break; 1814 } 1815 1816 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1817 "Lookup of token family %s at %llu yielded %s\n", 1818 slug, 1819 (unsigned long long) valid_at.abs_time.abs_value_us, 1820 NULL == key_details.pub.public_key ? "no key" : "a key"); 1821 1822 if (NULL == family) 1823 { 1824 add_family_key (oc, 1825 &key_details); 1826 family = find_family (oc, 1827 slug); 1828 GNUNET_assert (NULL != family); 1829 } 1830 /* we don't need the full family details anymore */ 1831 GNUNET_free (key_details.token_family.slug); 1832 GNUNET_free (key_details.token_family.name); 1833 GNUNET_free (key_details.token_family.description); 1834 json_decref (key_details.token_family.description_i18n); 1835 json_decref (key_details.token_family.extra_data); 1836 1837 if (NULL != key_details.pub.public_key) 1838 { 1839 /* lookup_token_family_key must have found a matching key, 1840 and it must have been added. Find and use the index. */ 1841 GNUNET_CRYPTO_blind_sign_pub_decref (key_details.pub.public_key); 1842 GNUNET_CRYPTO_blind_sign_priv_decref (key_details.priv.private_key); 1843 GNUNET_free (key_details.token_family.cipher_spec); 1844 GNUNET_assert (GNUNET_OK == 1845 find_key_index (family, 1846 valid_at, 1847 key_index)); 1848 return GNUNET_OK; 1849 } 1850 1851 /* No suitable key exists, create one! */ 1852 { 1853 struct TALER_MERCHANT_ContractTokenFamilyKey key; 1854 enum GNUNET_DB_QueryStatus iqs; 1855 struct TALER_TokenIssuePrivateKey token_priv; 1856 struct GNUNET_TIME_Timestamp key_expires; 1857 struct GNUNET_TIME_Timestamp round_start; 1858 1859 if (GNUNET_OK != 1860 get_rounded_time_interval_down ( 1861 key_details.token_family.validity_granularity, 1862 valid_at, 1863 &round_start)) 1864 { 1865 GNUNET_break (0); 1866 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1867 "Unsupported validity granularity interval %s found in database for token family %s!\n", 1868 GNUNET_TIME_relative2s ( 1869 key_details.token_family.validity_granularity, 1870 false), 1871 slug); 1872 GNUNET_free (key_details.token_family.cipher_spec); 1873 reply_with_error (oc, 1874 MHD_HTTP_INTERNAL_SERVER_ERROR, 1875 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 1876 "get_rounded_time_interval_down failed"); 1877 return GNUNET_SYSERR; 1878 } 1879 if (GNUNET_TIME_relative_cmp ( 1880 key_details.token_family.duration, 1881 <, 1882 GNUNET_TIME_relative_add ( 1883 key_details.token_family.validity_granularity, 1884 key_details.token_family.start_offset))) 1885 { 1886 GNUNET_break (0); 1887 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1888 "Inconsistent duration %s found in database for token family %s (below validity granularity plus start_offset)!\n", 1889 GNUNET_TIME_relative2s (key_details.token_family.duration, 1890 false), 1891 slug); 1892 GNUNET_free (key_details.token_family.cipher_spec); 1893 reply_with_error (oc, 1894 MHD_HTTP_INTERNAL_SERVER_ERROR, 1895 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 1896 "duration, validty_granularity and start_offset inconsistent for token family"); 1897 return GNUNET_SYSERR; 1898 } 1899 key.valid_after 1900 = GNUNET_TIME_timestamp_max ( 1901 GNUNET_TIME_absolute_to_timestamp ( 1902 GNUNET_TIME_absolute_subtract ( 1903 round_start.abs_time, 1904 key_details.token_family.start_offset)), 1905 key_details.token_family.valid_after); 1906 key.valid_before 1907 = GNUNET_TIME_timestamp_min ( 1908 GNUNET_TIME_absolute_to_timestamp ( 1909 GNUNET_TIME_absolute_add ( 1910 key.valid_after.abs_time, 1911 key_details.token_family.duration)), 1912 key_details.token_family.valid_before); 1913 GNUNET_assert (GNUNET_OK == 1914 get_rounded_time_interval_down ( 1915 key_details.token_family.validity_granularity, 1916 key.valid_before, 1917 &key_expires)); 1918 if (GNUNET_TIME_timestamp_cmp ( 1919 key_expires, 1920 ==, 1921 round_start)) 1922 { 1923 /* valid_before does not actually end after the 1924 next rounded validity period would start; 1925 determine next rounded validity period 1926 start point and extend valid_before to cover 1927 the full validity period */ 1928 GNUNET_assert ( 1929 GNUNET_OK == 1930 get_rounded_time_interval_up ( 1931 key_details.token_family.validity_granularity, 1932 key.valid_before, 1933 &key_expires)); 1934 /* This should basically always end up being key_expires */ 1935 key.valid_before = GNUNET_TIME_timestamp_max (key.valid_before, 1936 key_expires); 1937 } 1938 if (GNUNET_OK != 1939 create_key (key_details.token_family.cipher_spec, 1940 &token_priv, 1941 &key.pub)) 1942 { 1943 GNUNET_break (0); 1944 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1945 "Unsupported cipher family %s found in database for token family %s!\n", 1946 key_details.token_family.cipher_spec, 1947 slug); 1948 GNUNET_free (key_details.token_family.cipher_spec); 1949 reply_with_error (oc, 1950 MHD_HTTP_INTERNAL_SERVER_ERROR, 1951 TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, 1952 "invalid cipher stored in local database for token family"); 1953 return GNUNET_SYSERR; 1954 } 1955 GNUNET_free (key_details.token_family.cipher_spec); 1956 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1957 "Storing new key for slug %s of %s\n", 1958 slug, 1959 oc->hc->instance->settings.id); 1960 iqs = TALER_MERCHANTDB_insert_token_family_key (TMH_db, 1961 oc->hc->instance->settings.id, 1962 slug, 1963 &key.pub, 1964 &token_priv, 1965 key_expires, 1966 key.valid_after, 1967 key.valid_before); 1968 GNUNET_CRYPTO_blind_sign_priv_decref (token_priv.private_key); 1969 switch (iqs) 1970 { 1971 case GNUNET_DB_STATUS_HARD_ERROR: 1972 GNUNET_break (0); 1973 reply_with_error (oc, 1974 MHD_HTTP_INTERNAL_SERVER_ERROR, 1975 TALER_EC_GENERIC_DB_STORE_FAILED, 1976 NULL); 1977 return GNUNET_SYSERR; 1978 case GNUNET_DB_STATUS_SOFT_ERROR: 1979 /* Single-statement transaction shouldn't possibly cause serialization errors. 1980 Thus treating like a hard error. */ 1981 GNUNET_break (0); 1982 reply_with_error (oc, 1983 MHD_HTTP_INTERNAL_SERVER_ERROR, 1984 TALER_EC_GENERIC_DB_SOFT_FAILURE, 1985 NULL); 1986 return GNUNET_SYSERR; 1987 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1988 GNUNET_break (0); 1989 reply_with_error (oc, 1990 MHD_HTTP_INTERNAL_SERVER_ERROR, 1991 TALER_EC_GENERIC_DB_STORE_FAILED, 1992 NULL); 1993 return GNUNET_SYSERR; 1994 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1995 break; 1996 } 1997 *key_index = family->keys_len; 1998 GNUNET_array_append (family->keys, 1999 family->keys_len, 2000 key); 2001 } 2002 return GNUNET_OK; 2003 } 2004 2005 2006 /** 2007 * Build JSON array that represents all of the token families 2008 * in the contract. 2009 * 2010 * @param[in] oc v1-style order context 2011 * @return JSON array with token families for the contract 2012 */ 2013 static json_t * 2014 output_token_families (struct OrderContext *oc) 2015 { 2016 json_t *token_families = json_object (); 2017 2018 GNUNET_assert (NULL != token_families); 2019 for (unsigned int i = 0; i<oc->parse_choices.token_families_len; i++) 2020 { 2021 const struct TALER_MERCHANT_ContractTokenFamily *family 2022 = &oc->parse_choices.token_families[i]; 2023 json_t *jfamily; 2024 2025 jfamily = TALER_MERCHANT_json_from_token_family (family); 2026 2027 GNUNET_assert (jfamily != NULL); 2028 2029 GNUNET_assert (0 == 2030 json_object_set_new (token_families, 2031 family->slug, 2032 jfamily)); 2033 } 2034 return token_families; 2035 } 2036 2037 2038 /** 2039 * Build JSON array that represents all of the contract choices 2040 * in the contract. 2041 * 2042 * @param[in] oc v1-style order context 2043 * @return JSON array with token families for the contract 2044 */ 2045 static json_t * 2046 output_contract_choices (struct OrderContext *oc) 2047 { 2048 json_t *choices = json_array (); 2049 2050 GNUNET_assert (NULL != choices); 2051 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2052 { 2053 oc->parse_choices.choices[i].max_fee = 2054 oc->set_max_fee.details.v1.max_fees[i]; 2055 GNUNET_assert (0 == json_array_append_new ( 2056 choices, 2057 TALER_MERCHANT_json_from_contract_choice ( 2058 &oc->parse_choices.choices[i]))); 2059 } 2060 return choices; 2061 } 2062 2063 2064 /** 2065 * Serialize order into @a oc->serialize_order.contract, 2066 * ready to be stored in the database. Upon success, continue 2067 * processing with check_contract(). 2068 * 2069 * @param[in,out] oc order context 2070 */ 2071 static void 2072 phase_serialize_order (struct OrderContext *oc) 2073 { 2074 const struct TALER_MERCHANTDB_InstanceSettings *settings = 2075 &oc->hc->instance->settings; 2076 json_t *merchant; 2077 2078 merchant = GNUNET_JSON_PACK ( 2079 GNUNET_JSON_pack_string ("name", 2080 settings->name), 2081 GNUNET_JSON_pack_allow_null ( 2082 GNUNET_JSON_pack_string ("website", 2083 settings->website)), 2084 GNUNET_JSON_pack_allow_null ( 2085 GNUNET_JSON_pack_string ("email", 2086 settings->email)), 2087 GNUNET_JSON_pack_allow_null ( 2088 GNUNET_JSON_pack_string ("logo", 2089 settings->logo))); 2090 GNUNET_assert (NULL != merchant); 2091 { 2092 json_t *loca; 2093 2094 /* Handle merchant address */ 2095 loca = settings->address; 2096 if (NULL != loca) 2097 { 2098 loca = json_deep_copy (loca); 2099 GNUNET_assert (NULL != loca); 2100 GNUNET_assert (0 == 2101 json_object_set_new (merchant, 2102 "address", 2103 loca)); 2104 } 2105 } 2106 { 2107 json_t *juri; 2108 2109 /* Handle merchant jurisdiction */ 2110 juri = settings->jurisdiction; 2111 if (NULL != juri) 2112 { 2113 juri = json_deep_copy (juri); 2114 GNUNET_assert (NULL != juri); 2115 GNUNET_assert (0 == 2116 json_object_set_new (merchant, 2117 "jurisdiction", 2118 juri)); 2119 } 2120 } 2121 2122 oc->serialize_order.contract = GNUNET_JSON_PACK ( 2123 GNUNET_JSON_pack_string ( 2124 "order_id", 2125 oc->parse_order.order->order_id), 2126 GNUNET_JSON_pack_object_steal ( 2127 NULL, 2128 TALER_MERCHANT_base_terms_serialize (oc->parse_order.order->base)), 2129 GNUNET_JSON_pack_array_incref ( 2130 "products", 2131 oc->merge_inventory.products), 2132 GNUNET_JSON_pack_data_auto ( 2133 "h_wire", 2134 &oc->select_wire_method.wm->h_wire), 2135 GNUNET_JSON_pack_string ( 2136 "wire_method", 2137 oc->select_wire_method.wm->wire_method), 2138 GNUNET_JSON_pack_timestamp ( 2139 "timestamp", 2140 oc->parse_order.order->timestamp), 2141 GNUNET_JSON_pack_timestamp ( 2142 "pay_deadline", 2143 oc->parse_order.order->pay_deadline), 2144 GNUNET_JSON_pack_timestamp ( 2145 "wire_transfer_deadline", 2146 oc->parse_order.order->wire_transfer_deadline), 2147 GNUNET_JSON_pack_string ( 2148 "merchant_base_url", 2149 oc->parse_order.merchant_base_url), 2150 GNUNET_JSON_pack_object_steal ( 2151 "merchant", 2152 merchant), 2153 GNUNET_JSON_pack_data_auto ( 2154 "merchant_pub", 2155 &oc->hc->instance->merchant_pub), 2156 GNUNET_JSON_pack_array_incref ( 2157 "exchanges", 2158 oc->select_wire_method.exchanges)); 2159 2160 { 2161 json_t *xtra; 2162 2163 switch (oc->parse_order.order->base->version) 2164 { 2165 case TALER_MERCHANT_CONTRACT_VERSION_0: 2166 xtra = GNUNET_JSON_PACK ( 2167 TALER_JSON_pack_amount ("max_fee", 2168 &oc->set_max_fee.details.v0.max_fee), 2169 GNUNET_JSON_pack_allow_null ( 2170 TALER_JSON_pack_amount ( 2171 "tip", 2172 oc->parse_order.order->details.v0.no_tip 2173 ? NULL 2174 : &oc->parse_order.order->details.v0.tip)), 2175 TALER_JSON_pack_amount ( 2176 "amount", 2177 &oc->parse_order.order->details.v0.brutto)); 2178 break; 2179 case TALER_MERCHANT_CONTRACT_VERSION_1: 2180 { 2181 json_t *token_families = output_token_families (oc); 2182 json_t *choices = output_contract_choices (oc); 2183 2184 if ( (NULL == token_families) || 2185 (NULL == choices) ) 2186 { 2187 GNUNET_break (0); 2188 return; 2189 } 2190 xtra = GNUNET_JSON_PACK ( 2191 GNUNET_JSON_pack_array_steal ("choices", 2192 choices), 2193 GNUNET_JSON_pack_object_steal ("token_families", 2194 token_families)); 2195 break; 2196 } 2197 default: 2198 GNUNET_assert (0); 2199 } 2200 GNUNET_assert (0 == 2201 json_object_update (oc->serialize_order.contract, 2202 xtra)); 2203 json_decref (xtra); 2204 } 2205 2206 2207 /* Pack does not work here, because it doesn't set zero-values for timestamps */ 2208 GNUNET_assert (0 == 2209 json_object_set_new ( 2210 oc->serialize_order.contract, 2211 "refund_deadline", 2212 GNUNET_JSON_from_timestamp ( 2213 oc->parse_order.order->refund_deadline))); 2214 /* auto_refund should only be set if it is not 0 */ 2215 if (! GNUNET_TIME_relative_is_zero ( 2216 oc->parse_order.order->base->auto_refund)) 2217 { 2218 /* Pack does not work here, because it sets zero-values for relative times */ 2219 GNUNET_assert (0 == 2220 json_object_set_new ( 2221 oc->serialize_order.contract, 2222 "auto_refund", 2223 GNUNET_JSON_from_time_rel ( 2224 oc->parse_order.order->base->auto_refund))); 2225 } 2226 2227 oc->phase++; 2228 } 2229 2230 2231 /* ***************** ORDER_PHASE_SET_MAX_FEE **************** */ 2232 2233 2234 /** 2235 * Set @a max_fee in @a oc based on @a max_stefan_fee value if not overridden 2236 * by @a client_fee. If neither is set, set the fee to zero using currency 2237 * from @a brutto. 2238 * 2239 * @param[in,out] oc order context 2240 * @param brutto brutto amount to compute fee for 2241 * @param client_fee client-given fee override (or invalid) 2242 * @param max_stefan_fee maximum STEFAN fee of any exchange 2243 * @param max_fee set to the maximum stefan fee 2244 */ 2245 static void 2246 compute_fee (struct OrderContext *oc, 2247 const struct TALER_Amount *brutto, 2248 const struct TALER_Amount *client_fee, 2249 const struct TALER_Amount *max_stefan_fee, 2250 struct TALER_Amount *max_fee) 2251 { 2252 const struct TALER_MERCHANTDB_InstanceSettings *settings 2253 = &oc->hc->instance->settings; 2254 2255 if (GNUNET_OK == 2256 TALER_amount_is_valid (client_fee)) 2257 { 2258 *max_fee = *client_fee; 2259 return; 2260 } 2261 if ( (settings->use_stefan) && 2262 (NULL != max_stefan_fee) && 2263 (GNUNET_OK == 2264 TALER_amount_is_valid (max_stefan_fee)) ) 2265 { 2266 *max_fee = *max_stefan_fee; 2267 return; 2268 } 2269 GNUNET_assert ( 2270 GNUNET_OK == 2271 TALER_amount_set_zero (brutto->currency, 2272 max_fee)); 2273 } 2274 2275 2276 /** 2277 * Initialize "set_max_fee" in @a oc based on STEFAN value or client 2278 * preference. Upon success, continue processing in next phase. 2279 * 2280 * @param[in,out] oc order context 2281 */ 2282 static void 2283 phase_set_max_fee (struct OrderContext *oc) 2284 { 2285 switch (oc->parse_order.order->base->version) 2286 { 2287 case TALER_MERCHANT_CONTRACT_VERSION_0: 2288 compute_fee (oc, 2289 &oc->parse_order.order->details.v0.brutto, 2290 &oc->parse_order.order->details.v0.max_fee, 2291 &oc->set_exchanges.details.v0.max_stefan_fee, 2292 &oc->set_max_fee.details.v0.max_fee); 2293 break; 2294 case TALER_MERCHANT_CONTRACT_VERSION_1: 2295 oc->set_max_fee.details.v1.max_fees 2296 = GNUNET_new_array (oc->parse_choices.choices_len, 2297 struct TALER_Amount); 2298 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2299 compute_fee (oc, 2300 &oc->parse_choices.choices[i].amount, 2301 &oc->parse_choices.choices[i].max_fee, 2302 NULL != oc->set_exchanges.details.v1.max_stefan_fees 2303 ? &oc->set_exchanges.details.v1.max_stefan_fees[i] 2304 : NULL, 2305 &oc->set_max_fee.details.v1.max_fees[i]); 2306 break; 2307 default: 2308 GNUNET_break (0); 2309 break; 2310 } 2311 oc->phase++; 2312 } 2313 2314 2315 /* ***************** ORDER_PHASE_SELECT_WIRE_METHOD **************** */ 2316 2317 /** 2318 * Phase to select a wire method that will be acceptable for the order. 2319 * If none is "perfect" (allows all choices), might jump back to the 2320 * previous phase to force "/keys" downloads to see if that helps. 2321 * 2322 * @param[in,out] oc order context 2323 */ 2324 static void 2325 phase_select_wire_method (struct OrderContext *oc) 2326 { 2327 const struct TALER_Amount *ea; 2328 struct WireMethodCandidate *best = NULL; 2329 unsigned int max_choices = 0; 2330 unsigned int want_choices = 0; 2331 bool zero_amount = false; 2332 2333 switch (oc->parse_order.order->base->version) 2334 { 2335 case TALER_MERCHANT_CONTRACT_VERSION_0: 2336 ea = &oc->parse_order.order->details.v0.brutto; 2337 if (TALER_amount_is_zero (ea)) 2338 zero_amount = true; 2339 break; 2340 case TALER_MERCHANT_CONTRACT_VERSION_1: 2341 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2342 { 2343 ea = &oc->parse_choices.choices[i].amount; 2344 if (TALER_amount_is_zero (ea)) 2345 zero_amount = true; 2346 } 2347 break; 2348 default: 2349 GNUNET_assert (0); 2350 } 2351 2352 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 2353 NULL != wmc; 2354 wmc = wmc->next) 2355 { 2356 unsigned int num_choices = 0; 2357 2358 switch (oc->parse_order.order->base->version) 2359 { 2360 case TALER_MERCHANT_CONTRACT_VERSION_0: 2361 want_choices = 1; 2362 ea = &oc->parse_order.order->details.v0.brutto; 2363 if (TALER_amount_is_zero (ea) || 2364 TALER_amount_set_test_above (&wmc->total_exchange_limits, 2365 ea)) 2366 num_choices++; 2367 break; 2368 case TALER_MERCHANT_CONTRACT_VERSION_1: 2369 want_choices = oc->parse_choices.choices_len; 2370 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2371 { 2372 ea = &oc->parse_choices.choices[i].amount; 2373 if (TALER_amount_is_zero (ea) || 2374 TALER_amount_set_test_above (&wmc->total_exchange_limits, 2375 ea)) 2376 num_choices++; 2377 } 2378 break; 2379 default: 2380 GNUNET_assert (0); 2381 } 2382 if (num_choices > max_choices) 2383 { 2384 best = wmc; 2385 max_choices = num_choices; 2386 } 2387 } 2388 2389 if ( (want_choices > max_choices) && 2390 (oc->set_exchanges.promising_exchange) && 2391 (! oc->set_exchanges.forced_reload) ) 2392 { 2393 oc->set_exchanges.exchange_ok = false; 2394 /* Not all choices in the contract can work with these 2395 exchanges, try again with forcing /keys download */ 2396 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 2397 NULL != wmc; 2398 wmc = wmc->next) 2399 { 2400 json_array_clear (wmc->exchanges); 2401 TALER_amount_set_free (&wmc->total_exchange_limits); 2402 } 2403 oc->phase = ORDER_PHASE_SET_EXCHANGES; 2404 return; 2405 } 2406 2407 if ( (NULL == best) && 2408 (! zero_amount) && 2409 (NULL != oc->parse_request.payment_target) ) 2410 { 2411 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2412 "Cannot create order: lacking suitable exchanges for payment target `%s'\n", 2413 oc->parse_request.payment_target); 2414 reply_with_error ( 2415 oc, 2416 MHD_HTTP_CONFLICT, 2417 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, 2418 oc->parse_request.payment_target); 2419 return; 2420 } 2421 2422 if ( (NULL == best) && 2423 (! zero_amount) ) 2424 { 2425 enum MHD_Result mret; 2426 2427 /* We actually do not have ANY workable exchange(s) */ 2428 mret = TALER_MHD_reply_json_steal ( 2429 oc->connection, 2430 GNUNET_JSON_PACK ( 2431 TALER_JSON_pack_ec ( 2432 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_AMOUNT_EXCEEDS_LEGAL_LIMITS), 2433 GNUNET_JSON_pack_allow_null ( 2434 GNUNET_JSON_pack_array_incref ( 2435 "exchange_rejections", 2436 oc->set_exchanges.exchange_rejections))), 2437 MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS); 2438 finalize_order (oc, 2439 mret); 2440 return; 2441 } 2442 2443 if (want_choices > max_choices) 2444 { 2445 /* Some choices are unpayable */ 2446 GNUNET_log ( 2447 GNUNET_ERROR_TYPE_WARNING, 2448 "Creating order, but some choices do not work with the selected wire method\n"); 2449 } 2450 if ( (0 == json_array_size (best->exchanges)) && 2451 (oc->add_payment_details.need_exchange) ) 2452 { 2453 /* We did not find any reasonable exchange */ 2454 GNUNET_log ( 2455 GNUNET_ERROR_TYPE_WARNING, 2456 "Creating order, but only for choices without payment\n"); 2457 } 2458 2459 oc->select_wire_method.wm 2460 = best->wm; 2461 oc->select_wire_method.exchanges 2462 = json_incref (best->exchanges); 2463 oc->phase++; 2464 } 2465 2466 2467 /* ***************** ORDER_PHASE_SET_EXCHANGES **************** */ 2468 2469 /** 2470 * Exchange `/keys` processing is done, resume handling 2471 * the order. 2472 * 2473 * @param[in,out] oc context to resume 2474 */ 2475 static void 2476 resume_with_keys (struct OrderContext *oc) 2477 { 2478 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2479 "Resuming order processing after /keys downloads\n"); 2480 GNUNET_assert (GNUNET_YES == oc->suspended); 2481 GNUNET_CONTAINER_DLL_remove (oc_head, 2482 oc_tail, 2483 oc); 2484 oc->suspended = GNUNET_NO; 2485 MHD_resume_connection (oc->connection); 2486 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 2487 } 2488 2489 2490 /** 2491 * Given a @a brutto amount for exchange with @a keys, set the 2492 * @a stefan_fee. Note that @a stefan_fee is updated to the maximum 2493 * of the input and the computed fee. 2494 * 2495 * @param[in,out] keys exchange keys 2496 * @param brutto some brutto amount the client is to pay 2497 * @param[in,out] stefan_fee set to STEFAN fee to be paid by the merchant 2498 */ 2499 static void 2500 compute_stefan_fee (const struct TALER_EXCHANGE_Keys *keys, 2501 const struct TALER_Amount *brutto, 2502 struct TALER_Amount *stefan_fee) 2503 { 2504 struct TALER_Amount net; 2505 2506 if (GNUNET_SYSERR != 2507 TALER_EXCHANGE_keys_stefan_b2n (keys, 2508 brutto, 2509 &net)) 2510 { 2511 struct TALER_Amount fee; 2512 2513 TALER_EXCHANGE_keys_stefan_round (keys, 2514 &net); 2515 if (-1 == TALER_amount_cmp (brutto, 2516 &net)) 2517 { 2518 /* brutto < netto! */ 2519 /* => after rounding, there is no real difference */ 2520 net = *brutto; 2521 } 2522 GNUNET_assert (0 <= 2523 TALER_amount_subtract (&fee, 2524 brutto, 2525 &net)); 2526 if ( (GNUNET_OK != 2527 TALER_amount_is_valid (stefan_fee)) || 2528 (-1 == TALER_amount_cmp (stefan_fee, 2529 &fee)) ) 2530 { 2531 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2532 "Updated STEFAN-based fee to %s\n", 2533 TALER_amount2s (&fee)); 2534 *stefan_fee = fee; 2535 } 2536 } 2537 } 2538 2539 2540 /** 2541 * Update MAX STEFAN fees based on @a keys. 2542 * 2543 * @param[in,out] oc order context to update 2544 * @param keys keys to derive STEFAN fees from 2545 */ 2546 static void 2547 update_stefan (struct OrderContext *oc, 2548 const struct TALER_EXCHANGE_Keys *keys) 2549 { 2550 switch (oc->parse_order.order->base->version) 2551 { 2552 case TALER_MERCHANT_CONTRACT_VERSION_0: 2553 compute_stefan_fee (keys, 2554 &oc->parse_order.order->details.v0.brutto, 2555 &oc->set_exchanges.details.v0.max_stefan_fee); 2556 break; 2557 case TALER_MERCHANT_CONTRACT_VERSION_1: 2558 oc->set_exchanges.details.v1.max_stefan_fees 2559 = GNUNET_new_array (oc->parse_choices.choices_len, 2560 struct TALER_Amount); 2561 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 2562 if (0 == strcasecmp (keys->currency, 2563 oc->parse_choices.choices[i].amount.currency)) 2564 compute_stefan_fee (keys, 2565 &oc->parse_choices.choices[i].amount, 2566 &oc->set_exchanges.details.v1.max_stefan_fees[i]); 2567 break; 2568 default: 2569 GNUNET_assert (0); 2570 } 2571 } 2572 2573 2574 /** 2575 * Check our KYC status at all exchanges as our current limit is 2576 * too low and we failed to create an order. 2577 * 2578 * @param oc order context 2579 * @param wmc wire method candidate to notify for 2580 * @param exchange_url exchange to notify about 2581 */ 2582 static void 2583 notify_kyc_required (const struct OrderContext *oc, 2584 const struct WireMethodCandidate *wmc, 2585 const char *exchange_url) 2586 { 2587 struct GNUNET_DB_EventHeaderP es = { 2588 .size = htons (sizeof (es)), 2589 .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED) 2590 }; 2591 char *hws; 2592 char *extra; 2593 2594 hws = GNUNET_STRINGS_data_to_string_alloc ( 2595 &wmc->wm->h_wire, 2596 sizeof (wmc->wm->h_wire)); 2597 2598 GNUNET_asprintf (&extra, 2599 "%s %s", 2600 hws, 2601 exchange_url); 2602 TALER_MERCHANTDB_event_notify (TMH_db, 2603 &es, 2604 extra, 2605 strlen (extra) + 1); 2606 GNUNET_free (extra); 2607 GNUNET_free (hws); 2608 } 2609 2610 2611 /** 2612 * Add a reason why a particular exchange was rejected to our 2613 * response data. 2614 * 2615 * @param[in,out] oc order context to update 2616 * @param exchange_url exchange this is about 2617 * @param ec error code to set for the exchange 2618 */ 2619 static void 2620 add_rejection (struct OrderContext *oc, 2621 const char *exchange_url, 2622 enum TALER_ErrorCode ec) 2623 { 2624 if (NULL == oc->set_exchanges.exchange_rejections) 2625 { 2626 oc->set_exchanges.exchange_rejections = json_array (); 2627 GNUNET_assert (NULL != oc->set_exchanges.exchange_rejections); 2628 } 2629 GNUNET_assert (0 == 2630 json_array_append_new ( 2631 oc->set_exchanges.exchange_rejections, 2632 GNUNET_JSON_PACK ( 2633 GNUNET_JSON_pack_string ("exchange_url", 2634 exchange_url), 2635 TALER_JSON_pack_ec (ec)))); 2636 } 2637 2638 2639 /** 2640 * Checks the limits that apply for this @a exchange and 2641 * the @a wmc and if the exchange is acceptable at all, adds it 2642 * to the list of exchanges for the @a wmc. 2643 * 2644 * @param oc context of the order 2645 * @param exchange internal handle for the exchange 2646 * @param exchange_url base URL of this exchange 2647 * @param wmc wire method to evaluate this exchange for 2648 * @return true if the exchange is acceptable for the contract 2649 */ 2650 static bool 2651 get_acceptable (struct OrderContext *oc, 2652 const struct TMH_Exchange *exchange, 2653 const char *exchange_url, 2654 struct WireMethodCandidate *wmc) 2655 { 2656 const struct TALER_Amount *max_needed = NULL; 2657 unsigned int priority = 42; /* make compiler happy */ 2658 json_t *j_exchange; 2659 enum TMH_ExchangeStatus res; 2660 struct TALER_Amount max_amount; 2661 2662 for (unsigned int i = 0; 2663 i<oc->add_payment_details.num_max_choice_limits; 2664 i++) 2665 { 2666 const struct TALER_Amount *val 2667 = &oc->add_payment_details.max_choice_limits[i]; 2668 2669 if (0 == strcasecmp (val->currency, 2670 TMH_EXCHANGES_get_currency (exchange))) 2671 { 2672 max_needed = val; 2673 break; 2674 } 2675 } 2676 if (NULL == max_needed) 2677 { 2678 /* exchange currency not relevant for any of our choices, skip it */ 2679 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2680 "Exchange %s with currency `%s' is not applicable to this order\n", 2681 exchange_url, 2682 TMH_EXCHANGES_get_currency (exchange)); 2683 add_rejection (oc, 2684 exchange_url, 2685 TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH); 2686 return false; 2687 } 2688 2689 max_amount = *max_needed; 2690 res = TMH_exchange_check_debit ( 2691 oc->hc->instance->settings.id, 2692 exchange, 2693 wmc->wm, 2694 &max_amount); 2695 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2696 "Exchange %s evaluated at %d with max %s\n", 2697 exchange_url, 2698 res, 2699 TALER_amount2s (&max_amount)); 2700 if (TALER_amount_is_zero (&max_amount)) 2701 { 2702 if (! TALER_amount_is_zero (max_needed)) 2703 { 2704 /* Trigger re-checking the current deposit limit when 2705 * paying non-zero amount with zero deposit limit */ 2706 notify_kyc_required (oc, 2707 wmc, 2708 exchange_url); 2709 } 2710 /* If deposit is impossible, we don't list the 2711 * exchange in the contract terms. */ 2712 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2713 "Exchange %s deposit limit is zero, skipping it\n", 2714 exchange_url); 2715 add_rejection (oc, 2716 exchange_url, 2717 TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED); 2718 return false; 2719 } 2720 switch (res) 2721 { 2722 case TMH_ES_OK: 2723 case TMH_ES_RETRY_OK: 2724 priority = 1024; /* high */ 2725 oc->set_exchanges.exchange_ok = true; 2726 break; 2727 case TMH_ES_NO_ACC: 2728 if (oc->set_exchanges.forced_reload) 2729 priority = 0; /* fresh negative response */ 2730 else 2731 priority = 512; /* stale negative response */ 2732 break; 2733 case TMH_ES_NO_CURR: 2734 if (oc->set_exchanges.forced_reload) 2735 priority = 0; /* fresh negative response */ 2736 else 2737 priority = 512; /* stale negative response */ 2738 break; 2739 case TMH_ES_NO_KEYS: 2740 if (oc->set_exchanges.forced_reload) 2741 priority = 256; /* fresh, no accounts yet */ 2742 else 2743 priority = 768; /* stale, no accounts yet */ 2744 break; 2745 case TMH_ES_NO_ACC_RETRY_OK: 2746 if (oc->set_exchanges.forced_reload) 2747 { 2748 priority = 0; /* fresh negative response */ 2749 } 2750 else 2751 { 2752 oc->set_exchanges.promising_exchange = true; 2753 priority = 512; /* stale negative response */ 2754 } 2755 break; 2756 case TMH_ES_NO_CURR_RETRY_OK: 2757 if (oc->set_exchanges.forced_reload) 2758 priority = 0; /* fresh negative response */ 2759 else 2760 priority = 512; /* stale negative response */ 2761 break; 2762 case TMH_ES_NO_KEYS_RETRY_OK: 2763 if (oc->set_exchanges.forced_reload) 2764 { 2765 priority = 256; /* fresh, no accounts yet */ 2766 } 2767 else 2768 { 2769 oc->set_exchanges.promising_exchange = true; 2770 priority = 768; /* stale, no accounts yet */ 2771 } 2772 break; 2773 } 2774 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2775 "Exchange %s deposit limit is %s, adding it!\n", 2776 exchange_url, 2777 TALER_amount2s (&max_amount)); 2778 2779 j_exchange = GNUNET_JSON_PACK ( 2780 GNUNET_JSON_pack_string ("url", 2781 exchange_url), 2782 GNUNET_JSON_pack_uint64 ("priority", 2783 priority), 2784 TALER_JSON_pack_amount ("max_contribution", 2785 &max_amount), 2786 GNUNET_JSON_pack_data_auto ("master_pub", 2787 TMH_EXCHANGES_get_master_pub (exchange))); 2788 GNUNET_assert (NULL != j_exchange); 2789 /* Add exchange to list of exchanges for this wire method 2790 candidate */ 2791 GNUNET_assert (0 == 2792 json_array_append_new (wmc->exchanges, 2793 j_exchange)); 2794 GNUNET_assert (0 <= 2795 TALER_amount_set_add (&wmc->total_exchange_limits, 2796 &max_amount, 2797 max_needed)); 2798 return true; 2799 } 2800 2801 2802 /** 2803 * Function called with the result of a #TMH_EXCHANGES_keys4exchange() 2804 * operation. 2805 * 2806 * @param cls closure with our `struct RekeyExchange *` 2807 * @param keys the keys of the exchange 2808 * @param exchange representation of the exchange 2809 */ 2810 static void 2811 keys_cb ( 2812 void *cls, 2813 struct TALER_EXCHANGE_Keys *keys, 2814 struct TMH_Exchange *exchange) 2815 { 2816 struct RekeyExchange *rx = cls; 2817 struct OrderContext *oc = rx->oc; 2818 const struct TALER_MERCHANTDB_InstanceSettings *settings = 2819 &oc->hc->instance->settings; 2820 bool applicable = false; 2821 2822 rx->fo = NULL; 2823 GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head, 2824 oc->set_exchanges.pending_reload_tail, 2825 rx); 2826 if (NULL == keys) 2827 { 2828 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2829 "Failed to download %skeys\n", 2830 rx->url); 2831 oc->set_exchanges.promising_exchange = true; 2832 add_rejection (oc, 2833 rx->url, 2834 TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE); 2835 goto cleanup; 2836 } 2837 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2838 "Got response for %skeys\n", 2839 rx->url); 2840 2841 /* Evaluate the use of this exchange for each wire method candidate */ 2842 for (unsigned int j = 0; j<keys->accounts_len; j++) 2843 { 2844 struct TALER_FullPayto full_payto = keys->accounts[j].fpayto_uri; 2845 char *wire_method = TALER_payto_get_method (full_payto.full_payto); 2846 2847 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2848 "Exchange `%s' has wire method `%s'\n", 2849 rx->url, 2850 wire_method); 2851 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 2852 NULL != wmc; 2853 wmc = wmc->next) 2854 { 2855 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2856 "Order could use wire method `%s'\n", 2857 wmc->wm->wire_method); 2858 if (0 == strcmp (wmc->wm->wire_method, 2859 wire_method) ) 2860 { 2861 applicable |= get_acceptable (oc, 2862 exchange, 2863 rx->url, 2864 wmc); 2865 } 2866 } 2867 GNUNET_free (wire_method); 2868 } 2869 if ( (! applicable) && 2870 (! oc->set_exchanges.forced_reload) ) 2871 { 2872 /* Checks for 'forced_reload' to not log the error *again* 2873 if we forced a re-load and are encountering the 2874 applicability error a 2nd time */ 2875 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2876 "Exchange `%s' %u wire methods are not applicable to this order\n", 2877 rx->url, 2878 keys->accounts_len); 2879 add_rejection (oc, 2880 rx->url, 2881 TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED); 2882 } 2883 if (applicable && 2884 settings->use_stefan) 2885 update_stefan (oc, 2886 keys); 2887 cleanup: 2888 GNUNET_free (rx->url); 2889 GNUNET_free (rx); 2890 if (NULL != oc->set_exchanges.pending_reload_head) 2891 return; 2892 resume_with_keys (oc); 2893 } 2894 2895 2896 /** 2897 * Force re-downloading of /keys from @a exchange, 2898 * we currently have no acceptable exchange, so we 2899 * should try to get one. 2900 * 2901 * @param cls closure with our `struct OrderContext` 2902 * @param url base URL of the exchange 2903 * @param exchange internal handle for the exchange 2904 */ 2905 static void 2906 get_exchange_keys (void *cls, 2907 const char *url, 2908 const struct TMH_Exchange *exchange) 2909 { 2910 struct OrderContext *oc = cls; 2911 struct RekeyExchange *rx; 2912 2913 rx = GNUNET_new (struct RekeyExchange); 2914 rx->oc = oc; 2915 rx->url = GNUNET_strdup (url); 2916 GNUNET_CONTAINER_DLL_insert (oc->set_exchanges.pending_reload_head, 2917 oc->set_exchanges.pending_reload_tail, 2918 rx); 2919 if (oc->set_exchanges.forced_reload) 2920 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2921 "Forcing download of %skeys\n", 2922 url); 2923 rx->fo = TMH_EXCHANGES_keys4exchange (url, 2924 oc->set_exchanges.forced_reload, 2925 &keys_cb, 2926 rx); 2927 } 2928 2929 2930 /** 2931 * Task run when we are timing out on /keys and will just 2932 * proceed with what we got. 2933 * 2934 * @param cls our `struct OrderContext *` to resume 2935 */ 2936 static void 2937 wakeup_timeout (void *cls) 2938 { 2939 struct OrderContext *oc = cls; 2940 2941 oc->set_exchanges.wakeup_task = NULL; 2942 GNUNET_assert (GNUNET_YES == oc->suspended); 2943 GNUNET_CONTAINER_DLL_remove (oc_head, 2944 oc_tail, 2945 oc); 2946 MHD_resume_connection (oc->connection); 2947 oc->suspended = GNUNET_NO; 2948 TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ 2949 } 2950 2951 2952 /** 2953 * Set list of acceptable exchanges in @a oc. Upon success, continues 2954 * processing with add_payment_details(). 2955 * 2956 * @param[in,out] oc order context 2957 * @return true to suspend execution 2958 */ 2959 static bool 2960 phase_set_exchanges (struct OrderContext *oc) 2961 { 2962 if (NULL != oc->set_exchanges.wakeup_task) 2963 { 2964 GNUNET_SCHEDULER_cancel (oc->set_exchanges.wakeup_task); 2965 oc->set_exchanges.wakeup_task = NULL; 2966 } 2967 2968 if (! oc->add_payment_details.need_exchange) 2969 { 2970 /* Total amount is zero, so we don't actually need exchanges! */ 2971 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2972 "Order total is zero, no need for exchanges\n"); 2973 oc->select_wire_method.exchanges = json_array (); 2974 GNUNET_assert (NULL != oc->select_wire_method.exchanges); 2975 /* Pick first one, doesn't matter as the amount is zero */ 2976 oc->select_wire_method.wm = oc->hc->instance->wm_head; 2977 oc->phase = ORDER_PHASE_SET_MAX_FEE; 2978 return false; 2979 } 2980 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2981 "Trying to find exchanges\n"); 2982 if (NULL == oc->set_exchanges.pending_reload_head) 2983 { 2984 if (! oc->set_exchanges.exchanges_tried) 2985 { 2986 oc->set_exchanges.exchanges_tried = true; 2987 oc->set_exchanges.keys_timeout 2988 = GNUNET_TIME_relative_to_absolute (MAX_KEYS_WAIT); 2989 TMH_exchange_get_trusted (&get_exchange_keys, 2990 oc); 2991 } 2992 else if ( (! oc->set_exchanges.forced_reload) && 2993 (oc->set_exchanges.promising_exchange) && 2994 (! oc->set_exchanges.exchange_ok) ) 2995 { 2996 for (struct WireMethodCandidate *wmc = oc->add_payment_details.wmc_head; 2997 NULL != wmc; 2998 wmc = wmc->next) 2999 GNUNET_break (0 == 3000 json_array_clear (wmc->exchanges)); 3001 /* Try one more time with forcing /keys download */ 3002 oc->set_exchanges.forced_reload = true; 3003 TMH_exchange_get_trusted (&get_exchange_keys, 3004 oc); 3005 } 3006 } 3007 if (GNUNET_TIME_absolute_is_past (oc->set_exchanges.keys_timeout)) 3008 { 3009 struct RekeyExchange *rx; 3010 3011 while (NULL != (rx = oc->set_exchanges.pending_reload_head)) 3012 { 3013 GNUNET_CONTAINER_DLL_remove (oc->set_exchanges.pending_reload_head, 3014 oc->set_exchanges.pending_reload_tail, 3015 rx); 3016 TMH_EXCHANGES_keys4exchange_cancel (rx->fo); 3017 GNUNET_free (rx->url); 3018 GNUNET_free (rx); 3019 } 3020 } 3021 if (NULL != oc->set_exchanges.pending_reload_head) 3022 { 3023 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3024 "Still trying to (re)load %skeys\n", 3025 oc->set_exchanges.pending_reload_head->url); 3026 oc->set_exchanges.wakeup_task 3027 = GNUNET_SCHEDULER_add_at (oc->set_exchanges.keys_timeout, 3028 &wakeup_timeout, 3029 oc); 3030 MHD_suspend_connection (oc->connection); 3031 oc->suspended = GNUNET_YES; 3032 GNUNET_CONTAINER_DLL_insert (oc_head, 3033 oc_tail, 3034 oc); 3035 return true; /* reloads pending */ 3036 } 3037 oc->phase++; 3038 return false; 3039 } 3040 3041 3042 /* ***************** ORDER_PHASE_ADD_PAYMENT_DETAILS **************** */ 3043 3044 /** 3045 * Process the @a payment_target and add the details of how the 3046 * order could be paid to @a order. On success, continue 3047 * processing with add_payment_fees(). 3048 * 3049 * @param[in,out] oc order context 3050 */ 3051 static void 3052 phase_add_payment_details (struct OrderContext *oc) 3053 { 3054 /* First, determine the maximum amounts that could be paid per currency */ 3055 switch (oc->parse_order.order->base->version) 3056 { 3057 case TALER_MERCHANT_CONTRACT_VERSION_0: 3058 GNUNET_array_append (oc->add_payment_details.max_choice_limits, 3059 oc->add_payment_details.num_max_choice_limits, 3060 oc->parse_order.order->details.v0.brutto); 3061 if (! TALER_amount_is_zero ( 3062 &oc->parse_order.order->details.v0.brutto)) 3063 { 3064 oc->add_payment_details.need_exchange = true; 3065 } 3066 break; 3067 case TALER_MERCHANT_CONTRACT_VERSION_1: 3068 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 3069 { 3070 const struct TALER_Amount *amount 3071 = &oc->parse_choices.choices[i].amount; 3072 bool found = false; 3073 3074 if (! TALER_amount_is_zero (amount)) 3075 { 3076 oc->add_payment_details.need_exchange = true; 3077 } 3078 for (unsigned int j = 0; 3079 j<oc->add_payment_details.num_max_choice_limits; 3080 j++) 3081 { 3082 struct TALER_Amount *mx = &oc->add_payment_details.max_choice_limits[j]; 3083 if (GNUNET_YES == 3084 TALER_amount_cmp_currency (mx, 3085 amount)) 3086 { 3087 TALER_amount_max (mx, 3088 mx, 3089 amount); 3090 found = true; 3091 break; 3092 } 3093 } 3094 if (! found) 3095 { 3096 GNUNET_array_append (oc->add_payment_details.max_choice_limits, 3097 oc->add_payment_details.num_max_choice_limits, 3098 *amount); 3099 } 3100 } 3101 break; 3102 default: 3103 GNUNET_assert (0); 3104 } 3105 3106 /* Then, create a candidate for each available wire method */ 3107 for (struct TMH_WireMethod *wm = oc->hc->instance->wm_head; 3108 NULL != wm; 3109 wm = wm->next) 3110 { 3111 struct WireMethodCandidate *wmc; 3112 3113 /* Locate wire method that has a matching payment target */ 3114 if (! wm->active) 3115 continue; /* ignore inactive methods */ 3116 if ( (NULL != oc->parse_request.payment_target) && 3117 (0 != strcasecmp (oc->parse_request.payment_target, 3118 wm->wire_method) ) ) 3119 continue; /* honor client preference */ 3120 wmc = GNUNET_new (struct WireMethodCandidate); 3121 wmc->wm = wm; 3122 wmc->exchanges = json_array (); 3123 GNUNET_assert (NULL != wmc->exchanges); 3124 GNUNET_CONTAINER_DLL_insert (oc->add_payment_details.wmc_head, 3125 oc->add_payment_details.wmc_tail, 3126 wmc); 3127 } 3128 3129 if (NULL == oc->add_payment_details.wmc_head) 3130 { 3131 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3132 "No wire method available for instance '%s'\n", 3133 oc->hc->instance->settings.id); 3134 reply_with_error (oc, 3135 MHD_HTTP_NOT_FOUND, 3136 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE, 3137 oc->parse_request.payment_target); 3138 return; 3139 } 3140 3141 /* next, we'll evaluate available exchanges */ 3142 oc->phase++; 3143 } 3144 3145 3146 /* ***************** ORDER_PHASE_MERGE_INVENTORY **************** */ 3147 3148 3149 /** 3150 * Helper function to sort uint64_t array with qsort(). 3151 * 3152 * @param a pointer to element to compare 3153 * @param b pointer to element to compare 3154 * @return 0 on equal, -1 on smaller, 1 on larger 3155 */ 3156 static int 3157 uint64_cmp (const void *a, 3158 const void *b) 3159 { 3160 uint64_t ua = *(const uint64_t *) a; 3161 uint64_t ub = *(const uint64_t *) b; 3162 3163 if (ua < ub) 3164 return -1; 3165 if (ua > ub) 3166 return 1; 3167 return 0; 3168 } 3169 3170 3171 /** 3172 * Merge the inventory products into products, querying the 3173 * database about the details of those products. Upon success, 3174 * continue processing by calling add_payment_details(). 3175 * 3176 * @param[in,out] oc order context to process 3177 */ 3178 static void 3179 phase_merge_inventory (struct OrderContext *oc) 3180 { 3181 uint64_t pots[oc->parse_order.order->products_len + 1]; 3182 size_t pots_off = 0; 3183 3184 if (0 != oc->parse_order.order->base->default_money_pot) 3185 pots[pots_off++] = oc->parse_order.order->base->default_money_pot; 3186 /** 3187 * parse_request.inventory_products => instructions to add products to contract terms 3188 * parse_order.products => contains products that are not from the backend-managed inventory. 3189 */ 3190 oc->merge_inventory.products = json_array (); 3191 for (size_t i = 0; i<oc->parse_order.order->products_len; i++) 3192 { 3193 GNUNET_assert ( 3194 0 == 3195 json_array_append_new ( 3196 oc->merge_inventory.products, 3197 TALER_MERCHANT_product_sold_serialize ( 3198 &oc->parse_order.order->products[i]))); 3199 if (0 != oc->parse_order.order->products[i].product_money_pot) 3200 pots[pots_off++] = oc->parse_order.order->products[i].product_money_pot; 3201 } 3202 3203 /* make sure pots array only has distinct elements */ 3204 qsort (pots, 3205 pots_off, 3206 sizeof (uint64_t), 3207 &uint64_cmp); 3208 { 3209 size_t e = 0; 3210 3211 for (size_t i = 1; i<pots_off; i++) 3212 { 3213 if (pots[e] != pots[i]) 3214 pots[++e] = pots[i]; 3215 } 3216 if (pots_off > 0) 3217 e++; 3218 pots_off = e; 3219 } 3220 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3221 "Found %u unique money pots in order\n", 3222 (unsigned int) pots_off); 3223 3224 /* check if all money pots exist; note that we do NOT treat 3225 the inventory products to this check, as (1) the foreign key 3226 constraint should ensure this, and (2) if the money pot 3227 were deleted (concurrently), the value is specified to be 3228 considered 0 (aka none) and so we can proceed anyway. */ 3229 if (pots_off > 0) 3230 { 3231 enum GNUNET_DB_QueryStatus qs; 3232 uint64_t pot_missing; 3233 3234 qs = TALER_MERCHANTDB_check_money_pots (TMH_db, 3235 oc->hc->instance->settings.id, 3236 pots_off, 3237 pots, 3238 &pot_missing); 3239 switch (qs) 3240 { 3241 case GNUNET_DB_STATUS_HARD_ERROR: 3242 case GNUNET_DB_STATUS_SOFT_ERROR: 3243 GNUNET_break (0); 3244 reply_with_error (oc, 3245 MHD_HTTP_INTERNAL_SERVER_ERROR, 3246 TALER_EC_GENERIC_DB_FETCH_FAILED, 3247 "check_money_pots"); 3248 return; 3249 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 3250 /* great, good case! */ 3251 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3252 "All money pots exist\n"); 3253 break; 3254 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 3255 { 3256 char mstr[32]; 3257 3258 GNUNET_snprintf (mstr, 3259 sizeof (mstr), 3260 "%llu", 3261 (unsigned long long) pot_missing); 3262 reply_with_error (oc, 3263 MHD_HTTP_NOT_FOUND, 3264 TALER_EC_MERCHANT_GENERIC_MONEY_POT_UNKNOWN, 3265 mstr); 3266 return; 3267 } 3268 } 3269 } 3270 3271 /* Populate products from inventory product array and database */ 3272 { 3273 GNUNET_assert (NULL != oc->merge_inventory.products); 3274 for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++) 3275 { 3276 struct InventoryProduct *ip 3277 = &oc->parse_request.inventory_products[i]; 3278 struct TALER_MERCHANTDB_ProductDetails pd; 3279 enum GNUNET_DB_QueryStatus qs; 3280 size_t num_categories = 0; 3281 uint64_t *categories = NULL; 3282 3283 qs = TALER_MERCHANTDB_lookup_product (TMH_db, 3284 oc->hc->instance->settings.id, 3285 ip->product_id, 3286 &pd, 3287 &num_categories, 3288 &categories); 3289 if (qs <= 0) 3290 { 3291 enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 3292 unsigned int http_status = 0; 3293 3294 switch (qs) 3295 { 3296 case GNUNET_DB_STATUS_HARD_ERROR: 3297 GNUNET_break (0); 3298 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 3299 ec = TALER_EC_GENERIC_DB_FETCH_FAILED; 3300 break; 3301 case GNUNET_DB_STATUS_SOFT_ERROR: 3302 GNUNET_break (0); 3303 http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; 3304 ec = TALER_EC_GENERIC_DB_SOFT_FAILURE; 3305 break; 3306 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 3307 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3308 "Product %s from order unknown\n", 3309 ip->product_id); 3310 http_status = MHD_HTTP_NOT_FOUND; 3311 ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN; 3312 break; 3313 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 3314 /* case listed to make compilers happy */ 3315 GNUNET_assert (0); 3316 } 3317 reply_with_error (oc, 3318 http_status, 3319 ec, 3320 ip->product_id); 3321 return; 3322 } 3323 GNUNET_free (categories); 3324 oc->parse_order.order->base->minimum_age 3325 = GNUNET_MAX (oc->parse_order.order->base->minimum_age, 3326 pd.minimum_age); 3327 { 3328 const char *eparam; 3329 3330 if ( (! ip->quantity_missing) && 3331 (ip->quantity > (uint64_t) INT64_MAX) ) 3332 { 3333 GNUNET_break_op (0); 3334 reply_with_error (oc, 3335 MHD_HTTP_BAD_REQUEST, 3336 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3337 "quantity"); 3338 TALER_MERCHANTDB_product_details_free (&pd); 3339 return; 3340 } 3341 if (GNUNET_OK != 3342 TALER_MERCHANT_vk_process_quantity_inputs ( 3343 TALER_MERCHANT_VK_QUANTITY, 3344 pd.allow_fractional_quantity, 3345 ip->quantity_missing, 3346 (int64_t) ip->quantity, 3347 ip->unit_quantity_missing, 3348 ip->unit_quantity, 3349 &ip->quantity, 3350 &ip->quantity_frac, 3351 &eparam)) 3352 { 3353 GNUNET_break_op (0); 3354 reply_with_error (oc, 3355 MHD_HTTP_BAD_REQUEST, 3356 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3357 eparam); 3358 TALER_MERCHANTDB_product_details_free (&pd); 3359 return; 3360 } 3361 } 3362 { 3363 struct TALER_MERCHANT_ProductSold ps = { 3364 .product_id = (char *) ip->product_id, 3365 .product_name = pd.product_name, 3366 .description = pd.description, 3367 .description_i18n = pd.description_i18n, 3368 .unit_quantity.integer = ip->quantity, 3369 .unit_quantity.fractional = ip->quantity_frac, 3370 .prices_length = pd.price_array_length, 3371 .prices = GNUNET_new_array (pd.price_array_length, 3372 struct TALER_Amount), 3373 .prices_are_net = pd.price_is_net, 3374 .image = pd.image, 3375 .taxes = pd.taxes, 3376 .delivery_date = oc->parse_order.order->base->delivery_date, 3377 .product_money_pot = pd.money_pot_id, 3378 .unit = pd.unit, 3379 3380 }; 3381 json_t *p; 3382 char unit_quantity_buf[64]; 3383 3384 for (size_t j = 0; j<pd.price_array_length; j++) 3385 { 3386 struct TALER_Amount atomic_amount; 3387 3388 GNUNET_assert ( 3389 GNUNET_OK == 3390 TALER_amount_set_zero (pd.price_array[j].currency, 3391 &atomic_amount)); 3392 atomic_amount.fraction = 1; 3393 GNUNET_assert ( 3394 GNUNET_OK == 3395 TALER_MERCHANT_amount_multiply_by_quantity ( 3396 &ps.prices[j], 3397 &pd.price_array[j], 3398 &ps.unit_quantity, 3399 TALER_MERCHANT_ROUND_UP, 3400 &atomic_amount)); 3401 } 3402 3403 TALER_MERCHANT_vk_format_fractional_string ( 3404 TALER_MERCHANT_VK_QUANTITY, 3405 ip->quantity, 3406 ip->quantity_frac, 3407 sizeof (unit_quantity_buf), 3408 unit_quantity_buf); 3409 if (0 != pd.money_pot_id) 3410 pots[pots_off++] = pd.money_pot_id; 3411 p = TALER_MERCHANT_product_sold_serialize (&ps); 3412 GNUNET_assert (NULL != p); 3413 GNUNET_free (ps.prices); 3414 GNUNET_assert (0 == 3415 json_array_append_new (oc->merge_inventory.products, 3416 p)); 3417 } 3418 TALER_MERCHANTDB_product_details_free (&pd); 3419 } 3420 } 3421 3422 /* check if final product list is well-formed */ 3423 if (! TMH_products_array_valid (oc->merge_inventory.products)) 3424 { 3425 GNUNET_break_op (0); 3426 reply_with_error (oc, 3427 MHD_HTTP_BAD_REQUEST, 3428 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3429 "order:products"); 3430 return; 3431 } 3432 oc->phase++; 3433 } 3434 3435 3436 /* ***************** ORDER_PHASE_PARSE_CHOICES **************** */ 3437 3438 /** 3439 * Callback function that is called for each donau instance. 3440 * It simply adds the provided donau_url to the json. 3441 * 3442 * @param cls closure with our `struct TALER_MERCHANT_ContractOutput *` 3443 * @param donau_url the URL of the donau instance 3444 */ 3445 static void 3446 add_donau_url (void *cls, 3447 const char *donau_url) 3448 { 3449 struct TALER_MERCHANT_ContractOutput *output = cls; 3450 3451 GNUNET_array_append (output->details.donation_receipt.donau_urls, 3452 output->details.donation_receipt.donau_urls_len, 3453 GNUNET_strdup (donau_url)); 3454 } 3455 3456 3457 /** 3458 * Add the donau output to the contract output. 3459 * 3460 * @param oc order context 3461 * @param output contract output to add donau URLs to 3462 */ 3463 static bool 3464 add_donau_output (struct OrderContext *oc, 3465 struct TALER_MERCHANT_ContractOutput *output) 3466 { 3467 enum GNUNET_DB_QueryStatus qs; 3468 3469 qs = TALER_MERCHANTDB_select_donau_instances_filtered ( 3470 TMH_db, 3471 output->details.donation_receipt.amount.currency, 3472 &add_donau_url, 3473 output); 3474 if (qs < 0) 3475 { 3476 GNUNET_break (0); 3477 reply_with_error (oc, 3478 MHD_HTTP_INTERNAL_SERVER_ERROR, 3479 TALER_EC_GENERIC_DB_FETCH_FAILED, 3480 "donau url parsing db call"); 3481 for (unsigned int i = 0; 3482 i < output->details.donation_receipt.donau_urls_len; 3483 i++) 3484 GNUNET_free (output->details.donation_receipt.donau_urls[i]); 3485 GNUNET_array_grow (output->details.donation_receipt.donau_urls, 3486 output->details.donation_receipt.donau_urls_len, 3487 0); 3488 return false; 3489 } 3490 return true; 3491 } 3492 3493 3494 /** 3495 * Parse contract choices. Upon success, continue 3496 * processing with merge_inventory(). 3497 * 3498 * @param[in,out] oc order context 3499 */ 3500 static void 3501 phase_parse_choices (struct OrderContext *oc) 3502 { 3503 switch (oc->parse_order.order->base->version) 3504 { 3505 case TALER_MERCHANT_CONTRACT_VERSION_0: 3506 oc->phase++; 3507 return; 3508 case TALER_MERCHANT_CONTRACT_VERSION_1: 3509 /* handle below */ 3510 break; 3511 default: 3512 GNUNET_assert (0); 3513 } 3514 3515 /* Convert order choices to contract choices */ 3516 GNUNET_array_grow (oc->parse_choices.choices, 3517 oc->parse_choices.choices_len, 3518 oc->parse_order.order->details.v1.choices_len); 3519 for (unsigned int i = 0; i<oc->parse_choices.choices_len; i++) 3520 { 3521 const struct TALER_MERCHANT_OrderChoice *ochoice 3522 = &oc->parse_order.order->details.v1.choices[i]; 3523 struct TALER_MERCHANT_ContractChoice *cchoice 3524 = &oc->parse_choices.choices[i]; 3525 unsigned int off; 3526 3527 if (! TMH_test_exchange_configured_for_currency ( 3528 ochoice->amount.currency)) 3529 { 3530 GNUNET_break_op (0); 3531 reply_with_error (oc, 3532 MHD_HTTP_CONFLICT, 3533 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, 3534 ochoice->amount.currency); 3535 return; 3536 } 3537 cchoice->amount = ochoice->amount; 3538 cchoice->tip = ochoice->tip; 3539 cchoice->no_tip = ochoice->no_tip; 3540 if (NULL != ochoice->description) 3541 cchoice->description = GNUNET_strdup (ochoice->description); 3542 if (NULL != ochoice->description_i18n) 3543 cchoice->description_i18n = json_incref (ochoice->description_i18n); 3544 cchoice->max_fee = ochoice->max_fee; 3545 3546 /* convert inputs */ 3547 GNUNET_array_grow (cchoice->inputs, 3548 cchoice->inputs_len, 3549 ochoice->inputs_len); 3550 off = 0; 3551 for (unsigned int j = 0; j < ochoice->inputs_len; j++) 3552 { 3553 const struct TALER_MERCHANT_OrderInput *order_input 3554 = &ochoice->inputs[j]; 3555 struct TALER_MERCHANT_ContractInput *contract_input 3556 = &cchoice->inputs[off]; 3557 3558 contract_input->type = order_input->type; 3559 switch (order_input->type) 3560 { 3561 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 3562 GNUNET_assert (0); 3563 break; 3564 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 3565 /* Ignore inputs tokens with 'count' field set to 0 */ 3566 if (0 == order_input->details.token.count) 3567 continue; 3568 contract_input->details.token.count 3569 = order_input->details.token.count; 3570 contract_input->details.token.token_family_slug 3571 = order_input->details.token.token_family_slug; 3572 if (GNUNET_OK != 3573 add_input_token_family (oc, 3574 contract_input->details.token.token_family_slug)) 3575 { 3576 GNUNET_break_op (0); 3577 return; 3578 } 3579 off++; 3580 continue; 3581 } /* switch input type */ 3582 GNUNET_assert (0); 3583 } /* for all inputs */ 3584 GNUNET_array_grow (cchoice->inputs, 3585 cchoice->inputs_len, 3586 off); 3587 3588 /* convert outputs */ 3589 GNUNET_array_grow (cchoice->outputs, 3590 cchoice->outputs_len, 3591 ochoice->outputs_len); 3592 off = 0; 3593 for (unsigned int j = 0; j < ochoice->outputs_len; j++) 3594 { 3595 const struct TALER_MERCHANT_OrderOutput *order_output 3596 = &ochoice->outputs[j]; 3597 struct TALER_MERCHANT_ContractOutput *contract_output 3598 = &cchoice->outputs[off]; 3599 3600 contract_output->type = order_output->type; 3601 switch (order_output->type) 3602 { 3603 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 3604 GNUNET_assert (0); 3605 break; 3606 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 3607 if (order_output->details.donation_receipt.no_amount) 3608 { 3609 contract_output->details.donation_receipt.amount 3610 = ochoice->amount; 3611 } 3612 else 3613 { 3614 contract_output->details.donation_receipt.amount 3615 = order_output->details.donation_receipt.amount; 3616 } 3617 if (! add_donau_output (oc, 3618 contract_output)) 3619 { 3620 GNUNET_break (0); 3621 return; 3622 } 3623 off++; 3624 continue; 3625 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 3626 /* Ignore inputs tokens with 'count' field set to 0 */ 3627 if (0 == order_output->details.token.count) 3628 continue; 3629 3630 contract_output->details.token.token_family_slug 3631 = order_output->details.token.token_family_slug; 3632 contract_output->details.token.count 3633 = order_output->details.token.count; 3634 if (0 == order_output->details.token.valid_at.abs_time.abs_value_us) 3635 contract_output->details.token.valid_at 3636 = GNUNET_TIME_timestamp_get (); 3637 else 3638 contract_output->details.token.valid_at 3639 = order_output->details.token.valid_at; 3640 if (GNUNET_OK != 3641 add_output_token_family ( 3642 oc, 3643 contract_output->details.token.token_family_slug, 3644 contract_output->details.token.valid_at, 3645 &contract_output->details.token.key_index)) 3646 3647 { 3648 /* note: reply_with_error() was already called */ 3649 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 3650 "Could not handle output token family `%s'\n", 3651 contract_output->details.token.token_family_slug); 3652 return; 3653 } 3654 off++; 3655 continue; 3656 } /* end switch */ 3657 GNUNET_assert (0); 3658 } /* for outputs */ 3659 GNUNET_array_grow (cchoice->outputs, 3660 cchoice->outputs_len, 3661 off); 3662 } /* for all choices */ 3663 oc->phase++; 3664 } 3665 3666 3667 /* ***************** ORDER_PHASE_PARSE_ORDER **************** */ 3668 3669 3670 /** 3671 * Parse the order field of the request. Upon success, continue 3672 * processing with parse_choices(). 3673 * 3674 * @param[in,out] oc order context 3675 */ 3676 static void 3677 phase_parse_order (struct OrderContext *oc) 3678 { 3679 const struct TALER_MERCHANTDB_InstanceSettings *settings = 3680 &oc->hc->instance->settings; 3681 bool computed_refund_deadline = false; 3682 3683 oc->parse_order.order 3684 = TALER_MERCHANT_order_parse ( 3685 oc->parse_request.order); 3686 if (NULL == oc->parse_order.order) 3687 { 3688 GNUNET_break_op (0); 3689 reply_with_error (oc, 3690 MHD_HTTP_BAD_REQUEST, 3691 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3692 "order"); 3693 return; 3694 } 3695 3696 switch (oc->parse_order.order->base->version) 3697 { 3698 case TALER_MERCHANT_CONTRACT_VERSION_0: 3699 if (! TMH_test_exchange_configured_for_currency ( 3700 oc->parse_order.order->details.v0.brutto.currency)) 3701 { 3702 GNUNET_break_op (0); 3703 reply_with_error ( 3704 oc, 3705 MHD_HTTP_CONFLICT, 3706 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGE_FOR_CURRENCY, 3707 oc->parse_order.order->details.v0.brutto.currency); 3708 return; 3709 } 3710 break; 3711 case TALER_MERCHANT_CONTRACT_VERSION_1: 3712 break; 3713 default: 3714 GNUNET_break_op (0); 3715 reply_with_error (oc, 3716 MHD_HTTP_BAD_REQUEST, 3717 TALER_EC_GENERIC_VERSION_MALFORMED, 3718 "invalid version specified in order, supported are null, '0' or '1'"); 3719 return; 3720 } 3721 3722 /* Add order_id if it doesn't exist. */ 3723 if (NULL == oc->parse_order.order->order_id) 3724 { 3725 char buf[256]; 3726 time_t timer; 3727 struct tm *tm_info; 3728 size_t off; 3729 uint64_t rand; 3730 char *last; 3731 3732 time (&timer); 3733 tm_info = localtime (&timer); 3734 if (NULL == tm_info) 3735 { 3736 reply_with_error ( 3737 oc, 3738 MHD_HTTP_INTERNAL_SERVER_ERROR, 3739 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME, 3740 NULL); 3741 return; 3742 } 3743 off = strftime (buf, 3744 sizeof (buf) - 1, 3745 "%Y.%j", 3746 tm_info); 3747 /* Check for error state of strftime */ 3748 GNUNET_assert (0 != off); 3749 buf[off++] = '-'; 3750 rand = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, 3751 UINT64_MAX); 3752 last = GNUNET_STRINGS_data_to_string (&rand, 3753 sizeof (uint64_t), 3754 &buf[off], 3755 sizeof (buf) - off); 3756 GNUNET_assert (NULL != last); 3757 *last = '\0'; 3758 3759 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3760 "Assigning order ID `%s' server-side\n", 3761 buf); 3762 oc->parse_order.order->order_id = GNUNET_strdup (buf); 3763 } 3764 3765 /* Patch fulfillment URL with order_id (implements #6467). */ 3766 if (NULL != oc->parse_order.order->base->fulfillment_url) 3767 { 3768 const char *pos; 3769 3770 pos = strstr (oc->parse_order.order->base->fulfillment_url, 3771 "${ORDER_ID}"); 3772 if (NULL != pos) 3773 { 3774 /* replace ${ORDER_ID} with the real order_id */ 3775 char *nurl; 3776 3777 /* We only allow one placeholder */ 3778 if (strstr (pos + strlen ("${ORDER_ID}"), 3779 "${ORDER_ID}")) 3780 { 3781 GNUNET_break_op (0); 3782 reply_with_error (oc, 3783 MHD_HTTP_BAD_REQUEST, 3784 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3785 "fulfillment_url"); 3786 return; 3787 } 3788 3789 GNUNET_asprintf ( 3790 &nurl, 3791 "%.*s%s%s", 3792 /* first output URL until ${ORDER_ID} */ 3793 (int) (pos - oc->parse_order.order->base->fulfillment_url), 3794 oc->parse_order.order->base->fulfillment_url, 3795 /* replace ${ORDER_ID} with the right order_id */ 3796 oc->parse_order.order->order_id, 3797 /* append rest of original URL */ 3798 pos + strlen ("${ORDER_ID}")); 3799 oc->parse_order.order->base->fulfillment_url = GNUNET_strdup (nurl); 3800 GNUNET_free (nurl); 3801 } 3802 } 3803 3804 if ( (GNUNET_TIME_absolute_is_zero ( 3805 oc->parse_order.order->pay_deadline.abs_time)) || 3806 (GNUNET_TIME_absolute_is_never ( 3807 oc->parse_order.order->pay_deadline.abs_time)) ) 3808 { 3809 oc->parse_order.order->pay_deadline 3810 = GNUNET_TIME_relative_to_timestamp ( 3811 settings->default_pay_delay); 3812 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3813 "Pay deadline was zero (or never), setting to %s\n", 3814 GNUNET_TIME_timestamp2s ( 3815 oc->parse_order.order->pay_deadline)); 3816 } 3817 else if (GNUNET_TIME_absolute_is_past ( 3818 oc->parse_order.order->pay_deadline.abs_time)) 3819 { 3820 GNUNET_break_op (0); 3821 reply_with_error ( 3822 oc, 3823 MHD_HTTP_BAD_REQUEST, 3824 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PAY_DEADLINE_IN_PAST, 3825 NULL); 3826 return; 3827 } 3828 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3829 "Pay deadline is %s\n", 3830 GNUNET_TIME_timestamp2s ( 3831 oc->parse_order.order->pay_deadline)); 3832 3833 /* Check soundness of refund deadline, and that a timestamp 3834 * is actually present. */ 3835 { 3836 struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); 3837 3838 /* Add timestamp if it doesn't exist (or is zero) */ 3839 if (GNUNET_TIME_absolute_is_zero ( 3840 oc->parse_order.order->timestamp.abs_time)) 3841 { 3842 oc->parse_order.order->timestamp = now; 3843 } 3844 3845 /* If no refund_deadline given, set one based on refund_delay. */ 3846 if (GNUNET_TIME_absolute_is_never ( 3847 oc->parse_order.order->refund_deadline.abs_time)) 3848 { 3849 if (GNUNET_TIME_relative_is_zero ( 3850 oc->parse_request.refund_delay)) 3851 { 3852 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3853 "Refund delay is zero, no refunds are possible for this order\n"); 3854 oc->parse_order.order->refund_deadline = GNUNET_TIME_UNIT_ZERO_TS; 3855 } 3856 else 3857 { 3858 computed_refund_deadline = true; 3859 oc->parse_order.order->refund_deadline 3860 = GNUNET_TIME_absolute_to_timestamp ( 3861 GNUNET_TIME_absolute_add ( 3862 oc->parse_order.order->pay_deadline.abs_time, 3863 oc->parse_request.refund_delay)); 3864 } 3865 } 3866 3867 if ( (! GNUNET_TIME_absolute_is_zero ( 3868 oc->parse_order.order->base->delivery_date.abs_time)) && 3869 (GNUNET_TIME_absolute_is_past ( 3870 oc->parse_order.order->base->delivery_date.abs_time)) ) 3871 { 3872 GNUNET_break_op (0); 3873 reply_with_error ( 3874 oc, 3875 MHD_HTTP_BAD_REQUEST, 3876 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST, 3877 NULL); 3878 return; 3879 } 3880 } 3881 3882 if ( (! GNUNET_TIME_absolute_is_zero ( 3883 oc->parse_order.order->refund_deadline.abs_time)) && 3884 (GNUNET_TIME_absolute_is_past ( 3885 oc->parse_order.order->refund_deadline.abs_time)) ) 3886 { 3887 GNUNET_break_op (0); 3888 reply_with_error ( 3889 oc, 3890 MHD_HTTP_BAD_REQUEST, 3891 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_DEADLINE_IN_PAST, 3892 NULL); 3893 return; 3894 } 3895 3896 if (GNUNET_TIME_absolute_is_never ( 3897 oc->parse_order.order->wire_transfer_deadline.abs_time)) 3898 { 3899 struct GNUNET_TIME_Absolute start; 3900 3901 start = GNUNET_TIME_absolute_max ( 3902 oc->parse_order.order->refund_deadline.abs_time, 3903 oc->parse_order.order->pay_deadline.abs_time); 3904 oc->parse_order.order->wire_transfer_deadline 3905 = GNUNET_TIME_absolute_to_timestamp ( 3906 GNUNET_TIME_round_up ( 3907 GNUNET_TIME_absolute_add ( 3908 start, 3909 settings->default_wire_transfer_delay), 3910 settings->default_wire_transfer_rounding_interval)); 3911 if (GNUNET_TIME_absolute_is_never ( 3912 oc->parse_order.order->wire_transfer_deadline.abs_time)) 3913 { 3914 GNUNET_break_op (0); 3915 reply_with_error ( 3916 oc, 3917 MHD_HTTP_BAD_REQUEST, 3918 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER, 3919 "order:wire_transfer_deadline"); 3920 return; 3921 } 3922 } 3923 else if (computed_refund_deadline) 3924 { 3925 /* if we computed the refund_deadline from default settings 3926 and did have a configured wire_deadline, make sure that 3927 the refund_deadline is at or below the wire_deadline. */ 3928 oc->parse_order.order->refund_deadline 3929 = GNUNET_TIME_timestamp_min ( 3930 oc->parse_order.order->refund_deadline, 3931 oc->parse_order.order->wire_transfer_deadline); 3932 } 3933 if (GNUNET_TIME_timestamp_cmp ( 3934 oc->parse_order.order->wire_transfer_deadline, 3935 <, 3936 oc->parse_order.order->refund_deadline)) 3937 { 3938 GNUNET_break_op (0); 3939 reply_with_error ( 3940 oc, 3941 MHD_HTTP_BAD_REQUEST, 3942 TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE, 3943 "order:wire_transfer_deadline;order:refund_deadline"); 3944 return; 3945 } 3946 3947 { 3948 char *url; 3949 3950 url = make_merchant_base_url (oc->connection, 3951 settings->id); 3952 if (NULL == url) 3953 { 3954 GNUNET_break_op (0); 3955 reply_with_error ( 3956 oc, 3957 MHD_HTTP_BAD_REQUEST, 3958 TALER_EC_GENERIC_PARAMETER_MISSING, 3959 "order:merchant_base_url"); 3960 return; 3961 } 3962 oc->parse_order.merchant_base_url = url; 3963 } 3964 3965 // FIXME: move to util during parsing! 3966 if ( (NULL != oc->parse_order.order->base->delivery_location) && 3967 (! TMH_location_object_valid (oc->parse_order.order->base->delivery_location)) ) 3968 { 3969 GNUNET_break_op (0); 3970 reply_with_error (oc, 3971 MHD_HTTP_BAD_REQUEST, 3972 TALER_EC_GENERIC_PARAMETER_MALFORMED, 3973 "delivery_location"); 3974 return; 3975 } 3976 3977 oc->phase++; 3978 } 3979 3980 3981 /* ***************** ORDER_PHASE_PARSE_REQUEST **************** */ 3982 3983 /** 3984 * Parse the client request. Upon success, 3985 * continue processing by calling parse_order(). 3986 * 3987 * @param[in,out] oc order context to process 3988 */ 3989 static void 3990 phase_parse_request (struct OrderContext *oc) 3991 { 3992 const json_t *ip = NULL; 3993 const json_t *uuid = NULL; 3994 const char *otp_id = NULL; 3995 bool create_token = true; /* default */ 3996 struct GNUNET_JSON_Specification spec[] = { 3997 GNUNET_JSON_spec_json ("order", 3998 &oc->parse_request.order), 3999 GNUNET_JSON_spec_mark_optional ( 4000 GNUNET_JSON_spec_relative_time ("refund_delay", 4001 &oc->parse_request.refund_delay), 4002 NULL), 4003 GNUNET_JSON_spec_mark_optional ( 4004 GNUNET_JSON_spec_string ("payment_target", 4005 &oc->parse_request.payment_target), 4006 NULL), 4007 GNUNET_JSON_spec_mark_optional ( 4008 GNUNET_JSON_spec_array_const ("inventory_products", 4009 &ip), 4010 NULL), 4011 GNUNET_JSON_spec_mark_optional ( 4012 GNUNET_JSON_spec_string ("session_id", 4013 &oc->parse_request.session_id), 4014 NULL), 4015 GNUNET_JSON_spec_mark_optional ( 4016 GNUNET_JSON_spec_array_const ("lock_uuids", 4017 &uuid), 4018 NULL), 4019 GNUNET_JSON_spec_mark_optional ( 4020 GNUNET_JSON_spec_bool ("create_token", 4021 &create_token), 4022 NULL), 4023 GNUNET_JSON_spec_mark_optional ( 4024 GNUNET_JSON_spec_string ("otp_id", 4025 &otp_id), 4026 NULL), 4027 GNUNET_JSON_spec_end () 4028 }; 4029 enum GNUNET_GenericReturnValue ret; 4030 4031 oc->parse_request.refund_delay 4032 = oc->hc->instance->settings.default_refund_delay; 4033 ret = TALER_MHD_parse_json_data (oc->connection, 4034 oc->hc->request_body, 4035 spec); 4036 if (GNUNET_OK != ret) 4037 { 4038 GNUNET_break_op (0); 4039 finalize_order2 (oc, 4040 ret); 4041 return; 4042 } 4043 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 4044 "Refund delay is %s\n", 4045 GNUNET_TIME_relative2s (oc->parse_request.refund_delay, 4046 false)); 4047 TALER_MERCHANTDB_expire_locks (TMH_db); 4048 if (NULL != otp_id) 4049 { 4050 struct TALER_MERCHANTDB_OtpDeviceDetails td; 4051 enum GNUNET_DB_QueryStatus qs; 4052 4053 memset (&td, 4054 0, 4055 sizeof (td)); 4056 qs = TALER_MERCHANTDB_select_otp (TMH_db, 4057 oc->hc->instance->settings.id, 4058 otp_id, 4059 &td); 4060 switch (qs) 4061 { 4062 case GNUNET_DB_STATUS_HARD_ERROR: 4063 GNUNET_break (0); 4064 reply_with_error (oc, 4065 MHD_HTTP_INTERNAL_SERVER_ERROR, 4066 TALER_EC_GENERIC_DB_FETCH_FAILED, 4067 "select_otp"); 4068 return; 4069 case GNUNET_DB_STATUS_SOFT_ERROR: 4070 GNUNET_break (0); 4071 reply_with_error (oc, 4072 MHD_HTTP_INTERNAL_SERVER_ERROR, 4073 TALER_EC_GENERIC_DB_SOFT_FAILURE, 4074 "select_otp"); 4075 return; 4076 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 4077 reply_with_error (oc, 4078 MHD_HTTP_NOT_FOUND, 4079 TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN, 4080 otp_id); 4081 return; 4082 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 4083 break; 4084 } 4085 oc->parse_request.pos_key = td.otp_key; 4086 oc->parse_request.pos_algorithm = td.otp_algorithm; 4087 GNUNET_free (td.otp_description); 4088 } 4089 if (create_token) 4090 { 4091 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 4092 &oc->parse_request.claim_token, 4093 sizeof (oc->parse_request.claim_token)); 4094 } 4095 /* Compute h_post_data (for idempotency check) */ 4096 { 4097 char *req_body_enc; 4098 4099 /* Dump normalized JSON to string. */ 4100 if (NULL == (req_body_enc 4101 = json_dumps (oc->hc->request_body, 4102 JSON_ENCODE_ANY 4103 | JSON_COMPACT 4104 | JSON_SORT_KEYS))) 4105 { 4106 GNUNET_break (0); 4107 GNUNET_JSON_parse_free (spec); 4108 reply_with_error (oc, 4109 MHD_HTTP_INTERNAL_SERVER_ERROR, 4110 TALER_EC_GENERIC_ALLOCATION_FAILURE, 4111 "request body normalization for hashing"); 4112 return; 4113 } 4114 GNUNET_CRYPTO_hash (req_body_enc, 4115 strlen (req_body_enc), 4116 &oc->parse_request.h_post_data.hash); 4117 GNUNET_free (req_body_enc); 4118 } 4119 4120 /* parse the inventory_products (optionally given) */ 4121 if (NULL != ip) 4122 { 4123 unsigned int ipl = (unsigned int) json_array_size (ip); 4124 4125 if ( (json_array_size (ip) != (size_t) ipl) || 4126 (ipl > MAX_PRODUCTS) ) 4127 { 4128 GNUNET_break_op (0); 4129 GNUNET_JSON_parse_free (spec); 4130 reply_with_error (oc, 4131 MHD_HTTP_BAD_REQUEST, 4132 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4133 "inventory_products (too many)"); 4134 return; 4135 } 4136 GNUNET_array_grow (oc->parse_request.inventory_products, 4137 oc->parse_request.inventory_products_length, 4138 (unsigned int) json_array_size (ip)); 4139 for (unsigned int i = 0; i<oc->parse_request.inventory_products_length; i++) 4140 { 4141 struct InventoryProduct *ipr = &oc->parse_request.inventory_products[i]; 4142 const char *error_name; 4143 unsigned int error_line; 4144 struct GNUNET_JSON_Specification ispec[] = { 4145 GNUNET_JSON_spec_string ("product_id", 4146 &ipr->product_id), 4147 GNUNET_JSON_spec_mark_optional ( 4148 GNUNET_JSON_spec_uint64 ("quantity", 4149 &ipr->quantity), 4150 &ipr->quantity_missing), 4151 GNUNET_JSON_spec_mark_optional ( 4152 GNUNET_JSON_spec_string ("unit_quantity", 4153 &ipr->unit_quantity), 4154 &ipr->unit_quantity_missing), 4155 GNUNET_JSON_spec_mark_optional ( 4156 GNUNET_JSON_spec_uint64 ("product_money_pot", 4157 &ipr->product_money_pot), 4158 NULL), 4159 GNUNET_JSON_spec_end () 4160 }; 4161 4162 ret = GNUNET_JSON_parse (json_array_get (ip, 4163 i), 4164 ispec, 4165 &error_name, 4166 &error_line); 4167 if (GNUNET_OK != ret) 4168 { 4169 GNUNET_break_op (0); 4170 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 4171 "Product parsing failed at #%u: %s:%u\n", 4172 i, 4173 error_name, 4174 error_line); 4175 reply_with_error (oc, 4176 MHD_HTTP_BAD_REQUEST, 4177 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4178 "inventory_products"); 4179 return; 4180 } 4181 if (ipr->quantity_missing && ipr->unit_quantity_missing) 4182 { 4183 ipr->quantity = 1; 4184 ipr->quantity_missing = false; 4185 } 4186 } 4187 } 4188 4189 /* parse the lock_uuids (optionally given) */ 4190 if (NULL != uuid) 4191 { 4192 GNUNET_array_grow (oc->parse_request.uuids, 4193 oc->parse_request.uuids_length, 4194 json_array_size (uuid)); 4195 for (unsigned int i = 0; i<oc->parse_request.uuids_length; i++) 4196 { 4197 json_t *ui = json_array_get (uuid, 4198 i); 4199 4200 if (! json_is_string (ui)) 4201 { 4202 GNUNET_break_op (0); 4203 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 4204 "UUID parsing failed at #%u\n", 4205 i); 4206 reply_with_error (oc, 4207 MHD_HTTP_BAD_REQUEST, 4208 TALER_EC_GENERIC_PARAMETER_MALFORMED, 4209 "lock_uuids"); 4210 return; 4211 } 4212 TMH_uuid_from_string (json_string_value (ui), 4213 &oc->parse_request.uuids[i]); 4214 } 4215 } 4216 oc->phase++; 4217 } 4218 4219 4220 /* ***************** Main handler **************** */ 4221 4222 4223 enum MHD_Result 4224 TMH_private_post_orders ( 4225 const struct TMH_RequestHandler *rh, 4226 struct MHD_Connection *connection, 4227 struct TMH_HandlerContext *hc) 4228 { 4229 struct OrderContext *oc = hc->ctx; 4230 4231 if (NULL == oc) 4232 { 4233 oc = GNUNET_new (struct OrderContext); 4234 hc->ctx = oc; 4235 hc->cc = &clean_order; 4236 oc->connection = connection; 4237 oc->hc = hc; 4238 } 4239 while (1) 4240 { 4241 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4242 "Processing order in phase %d\n", 4243 oc->phase); 4244 switch (oc->phase) 4245 { 4246 case ORDER_PHASE_PARSE_REQUEST: 4247 phase_parse_request (oc); 4248 break; 4249 case ORDER_PHASE_PARSE_ORDER: 4250 phase_parse_order (oc); 4251 break; 4252 case ORDER_PHASE_PARSE_CHOICES: 4253 phase_parse_choices (oc); 4254 break; 4255 case ORDER_PHASE_MERGE_INVENTORY: 4256 phase_merge_inventory (oc); 4257 break; 4258 case ORDER_PHASE_ADD_PAYMENT_DETAILS: 4259 phase_add_payment_details (oc); 4260 break; 4261 case ORDER_PHASE_SET_EXCHANGES: 4262 if (phase_set_exchanges (oc)) 4263 return MHD_YES; 4264 break; 4265 case ORDER_PHASE_SELECT_WIRE_METHOD: 4266 phase_select_wire_method (oc); 4267 break; 4268 case ORDER_PHASE_SET_MAX_FEE: 4269 phase_set_max_fee (oc); 4270 break; 4271 case ORDER_PHASE_SERIALIZE_ORDER: 4272 phase_serialize_order (oc); 4273 break; 4274 case ORDER_PHASE_CHECK_CONTRACT: 4275 phase_check_contract (oc); 4276 break; 4277 case ORDER_PHASE_SALT_FORGETTABLE: 4278 phase_salt_forgettable (oc); 4279 break; 4280 case ORDER_PHASE_EXECUTE_ORDER: 4281 phase_execute_order (oc); 4282 break; 4283 case ORDER_PHASE_FINISHED_MHD_YES: 4284 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4285 "Finished processing order (1)\n"); 4286 return MHD_YES; 4287 case ORDER_PHASE_FINISHED_MHD_NO: 4288 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 4289 "Finished processing order (0)\n"); 4290 return MHD_NO; 4291 } 4292 } 4293 } 4294 4295 4296 /* end of taler-merchant-httpd_post-private-orders.c */