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

Generated by: LCOV version 1.14