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: 71 391 18.2 %
Date: 2022-06-30 06:15:34 Functions: 5 16 31.2 %
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           3 : retry_exchange (void *cls)
     303             : {
     304           3 :   struct Exchange *exchange = cls;
     305             : 
     306             :   /* might be a scheduled reload and not our first attempt */
     307           3 :   exchange->retry_task = NULL;
     308           3 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     309             :               "Connecting to exchange %s in retry_exchange()\n",
     310             :               exchange->url);
     311           3 :   if (NULL != exchange->conn)
     312             :   {
     313           0 :     TALER_EXCHANGE_disconnect (exchange->conn);
     314           0 :     exchange->conn = NULL;
     315             :   }
     316           6 :   exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx,
     317           3 :                                            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           3 :   GNUNET_break (NULL != exchange->conn);
     324           3 : }
     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           0 : 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           0 :   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           0 :   if (NULL == f)
     355             :   {
     356           0 :     f = GNUNET_new (struct FeesByWireMethod);
     357           0 :     f->wire_method = GNUNET_strdup (wire_method);
     358           0 :     f->payto_uri = GNUNET_strdup (payto_uri);
     359           0 :     GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
     360             :                                  exchange->wire_fees_tail,
     361             :                                  f);
     362             :   }
     363           0 :   endp = f->af;
     364           0 :   while ( (NULL != endp) &&
     365           0 :           (NULL != endp->next) )
     366           0 :     endp = endp->next;
     367           0 :   while ( (NULL != endp) &&
     368           0 :           (NULL != fees) &&
     369           0 :           (GNUNET_TIME_timestamp_cmp (fees->start_date,
     370             :                                       <,
     371             :                                       endp->end_date)) )
     372           0 :     fees = fees->next;
     373           0 :   if ( (NULL != endp) &&
     374           0 :        (NULL != fees) &&
     375           0 :        (GNUNET_TIME_timestamp_cmp (fees->start_date,
     376             :                                    !=,
     377             :                                    endp->end_date)) )
     378             :   {
     379             :     /* Hole in the fee structure, not allowed! */
     380           0 :     GNUNET_break_op (0);
     381           0 :     return TALER_EC_MERCHANT_GENERIC_HOLE_IN_WIRE_FEE_STRUCTURE;
     382             :   }
     383           0 :   while (NULL != fees)
     384             :   {
     385             :     struct GNUNET_HashCode h_wire_method;
     386             :     enum GNUNET_DB_QueryStatus qs;
     387             : 
     388           0 :     af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
     389           0 :     *af = *fees;
     390           0 :     GNUNET_CRYPTO_hash (wire_method,
     391           0 :                         strlen (wire_method) + 1,
     392             :                         &h_wire_method);
     393           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     394             :                 "Storing wire fee for `%s' and method `%s' at %s in DB; the fee is %s\n",
     395             :                 TALER_B2S (master_pub),
     396             :                 wire_method,
     397             :                 GNUNET_TIME_timestamp2s (af->start_date),
     398             :                 TALER_amount2s (&af->fees.wire));
     399           0 :     TMH_db->preflight (TMH_db->cls);
     400           0 :     if (GNUNET_OK !=
     401           0 :         TMH_db->start (TMH_db->cls,
     402             :                        "store wire fee"))
     403             :     {
     404           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     405             :                   "Failed to start database transaction to store exchange wire fees (will try to continue anyway)!\n");
     406           0 :       GNUNET_free (af);
     407           0 :       fees = fees->next;
     408           0 :       continue;
     409             :     }
     410           0 :     qs = TMH_db->store_wire_fee_by_exchange (TMH_db->cls,
     411             :                                              master_pub,
     412             :                                              &h_wire_method,
     413           0 :                                              &af->fees,
     414             :                                              af->start_date,
     415             :                                              af->end_date,
     416           0 :                                              &af->master_sig);
     417           0 :     if (0 > qs)
     418             :     {
     419           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     420             :                   "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
     421           0 :       GNUNET_free (af);
     422           0 :       fees = fees->next;
     423           0 :       TMH_db->rollback (TMH_db->cls);
     424           0 :       continue;
     425             :     }
     426           0 :     if (0 == qs)
     427             :     {
     428             :       /* Entry was already in DB, fine, continue as if we had succeeded */
     429           0 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     430             :                   "Fees already in DB, rolling back transaction attempt!\n");
     431           0 :       TMH_db->rollback (TMH_db->cls);
     432             :     }
     433           0 :     if (0 < qs)
     434             :     {
     435             :       /* Inserted into DB, make sure transaction completes */
     436           0 :       qs = TMH_db->commit (TMH_db->cls);
     437           0 :       if (0 > qs)
     438             :       {
     439           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     440             :                     "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
     441           0 :         GNUNET_free (af);
     442           0 :         fees = fees->next;
     443           0 :         continue;
     444             :       }
     445             :     }
     446           0 :     af->next = NULL;
     447           0 :     if (NULL == endp)
     448           0 :       f->af = af;
     449             :     else
     450           0 :       endp->next = af;
     451           0 :     endp = af;
     452           0 :     fees = fees->next;
     453             :   }
     454           0 :   return TALER_EC_NONE;
     455             : }
     456             : 
     457             : 
     458             : /**
     459             :  * Function called with information about the wire accounts
     460             :  * of the exchange.  Stores the wire fees with the
     461             :  * exchange for laster use.
     462             :  *
     463             :  * @param exchange the exchange
     464             :  * @param master_pub public key of the exchange
     465             :  * @param accounts_len length of the @a accounts array
     466             :  * @param accounts list of wire accounts of the exchange
     467             :  * @return #TALER_EC_NONE on success
     468             :  */
     469             : static enum TALER_ErrorCode
     470           0 : process_wire_accounts (struct Exchange *exchange,
     471             :                        const struct TALER_MasterPublicKeyP *master_pub,
     472             :                        unsigned int accounts_len,
     473             :                        const struct TALER_EXCHANGE_WireAccount *accounts)
     474             : {
     475           0 :   for (unsigned int i = 0; i<accounts_len; i++)
     476             :   {
     477             :     enum TALER_ErrorCode ec;
     478             :     char *method;
     479             : 
     480           0 :     method = TALER_payto_get_method (accounts[i].payto_uri);
     481           0 :     if (NULL == method)
     482             :     {
     483             :       /* malformed payto:// URI returned by exchange */
     484           0 :       GNUNET_break_op (0);
     485           0 :       return TALER_EC_GENERIC_PAYTO_URI_MALFORMED;
     486             :     }
     487           0 :     ec = process_wire_fees (exchange,
     488             :                             master_pub,
     489             :                             method,
     490           0 :                             accounts[i].payto_uri,
     491           0 :                             accounts[i].fees);
     492           0 :     GNUNET_free (method);
     493           0 :     if (TALER_EC_NONE != ec)
     494           0 :       return ec;
     495             :   }
     496           0 :   return TALER_EC_NONE;
     497             : }
     498             : 
     499             : 
     500             : /**
     501             :  * Obtain applicable fees for @a exchange and @a wire_method.
     502             :  *
     503             :  * @param exchange the exchange to query
     504             :  * @param now current time
     505             :  * @param wire_method the wire method we want the fees for
     506             :  * @return NULL if we do not have fees for this method yet
     507             :  */
     508             : static struct FeesByWireMethod *
     509           0 : get_wire_fees (struct Exchange *exchange,
     510             :                struct GNUNET_TIME_Timestamp now,
     511             :                const char *wire_method)
     512             : {
     513           0 :   for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
     514             :        NULL != fbw;
     515           0 :        fbw = fbw->next)
     516             :   {
     517           0 :     if (0 == strcasecmp (fbw->wire_method,
     518             :                          wire_method) )
     519             :     {
     520             :       struct TALER_EXCHANGE_WireAggregateFees *af;
     521             : 
     522             :       /* Advance through list up to current time */
     523           0 :       while ( (NULL != (af = fbw->af)) &&
     524           0 :               (GNUNET_TIME_timestamp_cmp (now,
     525             :                                           >=,
     526             :                                           af->end_date)) )
     527             :       {
     528           0 :         fbw->af = af->next;
     529           0 :         GNUNET_free (af);
     530             :       }
     531           0 :       return fbw;
     532             :     }
     533           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     534             :                 "Exchange supports `%s' as a wire method (but we do not use that one)\n",
     535             :                 fbw->wire_method);
     536             :   }
     537           0 :   return NULL;
     538             : }
     539             : 
     540             : 
     541             : /**
     542             :  * Check if we have any remaining pending requests for the
     543             :  * given @a exchange, and if we have the required data, call
     544             :  * the callback.
     545             :  *
     546             :  * @param exchange the exchange to check for pending find operations
     547             :  * @return true if we need /wire data from @a exchange
     548             :  */
     549             : static bool
     550           0 : process_find_operations (struct Exchange *exchange)
     551             : {
     552             :   struct TMH_EXCHANGES_FindOperation *fn;
     553             :   struct GNUNET_TIME_Timestamp now;
     554             :   bool need_wire;
     555             : 
     556           0 :   now = GNUNET_TIME_timestamp_get ();
     557           0 :   need_wire = false;
     558           0 :   for (struct TMH_EXCHANGES_FindOperation *fo = exchange->fo_head;
     559             :        NULL != fo;
     560           0 :        fo = fn)
     561             :   {
     562             :     struct FeesByWireMethod *fbw;
     563             : 
     564           0 :     fn = fo->next;
     565           0 :     if (NULL != fo->wire_method)
     566             :     {
     567             :       /* Find fee structure for our wire method */
     568           0 :       fbw = get_wire_fees (exchange,
     569             :                            now,
     570           0 :                            fo->wire_method);
     571           0 :       if (NULL == fbw)
     572             :       {
     573           0 :         need_wire = true;
     574             :         /* Do not warn if this is before our first attempt */
     575           0 :         if (! GNUNET_TIME_relative_is_zero (exchange->wire_retry_delay))
     576           0 :           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     577             :                       "Exchange does not support `%s' wire method (will retry later)\n",
     578             :                       fo->wire_method);
     579           0 :         fbw = NULL;
     580           0 :         continue;
     581             :       }
     582           0 :       if (NULL == fbw->af)
     583             :       {
     584             :         /* Disagreement on the current time */
     585           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     586             :                     "Exchange has no wire fees configured for `%s' wire method (will retry later)\n",
     587             :                     fo->wire_method);
     588           0 :         fbw = NULL;
     589           0 :         continue;
     590             :       }
     591           0 :       if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date,
     592             :                                      >,
     593             :                                      now))
     594             :       {
     595             :         /* Disagreement on the current time */
     596           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     597             :                     "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n",
     598             :                     GNUNET_TIME_relative2s (
     599             :                       GNUNET_TIME_absolute_get_remaining (
     600             :                         fbw->af->start_date.abs_time),
     601             :                       true));
     602           0 :         fbw = NULL;
     603           0 :         continue;
     604             :       }
     605             :     }
     606             :     else
     607             :     {
     608             :       /* no wire transfer method given, so we yield no fee */
     609           0 :       fbw = NULL;
     610             :     }
     611             :     {
     612           0 :       struct TALER_EXCHANGE_HttpResponse hr = {
     613             :         .http_status = MHD_HTTP_OK,
     614             :       };
     615             : 
     616           0 :       fo->fc (fo->fc_cls,
     617             :               &hr,
     618             :               exchange->conn,
     619             :               (NULL != fbw) ? fbw->payto_uri : NULL,
     620           0 :               (NULL != fbw) ? &fbw->af->fees.wire : NULL,
     621           0 :               exchange->trusted);
     622             :     }
     623           0 :     TMH_EXCHANGES_find_exchange_cancel (fo);
     624             :   }
     625           0 :   return need_wire;
     626             : }
     627             : 
     628             : 
     629             : static void
     630             : wire_task_cb (void *cls);
     631             : 
     632             : 
     633             : /**
     634             :  * Callbacks of this type are used to serve the result of submitting a
     635             :  * wire format inquiry request to a exchange.
     636             :  *
     637             :  * If the request fails to generate a valid response from the
     638             :  * exchange, @a http_status will also be zero.
     639             :  *
     640             :  * Must only be called if 'exchange->pending' is #GNUNET_NO,
     641             :  * that is #TALER_EXCHANGE_get_keys() will succeed.
     642             :  *
     643             :  * @param cls closure, a `struct Exchange`
     644             :  * @param hr HTTP response details
     645             :  * @param accounts_len length of the @a accounts array
     646             :  * @param accounts list of wire accounts of the exchange, NULL on error
     647             :  */
     648             : static void
     649           0 : handle_wire_data (void *cls,
     650             :                   const struct TALER_EXCHANGE_HttpResponse *hr,
     651             :                   unsigned int accounts_len,
     652             :                   const struct TALER_EXCHANGE_WireAccount *accounts)
     653             : {
     654           0 :   struct Exchange *exchange = cls;
     655             :   const struct TALER_EXCHANGE_Keys *keys;
     656             :   enum TALER_ErrorCode ecx;
     657             : 
     658           0 :   exchange->wire_request = NULL;
     659           0 :   if (MHD_HTTP_OK != hr->http_status)
     660             :   {
     661             :     struct TMH_EXCHANGES_FindOperation *fo;
     662             : 
     663           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     664             :                 "Failed to obtain /wire details from `%s': %u/%d\n",
     665             :                 exchange->url,
     666             :                 hr->http_status,
     667             :                 hr->ec);
     668           0 :     while (NULL != (fo = exchange->fo_head))
     669             :     {
     670           0 :       fo->fc (fo->fc_cls,
     671             :               hr,
     672             :               exchange->conn,
     673             :               NULL,
     674             :               NULL,
     675             :               GNUNET_NO);
     676           0 :       TMH_EXCHANGES_find_exchange_cancel (fo);
     677             :     }
     678           0 :     return;
     679             :   }
     680           0 :   keys = TALER_EXCHANGE_get_keys (exchange->conn);
     681           0 :   GNUNET_assert (NULL != keys);
     682           0 :   ecx = process_wire_accounts (exchange,
     683             :                                &keys->master_pub,
     684             :                                accounts_len,
     685             :                                accounts);
     686           0 :   if (TALER_EC_NONE != ecx)
     687             :   {
     688             :     /* Report hard failure to all callbacks! */
     689             :     struct TMH_EXCHANGES_FindOperation *fo;
     690           0 :     struct TALER_EXCHANGE_HttpResponse hrx = {
     691             :       .ec = ecx,
     692             :       .http_status = 0,
     693           0 :       .reply = hr->reply
     694             :     };
     695             : 
     696           0 :     GNUNET_break_op (0);
     697           0 :     while (NULL != (fo = exchange->fo_head))
     698             :     {
     699           0 :       fo->fc (fo->fc_cls,
     700             :               &hrx,
     701             :               NULL,
     702             :               NULL,
     703             :               NULL,
     704             :               GNUNET_NO);
     705           0 :       TMH_EXCHANGES_find_exchange_cancel (fo);
     706             :     }
     707           0 :     return;
     708             :   }
     709           0 :   if ( (process_find_operations (exchange)) &&
     710           0 :        (NULL == exchange->wire_task) &&
     711           0 :        (NULL == exchange->wire_request) )
     712             :   {
     713             :     /* need to run /wire again. But as we DID get a successful reply,
     714             :        and as the exchange is unlikely to offer new wire methods very
     715             :        frequently, start with some significant delay */
     716             :     exchange->wire_retry_delay
     717           0 :       = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MINUTES,
     718             :                                   exchange->wire_retry_delay);
     719           0 :     exchange->wire_retry_delay = RETRY_BACKOFF (exchange->wire_retry_delay);
     720           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     721             :                 "Exchange does not support our wire method. Retrying in %s\n",
     722             : 
     723             :                 GNUNET_STRINGS_relative_time_to_string (
     724             :                   exchange->wire_retry_delay,
     725             :                   GNUNET_YES));
     726             :     exchange->wire_task
     727           0 :       = GNUNET_SCHEDULER_add_delayed (exchange->wire_retry_delay,
     728             :                                       &wire_task_cb,
     729             :                                       exchange);
     730             :   }
     731             : }
     732             : 
     733             : 
     734             : /**
     735             :  * Check if we have any remaining pending requests for the
     736             :  * given @a exchange, and if we have the required data, call
     737             :  * the callback.  If requests without /wire data remain,
     738             :  * retry the /wire request after some delay.
     739             :  *
     740             :  * Must only be called if 'exchange->pending' is #GNUNET_NO,
     741             :  * that is #TALER_EXCHANGE_get_keys() will succeed.
     742             :  *
     743             :  * @param cls a `struct Exchange` to check
     744             :  */
     745             : static void
     746           0 : wire_task_cb (void *cls)
     747             : {
     748           0 :   struct Exchange *exchange = cls;
     749             : 
     750           0 :   exchange->wire_task = NULL;
     751           0 :   GNUNET_assert (! exchange->pending);
     752           0 :   if (! process_find_operations (exchange))
     753           0 :     return; /* no more need */
     754           0 :   GNUNET_assert (NULL == exchange->wire_request);
     755           0 :   exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
     756             :                                                 &handle_wire_data,
     757             :                                                 exchange);
     758             : }
     759             : 
     760             : 
     761             : /**
     762             :  * Free @a exchange.
     763             :  *
     764             :  * @param[in] exchange entry to free
     765             :  */
     766             : static void
     767           3 : free_exchange_entry (struct Exchange *exchange)
     768             : {
     769             :   struct FeesByWireMethod *f;
     770             : 
     771           3 :   GNUNET_CONTAINER_DLL_remove (exchange_head,
     772             :                                exchange_tail,
     773             :                                exchange);
     774           3 :   while (NULL != (f = exchange->wire_fees_head))
     775             :   {
     776             :     struct TALER_EXCHANGE_WireAggregateFees *af;
     777             : 
     778           0 :     GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head,
     779             :                                  exchange->wire_fees_tail,
     780             :                                  f);
     781           0 :     while (NULL != (af = f->af))
     782             :     {
     783           0 :       f->af = af->next;
     784           0 :       GNUNET_free (af);
     785             :     }
     786           0 :     GNUNET_free (f->wire_method);
     787           0 :     GNUNET_free (f->payto_uri);
     788           0 :     GNUNET_free (f);
     789             :   }
     790           3 :   if (NULL != exchange->wire_request)
     791             :   {
     792           0 :     TALER_EXCHANGE_wire_cancel (exchange->wire_request);
     793           0 :     exchange->wire_request = NULL;
     794             :   }
     795           3 :   if (NULL != exchange->wire_task)
     796             :   {
     797           0 :     GNUNET_SCHEDULER_cancel (exchange->wire_task);
     798           0 :     exchange->wire_task = NULL;
     799             :   }
     800           3 :   if (NULL != exchange->conn)
     801             :   {
     802           3 :     TALER_EXCHANGE_disconnect (exchange->conn);
     803           3 :     exchange->conn = NULL;
     804             :   }
     805           3 :   if (NULL != exchange->retry_task)
     806             :   {
     807           0 :     GNUNET_SCHEDULER_cancel (exchange->retry_task);
     808           0 :     exchange->retry_task = NULL;
     809             :   }
     810           3 :   GNUNET_assert (NULL == exchange->fo_head);
     811           3 :   GNUNET_assert (NULL == exchange->fo_tail);
     812           3 :   GNUNET_free (exchange->url);
     813           3 :   GNUNET_free (exchange);
     814           3 : }
     815             : 
     816             : 
     817             : /**
     818             :  * We failed downloading /keys from @a exchange. Tell clients
     819             :  * about our failure, abort pending operations and retry later.
     820             :  *
     821             :  * @param exchange exchange that failed
     822             :  * @param hr details about the HTTP reply
     823             :  * @param compat version compatibility data
     824             :  */
     825             : static void
     826           0 : fail_and_retry (struct Exchange *exchange,
     827             :                 const struct TALER_EXCHANGE_HttpResponse *hr,
     828             :                 enum TALER_EXCHANGE_VersionCompatibility compat)
     829             : {
     830             :   struct TMH_EXCHANGES_FindOperation *fo;
     831             : 
     832           0 :   exchange->pending = true;
     833           0 :   if (NULL != exchange->wire_request)
     834             :   {
     835           0 :     TALER_EXCHANGE_wire_cancel (exchange->wire_request);
     836           0 :     exchange->wire_request = NULL;
     837             :   }
     838           0 :   if (NULL != exchange->wire_task)
     839             :   {
     840           0 :     GNUNET_SCHEDULER_cancel (exchange->wire_task);
     841           0 :     exchange->wire_task = NULL;
     842             :   }
     843           0 :   while (NULL != (fo = exchange->fo_head))
     844             :   {
     845           0 :     fo->fc (fo->fc_cls,
     846             :             hr,
     847             :             NULL,
     848             :             NULL,
     849             :             NULL,
     850             :             GNUNET_NO);
     851           0 :     TMH_EXCHANGES_find_exchange_cancel (fo);
     852             :   }
     853           0 :   if (TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER == compat)
     854             :   {
     855             :     /* Log hard error: we likely need admin help! */
     856           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     857             :                 "Exchange `%s' runs an incompatible more recent version of the Taler protocol. Will not retry. This client may need to be updated.\n",
     858             :                 exchange->url);
     859             :     /* Theoretically, the exchange could downgrade,
     860             :        but let's not be too aggressive about retries
     861             :        on this one. */
     862           0 :     exchange->retry_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_HOURS,
     863             :                                                       exchange->retry_delay);
     864             :   }
     865           0 :   if ( (NULL == exchange->fo_head) &&
     866           0 :        (TALER_EC_GENERIC_CONFIGURATION_INVALID == hr->ec) )
     867             :   {
     868             :     /* This can NEVER work, so don't retry */
     869           0 :     free_exchange_entry (exchange);
     870           0 :     return;
     871             :   }
     872           0 :   exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
     873           0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     874             :               "Failed to fetch /keys from `%s': %d/%u, retrying in %s\n",
     875             :               exchange->url,
     876             :               (int) hr->ec,
     877             :               hr->http_status,
     878             :               GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay,
     879             :                                                       GNUNET_YES));
     880           0 :   if (NULL != exchange->retry_task)
     881           0 :     GNUNET_SCHEDULER_cancel (exchange->retry_task);
     882           0 :   exchange->first_retry = GNUNET_TIME_relative_to_absolute (
     883             :     exchange->retry_delay);
     884           0 :   exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
     885             :                                                        &retry_exchange,
     886             :                                                        exchange);
     887             : }
     888             : 
     889             : 
     890             : /**
     891             :  * Function called with information about who is auditing
     892             :  * a particular exchange and what key the exchange is using.
     893             :  *
     894             :  * @param cls closure, will be `struct Exchange` so that
     895             :  *   when this function gets called, it will change the flag 'pending'
     896             :  *   to 'false'. Note: 'keys' is automatically saved inside the exchange's
     897             :  *   handle, which is contained inside 'struct Exchange', when
     898             :  *   this callback is called. Thus, once 'pending' turns 'false',
     899             :  *   it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
     900             :  *   in order to get the "good" keys.
     901             :  * @param hr http response details
     902             :  * @param keys information about the various keys used
     903             :  *        by the exchange
     904             :  * @param compat version compatibility data
     905             :  */
     906             : static void
     907           0 : keys_mgmt_cb (void *cls,
     908             :               const struct TALER_EXCHANGE_HttpResponse *hr,
     909             :               const struct TALER_EXCHANGE_Keys *keys,
     910             :               enum TALER_EXCHANGE_VersionCompatibility compat)
     911             : {
     912           0 :   struct Exchange *exchange = cls;
     913             :   struct GNUNET_TIME_Timestamp expire;
     914             :   struct GNUNET_TIME_Relative delay;
     915             : 
     916           0 :   if ( (MHD_HTTP_OK != hr->http_status) ||
     917             :        (NULL == keys) )
     918             :   {
     919           0 :     fail_and_retry (exchange,
     920             :                     hr,
     921             :                     compat);
     922           0 :     return;
     923             :   }
     924           0 :   if ( (exchange->trusted) &&
     925           0 :        (0 != GNUNET_memcmp (&exchange->master_pub,
     926             :                             &keys->master_pub)) )
     927             :   {
     928             :     /* master pub differs => do not trust the exchange (without auditor) */
     929           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     930             :                 "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n",
     931             :                 exchange->url);
     932           0 :     exchange->trusted = false;
     933             :   }
     934           0 :   if (! exchange->trusted)
     935             :   {
     936           0 :     exchange->master_pub = keys->master_pub;
     937           0 :     for (struct Exchange *e = exchange_head;
     938             :          NULL != e;
     939           0 :          e = e->next)
     940             :     {
     941           0 :       if (e == exchange)
     942           0 :         continue;
     943           0 :       if (! e->trusted)
     944           0 :         continue;
     945           0 :       if (0 ==
     946           0 :           GNUNET_memcmp (&e->master_pub,
     947             :                          &exchange->master_pub))
     948           0 :         exchange->trusted = true; /* same exchange, different URL => trust applies */
     949             :     }
     950             :   }
     951           0 :   if (0 != (TALER_EXCHANGE_VC_NEWER & compat))
     952             :   {
     953             :     /* Warn user exactly once about need to upgrade */
     954             :     static int once;
     955             : 
     956           0 :     if (0 == once)
     957             :     {
     958           0 :       once = 1;
     959           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     960             :                   "Exchange `%s' runs a more recent version of the Taler protocol. You may want to update this client.\n",
     961             :                   exchange->url);
     962             :     }
     963             :   }
     964             : 
     965             :   /* store exchange online signing keys in our DB */
     966           0 :   for (unsigned int i = 0; i<keys->num_sign_keys; i++)
     967             :   {
     968           0 :     struct TALER_EXCHANGE_SigningPublicKey *sign_key = &keys->sign_keys[i];
     969             :     enum GNUNET_DB_QueryStatus qs;
     970             : 
     971           0 :     TMH_db->preflight (TMH_db->cls);
     972           0 :     qs = TMH_db->insert_exchange_signkey (TMH_db->cls,
     973             :                                           &keys->master_pub,
     974           0 :                                           &sign_key->key,
     975             :                                           sign_key->valid_from,
     976             :                                           sign_key->valid_until,
     977             :                                           sign_key->valid_legal,
     978           0 :                                           &sign_key->master_sig);
     979             :     /* 0 is OK, we may already have the key in the DB! */
     980           0 :     if (0 > qs)
     981             :     {
     982           0 :       GNUNET_break (0);
     983           0 :       fail_and_retry (exchange,
     984             :                       hr,
     985             :                       compat);
     986           0 :       return;
     987             :     }
     988             :   }
     989             : 
     990           0 :   exchange->first_retry = GNUNET_TIME_relative_to_absolute (RELOAD_DELAY);
     991           0 :   expire = TALER_EXCHANGE_check_keys_current (exchange->conn,
     992             :                                               TALER_EXCHANGE_CKF_NONE);
     993           0 :   if (0 == GNUNET_TIME_absolute_is_zero (expire.abs_time))
     994           0 :     delay = RELOAD_DELAY;
     995             :   else
     996           0 :     delay = GNUNET_TIME_absolute_get_remaining (expire.abs_time);
     997           0 :   if (GNUNET_TIME_relative_is_zero (delay))
     998             :   {
     999           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1000             :                 "/keys response from exchange expired immediately! Retrying in 1 minute.\n");
    1001           0 :     delay = GNUNET_TIME_UNIT_MINUTES;
    1002             :   }
    1003           0 :   exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
    1004           0 :   if (NULL != exchange->retry_task)
    1005           0 :     GNUNET_SCHEDULER_cancel (exchange->retry_task);
    1006             :   exchange->retry_task
    1007           0 :     = GNUNET_SCHEDULER_add_delayed (delay,
    1008             :                                     &retry_exchange,
    1009             :                                     exchange);
    1010           0 :   exchange->pending = false;
    1011           0 :   if ( (process_find_operations (exchange)) &&
    1012           0 :        (NULL == exchange->wire_request) &&
    1013           0 :        (NULL == exchange->wire_task) )
    1014             :   {
    1015           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1016             :                 "Got key data, but also need wire data. Will request /wire now\n");
    1017           0 :     exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
    1018             :                                                   &handle_wire_data,
    1019             :                                                   exchange);
    1020             :   }
    1021             : }
    1022             : 
    1023             : 
    1024             : /**
    1025             :  * Task to return find operation result asynchronously to caller.
    1026             :  *
    1027             :  * @param cls a `struct TMH_EXCHANGES_FindOperation`
    1028             :  */
    1029             : static void
    1030           0 : return_result (void *cls)
    1031             : {
    1032           0 :   struct TMH_EXCHANGES_FindOperation *fo = cls;
    1033           0 :   struct Exchange *exchange = fo->my_exchange;
    1034             : 
    1035           0 :   fo->at = NULL;
    1036           0 :   if ( (process_find_operations (exchange)) &&
    1037           0 :        (NULL == exchange->wire_request) &&
    1038           0 :        (! exchange->pending) &&
    1039           0 :        (NULL != exchange->wire_task) )
    1040             :   {
    1041           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1042             :                 "Do not have current wire data. Will re-request /wire in 1 minute\n");
    1043             :     exchange->wire_task
    1044           0 :       = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
    1045             :                                       &wire_task_cb,
    1046             :                                       exchange);
    1047             :   }
    1048           0 : }
    1049             : 
    1050             : 
    1051             : struct TMH_EXCHANGES_FindOperation *
    1052           0 : TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
    1053             :                              const char *wire_method,
    1054             :                              int force_reload,
    1055             :                              TMH_EXCHANGES_FindContinuation fc,
    1056             :                              void *fc_cls)
    1057             : {
    1058             :   struct Exchange *exchange;
    1059             :   struct TMH_EXCHANGES_FindOperation *fo;
    1060             :   struct GNUNET_TIME_Timestamp now;
    1061             : 
    1062           0 :   if (NULL == merchant_curl_ctx)
    1063             :   {
    1064           0 :     GNUNET_break (0);
    1065           0 :     return NULL;
    1066             :   }
    1067           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1068             :               "Trying to find chosen exchange `%s'\n",
    1069             :               chosen_exchange);
    1070             :   /* Check if the exchange is known */
    1071           0 :   for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
    1072             :   {
    1073             :     /* test it by checking URL */
    1074           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1075             :                 "Comparing chosen exchange url '%s' with known url '%s'.\n",
    1076             :                 chosen_exchange,
    1077             :                 exchange->url);
    1078           0 :     if (0 == strcmp (exchange->url,
    1079             :                      chosen_exchange))
    1080             :     {
    1081           0 :       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1082             :                   "The exchange `%s' is already known (good)\n",
    1083             :                   chosen_exchange);
    1084           0 :       break;
    1085             :     }
    1086             :   }
    1087           0 :   if (NULL == exchange)
    1088             :   {
    1089             :     /* This is a new exchange */
    1090           0 :     exchange = GNUNET_new (struct Exchange);
    1091           0 :     exchange->url = GNUNET_strdup (chosen_exchange);
    1092           0 :     exchange->pending = true;
    1093           0 :     GNUNET_CONTAINER_DLL_insert (exchange_head,
    1094             :                                  exchange_tail,
    1095             :                                  exchange);
    1096           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1097             :                 "The exchange `%s' is new\n",
    1098             :                 chosen_exchange);
    1099             :   }
    1100             : 
    1101           0 :   fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation);
    1102           0 :   fo->fc = fc;
    1103           0 :   fo->fc_cls = fc_cls;
    1104           0 :   fo->my_exchange = exchange;
    1105           0 :   if (NULL != wire_method)
    1106           0 :     fo->wire_method = GNUNET_strdup (wire_method);
    1107           0 :   GNUNET_CONTAINER_DLL_insert (exchange->fo_head,
    1108             :                                exchange->fo_tail,
    1109             :                                fo);
    1110           0 :   if ( (force_reload) &&
    1111           0 :        (GNUNET_TIME_absolute_is_past (exchange->first_retry)) )
    1112             :   {
    1113             :     /* increment exponential-backoff */
    1114           0 :     exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
    1115             :     /* do not allow forced check until both backoff and #FORCED_RELOAD_DELAY
    1116             :        are satisfied again */
    1117             :     exchange->first_retry
    1118           0 :       = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_max (
    1119             :                                             exchange->retry_delay,
    1120             :                                             FORCED_RELOAD_DELAY));
    1121           0 :     TALER_EXCHANGE_check_keys_current (exchange->conn,
    1122             :                                        TALER_EXCHANGE_CKF_FORCE_DOWNLOAD);
    1123           0 :     return fo;
    1124             :   }
    1125             : 
    1126           0 :   now = GNUNET_TIME_timestamp_get ();
    1127           0 :   if ( (! exchange->pending) &&
    1128           0 :        ( (NULL == fo->wire_method) ||
    1129           0 :          (NULL != get_wire_fees (exchange,
    1130             :                                  now,
    1131           0 :                                  fo->wire_method)) ) )
    1132             :   {
    1133             :     /* We are not currently waiting for a reply, immediately
    1134             :        return result */
    1135           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1136             :                 "The exchange `%s' is ready\n",
    1137             :                 chosen_exchange);
    1138           0 :     GNUNET_assert (NULL == fo->at);
    1139           0 :     fo->at = GNUNET_SCHEDULER_add_now (&return_result,
    1140             :                                        fo);
    1141           0 :     return fo;
    1142             :   }
    1143             : 
    1144             :   /* If new or resumed, (re)try fetching /keys */
    1145           0 :   if ( (NULL == exchange->conn) &&
    1146           0 :        (NULL == exchange->retry_task) &&
    1147           0 :        (exchange->pending) )
    1148             :   {
    1149           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1150             :                 "Do not have current /keys data for `%s'. Will request /keys now\n",
    1151             :                 chosen_exchange);
    1152           0 :     exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
    1153             :                                                      exchange);
    1154             :   }
    1155           0 :   else if ( (! exchange->pending) &&
    1156           0 :             (NULL == exchange->wire_task) &&
    1157           0 :             (NULL == exchange->wire_request) )
    1158             :   {
    1159           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1160             :                 "Do not have required wire data. Will re-request /wire now\n");
    1161           0 :     exchange->wire_task = GNUNET_SCHEDULER_add_now (&wire_task_cb,
    1162             :                                                     exchange);
    1163             :   }
    1164           0 :   return fo;
    1165             : }
    1166             : 
    1167             : 
    1168             : void
    1169           0 : TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo)
    1170             : {
    1171           0 :   struct Exchange *exchange = fo->my_exchange;
    1172             : 
    1173           0 :   if (NULL != fo->at)
    1174             :   {
    1175           0 :     GNUNET_SCHEDULER_cancel (fo->at);
    1176           0 :     fo->at = NULL;
    1177             :   }
    1178           0 :   GNUNET_CONTAINER_DLL_remove (exchange->fo_head,
    1179             :                                exchange->fo_tail,
    1180             :                                fo);
    1181           0 :   GNUNET_free (fo->wire_method);
    1182           0 :   GNUNET_free (fo);
    1183           0 : }
    1184             : 
    1185             : 
    1186             : /**
    1187             :  * Function called on each configuration section. Finds sections
    1188             :  * about exchanges, parses the entries and tries to connect to
    1189             :  * it in order to fetch /keys.
    1190             :  *
    1191             :  * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
    1192             :  * @param section name of the section
    1193             :  */
    1194             : static void
    1195          57 : accept_exchanges (void *cls,
    1196             :                   const char *section)
    1197             : {
    1198          57 :   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
    1199             :   char *url;
    1200             :   char *mks;
    1201             :   struct Exchange *exchange;
    1202             :   char *currency;
    1203             : 
    1204          57 :   if (0 != strncasecmp (section,
    1205             :                         "merchant-exchange-",
    1206             :                         strlen ("merchant-exchange-")))
    1207          54 :     return;
    1208           6 :   if (GNUNET_OK !=
    1209           6 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1210             :                                              section,
    1211             :                                              "CURRENCY",
    1212             :                                              &currency))
    1213             :   {
    1214           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1215             :                                section,
    1216             :                                "CURRENCY");
    1217           0 :     return;
    1218             :   }
    1219           6 :   if (0 != strcasecmp (currency,
    1220             :                        TMH_currency))
    1221             :   {
    1222           3 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1223             :                 "Exchange given in section `%s' is for another currency. Skipping.\n",
    1224             :                 section);
    1225           3 :     GNUNET_free (currency);
    1226           3 :     return;
    1227             :   }
    1228           3 :   GNUNET_free (currency);
    1229           3 :   if (GNUNET_OK !=
    1230           3 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1231             :                                              section,
    1232             :                                              "EXCHANGE_BASE_URL",
    1233             :                                              &url))
    1234             :   {
    1235           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1236             :                                section,
    1237             :                                "EXCHANGE_BASE_URL");
    1238           0 :     return;
    1239             :   }
    1240           3 :   exchange = GNUNET_new (struct Exchange);
    1241           3 :   exchange->url = url;
    1242           3 :   if (GNUNET_OK ==
    1243           3 :       GNUNET_CONFIGURATION_get_value_string (cfg,
    1244             :                                              section,
    1245             :                                              "MASTER_KEY",
    1246             :                                              &mks))
    1247             :   {
    1248           3 :     if (GNUNET_OK ==
    1249           3 :         GNUNET_CRYPTO_eddsa_public_key_from_string (mks,
    1250             :                                                     strlen (mks),
    1251             :                                                     &exchange->master_pub.
    1252             :                                                     eddsa_pub))
    1253             :     {
    1254           3 :       exchange->trusted = true;
    1255             :     }
    1256             :     else
    1257             :     {
    1258           0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    1259             :                                  section,
    1260             :                                  "MASTER_KEY",
    1261             :                                  _ ("ill-formed EdDSA key"));
    1262             :     }
    1263           3 :     GNUNET_free (mks);
    1264             :   }
    1265             :   else
    1266             :   {
    1267           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1268             :                 "MASTER_KEY missing in section '%s', not trusting exchange\n",
    1269             :                 section);
    1270             : 
    1271             :   }
    1272           3 :   GNUNET_CONTAINER_DLL_insert (exchange_head,
    1273             :                                exchange_tail,
    1274             :                                exchange);
    1275           3 :   exchange->pending = true;
    1276           3 :   GNUNET_assert (NULL == exchange->retry_task);
    1277           3 :   exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
    1278             :                                                    exchange);
    1279             : }
    1280             : 
    1281             : 
    1282             : enum GNUNET_GenericReturnValue
    1283           3 : TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
    1284             : {
    1285             :   merchant_curl_ctx
    1286           3 :     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1287             :                         &merchant_curl_rc);
    1288           3 :   if (NULL == merchant_curl_ctx)
    1289             :   {
    1290           0 :     GNUNET_break (0);
    1291           0 :     return GNUNET_SYSERR;
    1292             :   }
    1293           3 :   merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx);
    1294           3 :   GNUNET_CURL_enable_async_scope_header (merchant_curl_ctx,
    1295             :                                          "Taler-Correlation-Id");
    1296             :   /* get exchanges from the merchant configuration and try to connect to them */
    1297           3 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
    1298             :                                          &accept_exchanges,
    1299             :                                          (void *) cfg);
    1300             :   /* build JSON with list of trusted exchanges (will be included in contracts) */
    1301           3 :   TMH_trusted_exchanges = json_array ();
    1302           6 :   for (struct Exchange *exchange = exchange_head;
    1303             :        NULL != exchange;
    1304           3 :        exchange = exchange->next)
    1305             :   {
    1306             :     json_t *j_exchange;
    1307             : 
    1308           3 :     if (! exchange->trusted)
    1309           0 :       continue;
    1310           3 :     j_exchange = GNUNET_JSON_PACK (
    1311             :       GNUNET_JSON_pack_string ("url",
    1312             :                                exchange->url),
    1313             :       GNUNET_JSON_pack_data_auto ("master_pub",
    1314             :                                   &exchange->master_pub));
    1315           3 :     GNUNET_assert (0 ==
    1316             :                    json_array_append_new (TMH_trusted_exchanges,
    1317             :                                           j_exchange));
    1318             :   }
    1319           3 :   return json_array_size (TMH_trusted_exchanges);
    1320             : }
    1321             : 
    1322             : 
    1323             : void
    1324           3 : TMH_EXCHANGES_done ()
    1325             : {
    1326           6 :   while (NULL != exchange_head)
    1327           3 :     free_exchange_entry (exchange_head);
    1328           3 :   GNUNET_CURL_fini (merchant_curl_ctx);
    1329           3 :   merchant_curl_ctx = NULL;
    1330           3 :   GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
    1331           3 :   merchant_curl_rc = NULL;
    1332           3 :   json_decref (TMH_trusted_exchanges);
    1333           3 :   TMH_trusted_exchanges = NULL;
    1334           3 : }
    1335             : 
    1336             : 
    1337             : /* end of taler-merchant-httpd_exchanges.c */

Generated by: LCOV version 1.14