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