LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_post-tips-ID-pickup.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 0 356 0.0 %
Date: 2022-08-25 06:17:04 Functions: 0 10 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2017-2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-merchant-httpd_post-tips-ID-pickup.c
      18             :  * @brief implementation of a POST /tips/ID/pickup handler
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include <microhttpd.h>
      23             : #include <jansson.h>
      24             : #include <taler/taler_json_lib.h>
      25             : #include <taler/taler_signatures.h>
      26             : #include "taler-merchant-httpd.h"
      27             : #include "taler-merchant-httpd_mhd.h"
      28             : #include "taler-merchant-httpd_helper.h"
      29             : #include "taler-merchant-httpd_exchanges.h"
      30             : #include "taler-merchant-httpd_post-tips-ID-pickup.h"
      31             : 
      32             : 
      33             : /**
      34             :  * How often do we retry on serialization errors?
      35             :  */
      36             : #define MAX_RETRIES 3
      37             : 
      38             : /**
      39             :  * How long do we give the exchange operation to complete withdrawing
      40             :  * all of the planchets?
      41             :  */
      42             : #define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
      43             :     GNUNET_TIME_UNIT_SECONDS, 45)
      44             : 
      45             : 
      46             : /**
      47             :  * Active pickup operations.
      48             :  */
      49             : struct PickupContext;
      50             : 
      51             : 
      52             : /**
      53             :  * Handle for an individual planchet we are processing for a tip.
      54             :  */
      55             : struct PlanchetOperation
      56             : {
      57             :   /**
      58             :    * Active pickup operation this planchet belongs with.
      59             :    */
      60             :   struct PickupContext *pc;
      61             : 
      62             :   /**
      63             :    * Kept in a DLL.
      64             :    */
      65             :   struct PlanchetOperation *prev;
      66             : 
      67             :   /**
      68             :    * Kept in a DLL.
      69             :    */
      70             :   struct PlanchetOperation *next;
      71             : 
      72             :   /**
      73             :    * Find operation (while active), later NULL.
      74             :    */
      75             :   struct TMH_EXCHANGES_FindOperation *fo;
      76             : 
      77             :   /**
      78             :    * Withdraw handle (NULL while @e fo is active).
      79             :    */
      80             :   struct TALER_EXCHANGE_Withdraw2Handle *w2h;
      81             : 
      82             :   /**
      83             :    * Details about the planchet for withdrawing.
      84             :    */
      85             :   struct TALER_PlanchetDetail pd;
      86             : 
      87             :   /**
      88             :    * Offset of this planchet in the original request.
      89             :    */
      90             :   unsigned int offset;
      91             : };
      92             : 
      93             : 
      94             : /**
      95             :  * Active pickup operations.
      96             :  */
      97             : struct PickupContext
      98             : {
      99             :   /**
     100             :    * Kept in a DLL.
     101             :    */
     102             :   struct PickupContext *next;
     103             : 
     104             :   /**
     105             :    * Kept in a DLL.
     106             :    */
     107             :   struct PickupContext *prev;
     108             : 
     109             :   /**
     110             :    * The connection.
     111             :    */
     112             :   struct MHD_Connection *connection;
     113             : 
     114             :   /**
     115             :    * Timeout task.
     116             :    */
     117             :   struct GNUNET_SCHEDULER_Task *tt;
     118             : 
     119             :   /**
     120             :    * Head of DLL of exchange operations on planchets.
     121             :    */
     122             :   struct PlanchetOperation *po_head;
     123             : 
     124             :   /**
     125             :    * Tail of DLL of exchange operations on planchets.
     126             :    */
     127             :   struct PlanchetOperation *po_tail;
     128             : 
     129             :   /**
     130             :    * HTTP response to return (set on errors).
     131             :    */
     132             :   struct MHD_Response *response;
     133             : 
     134             :   /**
     135             :    * Find operation (while active), later NULL.
     136             :    */
     137             :   struct TMH_EXCHANGES_FindOperation *fo;
     138             : 
     139             :   /**
     140             :    * Which reserve are we draining?
     141             :    */
     142             :   struct TALER_ReservePrivateKeyP reserve_priv;
     143             : 
     144             :   /**
     145             :    * Which tip is being picked up?
     146             :    */
     147             :   struct TALER_TipIdentifierP tip_id;
     148             : 
     149             :   /**
     150             :    * What is the ID of the pickup operation? (Basically a
     151             :    * hash over the key inputs).
     152             :    */
     153             :   struct TALER_PickupIdentifierP pickup_id;
     154             : 
     155             :   /**
     156             :    * Array of our planchets.
     157             :    */
     158             :   struct TALER_PlanchetDetail *planchets;
     159             : 
     160             :   /**
     161             :    * Length of the @e planchets array.
     162             :    */
     163             :   unsigned int planchets_length;
     164             : 
     165             :   /**
     166             :    * HTTP status to use (set on errors).
     167             :    */
     168             :   unsigned int http_status;
     169             : 
     170             :   /**
     171             :    * Total amount requested in the pick up operation. Computed by
     172             :    * totaling up the amounts of all the @e planchets.
     173             :    */
     174             :   struct TALER_Amount total_requested;
     175             : 
     176             :   /**
     177             :    * True if @e total_requested has been initialized.
     178             :    */
     179             :   bool tr_initialized;
     180             : };
     181             : 
     182             : 
     183             : /**
     184             :  * Head of DLL.
     185             :  */
     186             : static struct PickupContext *pc_head;
     187             : 
     188             : /**
     189             :  * Tail of DLL.
     190             :  */
     191             : static struct PickupContext *pc_tail;
     192             : 
     193             : 
     194             : /**
     195             :  * Stop all ongoing operations associated with @a pc.
     196             :  */
     197             : static void
     198           0 : stop_operations (struct PickupContext *pc)
     199             : {
     200             :   struct PlanchetOperation *po;
     201             : 
     202           0 :   if (NULL != pc->tt)
     203             :   {
     204           0 :     GNUNET_SCHEDULER_cancel (pc->tt);
     205           0 :     pc->tt = NULL;
     206             :   }
     207           0 :   if (NULL != pc->fo)
     208             :   {
     209           0 :     TMH_EXCHANGES_find_exchange_cancel (pc->fo);
     210           0 :     pc->fo = NULL;
     211             :   }
     212           0 :   while (NULL != (po = pc->po_head))
     213             :   {
     214           0 :     if (NULL != po->fo)
     215             :     {
     216           0 :       TMH_EXCHANGES_find_exchange_cancel (po->fo);
     217           0 :       po->fo = NULL;
     218             :     }
     219           0 :     if (NULL != po->w2h)
     220             :     {
     221           0 :       TALER_EXCHANGE_withdraw2_cancel (po->w2h);
     222           0 :       po->w2h = NULL;
     223             :     }
     224           0 :     GNUNET_CONTAINER_DLL_remove (pc->po_head,
     225             :                                  pc->po_tail,
     226             :                                  po);
     227           0 :     GNUNET_free (po);
     228             :   }
     229           0 : }
     230             : 
     231             : 
     232             : /**
     233             :  * Function called to clean up.
     234             :  *
     235             :  * @param cls a `struct PickupContext *` to clean up
     236             :  */
     237             : static void
     238           0 : pick_context_cleanup (void *cls)
     239             : {
     240           0 :   struct PickupContext *pc = cls;
     241             : 
     242           0 :   stop_operations (pc); /* should not be any... */
     243           0 :   for (unsigned int i = 0; i<pc->planchets_length; i++)
     244           0 :     TALER_planchet_detail_free (&pc->planchets[i]);
     245           0 :   GNUNET_array_grow (pc->planchets,
     246             :                      pc->planchets_length,
     247             :                      0);
     248           0 :   GNUNET_free (pc);
     249           0 : }
     250             : 
     251             : 
     252             : void
     253           0 : TMH_force_tip_pickup_resume ()
     254             : {
     255             :   struct PickupContext *nxt;
     256             : 
     257           0 :   for (struct PickupContext *pc = pc_head;
     258             :        NULL != pc;
     259           0 :        pc = nxt)
     260             :   {
     261           0 :     nxt = pc->next;
     262           0 :     stop_operations (pc);
     263           0 :     GNUNET_CONTAINER_DLL_remove (pc_head,
     264             :                                  pc_tail,
     265             :                                  pc);
     266           0 :     MHD_resume_connection (pc->connection);
     267             :   }
     268           0 : }
     269             : 
     270             : 
     271             : /**
     272             :  * Callbacks of this type are used to serve the result of submitting a
     273             :  * withdraw request to a exchange without the (un)blinding factor.
     274             :  * We persist the result in the database and, if we were the last
     275             :  * planchet operation, resume HTTP processing.
     276             :  *
     277             :  * @param cls closure with a `struct PlanchetOperation *`
     278             :  * @param hr HTTP response data
     279             :  * @param blind_sig blind signature over the coin, NULL on error
     280             :  */
     281             : static void
     282           0 : withdraw_cb (void *cls,
     283             :              const struct TALER_EXCHANGE_HttpResponse *hr,
     284             :              const struct TALER_BlindedDenominationSignature *blind_sig)
     285             : {
     286           0 :   struct PlanchetOperation *po = cls;
     287           0 :   struct PickupContext *pc = po->pc;
     288             :   enum GNUNET_DB_QueryStatus qs;
     289             : 
     290           0 :   GNUNET_CONTAINER_DLL_remove (pc->po_head,
     291             :                                pc->po_tail,
     292             :                                po);
     293           0 :   TMH_db->preflight (TMH_db->cls);
     294           0 :   if (NULL == blind_sig)
     295             :   {
     296           0 :     GNUNET_free (po);
     297           0 :     stop_operations (pc);
     298           0 :     pc->http_status = MHD_HTTP_BAD_GATEWAY;
     299           0 :     pc->response =
     300           0 :       TALER_MHD_MAKE_JSON_PACK (
     301             :         TALER_JSON_pack_ec (TALER_EC_MERCHANT_TIP_PICKUP_EXCHANGE_ERROR),
     302             :         TMH_pack_exchange_reply (hr));
     303           0 :     GNUNET_CONTAINER_DLL_remove (pc_head,
     304             :                                  pc_tail,
     305             :                                  pc);
     306           0 :     MHD_resume_connection (pc->connection);
     307           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     308           0 :     return;
     309             :   }
     310           0 :   qs = TMH_db->insert_pickup_blind_signature (TMH_db->cls,
     311           0 :                                               &pc->pickup_id,
     312             :                                               po->offset,
     313             :                                               blind_sig);
     314           0 :   GNUNET_free (po);
     315           0 :   if (qs < 0)
     316             :   {
     317           0 :     stop_operations (pc);
     318           0 :     pc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     319           0 :     pc->response = TALER_MHD_make_error (
     320             :       TALER_EC_GENERIC_DB_STORE_FAILED,
     321             :       "blind signature");
     322           0 :     GNUNET_CONTAINER_DLL_remove (pc_head,
     323             :                                  pc_tail,
     324             :                                  pc);
     325           0 :     MHD_resume_connection (pc->connection);
     326           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     327           0 :     return;
     328             :   }
     329           0 :   if (NULL == pc->po_head)
     330             :   {
     331           0 :     stop_operations (pc); /* stops timeout job */
     332           0 :     GNUNET_CONTAINER_DLL_remove (pc_head,
     333             :                                  pc_tail,
     334             :                                  pc);
     335           0 :     MHD_resume_connection (pc->connection);
     336           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     337             :   }
     338             : }
     339             : 
     340             : 
     341             : /**
     342             :  * Function called with the result of a #TMH_EXCHANGES_find_exchange()
     343             :  * operation as part of a withdraw objective.  If the exchange is ready,
     344             :  * withdraws the planchet from the exchange.
     345             :  *
     346             :  * @param cls closure, with our `struct PlanchetOperation *`
     347             :  * @param hr HTTP response details
     348             :  * @param eh handle to the exchange context
     349             :  * @param payto_uri payto://-URI of the exchange
     350             :  * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
     351             :  * @param exchange_trusted true if this exchange is trusted by config
     352             :  */
     353             : static void
     354           0 : do_withdraw (void *cls,
     355             :              const struct TALER_EXCHANGE_HttpResponse *hr,
     356             :              struct TALER_EXCHANGE_Handle *eh,
     357             :              const char *payto_uri,
     358             :              const struct TALER_Amount *wire_fee,
     359             :              bool exchange_trusted)
     360             : {
     361           0 :   struct PlanchetOperation *po = cls;
     362           0 :   struct PickupContext *pc = po->pc;
     363             : 
     364           0 :   po->fo = NULL;
     365           0 :   TMH_db->preflight (TMH_db->cls);
     366           0 :   if (NULL == hr)
     367             :   {
     368           0 :     stop_operations (pc);
     369           0 :     GNUNET_CONTAINER_DLL_remove (pc->po_head,
     370             :                                  pc->po_tail,
     371             :                                  po);
     372           0 :     GNUNET_free (po);
     373           0 :     pc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
     374           0 :     pc->response = TALER_MHD_MAKE_JSON_PACK (
     375             :       TALER_JSON_pack_ec (
     376             :         TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT));
     377           0 :     MHD_resume_connection (pc->connection);
     378           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     379           0 :     return;
     380             :   }
     381           0 :   if (NULL == eh)
     382             :   {
     383           0 :     stop_operations (pc);
     384           0 :     GNUNET_CONTAINER_DLL_remove (pc->po_head,
     385             :                                  pc->po_tail,
     386             :                                  po);
     387           0 :     GNUNET_free (po);
     388           0 :     pc->http_status = MHD_HTTP_BAD_GATEWAY;
     389           0 :     pc->response =
     390           0 :       TALER_MHD_MAKE_JSON_PACK (
     391             :         TALER_JSON_pack_ec (
     392             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
     393             :         TMH_pack_exchange_reply (hr));
     394           0 :     MHD_resume_connection (pc->connection);
     395           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     396           0 :     return;
     397             :   }
     398           0 :   po->w2h = TALER_EXCHANGE_withdraw2 (eh,
     399           0 :                                       &po->pd,
     400           0 :                                       &pc->reserve_priv,
     401             :                                       &withdraw_cb,
     402             :                                       po);
     403             : }
     404             : 
     405             : 
     406             : /**
     407             :  * Withdraw @a planchet from @a exchange_url for @a pc operation at planchet
     408             :  * @a offset.  Sets up the respective operation and adds it @a pc's operation
     409             :  * list. Once the operation is complete, the resulting blind signature is
     410             :  * committed to the merchant's database. If all planchet operations are
     411             :  * completed, the HTTP processing is resumed.
     412             :  *
     413             :  * @param[in,out] pc a pending pickup operation that includes @a planchet
     414             :  * @param exchange_url identifies an exchange to do the pickup from
     415             :  * @param planchet details about the coin to pick up
     416             :  * @param offset offset of @a planchet in the list, needed to process the reply
     417             :  */
     418             : static void
     419           0 : try_withdraw (struct PickupContext *pc,
     420             :               const char *exchange_url,
     421             :               const struct TALER_PlanchetDetail *planchet,
     422             :               unsigned int offset)
     423             : {
     424             :   struct PlanchetOperation *po;
     425             : 
     426           0 :   TMH_db->preflight (TMH_db->cls);
     427           0 :   po = GNUNET_new (struct PlanchetOperation);
     428           0 :   po->pc = pc;
     429           0 :   po->pd = *planchet;
     430           0 :   po->offset = offset;
     431           0 :   po->fo = TMH_EXCHANGES_find_exchange (exchange_url,
     432             :                                         NULL,
     433             :                                         GNUNET_NO,
     434             :                                         &do_withdraw,
     435             :                                         po);
     436           0 :   GNUNET_assert (NULL != po->fo);
     437           0 :   GNUNET_CONTAINER_DLL_insert (pc->po_head,
     438             :                                pc->po_tail,
     439             :                                po);
     440           0 : }
     441             : 
     442             : 
     443             : /**
     444             :  * Handle timeout for pickup.
     445             :  *
     446             :  * @param cls a `struct PickupContext *`
     447             :  */
     448             : static void
     449           0 : do_timeout (void *cls)
     450             : {
     451           0 :   struct PickupContext *pc = cls;
     452             : 
     453           0 :   pc->tt = NULL;
     454           0 :   stop_operations (pc);
     455           0 :   pc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
     456           0 :   pc->response =  TALER_MHD_make_error (
     457             :     TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
     458             :     NULL);
     459           0 :   GNUNET_CONTAINER_DLL_remove (pc_head,
     460             :                                pc_tail,
     461             :                                pc);
     462           0 :   MHD_resume_connection (pc->connection);
     463           0 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     464           0 : }
     465             : 
     466             : 
     467             : /**
     468             :  * Function called with the result of a #TMH_EXCHANGES_find_exchange()
     469             :  * operation as part of a withdraw objective.  Here, we initialize
     470             :  * the "total_requested" amount by adding up the cost of the planchets
     471             :  * provided by the client.
     472             :  *
     473             :  * @param cls closure, with our `struct PickupContext *`
     474             :  * @param hr HTTP response details
     475             :  * @param eh handle to the exchange context
     476             :  * @param payto_uri payto://-URI of the exchange
     477             :  * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
     478             :  * @param exchange_trusted true if this exchange is trusted by config
     479             :  */
     480             : static void
     481           0 : compute_total_requested (void *cls,
     482             :                          const struct TALER_EXCHANGE_HttpResponse *hr,
     483             :                          struct TALER_EXCHANGE_Handle *eh,
     484             :                          const char *payto_uri,
     485             :                          const struct TALER_Amount *wire_fee,
     486             :                          bool exchange_trusted)
     487             : {
     488           0 :   struct PickupContext *pc = cls;
     489             :   const struct TALER_EXCHANGE_Keys *keys;
     490             : 
     491           0 :   pc->fo = NULL;
     492           0 :   stop_operations (pc); /* stops timeout job */
     493           0 :   if (NULL == hr)
     494             :   {
     495           0 :     pc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
     496           0 :     pc->response = TALER_MHD_MAKE_JSON_PACK (
     497             :       TALER_JSON_pack_ec (
     498             :         TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT));
     499           0 :     MHD_resume_connection (pc->connection);
     500           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     501           0 :     return;
     502             :   }
     503           0 :   if (NULL == eh)
     504             :   {
     505           0 :     pc->http_status = MHD_HTTP_BAD_GATEWAY;
     506           0 :     pc->response =
     507           0 :       TALER_MHD_MAKE_JSON_PACK (
     508             :         TALER_JSON_pack_ec (
     509             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
     510             :         TMH_pack_exchange_reply (hr));
     511           0 :     MHD_resume_connection (pc->connection);
     512           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     513           0 :     return;
     514             :   }
     515           0 :   if (NULL == (keys = TALER_EXCHANGE_get_keys (eh)))
     516             :   {
     517           0 :     pc->http_status = MHD_HTTP_BAD_GATEWAY;
     518           0 :     pc->response =
     519           0 :       TALER_MHD_MAKE_JSON_PACK (
     520             :         TALER_JSON_pack_ec (
     521             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE),
     522             :         TMH_pack_exchange_reply (hr));
     523           0 :     MHD_resume_connection (pc->connection);
     524           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     525           0 :     return;
     526             :   }
     527           0 :   GNUNET_assert (GNUNET_OK ==
     528             :                  TALER_amount_set_zero (TMH_currency,
     529             :                                         &pc->total_requested));
     530           0 :   for (unsigned int i = 0; i<pc->planchets_length; i++)
     531             :   {
     532           0 :     struct TALER_PlanchetDetail *pd = &pc->planchets[i];
     533             :     const struct TALER_EXCHANGE_DenomPublicKey *dpk;
     534             : 
     535           0 :     dpk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
     536           0 :                                                        &pd->denom_pub_hash);
     537           0 :     if (NULL == dpk)
     538             :     {
     539           0 :       pc->http_status = MHD_HTTP_CONFLICT;
     540           0 :       pc->response =
     541           0 :         TALER_MHD_MAKE_JSON_PACK (
     542             :           TALER_JSON_pack_ec (
     543             :             TALER_EC_MERCHANT_TIP_PICKUP_DENOMINATION_UNKNOWN),
     544             :           TMH_pack_exchange_reply (hr));
     545           0 :       MHD_resume_connection (pc->connection);
     546           0 :       TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     547           0 :       return;
     548             :     }
     549             : 
     550           0 :     if ( (GNUNET_YES !=
     551           0 :           TALER_amount_cmp_currency (&pc->total_requested,
     552           0 :                                      &dpk->value)) ||
     553             :          (0 >
     554           0 :           TALER_amount_add (&pc->total_requested,
     555           0 :                             &pc->total_requested,
     556             :                             &dpk->value)) )
     557             :     {
     558           0 :       pc->http_status = MHD_HTTP_BAD_REQUEST;
     559           0 :       pc->response =
     560           0 :         TALER_MHD_make_error (TALER_EC_MERCHANT_TIP_PICKUP_SUMMATION_FAILED,
     561             :                               "Could not add up values to compute pickup total");
     562           0 :       MHD_resume_connection (pc->connection);
     563           0 :       TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     564           0 :       return;
     565             :     }
     566             :   }
     567           0 :   pc->tr_initialized = true;
     568           0 :   MHD_resume_connection (pc->connection);
     569           0 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     570             : }
     571             : 
     572             : 
     573             : /**
     574             :  * The tip lookup operation failed. Generate an error response based on the @a qs.
     575             :  *
     576             :  * @param connection connection to generate error for
     577             :  * @param qs DB status to base error creation on
     578             :  * @return MHD result code
     579             :  */
     580             : static MHD_RESULT
     581           0 : reply_lookup_tip_failed (struct MHD_Connection *connection,
     582             :                          enum GNUNET_DB_QueryStatus qs)
     583             : {
     584             :   unsigned int response_code;
     585             :   enum TALER_ErrorCode ec;
     586             : 
     587           0 :   TMH_db->rollback (TMH_db->cls);
     588           0 :   switch (qs)
     589             :   {
     590           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     591           0 :     ec = TALER_EC_MERCHANT_GENERIC_TIP_ID_UNKNOWN;
     592           0 :     response_code = MHD_HTTP_NOT_FOUND;
     593           0 :     break;
     594           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     595           0 :     ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
     596           0 :     response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     597           0 :     break;
     598           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     599           0 :     ec = TALER_EC_GENERIC_DB_COMMIT_FAILED;
     600           0 :     response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     601           0 :     break;
     602           0 :   default:
     603           0 :     GNUNET_break (0);
     604           0 :     ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     605           0 :     response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     606           0 :     break;
     607             :   }
     608           0 :   return TALER_MHD_reply_with_error (connection,
     609             :                                      response_code,
     610             :                                      ec,
     611             :                                      NULL);
     612             : }
     613             : 
     614             : 
     615             : MHD_RESULT
     616           0 : TMH_post_tips_ID_pickup (const struct TMH_RequestHandler *rh,
     617             :                          struct MHD_Connection *connection,
     618             :                          struct TMH_HandlerContext *hc)
     619             : {
     620           0 :   struct PickupContext *pc = hc->ctx;
     621             :   char *exchange_url;
     622             :   struct TALER_Amount total_authorized;
     623             :   struct TALER_Amount total_picked_up;
     624             :   struct TALER_Amount total_remaining;
     625             :   struct GNUNET_TIME_Timestamp expiration;
     626             :   enum GNUNET_DB_QueryStatus qs;
     627             :   unsigned int num_retries;
     628             : 
     629           0 :   if (NULL == pc)
     630             :   {
     631             :     json_t *planchets;
     632             :     json_t *planchet;
     633             :     size_t index;
     634             : 
     635           0 :     pc = GNUNET_new (struct PickupContext);
     636           0 :     hc->ctx = pc;
     637           0 :     hc->cc = &pick_context_cleanup;
     638             : 
     639           0 :     GNUNET_assert (NULL != hc->infix);
     640           0 :     if (GNUNET_OK !=
     641           0 :         GNUNET_CRYPTO_hash_from_string (hc->infix,
     642             :                                         &pc->tip_id.hash))
     643             :     {
     644             :       /* tip_id has wrong encoding */
     645           0 :       GNUNET_break_op (0);
     646           0 :       return TALER_MHD_reply_with_error (connection,
     647             :                                          MHD_HTTP_BAD_REQUEST,
     648             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     649           0 :                                          hc->infix);
     650             :     }
     651             : 
     652             :     {
     653             :       struct GNUNET_JSON_Specification spec[] = {
     654           0 :         GNUNET_JSON_spec_json ("planchets",
     655             :                                &planchets),
     656           0 :         GNUNET_JSON_spec_end ()
     657             :       };
     658             :       {
     659             :         enum GNUNET_GenericReturnValue res;
     660             : 
     661           0 :         res = TALER_MHD_parse_json_data (connection,
     662           0 :                                          hc->request_body,
     663             :                                          spec);
     664           0 :         if (GNUNET_OK != res)
     665             :           return (GNUNET_NO == res)
     666             :                  ? MHD_YES
     667           0 :                  : MHD_NO;
     668             :       }
     669             :     }
     670           0 :     if (! json_is_array (planchets))
     671             :     {
     672           0 :       GNUNET_break_op (0);
     673           0 :       json_decref (planchets);
     674           0 :       return TALER_MHD_reply_with_error (connection,
     675             :                                          MHD_HTTP_BAD_REQUEST,
     676             :                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
     677             :                                          "planchets");
     678             :     }
     679             : 
     680           0 :     GNUNET_array_grow (pc->planchets,
     681             :                        pc->planchets_length,
     682             :                        json_array_size (planchets));
     683           0 :     json_array_foreach (planchets, index, planchet) {
     684           0 :       struct TALER_PlanchetDetail *pd = &pc->planchets[index];
     685             :       struct GNUNET_JSON_Specification spec[] = {
     686           0 :         GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
     687             :                                      &pd->denom_pub_hash),
     688           0 :         TALER_JSON_spec_blinded_planchet ("coin_ev",
     689             :                                           &pd->blinded_planchet),
     690           0 :         GNUNET_JSON_spec_end ()
     691             :       };
     692             :       {
     693             :         enum GNUNET_GenericReturnValue res;
     694             : 
     695           0 :         res = TALER_MHD_parse_json_data (connection,
     696             :                                          planchet,
     697             :                                          spec);
     698           0 :         if (GNUNET_OK != res)
     699             :         {
     700           0 :           json_decref (planchets);
     701             :           return (GNUNET_NO == res)
     702             :                  ? MHD_YES
     703           0 :                  : MHD_NO;
     704             :         }
     705             :       }
     706             :     }
     707           0 :     json_decref (planchets);
     708             :     {
     709             :       struct GNUNET_HashContext *hc;
     710             : 
     711           0 :       hc = GNUNET_CRYPTO_hash_context_start ();
     712           0 :       GNUNET_CRYPTO_hash_context_read (hc,
     713           0 :                                        &pc->tip_id,
     714             :                                        sizeof (pc->tip_id));
     715           0 :       for (unsigned int i = 0; i<pc->planchets_length; i++)
     716             :       {
     717           0 :         struct TALER_PlanchetDetail *pd = &pc->planchets[i];
     718             : 
     719           0 :         GNUNET_CRYPTO_hash_context_read (hc,
     720           0 :                                          &pd->denom_pub_hash,
     721             :                                          sizeof (pd->denom_pub_hash));
     722           0 :         TALER_blinded_planchet_hash_ (&pd->blinded_planchet,
     723             :                                       hc);
     724             :       }
     725           0 :       GNUNET_CRYPTO_hash_context_finish (hc,
     726             :                                          &pc->pickup_id.hash);
     727             :     }
     728             :   }
     729             : 
     730           0 :   if (NULL != pc->response)
     731             :   {
     732             :     MHD_RESULT ret;
     733             : 
     734           0 :     ret = MHD_queue_response (connection,
     735             :                               pc->http_status,
     736             :                               pc->response);
     737           0 :     pc->response = NULL;
     738           0 :     return ret;
     739             :   }
     740             : 
     741           0 :   if (! pc->tr_initialized)
     742             :   {
     743           0 :     qs = TMH_db->lookup_tip (TMH_db->cls,
     744           0 :                              hc->instance->settings.id,
     745           0 :                              &pc->tip_id,
     746             :                              &total_authorized,
     747             :                              &total_picked_up,
     748             :                              &expiration,
     749             :                              &exchange_url,
     750             :                              &pc->reserve_priv);
     751           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     752           0 :       return reply_lookup_tip_failed (connection,
     753             :                                       qs);
     754           0 :     MHD_suspend_connection (connection);
     755           0 :     pc->connection = connection;
     756           0 :     pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT,
     757             :                                            &do_timeout,
     758             :                                            pc);
     759           0 :     pc->fo = TMH_EXCHANGES_find_exchange (exchange_url,
     760             :                                           NULL,
     761             :                                           GNUNET_NO,
     762             :                                           &compute_total_requested,
     763             :                                           pc);
     764           0 :     GNUNET_free (exchange_url);
     765           0 :     return MHD_YES;
     766             :   }
     767             : 
     768             : 
     769           0 :   TMH_db->preflight (TMH_db->cls);
     770           0 :   num_retries = 0;
     771           0 : RETRY:
     772           0 :   num_retries++;
     773           0 :   if (num_retries > MAX_RETRIES)
     774             :   {
     775           0 :     GNUNET_break (0);
     776           0 :     return TALER_MHD_reply_with_error (connection,
     777             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     778             :                                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
     779             :                                        NULL);
     780             :   }
     781           0 :   if (GNUNET_OK !=
     782           0 :       TMH_db->start (TMH_db->cls,
     783             :                      "pickup tip"))
     784             :   {
     785           0 :     GNUNET_break (0);
     786           0 :     return TALER_MHD_reply_with_error (connection,
     787             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     788             :                                        TALER_EC_GENERIC_DB_START_FAILED,
     789             :                                        NULL);
     790             :   }
     791           0 :   {
     792           0 :     struct TALER_BlindedDenominationSignature sigs[
     793           0 :       GNUNET_NZL (pc->planchets_length)];
     794             : 
     795           0 :     memset (sigs,
     796             :             0,
     797             :             sizeof (sigs));
     798           0 :     qs = TMH_db->lookup_pickup (TMH_db->cls,
     799           0 :                                 hc->instance->settings.id,
     800           0 :                                 &pc->tip_id,
     801           0 :                                 &pc->pickup_id,
     802             :                                 &exchange_url,
     803             :                                 &pc->reserve_priv,
     804             :                                 pc->planchets_length,
     805             :                                 sigs);
     806           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     807             :                 "Lookup pickup `%s' resulted in %d\n",
     808             :                 GNUNET_h2s (&pc->pickup_id.hash),
     809             :                 qs);
     810           0 :     if (qs > GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)
     811           0 :       qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     812           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     813             :     {
     814           0 :       bool rollback = false;
     815             : 
     816           0 :       for (unsigned int i = 0; i< pc->planchets_length; i++)
     817             :       {
     818           0 :         if (TALER_DENOMINATION_INVALID != sigs[i].cipher)
     819           0 :           continue;
     820           0 :         if (! rollback)
     821             :         {
     822           0 :           TMH_db->rollback (TMH_db->cls);
     823           0 :           MHD_suspend_connection (connection);
     824           0 :           GNUNET_CONTAINER_DLL_insert (pc_head,
     825             :                                        pc_tail,
     826             :                                        pc);
     827           0 :           pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT,
     828             :                                                  &do_timeout,
     829             :                                                  pc);
     830           0 :           rollback = true;
     831             :         }
     832           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     833             :                     "Lookup pickup `%s' initiated withdraw #%u\n",
     834             :                     GNUNET_h2s (&pc->pickup_id.hash),
     835             :                     i);
     836           0 :         try_withdraw (pc,
     837             :                       exchange_url,
     838           0 :                       &pc->planchets[i],
     839             :                       i);
     840             :       }
     841           0 :       GNUNET_free (exchange_url);
     842           0 :       if (rollback)
     843           0 :         return MHD_YES;
     844             :       /* we got _all_ signatures, can continue! */
     845             :     }
     846           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
     847             :     {
     848             :       unsigned int response_code;
     849             :       enum TALER_ErrorCode ec;
     850             : 
     851           0 :       TMH_db->rollback (TMH_db->cls);
     852           0 :       switch (qs)
     853             :       {
     854           0 :       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     855             :         {
     856             :           json_t *blind_sigs;
     857             : 
     858           0 :           blind_sigs = json_array ();
     859           0 :           GNUNET_assert (NULL != blind_sigs);
     860           0 :           for (unsigned int i = 0; i<pc->planchets_length; i++)
     861             :           {
     862           0 :             GNUNET_assert (0 ==
     863             :                            json_array_append_new (
     864             :                              blind_sigs,
     865             :                              GNUNET_JSON_PACK (
     866             :                                TALER_JSON_pack_blinded_denom_sig ("blind_sig",
     867             :                                                                   &sigs[i]))));
     868           0 :             TALER_blinded_denom_sig_free (&sigs[i]);
     869             :           }
     870           0 :           return TALER_MHD_REPLY_JSON_PACK (
     871             :             connection,
     872             :             MHD_HTTP_OK,
     873             :             GNUNET_JSON_pack_array_steal ("blind_sigs",
     874             :                                           blind_sigs));
     875             :         }
     876             :         break;
     877           0 :       case GNUNET_DB_STATUS_SOFT_ERROR:
     878           0 :         goto RETRY;
     879           0 :       case GNUNET_DB_STATUS_HARD_ERROR:
     880           0 :         ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
     881           0 :         response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     882           0 :         break;
     883           0 :       default:
     884           0 :         GNUNET_break (0);
     885           0 :         ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     886           0 :         response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     887           0 :         break;
     888             :       }
     889           0 :       return TALER_MHD_reply_with_error (connection,
     890             :                                          response_code,
     891             :                                          ec,
     892             :                                          NULL);
     893             :     }
     894             :   }
     895             : 
     896           0 :   qs = TMH_db->lookup_tip (TMH_db->cls,
     897           0 :                            hc->instance->settings.id,
     898           0 :                            &pc->tip_id,
     899             :                            &total_authorized,
     900             :                            &total_picked_up,
     901             :                            &expiration,
     902             :                            &exchange_url,
     903             :                            &pc->reserve_priv);
     904           0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     905             :   {
     906           0 :     TMH_db->rollback (TMH_db->cls);
     907           0 :     goto RETRY;
     908             :   }
     909           0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     910             :   {
     911           0 :     TMH_db->rollback (TMH_db->cls);
     912           0 :     return reply_lookup_tip_failed (connection,
     913             :                                     qs);
     914             :   }
     915           0 :   if (GNUNET_TIME_absolute_is_past (expiration.abs_time))
     916             :   {
     917           0 :     GNUNET_free (exchange_url);
     918           0 :     TMH_db->rollback (TMH_db->cls);
     919           0 :     return TALER_MHD_reply_with_error (connection,
     920             :                                        MHD_HTTP_GONE,
     921             :                                        TALER_EC_MERCHANT_TIP_PICKUP_HAS_EXPIRED,
     922           0 :                                        hc->infix);
     923             :   }
     924           0 :   if (0 >
     925           0 :       TALER_amount_subtract (&total_remaining,
     926             :                              &total_authorized,
     927             :                              &total_picked_up))
     928             :   {
     929           0 :     GNUNET_free (exchange_url);
     930           0 :     GNUNET_break_op (0);
     931           0 :     TMH_db->rollback (TMH_db->cls);
     932           0 :     return TALER_MHD_reply_with_error (connection,
     933             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     934             :                                        TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     935             :                                        "picked up amount exceeds authorized amount");
     936             :   }
     937             : 
     938           0 :   if (0 >
     939           0 :       TALER_amount_cmp (&total_remaining,
     940           0 :                         &pc->total_requested))
     941             :   {
     942             :     /* total_remaining < pc->total_requested */
     943           0 :     GNUNET_free (exchange_url);
     944           0 :     GNUNET_break_op (0);
     945           0 :     TMH_db->rollback (TMH_db->cls);
     946           0 :     return TALER_MHD_reply_with_error (connection,
     947             :                                        MHD_HTTP_BAD_REQUEST,
     948             :                                        TALER_EC_MERCHANT_TIP_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING,
     949           0 :                                        hc->infix);
     950             :   }
     951             : 
     952           0 :   GNUNET_assert (0 <
     953             :                  TALER_amount_add (&total_picked_up,
     954             :                                    &total_picked_up,
     955             :                                    &pc->total_requested));
     956           0 :   qs = TMH_db->insert_pickup (TMH_db->cls,
     957           0 :                               hc->instance->settings.id,
     958           0 :                               &pc->tip_id,
     959             :                               &total_picked_up,
     960           0 :                               &pc->pickup_id,
     961           0 :                               &pc->total_requested);
     962           0 :   if (qs < 0)
     963             :   {
     964           0 :     TMH_db->rollback (TMH_db->cls);
     965           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     966           0 :       goto RETRY;
     967           0 :     GNUNET_free (exchange_url);
     968           0 :     return TALER_MHD_reply_with_error (connection,
     969             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     970             :                                        TALER_EC_GENERIC_DB_STORE_FAILED,
     971             :                                        "pickup");
     972             :   }
     973           0 :   qs = TMH_db->commit (TMH_db->cls);
     974           0 :   if (qs < 0)
     975             :   {
     976           0 :     TMH_db->rollback (TMH_db->cls);
     977           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     978           0 :       goto RETRY;
     979           0 :     GNUNET_free (exchange_url);
     980           0 :     return TALER_MHD_reply_with_error (connection,
     981             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     982             :                                        TALER_EC_GENERIC_DB_COMMIT_FAILED,
     983             :                                        NULL);
     984             :   }
     985           0 :   MHD_suspend_connection (connection);
     986           0 :   GNUNET_CONTAINER_DLL_insert (pc_head,
     987             :                                pc_tail,
     988             :                                pc);
     989           0 :   pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT,
     990             :                                          &do_timeout,
     991             :                                          pc);
     992           0 :   for (unsigned int i = 0; i<pc->planchets_length; i++)
     993             :   {
     994           0 :     try_withdraw (pc,
     995             :                   exchange_url,
     996           0 :                   &pc->planchets[i],
     997             :                   i);
     998             :   }
     999           0 :   GNUNET_free (exchange_url);
    1000           0 :   return MHD_YES;
    1001             : }

Generated by: LCOV version 1.14