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


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