LCOV - code coverage report
Current view: top level - lib - exchange_api_get-keys.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 65.9 % 205 135
Test Date: 2026-10-29 19:05:17 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2014-2026 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU 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
      15              :   <http://www.gnu.org/licenses/>
      16              : */
      17              : /**
      18              :  * @file lib/exchange_api_get-keys.c
      19              :  * @brief Implementation of GET /keys
      20              :  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
      21              :  * @author Christian Grothoff
      22              :  */
      23              : #include "platform.h"  /* UNNECESSARY? */
      24              : #include <microhttpd.h>
      25              : #include <gnunet/gnunet_curl_lib.h>
      26              : #include "taler/taler_json_lib.h"
      27              : #include "taler/taler_exchange_service.h"  /* UNNECESSARY? */
      28              : #include "taler/taler_signatures.h"
      29              : #include "exchange_api_handle.h"
      30              : #include "exchange_api_curl_defaults.h"
      31              : #include "taler/taler_curl_lib.h"
      32              : 
      33              : /**
      34              :  * If the "Expire" cache control header is missing, for
      35              :  * how long do we assume the reply to be valid at least?
      36              :  */
      37              : #define DEFAULT_EXPIRATION GNUNET_TIME_UNIT_HOURS
      38              : 
      39              : /**
      40              :  * If the "Expire" cache control header is missing, for
      41              :  * how long do we assume the reply to be valid at least?
      42              :  */
      43              : #define MINIMUM_EXPIRATION GNUNET_TIME_relative_multiply ( \
      44              :           GNUNET_TIME_UNIT_MINUTES, 2)
      45              : 
      46              : /**
      47              :  * Define a max length for the HTTP "Expire:" header
      48              :  */
      49              : #define MAX_DATE_LINE_LEN 32
      50              : 
      51              : 
      52              : /**
      53              :  * Handle for a GET /keys request.
      54              :  */
      55              : struct TALER_EXCHANGE_GetKeysHandle
      56              : {
      57              : 
      58              :   /**
      59              :    * The exchange base URL (i.e. "https://exchange.demo.taler.net/")
      60              :    */
      61              :   char *exchange_url;
      62              : 
      63              :   /**
      64              :    * The url for the /keys request, set during _start.
      65              :    */
      66              :   char *url;
      67              : 
      68              :   /**
      69              :    * Previous /keys response, NULL for none.
      70              :    */
      71              :   struct TALER_EXCHANGE_Keys *prev_keys;
      72              : 
      73              :   /**
      74              :    * Entry for this request with the `struct GNUNET_CURL_Context`.
      75              :    */
      76              :   struct GNUNET_CURL_Job *job;
      77              : 
      78              :   /**
      79              :    * Expiration time according to "Expire:" header.
      80              :    * 0 if not provided by the server.
      81              :    */
      82              :   struct GNUNET_TIME_Timestamp expire;
      83              : 
      84              :   /**
      85              :    * Function to call with the exchange's certification data,
      86              :    * NULL if this has already been done.
      87              :    */
      88              :   TALER_EXCHANGE_GetKeysCallback cert_cb;
      89              : 
      90              :   /**
      91              :    * Closure to pass to @e cert_cb.
      92              :    */
      93              :   TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE *cert_cb_cls;
      94              : 
      95              :   /**
      96              :    * Reference to the execution context.
      97              :    */
      98              :   struct GNUNET_CURL_Context *ctx;
      99              : 
     100              : };
     101              : 
     102              : 
     103              : /**
     104              :  * Parse HTTP timestamp.
     105              :  *
     106              :  * @param dateline header to parse header
     107              :  * @param[out] at where to write the result
     108              :  * @return #GNUNET_OK on success
     109              :  */
     110              : static enum GNUNET_GenericReturnValue
     111           42 : parse_date_string (const char *dateline,
     112              :                    struct GNUNET_TIME_Timestamp *at)
     113              : {
     114              :   static const char *MONTHS[] =
     115              :   { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     116              :     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
     117              :   int year;
     118              :   int mon;
     119              :   int day;
     120              :   int hour;
     121              :   int min;
     122              :   int sec;
     123              :   char month[4];
     124              :   struct tm tm;
     125              :   time_t t;
     126              : 
     127              :   /* We recognize the three formats in RFC2616, section 3.3.1.  Month
     128              :      names are always in English.  The formats are:
     129              :       Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
     130              :       Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
     131              :       Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
     132              :      Note that the first is preferred.
     133              :    */
     134              : 
     135           42 :   if (strlen (dateline) > MAX_DATE_LINE_LEN)
     136              :   {
     137            0 :     GNUNET_break_op (0);
     138            0 :     return GNUNET_SYSERR;
     139              :   }
     140           42 :   while (*dateline == ' ')
     141            0 :     ++dateline;
     142          210 :   while (*dateline && *dateline != ' ')
     143          168 :     ++dateline;
     144           84 :   while (*dateline == ' ')
     145           42 :     ++dateline;
     146              :   /* We just skipped over the day of the week. Now we have:*/
     147           42 :   if ( (sscanf (dateline,
     148              :                 "%d %3s %d %d:%d:%d",
     149            0 :                 &day, month, &year, &hour, &min, &sec) != 6) &&
     150            0 :        (sscanf (dateline,
     151              :                 "%d-%3s-%d %d:%d:%d",
     152            0 :                 &day, month, &year, &hour, &min, &sec) != 6) &&
     153            0 :        (sscanf (dateline,
     154              :                 "%3s %d %d:%d:%d %d",
     155              :                 month, &day, &hour, &min, &sec, &year) != 6) )
     156              :   {
     157            0 :     GNUNET_break (0);
     158            0 :     return GNUNET_SYSERR;
     159              :   }
     160              :   /* Two digit dates are defined to be relative to 1900; all other dates
     161              :    * are supposed to be represented as four digits. */
     162           42 :   if (year < 100)
     163            0 :     year += 1900;
     164              : 
     165          446 :   for (mon = 0; ; mon++)
     166              :   {
     167          446 :     if (! MONTHS[mon])
     168              :     {
     169            0 :       GNUNET_break_op (0);
     170            0 :       return GNUNET_SYSERR;
     171              :     }
     172          446 :     if (0 == strcasecmp (month,
     173              :                          MONTHS[mon]))
     174           42 :       break;
     175              :   }
     176              : 
     177           42 :   memset (&tm, 0, sizeof(tm));
     178           42 :   tm.tm_year = year - 1900;
     179           42 :   tm.tm_mon = mon;
     180           42 :   tm.tm_mday = day;
     181           42 :   tm.tm_hour = hour;
     182           42 :   tm.tm_min = min;
     183           42 :   tm.tm_sec = sec;
     184              : 
     185           42 :   t = mktime (&tm);
     186           42 :   if (((time_t) -1) == t)
     187              :   {
     188            0 :     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     189              :                          "mktime");
     190            0 :     return GNUNET_SYSERR;
     191              :   }
     192           42 :   if (t < 0)
     193            0 :     t = 0; /* can happen due to timezone issues if date was 1.1.1970 */
     194           42 :   *at = GNUNET_TIME_timestamp_from_s (t);
     195           42 :   return GNUNET_OK;
     196              : }
     197              : 
     198              : 
     199              : /**
     200              :  * Function called for each header in the HTTP /keys response.
     201              :  * Finds the "Expire:" header and parses it, storing the result
     202              :  * in the "expire" field of the keys request.
     203              :  *
     204              :  * @param buffer header data received
     205              :  * @param size size of an item in @a buffer
     206              :  * @param nitems number of items in @a buffer
     207              :  * @param userdata the `struct TALER_EXCHANGE_GetKeysHandle`
     208              :  * @return `size * nitems` on success (everything else aborts)
     209              :  */
     210              : static size_t
     211          546 : header_cb (char *buffer,
     212              :            size_t size,
     213              :            size_t nitems,
     214              :            void *userdata)
     215              : {
     216          546 :   struct TALER_EXCHANGE_GetKeysHandle *kr = userdata;
     217          546 :   size_t total = size * nitems;
     218              :   char *val;
     219              : 
     220          546 :   if (total < strlen (MHD_HTTP_HEADER_EXPIRES ": "))
     221           42 :     return total;
     222          504 :   if (0 != strncasecmp (MHD_HTTP_HEADER_EXPIRES ": ",
     223              :                         buffer,
     224              :                         strlen (MHD_HTTP_HEADER_EXPIRES ": ")))
     225          462 :     return total;
     226           42 :   val = GNUNET_strndup (&buffer[strlen (MHD_HTTP_HEADER_EXPIRES ": ")],
     227              :                         total - strlen (MHD_HTTP_HEADER_EXPIRES ": "));
     228           42 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     229              :               "Found %s header `%s'\n",
     230              :               MHD_HTTP_HEADER_EXPIRES,
     231              :               val);
     232           42 :   if (GNUNET_OK !=
     233           42 :       parse_date_string (val,
     234              :                          &kr->expire))
     235              :   {
     236            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     237              :                 "Failed to parse %s-header `%s'\n",
     238              :                 MHD_HTTP_HEADER_EXPIRES,
     239              :                 val);
     240            0 :     kr->expire = GNUNET_TIME_UNIT_ZERO_TS;
     241              :   }
     242           42 :   GNUNET_free (val);
     243           42 :   return total;
     244              : }
     245              : 
     246              : 
     247              : /**
     248              :  * Callback used when downloading the reply to a /keys request
     249              :  * is complete.
     250              :  *
     251              :  * @param cls the `struct TALER_EXCHANGE_GetKeysHandle`
     252              :  * @param response_code HTTP response code, 0 on error
     253              :  * @param resp_obj parsed JSON result, NULL on error
     254              :  */
     255              : static void
     256           42 : keys_completed_cb (void *cls,
     257              :                    long response_code,
     258              :                    const void *resp_obj)
     259              : {
     260           42 :   struct TALER_EXCHANGE_GetKeysHandle *gkh = cls;
     261           42 :   const json_t *j = resp_obj;
     262           42 :   struct TALER_EXCHANGE_Keys *kd = NULL;
     263           42 :   struct TALER_EXCHANGE_KeysResponse kresp = {
     264              :     .hr.reply = j,
     265           42 :     .hr.http_status = (unsigned int) response_code,
     266              :     .details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR,
     267              :   };
     268              : 
     269           42 :   gkh->job = NULL;
     270           42 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     271              :               "Received keys from URL `%s' with status %ld and expiration %s.\n",
     272              :               gkh->url,
     273              :               response_code,
     274              :               GNUNET_TIME_timestamp2s (gkh->expire));
     275           42 :   if (GNUNET_TIME_absolute_is_past (gkh->expire.abs_time))
     276              :   {
     277            0 :     if (MHD_HTTP_OK == response_code)
     278            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     279              :                   "Exchange failed to give expiration time, assuming in %s\n",
     280              :                   GNUNET_TIME_relative2s (DEFAULT_EXPIRATION,
     281              :                                           true));
     282              :     gkh->expire
     283            0 :       = GNUNET_TIME_absolute_to_timestamp (
     284              :           GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
     285              :   }
     286           42 :   switch (response_code)
     287              :   {
     288            0 :   case 0:
     289            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     290              :                 "Failed to receive /keys response from exchange %s\n",
     291              :                 gkh->exchange_url);
     292            0 :     break;
     293           42 :   case MHD_HTTP_OK:
     294           42 :     if (NULL == j)
     295              :     {
     296            0 :       GNUNET_break (0);
     297            0 :       kresp.hr.http_status = 0;
     298            0 :       break;
     299              :     }
     300           42 :     kd = GNUNET_new (struct TALER_EXCHANGE_Keys);
     301           42 :     kd->exchange_url = GNUNET_strdup (gkh->exchange_url);
     302           42 :     if (NULL != gkh->prev_keys)
     303              :     {
     304            8 :       const struct TALER_EXCHANGE_Keys *kd_old = gkh->prev_keys;
     305              : 
     306              :       /* We keep the denomination keys and auditor signatures from the
     307              :          previous iteration (/keys cherry picking) */
     308              :       kd->num_denom_keys
     309            8 :         = kd_old->num_denom_keys;
     310              :       kd->last_denom_issue_date
     311            8 :         = kd_old->last_denom_issue_date;
     312            8 :       GNUNET_array_grow (kd->denom_keys,
     313              :                          kd->denom_keys_size,
     314              :                          kd->num_denom_keys);
     315              :       /* First make a shallow copy, we then need another pass for the RSA key... */
     316            8 :       GNUNET_memcpy (kd->denom_keys,
     317              :                      kd_old->denom_keys,
     318              :                      kd_old->num_denom_keys
     319              :                      * sizeof (struct TALER_EXCHANGE_DenomPublicKey));
     320           90 :       for (unsigned int i = 0; i<kd_old->num_denom_keys; i++)
     321           82 :         TALER_denom_pub_copy (&kd->denom_keys[i].key,
     322           82 :                               &kd_old->denom_keys[i].key);
     323            8 :       kd->num_auditors = kd_old->num_auditors;
     324              :       kd->auditors
     325            8 :         = GNUNET_new_array (kd->num_auditors,
     326              :                             struct TALER_EXCHANGE_AuditorInformation);
     327              :       /* Now the necessary deep copy... */
     328           10 :       for (unsigned int i = 0; i<kd_old->num_auditors; i++)
     329              :       {
     330            2 :         const struct TALER_EXCHANGE_AuditorInformation *aold =
     331            2 :           &kd_old->auditors[i];
     332            2 :         struct TALER_EXCHANGE_AuditorInformation *anew = &kd->auditors[i];
     333              : 
     334            2 :         anew->auditor_pub = aold->auditor_pub;
     335            2 :         anew->auditor_url = GNUNET_strdup (aold->auditor_url);
     336            2 :         anew->auditor_name = GNUNET_strdup (aold->auditor_name);
     337            2 :         GNUNET_array_grow (anew->denom_keys,
     338              :                            anew->num_denom_keys,
     339              :                            aold->num_denom_keys);
     340            2 :         GNUNET_memcpy (
     341              :           anew->denom_keys,
     342              :           aold->denom_keys,
     343              :           aold->num_denom_keys
     344              :           * sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
     345              :       }
     346              :     }
     347              :     /* Now decode fresh /keys response */
     348           42 :     if (GNUNET_OK !=
     349           42 :         TALER_EXCHANGE_decode_keys_json_ (j,
     350              :                                           true,
     351              :                                           kd,
     352              :                                           &kresp.details.ok.compat))
     353              :     {
     354            0 :       TALER_LOG_ERROR ("Could not decode /keys response\n");
     355            0 :       kd->rc = 1;
     356            0 :       TALER_EXCHANGE_keys_decref (kd);
     357            0 :       kd = NULL;
     358            0 :       kresp.hr.http_status = 0;
     359            0 :       kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     360            0 :       break;
     361              :     }
     362           42 :     if (NULL != gkh->prev_keys)
     363              :     {
     364            8 :       const struct TALER_EXCHANGE_Keys *kd_old = gkh->prev_keys;
     365              : 
     366              :       /* The denominations and auditor signatures we cherry-picked from
     367              :          the previous response were validated against the master public
     368              :          key of that response; refuse to re-attribute them to a
     369              :          different master public key (or currency). */
     370            8 :       if ( (0 !=
     371            8 :             GNUNET_memcmp (&kd->master_pub,
     372            8 :                            &kd_old->master_pub)) ||
     373              :            (0 !=
     374            8 :             strcmp (kd->currency,
     375            8 :                     kd_old->currency)) )
     376              :       {
     377            0 :         GNUNET_break_op (0);
     378            0 :         TALER_LOG_ERROR (
     379              :           "Exchange changed master public key or currency in /keys\n");
     380            0 :         kd->rc = 1;
     381            0 :         TALER_EXCHANGE_keys_decref (kd);
     382            0 :         kd = NULL;
     383            0 :         kresp.hr.http_status = 0;
     384            0 :         kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     385            0 :         break;
     386              :       }
     387              :     }
     388           42 :     kd->rc = 1;
     389           42 :     kd->key_data_expiration = gkh->expire;
     390           42 :     if (GNUNET_TIME_relative_cmp (
     391              :           GNUNET_TIME_absolute_get_remaining (gkh->expire.abs_time),
     392              :           <,
     393              :           MINIMUM_EXPIRATION))
     394              :     {
     395            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     396              :                   "Exchange returned keys with expiration time below %s. Compensating.\n",
     397              :                   GNUNET_TIME_relative2s (MINIMUM_EXPIRATION,
     398              :                                           true));
     399              :       kd->key_data_expiration
     400            0 :         = GNUNET_TIME_relative_to_timestamp (MINIMUM_EXPIRATION);
     401              :     }
     402              : 
     403           42 :     kresp.details.ok.keys = kd;
     404           42 :     break;
     405            0 :   case MHD_HTTP_BAD_REQUEST:
     406              :   case MHD_HTTP_UNAUTHORIZED:
     407              :   case MHD_HTTP_FORBIDDEN:
     408              :   case MHD_HTTP_NOT_FOUND:
     409            0 :     if (NULL == j)
     410              :     {
     411            0 :       kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     412            0 :       kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
     413              :     }
     414              :     else
     415              :     {
     416            0 :       kresp.hr.ec = TALER_JSON_get_error_code (j);
     417            0 :       kresp.hr.hint = TALER_JSON_get_error_hint (j);
     418              :     }
     419            0 :     break;
     420            0 :   default:
     421            0 :     if (NULL == j)
     422              :     {
     423            0 :       kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     424            0 :       kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
     425              :     }
     426              :     else
     427              :     {
     428            0 :       kresp.hr.ec = TALER_JSON_get_error_code (j);
     429            0 :       kresp.hr.hint = TALER_JSON_get_error_hint (j);
     430              :     }
     431            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     432              :                 "Unexpected response code %u/%d\n",
     433              :                 (unsigned int) response_code,
     434              :                 (int) kresp.hr.ec);
     435            0 :     break;
     436              :   }
     437           42 :   gkh->cert_cb (gkh->cert_cb_cls,
     438              :                 &kresp,
     439              :                 kd);
     440           42 :   TALER_EXCHANGE_get_keys_cancel (gkh);
     441           42 : }
     442              : 
     443              : 
     444              : struct TALER_EXCHANGE_GetKeysHandle *
     445           42 : TALER_EXCHANGE_get_keys_create (
     446              :   struct GNUNET_CURL_Context *ctx,
     447              :   const char *url)
     448              : {
     449              :   struct TALER_EXCHANGE_GetKeysHandle *gkh;
     450              : 
     451           42 :   gkh = GNUNET_new (struct TALER_EXCHANGE_GetKeysHandle);
     452           42 :   gkh->ctx = ctx;
     453           42 :   gkh->exchange_url = GNUNET_strdup (url);
     454           42 :   return gkh;
     455              : }
     456              : 
     457              : 
     458              : enum GNUNET_GenericReturnValue
     459            8 : TALER_EXCHANGE_get_keys_set_options_ (
     460              :   struct TALER_EXCHANGE_GetKeysHandle *gkh,
     461              :   unsigned int num_options,
     462              :   const struct TALER_EXCHANGE_GetKeysOptionValue *options)
     463              : {
     464           16 :   for (unsigned int i = 0; i < num_options; i++)
     465              :   {
     466           16 :     const struct TALER_EXCHANGE_GetKeysOptionValue *opt = &options[i];
     467              : 
     468           16 :     switch (opt->option)
     469              :     {
     470            8 :     case TALER_EXCHANGE_GET_KEYS_OPTION_END:
     471            8 :       return GNUNET_OK;
     472            8 :     case TALER_EXCHANGE_GET_KEYS_OPTION_LAST_KEYS:
     473            8 :       TALER_EXCHANGE_keys_decref (gkh->prev_keys);
     474            8 :       gkh->prev_keys = NULL;
     475            8 :       if (NULL != opt->details.last_keys)
     476              :         gkh->prev_keys
     477            8 :           = TALER_EXCHANGE_keys_incref (opt->details.last_keys);
     478            8 :       break;
     479              :     }
     480              :   }
     481            0 :   return GNUNET_OK;
     482              : }
     483              : 
     484              : 
     485              : enum TALER_ErrorCode
     486           42 : TALER_EXCHANGE_get_keys_start (
     487              :   struct TALER_EXCHANGE_GetKeysHandle *gkh,
     488              :   TALER_EXCHANGE_GetKeysCallback cert_cb,
     489              :   TALER_EXCHANGE_GET_KEYS_RESULT_CLOSURE *cert_cb_cls)
     490              : {
     491              :   CURL *eh;
     492           42 :   char last_date[80] = { 0 };
     493              : 
     494           42 :   gkh->cert_cb = cert_cb;
     495           42 :   gkh->cert_cb_cls = cert_cb_cls;
     496           42 :   if (NULL != gkh->prev_keys)
     497              :   {
     498            8 :     TALER_LOG_DEBUG ("Last DK issue date (before GETting /keys): %s\n",
     499              :                      GNUNET_TIME_timestamp2s (
     500              :                        gkh->prev_keys->last_denom_issue_date));
     501            8 :     GNUNET_snprintf (last_date,
     502              :                      sizeof (last_date),
     503              :                      "%llu",
     504              :                      (unsigned long long)
     505            8 :                      gkh->prev_keys->last_denom_issue_date.abs_time.abs_value_us
     506              :                      / 1000000LLU);
     507              :   }
     508           84 :   gkh->url = TALER_url_join (gkh->exchange_url,
     509              :                              "keys",
     510           42 :                              (NULL != gkh->prev_keys)
     511              :                              ? "last_issue_date"
     512              :                              : NULL,
     513           42 :                              (NULL != gkh->prev_keys)
     514              :                              ? last_date
     515              :                              : NULL,
     516              :                              NULL);
     517           42 :   if (NULL == gkh->url)
     518              :   {
     519            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     520              :                 "Could not construct request URL.\n");
     521            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     522              :   }
     523           42 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     524              :               "Requesting keys with URL `%s'.\n",
     525              :               gkh->url);
     526           42 :   eh = TALER_EXCHANGE_curl_easy_get_ (gkh->url);
     527           42 :   if (NULL == eh)
     528              :   {
     529            0 :     GNUNET_break (0);
     530            0 :     GNUNET_free (gkh->url);
     531            0 :     gkh->url = NULL;
     532            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     533              :   }
     534           42 :   GNUNET_break (CURLE_OK ==
     535              :                 curl_easy_setopt (eh,
     536              :                                   CURLOPT_VERBOSE,
     537              :                                   0));
     538           42 :   GNUNET_break (CURLE_OK ==
     539              :                 curl_easy_setopt (eh,
     540              :                                   CURLOPT_TIMEOUT,
     541              :                                   120 /* seconds */));
     542           42 :   GNUNET_assert (CURLE_OK ==
     543              :                  curl_easy_setopt (eh,
     544              :                                    CURLOPT_HEADERFUNCTION,
     545              :                                    &header_cb));
     546           42 :   GNUNET_assert (CURLE_OK ==
     547              :                  curl_easy_setopt (eh,
     548              :                                    CURLOPT_HEADERDATA,
     549              :                                    gkh));
     550           42 :   gkh->job = GNUNET_CURL_job_add_with_ct_json (gkh->ctx,
     551              :                                                eh,
     552              :                                                &keys_completed_cb,
     553              :                                                gkh);
     554           42 :   if (NULL == gkh->job)
     555              :   {
     556            0 :     GNUNET_free (gkh->url);
     557            0 :     gkh->url = NULL;
     558            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     559              :   }
     560           42 :   return TALER_EC_NONE;
     561              : }
     562              : 
     563              : 
     564              : void
     565           42 : TALER_EXCHANGE_get_keys_cancel (
     566              :   struct TALER_EXCHANGE_GetKeysHandle *gkh)
     567              : {
     568           42 :   if (NULL != gkh->job)
     569              :   {
     570            0 :     GNUNET_CURL_job_cancel (gkh->job);
     571            0 :     gkh->job = NULL;
     572              :   }
     573           42 :   TALER_EXCHANGE_keys_decref (gkh->prev_keys);
     574           42 :   GNUNET_free (gkh->exchange_url);
     575           42 :   GNUNET_free (gkh->url);
     576           42 :   GNUNET_free (gkh);
     577           42 : }
     578              : 
     579              : 
     580              : /* end of exchange_api_get-keys.c */
        

Generated by: LCOV version 2.0-1