LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-orders.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 67.6 % 1493 1009
Test Date: 2026-01-01 16:44:56 Functions: 93.2 % 44 41

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

Generated by: LCOV version 2.0-1