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 (8806B)


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