LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-orders.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 892 1286 69.4 %
Date: 2025-06-23 16:22:09 Functions: 36 38 94.7 %

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

Generated by: LCOV version 1.16