LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_exchanges.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 265 387 68.5 %
Date: 2021-08-30 06:54:17 Functions: 15 16 93.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2014-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 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_exchanges.c
      18             :  * @brief logic this HTTPD keeps for each exchange we interact with
      19             :  * @author Marcello Stanisci
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <taler/taler_json_lib.h>
      24             : #include "taler-merchant-httpd_exchanges.h"
      25             : #include "taler-merchant-httpd.h"
      26             : 
      27             : 
      28             : /**
      29             :  * Delay after which we'll re-fetch key information from the exchange.
      30             :  */
      31             : #define RELOAD_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
      32             : 
      33             : /**
      34             :  * Delay after which we'll allow clients to force us to re-fetch key
      35             :  * information from the exchange if we don't know the denomination key.
      36             :  */
      37             : #define FORCED_RELOAD_DELAY GNUNET_TIME_relative_multiply ( \
      38             :     GNUNET_TIME_UNIT_MINUTES, 15)
      39             : 
      40             : /**
      41             :  * Threshold after which exponential backoff should not increase.
      42             :  */
      43             : #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \
      44             :     GNUNET_TIME_UNIT_SECONDS, 60)
      45             : 
      46             : 
      47             : /**
      48             :  * Perform our exponential back-off calculation, starting at 1 ms
      49             :  * and then going by a factor of 2 up unto a maximum of RETRY_BACKOFF_THRESHOLD.
      50             :  *
      51             :  * @param r current backoff time, initially zero
      52             :  */
      53             : #define RETRY_BACKOFF(r) GNUNET_TIME_relative_min (RETRY_BACKOFF_THRESHOLD, \
      54             :                                                    GNUNET_TIME_relative_multiply ( \
      55             :                                                      GNUNET_TIME_relative_max ( \
      56             :                                                        GNUNET_TIME_UNIT_MILLISECONDS, \
      57             :                                                        (r)), 2));
      58             : 
      59             : 
      60             : /**
      61             :  * Exchange
      62             :  */
      63             : struct Exchange;
      64             : 
      65             : 
      66             : /**
      67             :  * Information we keep for a pending #MMH_EXCHANGES_find_exchange() operation.
      68             :  */
      69             : struct TMH_EXCHANGES_FindOperation
      70             : {
      71             : 
      72             :   /**
      73             :    * Kept in a DLL.
      74             :    */
      75             :   struct TMH_EXCHANGES_FindOperation *next;
      76             : 
      77             :   /**
      78             :    * Kept in a DLL.
      79             :    */
      80             :   struct TMH_EXCHANGES_FindOperation *prev;
      81             : 
      82             :   /**
      83             :    * Function to call with the result.
      84             :    */
      85             :   TMH_EXCHANGES_FindContinuation fc;
      86             : 
      87             :   /**
      88             :    * Closure for @e fc.
      89             :    */
      90             :   void *fc_cls;
      91             : 
      92             :   /**
      93             :    * Exchange we wait for the /keys for.
      94             :    */
      95             :   struct Exchange *my_exchange;
      96             : 
      97             :   /**
      98             :    * Wire method we care about for fees, NULL if we do not care about wire fees.
      99             :    */
     100             :   char *wire_method;
     101             : 
     102             :   /**
     103             :    * Task scheduled to asynchronously return the result to
     104             :    * the find continuation.
     105             :    */
     106             :   struct GNUNET_SCHEDULER_Task *at;
     107             : 
     108             : };
     109             : 
     110             : 
     111             : /**
     112             :  * Information about wire transfer fees of an exchange, by wire method.
     113             :  */
     114             : struct FeesByWireMethod
     115             : {
     116             : 
     117             :   /**
     118             :    * Kept in a DLL.
     119             :    */
     120             :   struct FeesByWireMethod *next;
     121             : 
     122             :   /**
     123             :    * Kept in a DLL.
     124             :    */
     125             :   struct FeesByWireMethod *prev;
     126             : 
     127             :   /**
     128             :    * Wire method these fees are for.
     129             :    */
     130             :   char *wire_method;
     131             : 
     132             :   /**
     133             :    * Full payto URI of the exchange.
     134             :    */
     135             :   char *payto_uri;
     136             : 
     137             :   /**
     138             :    * Applicable fees, NULL if unknown/error.
     139             :    */
     140             :   struct TALER_EXCHANGE_WireAggregateFees *af;
     141             : 
     142             : };
     143             : 
     144             : 
     145             : /**
     146             :  * Exchange
     147             :  */
     148             : struct Exchange
     149             : {
     150             : 
     151             :   /**
     152             :    * Kept in a DLL.
     153             :    */
     154             :   struct Exchange *next;
     155             : 
     156             :   /**
     157             :    * Kept in a DLL.
     158             :    */
     159             :   struct Exchange *prev;
     160             : 
     161             :   /**
     162             :    * Head of FOs pending for this exchange.
     163             :    */
     164             :   struct TMH_EXCHANGES_FindOperation *fo_head;
     165             : 
     166             :   /**
     167             :    * Tail of FOs pending for this exchange.
     168             :    */
     169             :   struct TMH_EXCHANGES_FindOperation *fo_tail;
     170             : 
     171             :   /**
     172             :    * (base) URL of the exchange.
     173             :    */
     174             :   char *url;
     175             : 
     176             :   /**
     177             :    * A connection to this exchange
     178             :    */
     179             :   struct TALER_EXCHANGE_Handle *conn;
     180             : 
     181             :   /**
     182             :    * Active /wire request to the exchange, or NULL.
     183             :    */
     184             :   struct TALER_EXCHANGE_WireHandle *wire_request;
     185             : 
     186             :   /**
     187             :    * Task to re-run /wire after some delay.
     188             :    */
     189             :   struct GNUNET_SCHEDULER_Task *wire_task;
     190             : 
     191             :   /**
     192             :    * Head of wire fees from /wire request.
     193             :    */
     194             :   struct FeesByWireMethod *wire_fees_head;
     195             : 
     196             :   /**
     197             :    * Tail of wire fees from /wire request.
     198             :    */
     199             :   struct FeesByWireMethod *wire_fees_tail;
     200             : 
     201             :   /**
     202             :    * Master public key, guaranteed to be set ONLY for
     203             :    * trusted exchanges.
     204             :    */
     205             :   struct TALER_MasterPublicKeyP master_pub;
     206             : 
     207             :   /**
     208             :    * How soon can may we, at the earliest, re-download /keys?
     209             :    */
     210             :   struct GNUNET_TIME_Absolute first_retry;
     211             : 
     212             :   /**
     213             :    * How long should we wait between the next retry?
     214             :    */
     215             :   struct GNUNET_TIME_Relative retry_delay;
     216             : 
     217             :   /**
     218             :    * How long should we wait between the next retry for /wire?
     219             :    */
     220             :   struct GNUNET_TIME_Relative wire_retry_delay;
     221             : 
     222             :   /**
     223             :    * Task where we retry fetching /keys from the exchange.
     224             :    */
     225             :   struct GNUNET_SCHEDULER_Task *retry_task;
     226             : 
     227             :   /**
     228             :    * true to indicate that there is an ongoing
     229             :    * transfer we are waiting for,
     230             :    * false to indicate that key data is up-to-date.
     231             :    */
     232             :   bool pending;
     233             : 
     234             :   /**
     235             :    * true if this exchange is from our configuration and
     236             :    * explicitly trusted, false if we need to check each
     237             :    * key to be sure it is trusted.
     238             :    */
     239             :   bool trusted;
     240             : 
     241             : };
     242             : 
     243             : 
     244             : /**
     245             :  * Context for all exchange operations (useful to the event loop)
     246             :  */
     247             : static struct GNUNET_CURL_Context *merchant_curl_ctx;
     248             : 
     249             : /**
     250             :  * Context for integrating #merchant_curl_ctx with the
     251             :  * GNUnet event loop.
     252             :  */
     253             : static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
     254             : 
     255             : /**
     256             :  * Head of exchanges we know about.
     257             :  */
     258             : static struct Exchange *exchange_head;
     259             : 
     260             : /**
     261             :  * Tail of exchanges we know about.
     262             :  */
     263             : static struct Exchange *exchange_tail;
     264             : 
     265             : /**
     266             :  * List of our trusted exchanges for inclusion in contracts.
     267             :  */
     268             : json_t *TMH_trusted_exchanges;
     269             : 
     270             : 
     271             : /**
     272             :  * Function called with information about who is auditing
     273             :  * a particular exchange and what key the exchange is using.
     274             :  *
     275             :  * @param cls closure, will be `struct Exchange` so that
     276             :  *   when this function gets called, it will change the flag 'pending'
     277             :  *   to 'false'. Note: 'keys' is automatically saved inside the exchange's
     278             :  *   handle, which is contained inside 'struct Exchange', when
     279             :  *   this callback is called. Thus, once 'pending' turns 'false',
     280             :  *   it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
     281             :  *   in order to get the "good" keys.
     282             :  * @param hr http response details
     283             :  * @param keys information about the various keys used
     284             :  *        by the exchange
     285             :  * @param compat version compatibility data
     286             :  */
     287             : static void
     288             : keys_mgmt_cb (void *cls,
     289             :               const struct TALER_EXCHANGE_HttpResponse *hr,
     290             :               const struct TALER_EXCHANGE_Keys *keys,
     291             :               enum TALER_EXCHANGE_VersionCompatibility compat);
     292             : 
     293             : 
     294             : /**
     295             :  * Retry getting information from the given exchange in
     296             :  * the closure.
     297             :  *
     298             :  * @param cls the exchange
     299             :  *
     300             :  */
     301             : static void
     302          18 : retry_exchange (void *cls)
     303             : {
     304          18 :   struct Exchange *exchange = cls;
     305             : 
     306             :   /* might be a scheduled reload and not our first attempt */
     307          18 :   exchange->retry_task = NULL;
     308          18 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     309             :               "Connecting to exchange %s in retry_exchange()\n",
     310             :               exchange->url);
     311          18 :   if (NULL != exchange->conn)
     312             :   {
     313           0 :     TALER_EXCHANGE_disconnect (exchange->conn);
     314           0 :     exchange->conn = NULL;
     315             :   }
     316          36 :   exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx,
     317          18 :                                            exchange->url,
     318             :                                            &keys_mgmt_cb,
     319             :                                            exchange,
     320             :                                            TALER_EXCHANGE_OPTION_END);
     321             :   /* Note: while the API spec says 'returns NULL on error', the implementation
     322             :      actually never returns NULL. */
     323          18 :   GNUNET_break (NULL != exchange->conn);
     324          18 : }
     325             : 
     326             : 
     327             : /**
     328             :  * Function called with information about the wire fees
     329             :  * for each wire method.  Stores the wire fees with the
     330             :  * exchange for later use.
     331             :  *
     332             :  * @param exchange connection to the exchange
     333             :  * @param master_pub public key of the exchange
     334             :  * @param wire_method name of the wire method (i.e. "iban")
     335             :  * @param payto_uri full payto URI of the exchange
     336             :  * @param fees fee structure for this method
     337             :  * @return #TALER_EC_NONE on success
     338             :  */
     339             : static enum TALER_ErrorCode
     340           8 : process_wire_fees (struct Exchange *exchange,
     341             :                    const struct TALER_MasterPublicKeyP *master_pub,
     342             :                    const char *wire_method,
     343             :                    const char *payto_uri,
     344             :                    const struct TALER_EXCHANGE_WireAggregateFees *fees)
     345             : {
     346             :   struct FeesByWireMethod *f;
     347             :   struct TALER_EXCHANGE_WireAggregateFees *endp;
     348             :   struct TALER_EXCHANGE_WireAggregateFees *af;
     349             : 
     350           8 :   for (f = exchange->wire_fees_head; NULL != f; f = f->next)
     351           0 :     if (0 == strcasecmp (wire_method,
     352           0 :                          f->wire_method))
     353           0 :       break;
     354           8 :   if (NULL == f)
     355             :   {
     356           8 :     f = GNUNET_new (struct FeesByWireMethod);
     357           8 :     f->wire_method = GNUNET_strdup (wire_method);
     358           8 :     f->payto_uri = GNUNET_strdup (payto_uri);
     359           8 :     GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
     360             :                                  exchange->wire_fees_tail,
     361             :                                  f);
     362             :   }
     363           8 :   endp = f->af;
     364           8 :   while ( (NULL != endp) &&
     365           0 :           (NULL != endp->next) )
     366           0 :     endp = endp->next;
     367           8 :   while ( (NULL != endp) &&
     368           0 :           (NULL != fees) &&
     369           0 :           (fees->start_date.abs_value_us < endp->end_date.abs_value_us) )
     370           0 :     fees = fees->next;
     371           8 :   if ( (NULL != endp) &&
     372           0 :        (NULL != fees) &&
     373           0 :        (fees->start_date.abs_value_us != endp->end_date.abs_value_us) )
     374             :   {
     375             :     /* Hole in the fee structure, not allowed! */
     376           0 :     GNUNET_break_op (0);
     377           0 :     return TALER_EC_MERCHANT_GENERIC_HOLE_IN_WIRE_FEE_STRUCTURE;
     378             :   }
     379          40 :   while (NULL != fees)
     380             :   {
     381             :     struct GNUNET_HashCode h_wire_method;
     382             :     enum GNUNET_DB_QueryStatus qs;
     383             : 
     384          32 :     af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
     385          32 :     *af = *fees;
     386          32 :     GNUNET_CRYPTO_hash (wire_method,
     387          32 :                         strlen (wire_method) + 1,
     388             :                         &h_wire_method);
     389          32 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     390             :                 "Storing wire fee for `%s' and method `%s' at %s in DB; the fee is %s\n",
     391             :                 TALER_B2S (master_pub),
     392             :                 wire_method,
     393             :                 GNUNET_STRINGS_absolute_time_to_string (af->start_date),
     394             :                 TALER_amount2s (&af->wire_fee));
     395          32 :     TMH_db->preflight (TMH_db->cls);
     396          32 :     if (GNUNET_OK !=
     397          32 :         TMH_db->start (TMH_db->cls,
     398             :                        "store wire fee"))
     399             :     {
     400           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     401             :                   "Failed to start database transaction to store exchange wire fees (will try to continue anyway)!\n");
     402           0 :       GNUNET_free (af);
     403           0 :       fees = fees->next;
     404           0 :       continue;
     405             :     }
     406          32 :     qs = TMH_db->store_wire_fee_by_exchange (TMH_db->cls,
     407             :                                              master_pub,
     408             :                                              &h_wire_method,
     409          32 :                                              &af->wire_fee,
     410          32 :                                              &af->closing_fee,
     411             :                                              af->start_date,
     412             :                                              af->end_date,
     413          32 :                                              &af->master_sig);
     414          32 :     if (0 > qs)
     415             :     {
     416           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     417             :                   "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
     418           0 :       GNUNET_free (af);
     419           0 :       fees = fees->next;
     420           0 :       TMH_db->rollback (TMH_db->cls);
     421           0 :       continue;
     422             :     }
     423          32 :     if (0 == qs)
     424             :     {
     425             :       /* Entry was already in DB, fine, continue as if we had succeeded */
     426           0 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     427             :                   "Fees already in DB, rolling back transaction attempt!\n");
     428           0 :       TMH_db->rollback (TMH_db->cls);
     429             :     }
     430          32 :     if (0 < qs)
     431             :     {
     432             :       /* Inserted into DB, make sure transaction completes */
     433          32 :       qs = TMH_db->commit (TMH_db->cls);
     434          32 :       if (0 > qs)
     435             :       {
     436           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     437             :                     "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
     438           0 :         GNUNET_free (af);
     439           0 :         fees = fees->next;
     440           0 :         continue;
     441             :       }
     442             :     }
     443          32 :     af->next = NULL;
     444          32 :     if (NULL == endp)
     445           8 :       f->af = af;
     446             :     else
     447          24 :       endp->next = af;
     448          32 :     endp = af;
     449          32 :     fees = fees->next;
     450             :   }
     451           8 :   return TALER_EC_NONE;
     452             : }
     453             : 
     454             : 
     455             : /**
     456             :  * Function called with information about the wire accounts
     457             :  * of the exchange.  Stores the wire fees with the
     458             :  * exchange for laster use.
     459             :  *
     460             :  * @param exchange the exchange
     461             :  * @param master_pub public key of the exchange
     462             :  * @param accounts_len length of the @a accounts array
     463             :  * @param accounts list of wire accounts of the exchange
     464             :  * @return #TALER_EC_NONE on success
     465             :  */
     466             : static enum TALER_ErrorCode
     467           8 : process_wire_accounts (struct Exchange *exchange,
     468             :                        const struct TALER_MasterPublicKeyP *master_pub,
     469             :                        unsigned int accounts_len,
     470             :                        const struct TALER_EXCHANGE_WireAccount *accounts)
     471             : {
     472          16 :   for (unsigned int i = 0; i<accounts_len; i++)
     473             :   {
     474             :     enum TALER_ErrorCode ec;
     475             :     char *method;
     476             : 
     477           8 :     method = TALER_payto_get_method (accounts[i].payto_uri);
     478           8 :     if (NULL == method)
     479             :     {
     480             :       /* malformed payto:// URI returned by exchange */
     481           0 :       GNUNET_break_op (0);
     482           0 :       return TALER_EC_GENERIC_PAYTO_URI_MALFORMED;
     483             :     }
     484           8 :     ec = process_wire_fees (exchange,
     485             :                             master_pub,
     486             :                             method,
     487           8 :                             accounts[i].payto_uri,
     488           8 :                             accounts[i].fees);
     489           8 :     GNUNET_free (method);
     490           8 :     if (TALER_EC_NONE != ec)
     491           0 :       return ec;
     492             :   }
     493           8 :   return TALER_EC_NONE;
     494             : }
     495             : 
     496             : 
     497             : /**
     498             :  * Obtain applicable fees for @a exchange and @a wire_method.
     499             :  *
     500             :  * @param exchange the exchange to query
     501             :  * @param now current time
     502             :  * @param wire_method the wire method we want the fees for
     503             :  * @return NULL if we do not have fees for this method yet
     504             :  */
     505             : static struct FeesByWireMethod *
     506          53 : get_wire_fees (struct Exchange *exchange,
     507             :                struct GNUNET_TIME_Absolute now,
     508             :                const char *wire_method)
     509             : {
     510          53 :   for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
     511             :        NULL != fbw;
     512           0 :        fbw = fbw->next)
     513             :   {
     514          38 :     if (0 == strcasecmp (fbw->wire_method,
     515             :                          wire_method) )
     516             :     {
     517             :       struct TALER_EXCHANGE_WireAggregateFees *af;
     518             : 
     519             :       /* Advance through list up to current time */
     520          38 :       while ( (NULL != (af = fbw->af)) &&
     521          38 :               (now.abs_value_us >= af->end_date.abs_value_us) )
     522             :       {
     523           0 :         fbw->af = af->next;
     524           0 :         GNUNET_free (af);
     525             :       }
     526          38 :       return fbw;
     527             :     }
     528           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     529             :                 "Exchange supports `%s' as a wire method (but we do not use that one)\n",
     530             :                 fbw->wire_method);
     531             :   }
     532          15 :   return NULL;
     533             : }
     534             : 
     535             : 
     536             : /**
     537             :  * Check if we have any remaining pending requests for the
     538             :  * given @a exchange, and if we have the required data, call
     539             :  * the callback.
     540             :  *
     541             :  * @param exchange the exchange to check for pending find operations
     542             :  * @return true if we need /wire data from @a exchange
     543             :  */
     544             : static bool
     545          61 : process_find_operations (struct Exchange *exchange)
     546             : {
     547             :   struct TMH_EXCHANGES_FindOperation *fn;
     548             :   struct GNUNET_TIME_Absolute now;
     549             :   bool need_wire;
     550             : 
     551          61 :   now = GNUNET_TIME_absolute_get ();
     552          61 :   need_wire = false;
     553         115 :   for (struct TMH_EXCHANGES_FindOperation *fo = exchange->fo_head;
     554             :        NULL != fo;
     555          54 :        fo = fn)
     556             :   {
     557             :     struct FeesByWireMethod *fbw;
     558             : 
     559          54 :     fn = fo->next;
     560          54 :     if (NULL != fo->wire_method)
     561             :     {
     562             :       /* Find fee structure for our wire method */
     563          31 :       fbw = get_wire_fees (exchange,
     564             :                            now,
     565          31 :                            fo->wire_method);
     566          31 :       if (NULL == fbw)
     567             :       {
     568           8 :         need_wire = true;
     569             :         /* Do not warn if this is before our first attempt */
     570           8 :         if (! GNUNET_TIME_relative_is_zero (exchange->wire_retry_delay))
     571           0 :           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     572             :                       "Exchange does not support `%s' wire method (will retry later)\n",
     573             :                       fo->wire_method);
     574           8 :         fbw = NULL;
     575           8 :         continue;
     576             :       }
     577          23 :       if (fbw->af->start_date.abs_value_us > now.abs_value_us)
     578             :       {
     579             :         /* Disagreement on the current time */
     580           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     581             :                     "Exchange's earliest fee is %s adhead of our time. Clock skew issue?\n",
     582             :                     GNUNET_STRINGS_relative_time_to_string (
     583             :                       GNUNET_TIME_absolute_get_remaining (fbw->af->start_date),
     584             :                       GNUNET_YES));
     585           0 :         fbw = NULL;
     586           0 :         continue;
     587             :       }
     588             :     }
     589             :     else
     590             :     {
     591             :       /* no wire transfer method given, so we yield no fee */
     592          23 :       fbw = NULL;
     593             :     }
     594             :     {
     595          46 :       struct TALER_EXCHANGE_HttpResponse hr = {
     596             :         .http_status = MHD_HTTP_OK,
     597             :       };
     598             : 
     599          46 :       fo->fc (fo->fc_cls,
     600             :               &hr,
     601             :               exchange->conn,
     602             :               (NULL != fbw) ? fbw->payto_uri : NULL,
     603          23 :               (NULL != fbw) ? &fbw->af->wire_fee : NULL,
     604          46 :               exchange->trusted);
     605             :     }
     606          46 :     TMH_EXCHANGES_find_exchange_cancel (fo);
     607             :   }
     608          61 :   return need_wire;
     609             : }
     610             : 
     611             : 
     612             : static void
     613             : wire_task_cb (void *cls);
     614             : 
     615             : 
     616             : /**
     617             :  * Callbacks of this type are used to serve the result of submitting a
     618             :  * wire format inquiry request to a exchange.
     619             :  *
     620             :  * If the request fails to generate a valid response from the
     621             :  * exchange, @a http_status will also be zero.
     622             :  *
     623             :  * Must only be called if 'exchange->pending' is #GNUNET_NO,
     624             :  * that is #TALER_EXCHANGE_get_keys() will succeed.
     625             :  *
     626             :  * @param cls closure, a `struct Exchange`
     627             :  * @param hr HTTP response details
     628             :  * @param accounts_len length of the @a accounts array
     629             :  * @param accounts list of wire accounts of the exchange, NULL on error
     630             :  */
     631             : static void
     632           8 : handle_wire_data (void *cls,
     633             :                   const struct TALER_EXCHANGE_HttpResponse *hr,
     634             :                   unsigned int accounts_len,
     635             :                   const struct TALER_EXCHANGE_WireAccount *accounts)
     636             : {
     637           8 :   struct Exchange *exchange = cls;
     638             :   const struct TALER_EXCHANGE_Keys *keys;
     639             :   enum TALER_ErrorCode ecx;
     640             : 
     641           8 :   exchange->wire_request = NULL;
     642           8 :   if (MHD_HTTP_OK != hr->http_status)
     643             :   {
     644             :     struct TMH_EXCHANGES_FindOperation *fo;
     645             : 
     646           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     647             :                 "Failed to obtain /wire details from `%s': %u/%d\n",
     648             :                 exchange->url,
     649             :                 hr->http_status,
     650             :                 hr->ec);
     651           0 :     while (NULL != (fo = exchange->fo_head))
     652             :     {
     653           0 :       fo->fc (fo->fc_cls,
     654             :               hr,
     655             :               exchange->conn,
     656             :               NULL,
     657             :               NULL,
     658             :               GNUNET_NO);
     659           0 :       TMH_EXCHANGES_find_exchange_cancel (fo);
     660             :     }
     661           0 :     return;
     662             :   }
     663           8 :   keys = TALER_EXCHANGE_get_keys (exchange->conn);
     664           8 :   GNUNET_assert (NULL != keys);
     665           8 :   ecx = process_wire_accounts (exchange,
     666             :                                &keys->master_pub,
     667             :                                accounts_len,
     668             :                                accounts);
     669           8 :   if (TALER_EC_NONE != ecx)
     670             :   {
     671             :     /* Report hard failure to all callbacks! */
     672             :     struct TMH_EXCHANGES_FindOperation *fo;
     673           0 :     struct TALER_EXCHANGE_HttpResponse hrx = {
     674             :       .ec = ecx,
     675             :       .http_status = 0,
     676           0 :       .reply = hr->reply
     677             :     };
     678             : 
     679           0 :     GNUNET_break_op (0);
     680           0 :     while (NULL != (fo = exchange->fo_head))
     681             :     {
     682           0 :       fo->fc (fo->fc_cls,
     683             :               &hrx,
     684             :               NULL,
     685             :               NULL,
     686             :               NULL,
     687             :               GNUNET_NO);
     688           0 :       TMH_EXCHANGES_find_exchange_cancel (fo);
     689             :     }
     690           0 :     return;
     691             :   }
     692           8 :   if ( (process_find_operations (exchange)) &&
     693           0 :        (NULL == exchange->wire_task) &&
     694           0 :        (NULL == exchange->wire_request) )
     695             :   {
     696             :     /* need to run /wire again. But as we DID get a successful reply,
     697             :        and as the exchange is unlikely to offer new wire methods very
     698             :        frequently, start with some significant delay */
     699             :     exchange->wire_retry_delay
     700           0 :       = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MINUTES,
     701             :                                   exchange->wire_retry_delay);
     702           0 :     exchange->wire_retry_delay = RETRY_BACKOFF (exchange->wire_retry_delay);
     703           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     704             :                 "Exchange does not support our wire method. Retrying in %s\n",
     705             : 
     706             :                 GNUNET_STRINGS_relative_time_to_string (
     707             :                   exchange->wire_retry_delay,
     708             :                   GNUNET_YES));
     709             :     exchange->wire_task
     710           0 :       = GNUNET_SCHEDULER_add_delayed (exchange->wire_retry_delay,
     711             :                                       &wire_task_cb,
     712             :                                       exchange);
     713             :   }
     714             : }
     715             : 
     716             : 
     717             : /**
     718             :  * Check if we have any remaining pending requests for the
     719             :  * given @a exchange, and if we have the required data, call
     720             :  * the callback.  If requests without /wire data remain,
     721             :  * retry the /wire request after some delay.
     722             :  *
     723             :  * Must only be called if 'exchange->pending' is #GNUNET_NO,
     724             :  * that is #TALER_EXCHANGE_get_keys() will succeed.
     725             :  *
     726             :  * @param cls a `struct Exchange` to check
     727             :  */
     728             : static void
     729           7 : wire_task_cb (void *cls)
     730             : {
     731           7 :   struct Exchange *exchange = cls;
     732             : 
     733           7 :   exchange->wire_task = NULL;
     734           7 :   GNUNET_assert (! exchange->pending);
     735           7 :   if (! process_find_operations (exchange))
     736           0 :     return; /* no more need */
     737           7 :   GNUNET_assert (NULL == exchange->wire_request);
     738           7 :   exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
     739             :                                                 &handle_wire_data,
     740             :                                                 exchange);
     741             : }
     742             : 
     743             : 
     744             : /**
     745             :  * Free @a exchange.
     746             :  *
     747             :  * @param[in] exchange entry to free
     748             :  */
     749             : static void
     750          18 : free_exchange_entry (struct Exchange *exchange)
     751             : {
     752             :   struct FeesByWireMethod *f;
     753             : 
     754          18 :   GNUNET_CONTAINER_DLL_remove (exchange_head,
     755             :                                exchange_tail,
     756             :                                exchange);
     757          26 :   while (NULL != (f = exchange->wire_fees_head))
     758             :   {
     759             :     struct TALER_EXCHANGE_WireAggregateFees *af;
     760             : 
     761           8 :     GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head,
     762             :                                  exchange->wire_fees_tail,
     763             :                                  f);
     764          40 :     while (NULL != (af = f->af))
     765             :     {
     766          32 :       f->af = af->next;
     767          32 :       GNUNET_free (af);
     768             :     }
     769           8 :     GNUNET_free (f->wire_method);
     770           8 :     GNUNET_free (f->payto_uri);
     771           8 :     GNUNET_free (f);
     772             :   }
     773          18 :   if (NULL != exchange->wire_request)
     774             :   {
     775           0 :     TALER_EXCHANGE_wire_cancel (exchange->wire_request);
     776           0 :     exchange->wire_request = NULL;
     777             :   }
     778          18 :   if (NULL != exchange->wire_task)
     779             :   {
     780           0 :     GNUNET_SCHEDULER_cancel (exchange->wire_task);
     781           0 :     exchange->wire_task = NULL;
     782             :   }
     783          18 :   if (NULL != exchange->conn)
     784             :   {
     785          18 :     TALER_EXCHANGE_disconnect (exchange->conn);
     786          18 :     exchange->conn = NULL;
     787             :   }
     788          18 :   if (NULL != exchange->retry_task)
     789             :   {
     790           9 :     GNUNET_SCHEDULER_cancel (exchange->retry_task);
     791           9 :     exchange->retry_task = NULL;
     792             :   }
     793          18 :   GNUNET_assert (NULL == exchange->fo_head);
     794          18 :   GNUNET_assert (NULL == exchange->fo_tail);
     795          18 :   GNUNET_free (exchange->url);
     796          18 :   GNUNET_free (exchange);
     797          18 : }
     798             : 
     799             : 
     800             : /**
     801             :  * We failed downloading /keys from @a exchange. Tell clients
     802             :  * about our failure, abort pending operations and retry later.
     803             :  *
     804             :  * @param exchange exchange that failed
     805             :  * @param hr details about the HTTP reply
     806             :  * @param compat version compatibility data
     807             :  */
     808             : static void
     809           0 : fail_and_retry (struct Exchange *exchange,
     810             :                 const struct TALER_EXCHANGE_HttpResponse *hr,
     811             :                 enum TALER_EXCHANGE_VersionCompatibility compat)
     812             : {
     813             :   struct TMH_EXCHANGES_FindOperation *fo;
     814             : 
     815           0 :   exchange->pending = true;
     816           0 :   if (NULL != exchange->wire_request)
     817             :   {
     818           0 :     TALER_EXCHANGE_wire_cancel (exchange->wire_request);
     819           0 :     exchange->wire_request = NULL;
     820             :   }
     821           0 :   if (NULL != exchange->wire_task)
     822             :   {
     823           0 :     GNUNET_SCHEDULER_cancel (exchange->wire_task);
     824           0 :     exchange->wire_task = NULL;
     825             :   }
     826           0 :   while (NULL != (fo = exchange->fo_head))
     827             :   {
     828           0 :     fo->fc (fo->fc_cls,
     829             :             hr,
     830             :             NULL,
     831             :             NULL,
     832             :             NULL,
     833             :             GNUNET_NO);
     834           0 :     TMH_EXCHANGES_find_exchange_cancel (fo);
     835             :   }
     836           0 :   if (TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER == compat)
     837             :   {
     838             :     /* Log hard error: we likely need admin help! */
     839           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     840             :                 "Exchange `%s' runs an incompatible more recent version of the Taler protocol. Will not retry. This client may need to be updated.\n",
     841             :                 exchange->url);
     842             :     /* Theoretically, the exchange could downgrade,
     843             :        but let's not be too aggressive about retries
     844             :        on this one. */
     845           0 :     exchange->retry_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_HOURS,
     846             :                                                       exchange->retry_delay);
     847             :   }
     848           0 :   if ( (NULL == exchange->fo_head) &&
     849           0 :        (TALER_EC_GENERIC_CONFIGURATION_INVALID == hr->ec) )
     850             :   {
     851             :     /* This can NEVER work, so don't retry */
     852           0 :     free_exchange_entry (exchange);
     853           0 :     return;
     854             :   }
     855           0 :   exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
     856           0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     857             :               "Failed to fetch /keys from `%s': %d/%u, retrying in %s\n",
     858             :               exchange->url,
     859             :               (int) hr->ec,
     860             :               hr->http_status,
     861             :               GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay,
     862             :                                                       GNUNET_YES));
     863           0 :   GNUNET_assert (NULL == exchange->retry_task);
     864           0 :   exchange->first_retry = GNUNET_TIME_relative_to_absolute (
     865             :     exchange->retry_delay);
     866           0 :   exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
     867             :                                                        &retry_exchange,
     868             :                                                        exchange);
     869             : }
     870             : 
     871             : 
     872             : /**
     873             :  * Function called with information about who is auditing
     874             :  * a particular exchange and what key the exchange is using.
     875             :  *
     876             :  * @param cls closure, will be `struct Exchange` so that
     877             :  *   when this function gets called, it will change the flag 'pending'
     878             :  *   to 'false'. Note: 'keys' is automatically saved inside the exchange's
     879             :  *   handle, which is contained inside 'struct Exchange', when
     880             :  *   this callback is called. Thus, once 'pending' turns 'false',
     881             :  *   it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
     882             :  *   in order to get the "good" keys.
     883             :  * @param hr http response details
     884             :  * @param keys information about the various keys used
     885             :  *        by the exchange
     886             :  * @param compat version compatibility data
     887             :  */
     888             : static void
     889           9 : keys_mgmt_cb (void *cls,
     890             :               const struct TALER_EXCHANGE_HttpResponse *hr,
     891             :               const struct TALER_EXCHANGE_Keys *keys,
     892             :               enum TALER_EXCHANGE_VersionCompatibility compat)
     893             : {
     894           9 :   struct Exchange *exchange = cls;
     895             :   struct GNUNET_TIME_Absolute expire;
     896             :   struct GNUNET_TIME_Relative delay;
     897             : 
     898           9 :   if ( (MHD_HTTP_OK != hr->http_status) ||
     899             :        (NULL == keys) )
     900             :   {
     901           0 :     fail_and_retry (exchange,
     902             :                     hr,
     903             :                     compat);
     904           0 :     return;
     905             :   }
     906           9 :   if ( (exchange->trusted) &&
     907           8 :        (0 != GNUNET_memcmp (&exchange->master_pub,
     908             :                             &keys->master_pub)) )
     909             :   {
     910             :     /* master pub differs => do not trust the exchange (without auditor) */
     911           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     912             :                 "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n",
     913             :                 exchange->url);
     914           0 :     exchange->trusted = false;
     915             :   }
     916           9 :   if (! exchange->trusted)
     917             :   {
     918           1 :     exchange->master_pub = keys->master_pub;
     919           3 :     for (struct Exchange *e = exchange_head;
     920             :          NULL != e;
     921           2 :          e = e->next)
     922             :     {
     923           2 :       if (e == exchange)
     924           1 :         continue;
     925           1 :       if (! e->trusted)
     926           0 :         continue;
     927           1 :       if (0 ==
     928           1 :           GNUNET_memcmp (&e->master_pub,
     929             :                          &exchange->master_pub))
     930           1 :         exchange->trusted = true; /* same exchange, different URL => trust applies */
     931             :     }
     932             :   }
     933           9 :   if (0 != (TALER_EXCHANGE_VC_NEWER & compat))
     934             :   {
     935             :     /* Warn user exactly once about need to upgrade */
     936             :     static int once;
     937             : 
     938           0 :     if (0 == once)
     939             :     {
     940           0 :       once = 1;
     941           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     942             :                   "Exchange `%s' runs a more recent version of the Taler protocol. You may want to update this client.\n",
     943             :                   exchange->url);
     944             :     }
     945             :   }
     946             : 
     947             :   /* store exchange online signing keys in our DB */
     948          26 :   for (unsigned int i = 0; i<keys->num_sign_keys; i++)
     949             :   {
     950          17 :     struct TALER_EXCHANGE_SigningPublicKey *sign_key = &keys->sign_keys[i];
     951             :     enum GNUNET_DB_QueryStatus qs;
     952             : 
     953          17 :     TMH_db->preflight (TMH_db->cls);
     954          17 :     qs = TMH_db->insert_exchange_signkey (TMH_db->cls,
     955             :                                           &keys->master_pub,
     956          17 :                                           &sign_key->key,
     957             :                                           sign_key->valid_from,
     958             :                                           sign_key->valid_until,
     959             :                                           sign_key->valid_legal,
     960          17 :                                           &sign_key->master_sig);
     961             :     /* 0 is OK, we may already have the key in the DB! */
     962          17 :     if (0 > qs)
     963             :     {
     964           0 :       GNUNET_break (0);
     965           0 :       fail_and_retry (exchange,
     966             :                       hr,
     967             :                       compat);
     968           0 :       return;
     969             :     }
     970             :   }
     971             : 
     972           9 :   expire = TALER_EXCHANGE_check_keys_current (exchange->conn,
     973             :                                               TALER_EXCHANGE_CKF_NONE);
     974           9 :   exchange->first_retry = GNUNET_TIME_relative_to_absolute (RELOAD_DELAY);
     975           9 :   if (0 == expire.abs_value_us)
     976           0 :     delay = RELOAD_DELAY;
     977             :   else
     978           9 :     delay = GNUNET_TIME_absolute_get_remaining (expire);
     979           9 :   if (GNUNET_TIME_relative_is_zero (delay))
     980             :   {
     981           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     982             :                 "/keys response from exchange expired immediately! Retrying in 1 minute.\n");
     983           0 :     delay = GNUNET_TIME_UNIT_MINUTES;
     984             :   }
     985           9 :   exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
     986           9 :   if (NULL != exchange->retry_task)
     987           0 :     GNUNET_SCHEDULER_cancel (exchange->retry_task);
     988             :   exchange->retry_task
     989           9 :     = GNUNET_SCHEDULER_add_delayed (delay,
     990             :                                     &retry_exchange,
     991             :                                     exchange);
     992           9 :   exchange->pending = false;
     993           9 :   if ( (process_find_operations (exchange)) &&
     994           1 :        (NULL == exchange->wire_request) &&
     995           1 :        (NULL == exchange->wire_task) )
     996             :   {
     997           1 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     998             :                 "Got key data, but also need wire data. Will request /wire now\n");
     999           1 :     exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
    1000             :                                                   &handle_wire_data,
    1001             :                                                   exchange);
    1002             :   }
    1003             : }
    1004             : 
    1005             : 
    1006             : /**
    1007             :  * Task to return find operation result asynchronously to caller.
    1008             :  *
    1009             :  * @param cls a `struct TMH_EXCHANGES_FindOperation`
    1010             :  */
    1011             : static void
    1012          37 : return_result (void *cls)
    1013             : {
    1014          37 :   struct TMH_EXCHANGES_FindOperation *fo = cls;
    1015          37 :   struct Exchange *exchange = fo->my_exchange;
    1016             : 
    1017          37 :   fo->at = NULL;
    1018          37 :   if ( (process_find_operations (exchange)) &&
    1019           0 :        (NULL == exchange->wire_request) &&
    1020           0 :        (! exchange->pending) &&
    1021           0 :        (NULL != exchange->wire_task) )
    1022             :   {
    1023           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1024             :                 "Do not have current wire data. Will re-request /wire in 1 minute\n");
    1025             :     exchange->wire_task
    1026           0 :       = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
    1027             :                                       &wire_task_cb,
    1028             :                                       exchange);
    1029             :   }
    1030          37 : }
    1031             : 
    1032             : 
    1033             : struct TMH_EXCHANGES_FindOperation *
    1034          47 : TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
    1035             :                              const char *wire_method,
    1036             :                              int force_reload,
    1037             :                              TMH_EXCHANGES_FindContinuation fc,
    1038             :                              void *fc_cls)
    1039             : {
    1040             :   struct Exchange *exchange;
    1041             :   struct TMH_EXCHANGES_FindOperation *fo;
    1042             :   struct GNUNET_TIME_Absolute now;
    1043             : 
    1044          47 :   if (NULL == merchant_curl_ctx)
    1045             :   {
    1046           0 :     GNUNET_break (0);
    1047           0 :     return NULL;
    1048             :   }
    1049          47 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1050             :               "Trying to find chosen exchange `%s'\n",
    1051             :               chosen_exchange);
    1052             :   /* Check if the exchange is known */
    1053          73 :   for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
    1054             :   {
    1055             :     /* test it by checking URL */
    1056          71 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1057             :                 "Comparing chosen exchange url '%s' with known url '%s'.\n",
    1058             :                 chosen_exchange,
    1059             :                 exchange->url);
    1060          71 :     if (0 == strcmp (exchange->url,
    1061             :                      chosen_exchange))
    1062             :     {
    1063          45 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1064             :                   "The exchange `%s' is already known (good)\n",
    1065             :                   chosen_exchange);
    1066          45 :       break;
    1067             :     }
    1068             :   }
    1069          47 :   if (NULL == exchange)
    1070             :   {
    1071             :     /* This is a new exchange */
    1072           2 :     exchange = GNUNET_new (struct Exchange);
    1073           2 :     exchange->url = GNUNET_strdup (chosen_exchange);
    1074           2 :     exchange->pending = true;
    1075           2 :     GNUNET_CONTAINER_DLL_insert (exchange_head,
    1076             :                                  exchange_tail,
    1077             :                                  exchange);
    1078           2 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1079             :                 "The exchange `%s' is new\n",
    1080             :                 chosen_exchange);
    1081             :   }
    1082             : 
    1083          47 :   fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation);
    1084          47 :   fo->fc = fc;
    1085          47 :   fo->fc_cls = fc_cls;
    1086          47 :   fo->my_exchange = exchange;
    1087          47 :   if (NULL != wire_method)
    1088          23 :     fo->wire_method = GNUNET_strdup (wire_method);
    1089          47 :   GNUNET_CONTAINER_DLL_insert (exchange->fo_head,
    1090             :                                exchange->fo_tail,
    1091             :                                fo);
    1092          47 :   if ( (force_reload) &&
    1093           0 :        (GNUNET_TIME_absolute_is_past (exchange->first_retry)) )
    1094             :   {
    1095             :     /* increment exponential-backoff */
    1096           0 :     exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
    1097             :     /* do not allow forced check until both backoff and #FORCED_RELOAD_DELAY
    1098             :        are satisfied again */
    1099             :     exchange->first_retry
    1100           0 :       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_max (
    1101             :                                             exchange->retry_delay,
    1102             :                                             FORCED_RELOAD_DELAY));
    1103           0 :     TALER_EXCHANGE_check_keys_current (exchange->conn,
    1104             :                                        TALER_EXCHANGE_CKF_FORCE_DOWNLOAD);
    1105           0 :     return fo;
    1106             :   }
    1107             : 
    1108          47 :   now = GNUNET_TIME_absolute_get ();
    1109          47 :   (void) GNUNET_TIME_round_abs (&now);
    1110          47 :   if ( (! exchange->pending) &&
    1111          67 :        ( (NULL == fo->wire_method) ||
    1112          22 :          (NULL != get_wire_fees (exchange,
    1113             :                                  now,
    1114          22 :                                  fo->wire_method)) ) )
    1115             :   {
    1116             :     /* We are not currently waiting for a reply, immediately
    1117             :        return result */
    1118          38 :     GNUNET_assert (NULL == fo->at);
    1119          38 :     fo->at = GNUNET_SCHEDULER_add_now (&return_result,
    1120             :                                        fo);
    1121          38 :     return fo;
    1122             :   }
    1123             : 
    1124             :   /* If new or resumed, (re)try fetching /keys */
    1125           9 :   if ( (NULL == exchange->conn) &&
    1126           2 :        (NULL == exchange->retry_task) &&
    1127           2 :        (exchange->pending) )
    1128             :   {
    1129           2 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1130             :                 "Do not have current key data. Will request /keys now\n");
    1131           2 :     exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
    1132             :                                                      exchange);
    1133             :   }
    1134           7 :   else if ( (! exchange->pending) &&
    1135           7 :             (NULL == exchange->wire_task) &&
    1136           7 :             (NULL == exchange->wire_request) )
    1137             :   {
    1138           7 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1139             :                 "Do not have required wire data. Will re-request /wire now\n");
    1140           7 :     exchange->wire_task = GNUNET_SCHEDULER_add_now (&wire_task_cb,
    1141             :                                                     exchange);
    1142             :   }
    1143           9 :   return fo;
    1144             : }
    1145             : 
    1146             : 
    1147             : void
    1148          47 : TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo)
    1149             : {
    1150          47 :   struct Exchange *exchange = fo->my_exchange;
    1151             : 
    1152          47 :   if (NULL != fo->at)
    1153             :   {
    1154           1 :     GNUNET_SCHEDULER_cancel (fo->at);
    1155           1 :     fo->at = NULL;
    1156             :   }
    1157          47 :   GNUNET_CONTAINER_DLL_remove (exchange->fo_head,
    1158             :                                exchange->fo_tail,
    1159             :                                fo);
    1160          47 :   GNUNET_free (fo->wire_method);
    1161          47 :   GNUNET_free (fo);
    1162          47 : }
    1163             : 
    1164             : 
    1165             : /**
    1166             :  * Function called on each configuration section. Finds sections
    1167             :  * about exchanges, parses the entries and tries to connect to
    1168             :  * it in order to fetch /keys.
    1169             :  *
    1170             :  * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
    1171             :  * @param section name of the section
    1172             :  */
    1173             : static void
    1174         364 : accept_exchanges (void *cls,
    1175             :                   const char *section)
    1176             : {
    1177         364 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    1178             :   char *url;
    1179             :   char *mks;
    1180             :   struct Exchange *exchange;
    1181             :   char *currency;
    1182             : 
    1183         364 :   if (0 != strncasecmp (section,
    1184             :                         "merchant-exchange-",
    1185             :                         strlen ("merchant-exchange-")))
    1186         348 :     return;
    1187          32 :   if (GNUNET_OK !=
    1188          32 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1189             :                                              section,
    1190             :                                              "CURRENCY",
    1191             :                                              &currency))
    1192             :   {
    1193           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1194             :                                section,
    1195             :                                "CURRENCY");
    1196           0 :     return;
    1197             :   }
    1198          32 :   if (0 != strcasecmp (currency,
    1199             :                        TMH_currency))
    1200             :   {
    1201          16 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1202             :                 "Exchange given in section `%s' is for another currency. Skipping.\n",
    1203             :                 section);
    1204          16 :     GNUNET_free (currency);
    1205          16 :     return;
    1206             :   }
    1207          16 :   GNUNET_free (currency);
    1208          16 :   if (GNUNET_OK !=
    1209          16 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1210             :                                              section,
    1211             :                                              "EXCHANGE_BASE_URL",
    1212             :                                              &url))
    1213             :   {
    1214           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1215             :                                section,
    1216             :                                "EXCHANGE_BASE_URL");
    1217           0 :     return;
    1218             :   }
    1219          16 :   exchange = GNUNET_new (struct Exchange);
    1220          16 :   exchange->url = url;
    1221          16 :   if (GNUNET_OK ==
    1222          16 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1223             :                                              section,
    1224             :                                              "MASTER_KEY",
    1225             :                                              &mks))
    1226             :   {
    1227          16 :     if (GNUNET_OK ==
    1228          16 :         GNUNET_CRYPTO_eddsa_public_key_from_string (mks,
    1229             :                                                     strlen (mks),
    1230             :                                                     &exchange->master_pub.
    1231             :                                                     eddsa_pub))
    1232             :     {
    1233          16 :       exchange->trusted = true;
    1234             :     }
    1235             :     else
    1236             :     {
    1237           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1238             :                                  section,
    1239             :                                  "MASTER_KEY",
    1240             :                                  _ ("ill-formed EdDSA key"));
    1241             :     }
    1242          16 :     GNUNET_free (mks);
    1243             :   }
    1244             :   else
    1245             :   {
    1246           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1247             :                 "MASTER_KEY missing in section '%s', not trusting exchange\n",
    1248             :                 section);
    1249             : 
    1250             :   }
    1251          16 :   GNUNET_CONTAINER_DLL_insert (exchange_head,
    1252             :                                exchange_tail,
    1253             :                                exchange);
    1254          16 :   exchange->pending = true;
    1255          16 :   GNUNET_assert (NULL == exchange->retry_task);
    1256          16 :   exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
    1257             :                                                    exchange);
    1258             : }
    1259             : 
    1260             : 
    1261             : int
    1262          16 : TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
    1263             : {
    1264             :   merchant_curl_ctx
    1265          16 :     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1266             :                         &merchant_curl_rc);
    1267          16 :   if (NULL == merchant_curl_ctx)
    1268             :   {
    1269           0 :     GNUNET_break (0);
    1270           0 :     return GNUNET_SYSERR;
    1271             :   }
    1272          16 :   GNUNET_CURL_enable_async_scope_header (merchant_curl_ctx,
    1273             :                                          "Taler-Correlation-Id");
    1274          16 :   merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx);
    1275             :   /* get exchanges from the merchant configuration and try to connect to them */
    1276          16 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    1277             :                                          &accept_exchanges,
    1278             :                                          (void *) cfg);
    1279             :   /* build JSON with list of trusted exchanges (will be included in contracts) */
    1280          16 :   TMH_trusted_exchanges = json_array ();
    1281          32 :   for (struct Exchange *exchange = exchange_head;
    1282             :        NULL != exchange;
    1283          16 :        exchange = exchange->next)
    1284             :   {
    1285             :     json_t *j_exchange;
    1286             : 
    1287          16 :     if (! exchange->trusted)
    1288           0 :       continue;
    1289          16 :     j_exchange = GNUNET_JSON_PACK (
    1290             :       GNUNET_JSON_pack_string ("url",
    1291             :                                exchange->url),
    1292             :       GNUNET_JSON_pack_data_auto ("master_pub",
    1293             :                                   &exchange->master_pub));
    1294          16 :     GNUNET_assert (0 ==
    1295             :                    json_array_append_new (TMH_trusted_exchanges,
    1296             :                                           j_exchange));
    1297             :   }
    1298          16 :   return json_array_size (TMH_trusted_exchanges);
    1299             : }
    1300             : 
    1301             : 
    1302             : void
    1303          16 : TMH_EXCHANGES_done ()
    1304             : {
    1305          34 :   while (NULL != exchange_head)
    1306          18 :     free_exchange_entry (exchange_head);
    1307          16 :   GNUNET_CURL_fini (merchant_curl_ctx);
    1308          16 :   merchant_curl_ctx = NULL;
    1309          16 :   GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
    1310          16 :   merchant_curl_rc = NULL;
    1311          16 :   json_decref (TMH_trusted_exchanges);
    1312          16 :   TMH_trusted_exchanges = NULL;
    1313          16 : }
    1314             : 
    1315             : 
    1316             : /* end of taler-merchant-httpd_exchanges.c */

Generated by: LCOV version 1.14