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: 936 1420 65.9 %
Date: 2025-08-28 06:06:54 Functions: 38 42 90.5 %

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

Generated by: LCOV version 1.16