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