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