LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-reserves.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 3 115 2.6 %
Date: 2022-06-30 06:15:34 Functions: 1 5 20.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2021, 2022 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-reserves.c
      22             :  * @brief implementing POST /reserves request handling
      23             :  * @author Christian Grothoff
      24             :  */
      25             : #include "platform.h"
      26             : #include "taler-merchant-httpd_exchanges.h"
      27             : #include "taler-merchant-httpd_private-post-reserves.h"
      28             : #include "taler-merchant-httpd_reserves.h"
      29             : #include <taler/taler_json_lib.h>
      30             : 
      31             : 
      32             : /**
      33             :  * How long to wait before giving up processing with the exchange?
      34             :  */
      35             : #define EXCHANGE_GENERIC_TIMEOUT (GNUNET_TIME_relative_multiply ( \
      36             :                                     GNUNET_TIME_UNIT_SECONDS, \
      37             :                                     15))
      38             : 
      39             : 
      40             : /**
      41             :  * Information we keep for an individual call to the POST /reserves handler.
      42             :  */
      43             : struct PostReserveContext
      44             : {
      45             : 
      46             :   /**
      47             :    * Stored in a DLL.
      48             :    */
      49             :   struct PostReserveContext *next;
      50             : 
      51             :   /**
      52             :    * Stored in a DLL.
      53             :    */
      54             :   struct PostReserveContext *prev;
      55             : 
      56             :   /**
      57             :    * Array with @e coins_cnt coins we are despositing.
      58             :    */
      59             :   struct DepositConfirmation *dc;
      60             : 
      61             :   /**
      62             :    * MHD connection to return to
      63             :    */
      64             :   struct MHD_Connection *connection;
      65             : 
      66             :   /**
      67             :    * Details about the client's request.
      68             :    */
      69             :   struct TMH_HandlerContext *hc;
      70             : 
      71             :   /**
      72             :    * URL of the exchange.
      73             :    */
      74             :   const char *exchange_url;
      75             : 
      76             :   /**
      77             :    * URI of the exchange where the payment needs to be made to.
      78             :    */
      79             :   char *payto_uri;
      80             : 
      81             :   /**
      82             :    * Handle for contacting the exchange.
      83             :    */
      84             :   struct TMH_EXCHANGES_FindOperation *fo;
      85             : 
      86             :   /**
      87             :    * Task run on timeout.
      88             :    */
      89             :   struct GNUNET_SCHEDULER_Task *timeout_task;
      90             : 
      91             :   /**
      92             :    * Initial balance of the reserve.
      93             :    */
      94             :   struct TALER_Amount initial_balance;
      95             : 
      96             :   /**
      97             :    * When will the reserve expire.
      98             :    */
      99             :   struct GNUNET_TIME_Timestamp reserve_expiration;
     100             : 
     101             :   /**
     102             :    * Which HTTP status should we return?
     103             :    */
     104             :   unsigned int http_status;
     105             : 
     106             :   /**
     107             :    * Which error code should we return?
     108             :    */
     109             :   enum TALER_ErrorCode ec;
     110             : 
     111             :   /**
     112             :    * Did we suspend @a connection and are thus in
     113             :    * the #rc_head DLL (#GNUNET_YES). Set to
     114             :    * #GNUNET_NO if we are not suspended, and to
     115             :    * #GNUNET_SYSERR if we should close the connection
     116             :    * without a response due to shutdown.
     117             :    */
     118             :   enum GNUNET_GenericReturnValue suspended;
     119             : };
     120             : 
     121             : 
     122             : /**
     123             :  * Stored in a DLL.
     124             :  */
     125             : static struct PostReserveContext *rc_head;
     126             : 
     127             : /**
     128             :  * Stored in a DLL.
     129             :  */
     130             : static struct PostReserveContext *rc_tail;
     131             : 
     132             : 
     133             : /**
     134             :  * Force all post reserve contexts to be resumed as we are about
     135             :  * to shut down MHD.
     136             :  */
     137             : void
     138           3 : TMH_force_rc_resume ()
     139             : {
     140             :   struct PostReserveContext *rcn;
     141             : 
     142           3 :   for (struct PostReserveContext *rc = rc_head;
     143             :        NULL != rc;
     144           0 :        rc = rcn)
     145             :   {
     146           0 :     rcn = rc->next;
     147           0 :     if (NULL != rc->timeout_task)
     148             :     {
     149           0 :       GNUNET_SCHEDULER_cancel (rc->timeout_task);
     150           0 :       rc->timeout_task = NULL;
     151             :     }
     152           0 :     if (GNUNET_YES == rc->suspended)
     153             :     {
     154           0 :       rc->suspended = GNUNET_SYSERR;
     155           0 :       MHD_resume_connection (rc->connection);
     156           0 :       GNUNET_CONTAINER_DLL_remove (rc_head,
     157             :                                    rc_tail,
     158             :                                    rc);
     159             :     }
     160           0 :     if (NULL != rc->fo)
     161             :     {
     162           0 :       TMH_EXCHANGES_find_exchange_cancel (rc->fo);
     163           0 :       rc->fo = NULL;
     164             :     }
     165             :   }
     166           3 : }
     167             : 
     168             : 
     169             : /**
     170             :  * Custom cleanup routine for a `struct PostReserveContext`.
     171             :  *
     172             :  * @param cls the `struct PostReserveContext` to clean up.
     173             :  */
     174             : static void
     175           0 : reserve_context_cleanup (void *cls)
     176             : {
     177           0 :   struct PostReserveContext *rc = cls;
     178             : 
     179           0 :   if (NULL != rc->fo)
     180             :   {
     181           0 :     TMH_EXCHANGES_find_exchange_cancel (rc->fo);
     182           0 :     rc->fo = NULL;
     183             :   }
     184           0 :   if (NULL != rc->timeout_task)
     185             :   {
     186           0 :     GNUNET_SCHEDULER_cancel (rc->timeout_task);
     187           0 :     rc->timeout_task = NULL;
     188             :   }
     189           0 :   GNUNET_assert (GNUNET_YES != rc->suspended);
     190           0 :   GNUNET_free (rc->payto_uri);
     191           0 :   GNUNET_free (rc);
     192           0 : }
     193             : 
     194             : 
     195             : /**
     196             :  * Function called with the result of a #TMH_EXCHANGES_find_exchange()
     197             :  * operation.
     198             :  *
     199             :  * @param cls closure with our `struct PostReserveContext *`
     200             :  * @param hr HTTP response details
     201             :  * @param payto_uri URI of the exchange for the wire transfer, NULL on errors
     202             :  * @param eh handle to the exchange context
     203             :  * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
     204             :  * @param exchange_trusted true if this exchange is trusted by config
     205             :  */
     206             : static void
     207           0 : handle_exchange (void *cls,
     208             :                  const struct TALER_EXCHANGE_HttpResponse *hr,
     209             :                  struct TALER_EXCHANGE_Handle *eh,
     210             :                  const char *payto_uri,
     211             :                  const struct TALER_Amount *wire_fee,
     212             :                  bool exchange_trusted)
     213             : {
     214           0 :   struct PostReserveContext *rc = cls;
     215             :   const struct TALER_EXCHANGE_Keys *keys;
     216             : 
     217           0 :   rc->fo = NULL;
     218           0 :   if (NULL != rc->timeout_task)
     219             :   {
     220           0 :     GNUNET_SCHEDULER_cancel (rc->timeout_task);
     221           0 :     rc->timeout_task = NULL;
     222             :   }
     223           0 :   rc->suspended = GNUNET_NO;
     224           0 :   MHD_resume_connection (rc->connection);
     225           0 :   GNUNET_CONTAINER_DLL_remove (rc_head,
     226             :                                rc_tail,
     227             :                                rc);
     228           0 :   if (NULL == hr)
     229             :   {
     230           0 :     rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT;
     231           0 :     rc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
     232           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     233           0 :     return;
     234             :   }
     235           0 :   if (NULL == eh)
     236             :   {
     237           0 :     rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE;
     238           0 :     rc->http_status = MHD_HTTP_BAD_GATEWAY;
     239           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     240           0 :     return;
     241             :   }
     242           0 :   keys = TALER_EXCHANGE_get_keys (eh);
     243           0 :   if (NULL == keys)
     244             :   {
     245           0 :     rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE;
     246           0 :     rc->http_status = MHD_HTTP_BAD_GATEWAY;
     247           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     248           0 :     return;
     249             :   }
     250           0 :   if (NULL == payto_uri)
     251             :   {
     252           0 :     rc->ec = TALER_EC_MERCHANT_PRIVATE_POST_RESERVES_UNSUPPORTED_WIRE_METHOD;
     253           0 :     rc->http_status = MHD_HTTP_CONFLICT;
     254           0 :     TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     255           0 :     return;
     256             :   }
     257             :   rc->reserve_expiration
     258           0 :     = GNUNET_TIME_relative_to_timestamp (keys->reserve_closing_delay);
     259           0 :   rc->payto_uri = GNUNET_strdup (payto_uri);
     260           0 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     261             : }
     262             : 
     263             : 
     264             : /**
     265             :  * Handle a timeout for the processing of the wire request.
     266             :  *
     267             :  * @param cls closure
     268             :  */
     269             : static void
     270           0 : handle_exchange_timeout (void *cls)
     271             : {
     272           0 :   struct PostReserveContext *rc = cls;
     273             : 
     274           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     275             :               "Resuming POST /private/reserves with error after timeout\n");
     276           0 :   rc->timeout_task = NULL;
     277           0 :   if (NULL != rc->fo)
     278             :   {
     279           0 :     TMH_EXCHANGES_find_exchange_cancel (rc->fo);
     280           0 :     rc->fo = NULL;
     281             :   }
     282           0 :   rc->suspended = GNUNET_NO;
     283           0 :   MHD_resume_connection (rc->connection);
     284           0 :   GNUNET_CONTAINER_DLL_remove (rc_head,
     285             :                                rc_tail,
     286             :                                rc);
     287           0 :   rc->ec = TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT;
     288           0 :   rc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
     289           0 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     290           0 : }
     291             : 
     292             : 
     293             : MHD_RESULT
     294           0 : TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
     295             :                            struct MHD_Connection *connection,
     296             :                            struct TMH_HandlerContext *hc)
     297             : {
     298           0 :   struct PostReserveContext *rc = hc->ctx;
     299           0 :   struct TMH_MerchantInstance *mi = hc->instance;
     300             : 
     301           0 :   GNUNET_assert (NULL != mi);
     302           0 :   if (NULL == rc)
     303             :   {
     304             :     const char *wire_method;
     305             : 
     306           0 :     rc = GNUNET_new (struct PostReserveContext);
     307           0 :     rc->connection = connection;
     308           0 :     rc->hc = hc;
     309           0 :     hc->ctx = rc;
     310           0 :     hc->cc = &reserve_context_cleanup;
     311             : 
     312             :     {
     313             :       enum GNUNET_GenericReturnValue res;
     314             :       struct GNUNET_JSON_Specification spec[] = {
     315           0 :         GNUNET_JSON_spec_string ("exchange_url",
     316             :                                  &rc->exchange_url),
     317           0 :         GNUNET_JSON_spec_string ("wire_method",
     318             :                                  &wire_method),
     319           0 :         TALER_JSON_spec_amount ("initial_balance",
     320             :                                 TMH_currency,
     321             :                                 &rc->initial_balance),
     322           0 :         GNUNET_JSON_spec_end ()
     323             :       };
     324           0 :       res = TALER_MHD_parse_json_data (connection,
     325           0 :                                        hc->request_body,
     326             :                                        spec);
     327           0 :       if (GNUNET_OK != res)
     328             :         return (GNUNET_NO == res)
     329             :                ? MHD_YES
     330           0 :                : MHD_NO;
     331             :     }
     332           0 :     rc->fo = TMH_EXCHANGES_find_exchange (rc->exchange_url,
     333             :                                           wire_method,
     334             :                                           GNUNET_NO,
     335             :                                           &handle_exchange,
     336             :                                           rc);
     337             :     rc->timeout_task
     338           0 :       = GNUNET_SCHEDULER_add_delayed (EXCHANGE_GENERIC_TIMEOUT,
     339             :                                       &handle_exchange_timeout,
     340             :                                       rc);
     341           0 :     rc->suspended = GNUNET_YES;
     342           0 :     GNUNET_CONTAINER_DLL_insert (rc_head,
     343             :                                  rc_tail,
     344             :                                  rc);
     345           0 :     MHD_suspend_connection (connection);
     346           0 :     return MHD_YES;
     347             :   }
     348           0 :   if (GNUNET_SYSERR == rc->suspended)
     349           0 :     return MHD_NO; /* we are in shutdown */
     350             : 
     351           0 :   GNUNET_assert (GNUNET_NO == rc->suspended);
     352           0 :   if (NULL == rc->payto_uri)
     353             :   {
     354           0 :     return TALER_MHD_reply_with_error (connection,
     355             :                                        rc->http_status,
     356             :                                        rc->ec,
     357             :                                        NULL);
     358             :   }
     359             :   {
     360             :     struct TALER_ReservePublicKeyP reserve_pub;
     361             :     struct TALER_ReservePrivateKeyP reserve_priv;
     362             :     enum GNUNET_DB_QueryStatus qs;
     363             : 
     364           0 :     GNUNET_CRYPTO_eddsa_key_create (&reserve_priv.eddsa_priv);
     365           0 :     GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv,
     366             :                                         &reserve_pub.eddsa_pub);
     367           0 :     qs = TMH_db->insert_reserve (TMH_db->cls,
     368           0 :                                  mi->settings.id,
     369             :                                  &reserve_priv,
     370             :                                  &reserve_pub,
     371             :                                  rc->exchange_url,
     372           0 :                                  rc->payto_uri,
     373           0 :                                  &rc->initial_balance,
     374             :                                  rc->reserve_expiration);
     375           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     376           0 :     TMH_RESERVES_check (mi->settings.id,
     377             :                         rc->exchange_url,
     378             :                         &reserve_pub,
     379           0 :                         &rc->initial_balance);
     380           0 :     if (qs < 0)
     381           0 :       return TALER_MHD_reply_with_error (connection,
     382             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     383             :                                          TALER_EC_GENERIC_DB_STORE_FAILED,
     384             :                                          "reserve");
     385           0 :     return TALER_MHD_REPLY_JSON_PACK (
     386             :       connection,
     387             :       MHD_HTTP_OK,
     388             :       GNUNET_JSON_pack_data_auto ("reserve_pub",
     389             :                                   &reserve_pub),
     390             :       GNUNET_JSON_pack_string ("payto_uri",
     391             :                                rc->payto_uri));
     392             :   }
     393             : }
     394             : 
     395             : 
     396             : /* end of taler-merchant-httpd_private-post-reserves.c */

Generated by: LCOV version 1.14