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