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 */