LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-orders.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 67.0 % 1423 953
Test Date: 2025-12-10 19:49:57 Functions: 90.7 % 43 39

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

Generated by: LCOV version 2.0-1