LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-private-orders.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 67.7 % 1437 973
Test Date: 2026-09-04 23:42:01 Functions: 87.0 % 46 40

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

Generated by: LCOV version 2.0-1