exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

exchange_api_get-reserves-RESERVE_PUB.c (8876B)


      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-reserves-RESERVE_PUB.c
     19  * @brief Implementation of the GET /reserves/$RESERVE_PUB requests
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include <jansson.h>
     24 #include <microhttpd.h> /* just for HTTP status codes */
     25 #include <gnunet/gnunet_util_lib.h>
     26 #include <gnunet/gnunet_json_lib.h>
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include "taler/taler_exchange_service.h"
     29 #include "taler/taler_json_lib.h"
     30 #include "exchange_api_handle.h"
     31 #include "taler/taler_signatures.h"
     32 #include "exchange_api_curl_defaults.h"
     33 
     34 
     35 /**
     36  * @brief A GET /reserves/$RESERVE_PUB Handle
     37  */
     38 struct TALER_EXCHANGE_GetReservesHandle
     39 {
     40 
     41   /**
     42    * Base URL of the exchange.
     43    */
     44   char *base_url;
     45 
     46   /**
     47    * The url for this request.
     48    */
     49   char *url;
     50 
     51   /**
     52    * CURL context to use.
     53    */
     54   struct GNUNET_CURL_Context *ctx;
     55 
     56   /**
     57    * Handle for the request.
     58    */
     59   struct GNUNET_CURL_Job *job;
     60 
     61   /**
     62    * Function to call with the result.
     63    */
     64   TALER_EXCHANGE_GetReservesCallback cb;
     65 
     66   /**
     67    * Closure for @e cb.
     68    */
     69   TALER_EXCHANGE_GET_RESERVES_RESULT_CLOSURE *cb_cls;
     70 
     71   /**
     72    * Public key of the reserve we are querying.
     73    */
     74   struct TALER_ReservePublicKeyP reserve_pub;
     75 
     76   /**
     77    * Options for the request.
     78    */
     79   struct
     80   {
     81     /**
     82      * How long to wait for an answer (enables long polling).
     83      */
     84     struct GNUNET_TIME_Relative timeout;
     85   } options;
     86 
     87 };
     88 
     89 
     90 /**
     91  * We received an #MHD_HTTP_OK status code. Handle the JSON response.
     92  *
     93  * @param grh handle of the request
     94  * @param j JSON response
     95  * @return #GNUNET_OK on success
     96  */
     97 static enum GNUNET_GenericReturnValue
     98 handle_reserves_get_ok (struct TALER_EXCHANGE_GetReservesHandle *grh,
     99                         const json_t *j)
    100 {
    101   struct TALER_EXCHANGE_GetReservesResponse rs = {
    102     .hr.reply = j,
    103     .hr.http_status = MHD_HTTP_OK
    104   };
    105   struct GNUNET_JSON_Specification spec[] = {
    106     TALER_JSON_spec_amount_any ("balance",
    107                                 &rs.details.ok.balance),
    108     GNUNET_JSON_spec_mark_optional (
    109       GNUNET_JSON_spec_string (
    110         "last_origin",
    111         (const char **) &rs.details.ok.last_origin.full_payto),
    112       NULL),
    113     GNUNET_JSON_spec_end ()
    114   };
    115 
    116   if (GNUNET_OK !=
    117       GNUNET_JSON_parse (j,
    118                          spec,
    119                          NULL,
    120                          NULL))
    121   {
    122     GNUNET_break_op (0);
    123     return GNUNET_SYSERR;
    124   }
    125   grh->cb (grh->cb_cls,
    126            &rs);
    127   grh->cb = NULL;
    128   return GNUNET_OK;
    129 }
    130 
    131 
    132 /**
    133  * Function called when we're done processing the
    134  * HTTP GET /reserves/$RESERVE_PUB request.
    135  *
    136  * @param cls the `struct TALER_EXCHANGE_GetReservesHandle`
    137  * @param response_code HTTP response code, 0 on error
    138  * @param response parsed JSON result, NULL on error
    139  */
    140 static void
    141 handle_reserves_get_finished (void *cls,
    142                               long response_code,
    143                               const void *response)
    144 {
    145   struct TALER_EXCHANGE_GetReservesHandle *grh = cls;
    146   const json_t *j = response;
    147   struct TALER_EXCHANGE_GetReservesResponse rs = {
    148     .hr.reply = j,
    149     .hr.http_status = (unsigned int) response_code
    150   };
    151 
    152   grh->job = NULL;
    153   switch (response_code)
    154   {
    155   case 0:
    156     rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    157     break;
    158   case MHD_HTTP_OK:
    159     if (GNUNET_OK !=
    160         handle_reserves_get_ok (grh,
    161                                 j))
    162     {
    163       rs.hr.http_status = 0;
    164       rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    165     }
    166     break;
    167   case MHD_HTTP_BAD_REQUEST:
    168     /* This should never happen, either us or the exchange is buggy
    169        (or API version conflict); just pass JSON reply to the application */
    170     rs.hr.ec = TALER_JSON_get_error_code (j);
    171     rs.hr.hint = TALER_JSON_get_error_hint (j);
    172     break;
    173   case MHD_HTTP_NOT_FOUND:
    174     /* Nothing really to verify, this should never
    175        happen, we should pass the JSON reply to the application */
    176     rs.hr.ec = TALER_JSON_get_error_code (j);
    177     rs.hr.hint = TALER_JSON_get_error_hint (j);
    178     break;
    179   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    180     /* Server had an internal issue; we should retry, but this API
    181        leaves this to the application */
    182     rs.hr.ec = TALER_JSON_get_error_code (j);
    183     rs.hr.hint = TALER_JSON_get_error_hint (j);
    184     break;
    185   default:
    186     /* unexpected response code */
    187     GNUNET_break_op (0);
    188     rs.hr.ec = TALER_JSON_get_error_code (j);
    189     rs.hr.hint = TALER_JSON_get_error_hint (j);
    190     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    191                 "Unexpected response code %u/%d for GET %s\n",
    192                 (unsigned int) response_code,
    193                 (int) rs.hr.ec,
    194                 grh->url);
    195     break;
    196   }
    197   if (NULL != grh->cb)
    198   {
    199     grh->cb (grh->cb_cls,
    200              &rs);
    201     grh->cb = NULL;
    202   }
    203   TALER_EXCHANGE_get_reserves_cancel (grh);
    204 }
    205 
    206 
    207 struct TALER_EXCHANGE_GetReservesHandle *
    208 TALER_EXCHANGE_get_reserves_create (
    209   struct GNUNET_CURL_Context *ctx,
    210   const char *url,
    211   const struct TALER_ReservePublicKeyP *reserve_pub)
    212 {
    213   struct TALER_EXCHANGE_GetReservesHandle *grh;
    214 
    215   grh = GNUNET_new (struct TALER_EXCHANGE_GetReservesHandle);
    216   grh->ctx = ctx;
    217   grh->base_url = GNUNET_strdup (url);
    218   grh->reserve_pub = *reserve_pub;
    219   return grh;
    220 }
    221 
    222 
    223 enum GNUNET_GenericReturnValue
    224 TALER_EXCHANGE_get_reserves_set_options_ (
    225   struct TALER_EXCHANGE_GetReservesHandle *grh,
    226   unsigned int num_options,
    227   const struct TALER_EXCHANGE_GetReservesOptionValue *options)
    228 {
    229   for (unsigned int i = 0; i < num_options; i++)
    230   {
    231     switch (options[i].option)
    232     {
    233     case TALER_EXCHANGE_GET_RESERVES_OPTION_END:
    234       return GNUNET_OK;
    235     case TALER_EXCHANGE_GET_RESERVES_OPTION_TIMEOUT:
    236       grh->options.timeout = options[i].details.timeout;
    237       break;
    238     default:
    239       GNUNET_break (0);
    240       return GNUNET_SYSERR;
    241     }
    242   }
    243   return GNUNET_OK;
    244 }
    245 
    246 
    247 enum TALER_ErrorCode
    248 TALER_EXCHANGE_get_reserves_start (
    249   struct TALER_EXCHANGE_GetReservesHandle *grh,
    250   TALER_EXCHANGE_GetReservesCallback cb,
    251   TALER_EXCHANGE_GET_RESERVES_RESULT_CLOSURE *cb_cls)
    252 {
    253   char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 16];
    254   CURL *eh;
    255   unsigned int tms
    256     = (unsigned int) grh->options.timeout.rel_value_us
    257       / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
    258 
    259   if (NULL != grh->job)
    260   {
    261     GNUNET_break (0);
    262     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    263   }
    264   grh->cb = cb;
    265   grh->cb_cls = cb_cls;
    266   {
    267     char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
    268     char *end;
    269     char timeout_str[32];
    270 
    271     end = GNUNET_STRINGS_data_to_string (
    272       &grh->reserve_pub,
    273       sizeof (grh->reserve_pub),
    274       pub_str,
    275       sizeof (pub_str));
    276     *end = '\0';
    277     GNUNET_snprintf (arg_str,
    278                      sizeof (arg_str),
    279                      "reserves/%s",
    280                      pub_str);
    281     GNUNET_snprintf (timeout_str,
    282                      sizeof (timeout_str),
    283                      "%u",
    284                      tms);
    285     grh->url = TALER_url_join (grh->base_url,
    286                                arg_str,
    287                                "timeout_ms",
    288                                (0 == tms)
    289                                 ? NULL
    290                                 : timeout_str,
    291                                NULL);
    292   }
    293   if (NULL == grh->url)
    294     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    295   eh = TALER_EXCHANGE_curl_easy_get_ (grh->url);
    296   if (NULL == eh)
    297     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    298   if (0 != tms)
    299   {
    300     GNUNET_break (CURLE_OK ==
    301                   curl_easy_setopt (eh,
    302                                     CURLOPT_TIMEOUT_MS,
    303                                     (long) (tms + 100L)));
    304   }
    305   grh->job = GNUNET_CURL_job_add (grh->ctx,
    306                                   eh,
    307                                   &handle_reserves_get_finished,
    308                                   grh);
    309   if (NULL == grh->job)
    310     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    311   return TALER_EC_NONE;
    312 }
    313 
    314 
    315 void
    316 TALER_EXCHANGE_get_reserves_cancel (
    317   struct TALER_EXCHANGE_GetReservesHandle *grh)
    318 {
    319   if (NULL != grh->job)
    320   {
    321     GNUNET_CURL_job_cancel (grh->job);
    322     grh->job = NULL;
    323   }
    324   GNUNET_free (grh->url);
    325   GNUNET_free (grh->base_url);
    326   GNUNET_free (grh);
    327 }
    328 
    329 
    330 /* end of exchange_api_get-reserves-RESERVE_PUB.c */