exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-exchange-httpd_get-reserves-RESERVE_PUB.c (8139B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-exchange-httpd_get-reserves-RESERVE_PUB.c
     18  * @brief Handle /reserves/$RESERVE_PUB GET requests
     19  * @author Florian Dold
     20  * @author Benedikt Mueller
     21  * @author Christian Grothoff
     22  */
     23 #include "platform.h"  /* UNNECESSARY? */
     24 #include <gnunet/gnunet_util_lib.h>
     25 #include <jansson.h>
     26 #include "taler/taler_mhd_lib.h"
     27 #include "taler/taler_json_lib.h"
     28 #include "taler/taler_dbevents.h"  /* UNNECESSARY? */
     29 #include "taler-exchange-httpd_get-keys.h"
     30 #include "taler-exchange-httpd_get-reserves-RESERVE_PUB.h"
     31 #include "taler-exchange-httpd_responses.h"
     32 #include "exchange-database/get_reserve_balance.h"
     33 #include "exchange-database/event_listen.h"
     34 #include "exchange-database/event_listen_cancel.h"
     35 
     36 
     37 /**
     38  * Reserve GET request that is long-polling.
     39  */
     40 struct ReservePoller
     41 {
     42   /**
     43    * Kept in a DLL.
     44    */
     45   struct ReservePoller *next;
     46 
     47   /**
     48    * Kept in a DLL.
     49    */
     50   struct ReservePoller *prev;
     51 
     52   /**
     53    * Connection we are handling.
     54    */
     55   struct MHD_Connection *connection;
     56 
     57   /**
     58    * Our request context.
     59    */
     60   struct TEH_RequestContext *rc;
     61 
     62   /**
     63    * Subscription for the database event we are waiting for.
     64    */
     65   struct GNUNET_DB_EventHandler *eh;
     66 
     67   /**
     68    * When will this request time out?
     69    */
     70   struct GNUNET_TIME_Absolute timeout;
     71 
     72   /**
     73    * Public key of the reserve the inquiry is about.
     74    */
     75   struct TALER_ReservePublicKeyP reserve_pub;
     76 
     77   /**
     78    * Balance of the reserve, set in the callback.
     79    */
     80   struct TALER_Amount balance;
     81 
     82   /**
     83    * Last origin account of the reserve, NULL if only
     84    * P2P payments were made.
     85    */
     86   struct TALER_FullPayto origin_account;
     87 
     88   /**
     89    * True if we are still suspended.
     90    */
     91   bool suspended;
     92 
     93 };
     94 
     95 
     96 /**
     97  * Head of list of requests in long polling.
     98  */
     99 static struct ReservePoller *rp_head;
    100 
    101 /**
    102  * Tail of list of requests in long polling.
    103  */
    104 static struct ReservePoller *rp_tail;
    105 
    106 
    107 void
    108 TEH_reserves_get_cleanup ()
    109 {
    110   for (struct ReservePoller *rp = rp_head;
    111        NULL != rp;
    112        rp = rp->next)
    113   {
    114     if (rp->suspended)
    115     {
    116       rp->suspended = false;
    117       MHD_resume_connection (rp->connection);
    118     }
    119   }
    120 }
    121 
    122 
    123 /**
    124  * Function called once a connection is done to
    125  * clean up the `struct ReservePoller` state.
    126  *
    127  * @param rc context to clean up for
    128  */
    129 static void
    130 rp_cleanup (struct TEH_RequestContext *rc)
    131 {
    132   struct ReservePoller *rp = rc->rh_ctx;
    133 
    134   GNUNET_assert (! rp->suspended);
    135   if (NULL != rp->eh)
    136   {
    137     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    138                 "Cancelling DB event listening on cleanup (odd unless during shutdown)\n");
    139     TALER_EXCHANGEDB_event_listen_cancel (TEH_pg,
    140                                           rp->eh);
    141     rp->eh = NULL;
    142   }
    143   GNUNET_CONTAINER_DLL_remove (rp_head,
    144                                rp_tail,
    145                                rp);
    146   GNUNET_free (rp->origin_account.full_payto);
    147   GNUNET_free (rp);
    148 }
    149 
    150 
    151 /**
    152  * Function called on events received from Postgres.
    153  * Wakes up long pollers.
    154  *
    155  * @param cls the `struct TEH_RequestContext *`
    156  * @param extra additional event data provided
    157  * @param extra_size number of bytes in @a extra
    158  */
    159 static void
    160 db_event_cb (void *cls,
    161              const void *extra,
    162              size_t extra_size)
    163 {
    164   struct ReservePoller *rp = cls;
    165   struct GNUNET_AsyncScopeSave old_scope;
    166 
    167   (void) extra;
    168   (void) extra_size;
    169   if (! rp->suspended)
    170     return; /* might get multiple wake-up events */
    171   GNUNET_async_scope_enter (&rp->rc->async_scope_id,
    172                             &old_scope);
    173   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    174               "Resuming from long-polling on KYC status\n");
    175   TEH_check_invariants ();
    176   rp->suspended = false;
    177   MHD_resume_connection (rp->connection);
    178   TALER_MHD_daemon_trigger ();
    179   TEH_check_invariants ();
    180   GNUNET_async_scope_restore (&old_scope);
    181 }
    182 
    183 
    184 enum MHD_Result
    185 TEH_handler_reserves_get (
    186   struct TEH_RequestContext *rc,
    187   const struct TALER_ReservePublicKeyP *reserve_pub)
    188 {
    189   struct ReservePoller *rp = rc->rh_ctx;
    190 
    191   if (NULL == rp)
    192   {
    193     rp = GNUNET_new (struct ReservePoller);
    194     rp->connection = rc->connection;
    195     rp->rc = rc;
    196     rc->rh_ctx = rp;
    197     rc->rh_cleaner = &rp_cleanup;
    198     GNUNET_CONTAINER_DLL_insert (rp_head,
    199                                  rp_tail,
    200                                  rp);
    201     rp->reserve_pub = *reserve_pub;
    202     TALER_MHD_parse_request_timeout (rc->connection,
    203                                      &rp->timeout);
    204   }
    205 
    206   if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
    207        (NULL == rp->eh) )
    208   {
    209     struct TALER_EXCHANGEDB_ReserveEventP rep = {
    210       .header.size = htons (sizeof (rep)),
    211       .header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
    212       .reserve_pub = rp->reserve_pub
    213     };
    214 
    215     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    216                 "Starting DB event listening until %s\n",
    217                 GNUNET_TIME_absolute2s (rp->timeout));
    218     rp->eh = TALER_EXCHANGEDB_event_listen (
    219       TEH_pg,
    220       GNUNET_TIME_absolute_get_remaining (rp->timeout),
    221       &rep.header,
    222       &db_event_cb,
    223       rp);
    224   }
    225   {
    226     enum GNUNET_DB_QueryStatus qs;
    227 
    228     GNUNET_free (rp->origin_account.full_payto);
    229     qs = TALER_EXCHANGEDB_get_reserve_balance (TEH_pg,
    230                                                &rp->reserve_pub,
    231                                                &rp->balance,
    232                                                &rp->origin_account);
    233     switch (qs)
    234     {
    235     case GNUNET_DB_STATUS_SOFT_ERROR:
    236       GNUNET_break (0); /* single-shot query should never have soft-errors */
    237       return TALER_MHD_reply_with_error (rc->connection,
    238                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    239                                          TALER_EC_GENERIC_DB_SOFT_FAILURE,
    240                                          "get_reserve_balance");
    241     case GNUNET_DB_STATUS_HARD_ERROR:
    242       GNUNET_break (0);
    243       return TALER_MHD_reply_with_error (rc->connection,
    244                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    245                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    246                                          "get_reserve_balance");
    247     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    248       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    249                   "Got reserve balance of %s\n",
    250                   TALER_amount2s (&rp->balance));
    251       return TALER_MHD_REPLY_JSON_PACK (
    252         rc->connection,
    253         MHD_HTTP_OK,
    254         GNUNET_JSON_pack_allow_null (
    255           GNUNET_JSON_pack_string (
    256             "last_origin",
    257             rp->origin_account.full_payto)),
    258         TALER_JSON_pack_amount ("balance",
    259                                 &rp->balance));
    260     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    261       if (! GNUNET_TIME_absolute_is_future (rp->timeout))
    262       {
    263         return TALER_MHD_reply_with_error (rc->connection,
    264                                            MHD_HTTP_NOT_FOUND,
    265                                            TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
    266                                            NULL);
    267       }
    268       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    269                   "Long-polling on reserve for %s\n",
    270                   GNUNET_STRINGS_relative_time_to_string (
    271                     GNUNET_TIME_absolute_get_remaining (rp->timeout),
    272                     true));
    273       rp->suspended = true;
    274       MHD_suspend_connection (rc->connection);
    275       return MHD_YES;
    276     }
    277   }
    278   GNUNET_break (0);
    279   return MHD_NO;
    280 }
    281 
    282 
    283 /* end of taler-exchange-httpd_reserves_get.c */