LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-orders.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 65.1 % 1418 923
Test Date: 2025-10-24 18:15:31 Functions: 90.5 % 42 38

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

Generated by: LCOV version 2.0-1