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