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-contracts-CONTRACT_PUB.c (8239B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022-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-contracts-CONTRACT_PUB.c
     19  * @brief Implementation of the GET /contracts/$CONTRACT_PUB request
     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 /contracts/$CONTRACT_PUB Handle
     35  */
     36 struct TALER_EXCHANGE_GetContractsHandle
     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    * Handle for the request.
     51    */
     52   struct GNUNET_CURL_Job *job;
     53 
     54   /**
     55    * Function to call with the result.
     56    */
     57   TALER_EXCHANGE_GetContractsCallback cb;
     58 
     59   /**
     60    * Closure for @e cb.
     61    */
     62   TALER_EXCHANGE_GET_CONTRACTS_RESULT_CLOSURE *cb_cls;
     63 
     64   /**
     65    * CURL context to use.
     66    */
     67   struct GNUNET_CURL_Context *ctx;
     68 
     69   /**
     70    * Private key needed to decrypt the contract.
     71    */
     72   struct TALER_ContractDiffiePrivateP contract_priv;
     73 
     74   /**
     75    * Public key matching @e contract_priv.
     76    */
     77   struct TALER_ContractDiffiePublicP cpub;
     78 
     79 };
     80 
     81 
     82 /**
     83  * Function called when we're done processing the
     84  * HTTP GET /contracts/$CONTRACT_PUB request.
     85  *
     86  * @param cls the `struct TALER_EXCHANGE_GetContractsHandle`
     87  * @param response_code HTTP response code, 0 on error
     88  * @param response parsed JSON result, NULL on error
     89  */
     90 static void
     91 handle_contract_get_finished (void *cls,
     92                               long response_code,
     93                               const void *response)
     94 {
     95   struct TALER_EXCHANGE_GetContractsHandle *gch = cls;
     96   const json_t *j = response;
     97   struct TALER_EXCHANGE_GetContractsResponse dr = {
     98     .hr.reply = j,
     99     .hr.http_status = (unsigned int) response_code
    100   };
    101 
    102   gch->job = NULL;
    103   switch (response_code)
    104   {
    105   case 0:
    106     dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    107     break;
    108   case MHD_HTTP_OK:
    109     {
    110       void *econtract;
    111       size_t econtract_size;
    112       struct TALER_PurseContractSignatureP econtract_sig;
    113       struct GNUNET_JSON_Specification spec[] = {
    114         GNUNET_JSON_spec_fixed_auto ("purse_pub",
    115                                      &dr.details.ok.purse_pub),
    116         GNUNET_JSON_spec_fixed_auto ("econtract_sig",
    117                                      &econtract_sig),
    118         GNUNET_JSON_spec_varsize ("econtract",
    119                                   &econtract,
    120                                   &econtract_size),
    121         GNUNET_JSON_spec_end ()
    122       };
    123 
    124       if (GNUNET_OK !=
    125           GNUNET_JSON_parse (j,
    126                              spec,
    127                              NULL, NULL))
    128       {
    129         GNUNET_break_op (0);
    130         dr.hr.http_status = 0;
    131         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    132         break;
    133       }
    134       if (GNUNET_OK !=
    135           TALER_wallet_econtract_upload_verify (
    136             econtract,
    137             econtract_size,
    138             &gch->cpub,
    139             &dr.details.ok.purse_pub,
    140             &econtract_sig))
    141       {
    142         GNUNET_break (0);
    143         dr.hr.http_status = 0;
    144         dr.hr.ec = TALER_EC_EXCHANGE_CONTRACTS_SIGNATURE_INVALID;
    145         GNUNET_JSON_parse_free (spec);
    146         break;
    147       }
    148       dr.details.ok.econtract = econtract;
    149       dr.details.ok.econtract_size = econtract_size;
    150       gch->cb (gch->cb_cls,
    151                &dr);
    152       gch->cb = NULL;
    153       GNUNET_JSON_parse_free (spec);
    154       TALER_EXCHANGE_get_contracts_cancel (gch);
    155       return;
    156     }
    157   case MHD_HTTP_BAD_REQUEST:
    158     dr.hr.ec = TALER_JSON_get_error_code (j);
    159     dr.hr.hint = TALER_JSON_get_error_hint (j);
    160     /* This should never happen, either us or the exchange is buggy
    161        (or API version conflict); just pass JSON reply to the application */
    162     break;
    163   case MHD_HTTP_FORBIDDEN:
    164     dr.hr.ec = TALER_JSON_get_error_code (j);
    165     dr.hr.hint = TALER_JSON_get_error_hint (j);
    166     /* Nothing really to verify, exchange says one of the signatures is
    167        invalid; as we checked them, this should never happen, we
    168        should pass the JSON reply to the application */
    169     break;
    170   case MHD_HTTP_NOT_FOUND:
    171     dr.hr.ec = TALER_JSON_get_error_code (j);
    172     dr.hr.hint = TALER_JSON_get_error_hint (j);
    173     /* Exchange does not know about transaction;
    174        we should pass the reply to the application */
    175     break;
    176   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    177     dr.hr.ec = TALER_JSON_get_error_code (j);
    178     dr.hr.hint = TALER_JSON_get_error_hint (j);
    179     /* Server had an internal issue; we should retry, but this API
    180        leaves this to the application */
    181     break;
    182   default:
    183     /* unexpected response code */
    184     dr.hr.ec = TALER_JSON_get_error_code (j);
    185     dr.hr.hint = TALER_JSON_get_error_hint (j);
    186     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    187                 "Unexpected response code %u/%d for exchange GET contracts\n",
    188                 (unsigned int) response_code,
    189                 (int) dr.hr.ec);
    190     GNUNET_break_op (0);
    191     break;
    192   }
    193   if (NULL != gch->cb)
    194     gch->cb (gch->cb_cls,
    195              &dr);
    196   TALER_EXCHANGE_get_contracts_cancel (gch);
    197 }
    198 
    199 
    200 struct TALER_EXCHANGE_GetContractsHandle *
    201 TALER_EXCHANGE_get_contracts_create (
    202   struct GNUNET_CURL_Context *ctx,
    203   const char *url,
    204   const struct TALER_ContractDiffiePrivateP *contract_priv)
    205 {
    206   struct TALER_EXCHANGE_GetContractsHandle *gch;
    207 
    208   gch = GNUNET_new (struct TALER_EXCHANGE_GetContractsHandle);
    209   gch->ctx = ctx;
    210   gch->base_url = GNUNET_strdup (url);
    211   gch->contract_priv = *contract_priv;
    212   GNUNET_CRYPTO_ecdhe_key_get_public (&contract_priv->ecdhe_priv,
    213                                       &gch->cpub.ecdhe_pub);
    214   return gch;
    215 }
    216 
    217 
    218 enum TALER_ErrorCode
    219 TALER_EXCHANGE_get_contracts_start (
    220   struct TALER_EXCHANGE_GetContractsHandle *gch,
    221   TALER_EXCHANGE_GetContractsCallback cb,
    222   TALER_EXCHANGE_GET_CONTRACTS_RESULT_CLOSURE *cb_cls)
    223 {
    224   char arg_str[sizeof (gch->cpub) * 2 + 48];
    225   CURL *eh;
    226 
    227   if (NULL != gch->job)
    228   {
    229     GNUNET_break (0);
    230     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    231   }
    232   gch->cb = cb;
    233   gch->cb_cls = cb_cls;
    234   {
    235     char cpub_str[sizeof (gch->cpub) * 2];
    236     char *end;
    237 
    238     end = GNUNET_STRINGS_data_to_string (&gch->cpub,
    239                                          sizeof (gch->cpub),
    240                                          cpub_str,
    241                                          sizeof (cpub_str));
    242     *end = '\0';
    243     GNUNET_snprintf (arg_str,
    244                      sizeof (arg_str),
    245                      "contracts/%s",
    246                      cpub_str);
    247   }
    248   gch->url = TALER_url_join (gch->base_url,
    249                              arg_str,
    250                              NULL);
    251   if (NULL == gch->url)
    252     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    253   eh = TALER_EXCHANGE_curl_easy_get_ (gch->url);
    254   if (NULL == eh)
    255   {
    256     GNUNET_free (gch->url);
    257     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    258   }
    259   gch->job = GNUNET_CURL_job_add (gch->ctx,
    260                                   eh,
    261                                   &handle_contract_get_finished,
    262                                   gch);
    263   if (NULL == gch->job)
    264     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    265   return TALER_EC_NONE;
    266 }
    267 
    268 
    269 void
    270 TALER_EXCHANGE_get_contracts_cancel (
    271   struct TALER_EXCHANGE_GetContractsHandle *gch)
    272 {
    273   if (NULL != gch->job)
    274   {
    275     GNUNET_CURL_job_cancel (gch->job);
    276     gch->job = NULL;
    277   }
    278   GNUNET_free (gch->url);
    279   GNUNET_free (gch->base_url);
    280   GNUNET_free (gch);
    281 }
    282 
    283 
    284 /* end of exchange_api_get-contracts-CONTRACT_PUB.c */