LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_wire.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 86 119 72.3 %
Date: 2021-08-30 06:43:37 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2015-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 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_wire.c
      18             :  * @brief Handle /wire requests
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include <gnunet/gnunet_json_lib.h>
      23             : #include "taler_dbevents.h"
      24             : #include "taler-exchange-httpd_responses.h"
      25             : #include "taler-exchange-httpd_wire.h"
      26             : #include "taler_json_lib.h"
      27             : #include "taler_mhd_lib.h"
      28             : #include <jansson.h>
      29             : 
      30             : 
      31             : /**
      32             :  * Stores the latest generation of our wire response.
      33             :  */
      34             : static struct WireStateHandle *wire_state;
      35             : 
      36             : /**
      37             :  * Handler listening for wire updates by other exchange
      38             :  * services.
      39             :  */
      40             : static struct GNUNET_DB_EventHandler *wire_eh;
      41             : 
      42             : /**
      43             :  * Counter incremented whenever we have a reason to re-build the #wire_state
      44             :  * because something external changed.
      45             :  */
      46             : static uint64_t wire_generation;
      47             : 
      48             : 
      49             : /**
      50             :  * State we keep per thread to cache the /wire response.
      51             :  */
      52             : struct WireStateHandle
      53             : {
      54             :   /**
      55             :    * Cached JSON for /wire response.
      56             :    */
      57             :   json_t *wire_reply;
      58             : 
      59             :   /**
      60             :    * For which (global) wire_generation was this data structure created?
      61             :    * Used to check when we are outdated and need to be re-generated.
      62             :    */
      63             :   uint64_t wire_generation;
      64             : 
      65             :   /**
      66             :    * HTTP status to return with this response.
      67             :    */
      68             :   unsigned int http_status;
      69             : 
      70             : };
      71             : 
      72             : 
      73             : /**
      74             :  * Free memory associated with @a wsh
      75             :  *
      76             :  * @param[in] wsh wire state to destroy
      77             :  */
      78             : static void
      79           3 : destroy_wire_state (struct WireStateHandle *wsh)
      80             : {
      81           3 :   json_decref (wsh->wire_reply);
      82           3 :   GNUNET_free (wsh);
      83           3 : }
      84             : 
      85             : 
      86             : /**
      87             :  * Function called whenever another exchange process has updated
      88             :  * the wire data in the database.
      89             :  *
      90             :  * @param cls NULL
      91             :  * @param extra unused
      92             :  * @param extra_size number of bytes in @a extra unused
      93             :  */
      94             : static void
      95          26 : wire_update_event_cb (void *cls,
      96             :                       const void *extra,
      97             :                       size_t extra_size)
      98             : {
      99             :   (void) cls;
     100             :   (void) extra;
     101             :   (void) extra_size;
     102          26 :   wire_generation++;
     103          26 : }
     104             : 
     105             : 
     106             : int
     107          12 : TEH_wire_init ()
     108             : {
     109          12 :   struct GNUNET_DB_EventHeaderP es = {
     110          12 :     .size = htons (sizeof (es)),
     111          12 :     .type = htons (TALER_DBEVENT_EXCHANGE_KEYS_UPDATED),
     112             :   };
     113             : 
     114          12 :   wire_eh = TEH_plugin->event_listen (TEH_plugin->cls,
     115             :                                       GNUNET_TIME_UNIT_FOREVER_REL,
     116             :                                       &es,
     117             :                                       &wire_update_event_cb,
     118             :                                       NULL);
     119          12 :   if (NULL == wire_eh)
     120             :   {
     121           0 :     GNUNET_break (0);
     122           0 :     return GNUNET_SYSERR;
     123             :   }
     124          12 :   return GNUNET_OK;
     125             : }
     126             : 
     127             : 
     128             : void
     129          12 : TEH_WIRE_done ()
     130             : {
     131          12 :   if (NULL != wire_state)
     132             :   {
     133           3 :     destroy_wire_state (wire_state);
     134           3 :     wire_state = NULL;
     135             :   }
     136          12 :   if (NULL != wire_eh)
     137             :   {
     138          12 :     TEH_plugin->event_listen_cancel (TEH_plugin->cls,
     139             :                                      wire_eh);
     140          12 :     wire_eh = NULL;
     141             :   }
     142          12 : }
     143             : 
     144             : 
     145             : /**
     146             :  * Create standard JSON response format using
     147             :  * @param ec and @a detail
     148             :  *
     149             :  * @param ec error code to return
     150             :  * @param detail optional detail text to return, can be NULL
     151             :  * @return JSON response
     152             :  */
     153             : static json_t *
     154           1 : make_ec_reply (enum TALER_ErrorCode ec,
     155             :                const char *detail)
     156             : {
     157           1 :   return GNUNET_JSON_PACK (
     158             :     GNUNET_JSON_pack_uint64 ("code", ec),
     159             :     GNUNET_JSON_pack_string ("hint",
     160             :                              TALER_ErrorCode_get_hint (ec)),
     161             :     GNUNET_JSON_pack_allow_null (
     162             :       GNUNET_JSON_pack_string ("detail", detail)));
     163             : }
     164             : 
     165             : 
     166             : /**
     167             :  * Add information about a wire account to @a cls.
     168             :  *
     169             :  * @param cls a `json_t *` object to expand with wire account details
     170             :  * @param payto_uri the exchange bank account URI to add
     171             :  * @param master_sig master key signature affirming that this is a bank
     172             :  *                   account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
     173             :  */
     174             : static void
     175           2 : add_wire_account (void *cls,
     176             :                   const char *payto_uri,
     177             :                   const struct TALER_MasterSignatureP *master_sig)
     178             : {
     179           2 :   json_t *a = cls;
     180             : 
     181           2 :   if (0 !=
     182           2 :       json_array_append_new (
     183             :         a,
     184           2 :         GNUNET_JSON_PACK (
     185             :           GNUNET_JSON_pack_string ("payto_uri",
     186             :                                    payto_uri),
     187             :           GNUNET_JSON_pack_data_auto ("master_sig",
     188             :                                       master_sig))))
     189             :   {
     190           0 :     GNUNET_break (0);   /* out of memory!? */
     191           0 :     return;
     192             :   }
     193             : }
     194             : 
     195             : 
     196             : /**
     197             :  * Add information about a wire account to @a cls.
     198             :  *
     199             :  * @param cls a `json_t *` array to expand with wire account details
     200             :  * @param wire_fee the wire fee we charge
     201             :  * @param closing_fee the closing fee we charge
     202             :  * @param start_date from when are these fees valid (start date)
     203             :  * @param end_date until when are these fees valid (end date, exclusive)
     204             :  * @param master_sig master key signature affirming that this is the correct
     205             :  *                   fee (of purpose #TALER_SIGNATURE_MASTER_WIRE_FEES)
     206             :  */
     207             : static void
     208           2 : add_wire_fee (void *cls,
     209             :               const struct TALER_Amount *wire_fee,
     210             :               const struct TALER_Amount *closing_fee,
     211             :               struct GNUNET_TIME_Absolute start_date,
     212             :               struct GNUNET_TIME_Absolute end_date,
     213             :               const struct TALER_MasterSignatureP *master_sig)
     214             : {
     215           2 :   json_t *a = cls;
     216             : 
     217           2 :   if (0 !=
     218           2 :       json_array_append_new (
     219             :         a,
     220           2 :         GNUNET_JSON_PACK (
     221             :           TALER_JSON_pack_amount ("wire_fee",
     222             :                                   wire_fee),
     223             :           TALER_JSON_pack_amount ("closing_fee",
     224             :                                   closing_fee),
     225             :           GNUNET_JSON_pack_time_abs ("start_date",
     226             :                                      start_date),
     227             :           GNUNET_JSON_pack_time_abs ("end_date",
     228             :                                      end_date),
     229             :           GNUNET_JSON_pack_data_auto ("sig",
     230             :                                       master_sig))))
     231             :   {
     232           0 :     GNUNET_break (0);   /* out of memory!? */
     233           0 :     return;
     234             :   }
     235             : }
     236             : 
     237             : 
     238             : /**
     239             :  * Create the /wire response from our database state.
     240             :  *
     241             :  * @return NULL on error
     242             :  */
     243             : static struct WireStateHandle *
     244           3 : build_wire_state (void)
     245             : {
     246             :   json_t *wire_accounts_array;
     247             :   json_t *wire_fee_object;
     248           3 :   uint64_t wg = wire_generation; /* must be obtained FIRST */
     249             :   enum GNUNET_DB_QueryStatus qs;
     250             :   struct WireStateHandle *wsh;
     251             : 
     252           3 :   wsh = GNUNET_new (struct WireStateHandle);
     253           3 :   wsh->wire_generation = wg;
     254           3 :   wire_accounts_array = json_array ();
     255           3 :   GNUNET_assert (NULL != wire_accounts_array);
     256           3 :   qs = TEH_plugin->get_wire_accounts (TEH_plugin->cls,
     257             :                                       &add_wire_account,
     258             :                                       wire_accounts_array);
     259           3 :   if (0 > qs)
     260             :   {
     261           0 :     GNUNET_break (0);
     262           0 :     json_decref (wire_accounts_array);
     263           0 :     wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     264             :     wsh->wire_reply
     265           0 :       = make_ec_reply (TALER_EC_GENERIC_DB_FETCH_FAILED,
     266             :                        "get_wire_accounts");
     267           0 :     return wsh;
     268             :   }
     269           3 :   if (0 == json_array_size (wire_accounts_array))
     270             :   {
     271           1 :     json_decref (wire_accounts_array);
     272           1 :     wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     273             :     wsh->wire_reply
     274           1 :       = make_ec_reply (TALER_EC_EXCHANGE_WIRE_NO_ACCOUNTS_CONFIGURED,
     275             :                        NULL);
     276           1 :     return wsh;
     277             :   }
     278           2 :   wire_fee_object = json_object ();
     279           2 :   GNUNET_assert (NULL != wire_fee_object);
     280             :   {
     281             :     json_t *account;
     282             :     size_t index;
     283             : 
     284           4 :     json_array_foreach (wire_accounts_array, index, account) {
     285             :       char *wire_method;
     286           2 :       const char *payto_uri = json_string_value (json_object_get (account,
     287             :                                                                   "payto_uri"));
     288             : 
     289           2 :       GNUNET_assert (NULL != payto_uri);
     290           2 :       wire_method = TALER_payto_get_method (payto_uri);
     291           2 :       if (NULL == wire_method)
     292             :       {
     293           0 :         wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     294             :         wsh->wire_reply
     295           0 :           = make_ec_reply (TALER_EC_EXCHANGE_WIRE_INVALID_PAYTO_CONFIGURED,
     296             :                            payto_uri);
     297           0 :         json_decref (wire_accounts_array);
     298           0 :         json_decref (wire_fee_object);
     299           0 :         return wsh;
     300             :       }
     301           2 :       if (NULL == json_object_get (wire_fee_object,
     302             :                                    wire_method))
     303             :       {
     304           2 :         json_t *a = json_array ();
     305             : 
     306           2 :         GNUNET_assert (NULL != a);
     307           2 :         qs = TEH_plugin->get_wire_fees (TEH_plugin->cls,
     308             :                                         wire_method,
     309             :                                         &add_wire_fee,
     310             :                                         a);
     311           2 :         if (0 > qs)
     312             :         {
     313           0 :           GNUNET_break (0);
     314           0 :           json_decref (a);
     315           0 :           json_decref (wire_fee_object);
     316           0 :           json_decref (wire_accounts_array);
     317           0 :           GNUNET_free (wire_method);
     318           0 :           wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     319             :           wsh->wire_reply
     320           0 :             = make_ec_reply (TALER_EC_GENERIC_DB_FETCH_FAILED,
     321             :                              "get_wire_fees");
     322           0 :           return wsh;
     323             :         }
     324           2 :         if (0 == json_array_size (a))
     325             :         {
     326           0 :           json_decref (a);
     327           0 :           json_decref (wire_accounts_array);
     328           0 :           json_decref (wire_fee_object);
     329           0 :           wsh->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
     330             :           wsh->wire_reply
     331           0 :             = make_ec_reply (TALER_EC_EXCHANGE_WIRE_FEES_NOT_CONFIGURED,
     332             :                              wire_method);
     333           0 :           GNUNET_free (wire_method);
     334           0 :           return wsh;
     335             :         }
     336           2 :         GNUNET_assert (0 ==
     337             :                        json_object_set_new (wire_fee_object,
     338             :                                             wire_method,
     339             :                                             a));
     340             :       }
     341           2 :       GNUNET_free (wire_method);
     342             :     }
     343             :   }
     344           2 :   wsh->wire_reply = GNUNET_JSON_PACK (
     345             :     GNUNET_JSON_pack_array_steal ("accounts",
     346             :                                   wire_accounts_array),
     347             :     GNUNET_JSON_pack_object_steal ("fees",
     348             :                                    wire_fee_object),
     349             :     GNUNET_JSON_pack_data_auto ("master_public_key",
     350             :                                 &TEH_master_public_key));
     351           2 :   wsh->http_status = MHD_HTTP_OK;
     352           2 :   return wsh;
     353             : }
     354             : 
     355             : 
     356             : void
     357          19 : TEH_wire_update_state (void)
     358             : {
     359          19 :   struct GNUNET_DB_EventHeaderP es = {
     360          19 :     .size = htons (sizeof (es)),
     361          19 :     .type = htons (TALER_DBEVENT_EXCHANGE_WIRE_UPDATED),
     362             :   };
     363             : 
     364          19 :   TEH_plugin->event_notify (TEH_plugin->cls,
     365             :                             &es,
     366             :                             NULL,
     367             :                             0);
     368          19 :   wire_generation++;
     369          19 : }
     370             : 
     371             : 
     372             : /**
     373             :  * Return the current key state for this thread.  Possibly
     374             :  * re-builds the key state if we have reason to believe
     375             :  * that something changed.
     376             :  *
     377             :  * @return NULL on error
     378             :  */
     379             : struct WireStateHandle *
     380           6 : get_wire_state (void)
     381             : {
     382             :   struct WireStateHandle *old_wsh;
     383             : 
     384           6 :   old_wsh = wire_state;
     385           6 :   if ( (NULL == old_wsh) ||
     386           3 :        (old_wsh->wire_generation < wire_generation) )
     387             :   {
     388             :     struct WireStateHandle *wsh;
     389             : 
     390           3 :     wsh = build_wire_state ();
     391           3 :     wire_state = wsh;
     392           3 :     if (NULL != old_wsh)
     393           0 :       destroy_wire_state (old_wsh);
     394           3 :     return wsh;
     395             :   }
     396           3 :   return old_wsh;
     397             : }
     398             : 
     399             : 
     400             : MHD_RESULT
     401           6 : TEH_handler_wire (struct TEH_RequestContext *rc,
     402             :                   const char *const args[])
     403             : {
     404             :   struct WireStateHandle *wsh;
     405             : 
     406             :   (void) args;
     407           6 :   wsh = get_wire_state ();
     408           6 :   if (NULL == wsh)
     409           0 :     return TALER_MHD_reply_with_error (rc->connection,
     410             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     411             :                                        TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
     412             :                                        NULL);
     413           6 :   return TALER_MHD_reply_json (rc->connection,
     414           6 :                                wsh->wire_reply,
     415             :                                wsh->http_status);
     416             : }
     417             : 
     418             : 
     419             : /* end of taler-exchange-httpd_wire.c */

Generated by: LCOV version 1.14