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