exchange_api_post-kyc-wallet.c (8198B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2021-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_post-kyc-wallet.c 19 * @brief Implementation of the /kyc-wallet request 20 * @author Christian Grothoff 21 */ 22 #include "taler/platform.h" 23 #include <microhttpd.h> /* just for HTTP status codes */ 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_curl_lib.h> 26 #include "taler/taler_exchange_service.h" 27 #include "taler/taler_json_lib.h" 28 #include "taler/taler-exchange/post-kyc-wallet.h" 29 #include "taler/taler_signatures.h" 30 #include "exchange_api_curl_defaults.h" 31 #include "taler/taler_curl_lib.h" 32 33 34 /** 35 * @brief A POST /kyc-wallet handle 36 */ 37 struct TALER_EXCHANGE_PostKycWalletHandle 38 { 39 40 /** 41 * Context for curl easy post. Keeps the data that must 42 * persist for Curl to make the upload. 43 */ 44 struct TALER_CURL_PostContext post_ctx; 45 46 /** 47 * The base URL for this request. 48 */ 49 char *base_url; 50 51 /** 52 * The full URL for this request, set during _start. 53 */ 54 char *url; 55 56 /** 57 * Handle for the request. 58 */ 59 struct GNUNET_CURL_Job *job; 60 61 /** 62 * Function to call with the result. 63 */ 64 TALER_EXCHANGE_PostKycWalletCallback cb; 65 66 /** 67 * Closure for @e cb. 68 */ 69 TALER_EXCHANGE_POST_KYC_WALLET_RESULT_CLOSURE *cb_cls; 70 71 /** 72 * Reference to the execution context. 73 */ 74 struct GNUNET_CURL_Context *ctx; 75 76 /** 77 * Reserve private key of the wallet. 78 */ 79 struct TALER_ReservePrivateKeyP reserve_priv; 80 81 /** 82 * Balance (or balance threshold) crossed by the wallet. 83 */ 84 struct TALER_Amount balance; 85 86 }; 87 88 89 /** 90 * Function called when we're done processing the 91 * HTTP /kyc-wallet request. 92 * 93 * @param cls the `struct TALER_EXCHANGE_PostKycWalletHandle` 94 * @param response_code HTTP response code, 0 on error 95 * @param response parsed JSON result, NULL on error 96 */ 97 static void 98 handle_kyc_wallet_finished (void *cls, 99 long response_code, 100 const void *response) 101 { 102 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh = cls; 103 const json_t *j = response; 104 struct TALER_EXCHANGE_PostKycWalletResponse ks = { 105 .hr.reply = j, 106 .hr.http_status = (unsigned int) response_code 107 }; 108 109 pkwh->job = NULL; 110 switch (response_code) 111 { 112 case 0: 113 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 114 break; 115 case MHD_HTTP_OK: 116 { 117 struct GNUNET_JSON_Specification spec[] = { 118 GNUNET_JSON_spec_mark_optional ( 119 TALER_JSON_spec_amount_any ( 120 "next_threshold", 121 &ks.details.ok.next_threshold), 122 NULL), 123 GNUNET_JSON_spec_timestamp ( 124 "expiration_time", 125 &ks.details.ok.expiration_time), 126 GNUNET_JSON_spec_end () 127 }; 128 129 if (GNUNET_OK != 130 GNUNET_JSON_parse (j, 131 spec, 132 NULL, NULL)) 133 { 134 GNUNET_break_op (0); 135 ks.hr.http_status = 0; 136 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 137 break; 138 } 139 break; 140 } 141 case MHD_HTTP_NO_CONTENT: 142 break; 143 case MHD_HTTP_BAD_REQUEST: 144 ks.hr.ec = TALER_JSON_get_error_code (j); 145 /* This should never happen, either us or the exchange is buggy 146 (or API version conflict); just pass JSON reply to the application */ 147 break; 148 case MHD_HTTP_FORBIDDEN: 149 ks.hr.ec = TALER_JSON_get_error_code (j); 150 break; 151 case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: 152 ks.hr.ec = TALER_JSON_get_error_code (j); 153 ks.hr.hint = TALER_JSON_get_error_hint (j); 154 if (GNUNET_OK != 155 TALER_EXCHANGE_parse_451 (&ks.details.unavailable_for_legal_reasons, 156 j)) 157 { 158 GNUNET_break_op (0); 159 ks.hr.http_status = 0; 160 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 161 break; 162 } 163 break; 164 case MHD_HTTP_INTERNAL_SERVER_ERROR: 165 ks.hr.ec = TALER_JSON_get_error_code (j); 166 /* Server had an internal issue; we should retry, but this API 167 leaves this to the application */ 168 break; 169 default: 170 /* unexpected response code */ 171 GNUNET_break_op (0); 172 ks.hr.ec = TALER_JSON_get_error_code (j); 173 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 174 "Unexpected response code %u/%d for exchange /kyc-wallet\n", 175 (unsigned int) response_code, 176 (int) ks.hr.ec); 177 break; 178 } 179 if (NULL != pkwh->cb) 180 { 181 pkwh->cb (pkwh->cb_cls, 182 &ks); 183 pkwh->cb = NULL; 184 } 185 TALER_EXCHANGE_post_kyc_wallet_cancel (pkwh); 186 } 187 188 189 struct TALER_EXCHANGE_PostKycWalletHandle * 190 TALER_EXCHANGE_post_kyc_wallet_create ( 191 struct GNUNET_CURL_Context *ctx, 192 const char *url, 193 const struct TALER_ReservePrivateKeyP *reserve_priv, 194 const struct TALER_Amount *balance) 195 { 196 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh; 197 198 pkwh = GNUNET_new (struct TALER_EXCHANGE_PostKycWalletHandle); 199 pkwh->ctx = ctx; 200 pkwh->base_url = GNUNET_strdup (url); 201 pkwh->reserve_priv = *reserve_priv; 202 pkwh->balance = *balance; 203 return pkwh; 204 } 205 206 207 enum TALER_ErrorCode 208 TALER_EXCHANGE_post_kyc_wallet_start ( 209 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh, 210 TALER_EXCHANGE_PostKycWalletCallback cb, 211 TALER_EXCHANGE_POST_KYC_WALLET_RESULT_CLOSURE *cb_cls) 212 { 213 CURL *eh; 214 json_t *req; 215 struct TALER_ReservePublicKeyP reserve_pub; 216 struct TALER_ReserveSignatureP reserve_sig; 217 218 pkwh->cb = cb; 219 pkwh->cb_cls = cb_cls; 220 GNUNET_CRYPTO_eddsa_key_get_public (&pkwh->reserve_priv.eddsa_priv, 221 &reserve_pub.eddsa_pub); 222 TALER_wallet_account_setup_sign (&pkwh->reserve_priv, 223 &pkwh->balance, 224 &reserve_sig); 225 req = GNUNET_JSON_PACK ( 226 TALER_JSON_pack_amount ("balance", 227 &pkwh->balance), 228 GNUNET_JSON_pack_data_auto ("reserve_pub", 229 &reserve_pub), 230 GNUNET_JSON_pack_data_auto ("reserve_sig", 231 &reserve_sig)); 232 GNUNET_assert (NULL != req); 233 pkwh->url = TALER_url_join (pkwh->base_url, 234 "kyc-wallet", 235 NULL); 236 if (NULL == pkwh->url) 237 { 238 json_decref (req); 239 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 240 } 241 eh = TALER_EXCHANGE_curl_easy_get_ (pkwh->url); 242 if ( (NULL == eh) || 243 (GNUNET_OK != 244 TALER_curl_easy_post (&pkwh->post_ctx, 245 eh, 246 req)) ) 247 { 248 GNUNET_break (0); 249 if (NULL != eh) 250 curl_easy_cleanup (eh); 251 json_decref (req); 252 GNUNET_free (pkwh->url); 253 pkwh->url = NULL; 254 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 255 } 256 json_decref (req); 257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 258 "Requesting URL '%s'\n", 259 pkwh->url); 260 pkwh->job = GNUNET_CURL_job_add2 (pkwh->ctx, 261 eh, 262 pkwh->post_ctx.headers, 263 &handle_kyc_wallet_finished, 264 pkwh); 265 if (NULL == pkwh->job) 266 { 267 TALER_curl_easy_post_finished (&pkwh->post_ctx); 268 GNUNET_free (pkwh->url); 269 pkwh->url = NULL; 270 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 271 } 272 return TALER_EC_NONE; 273 } 274 275 276 void 277 TALER_EXCHANGE_post_kyc_wallet_cancel ( 278 struct TALER_EXCHANGE_PostKycWalletHandle *pkwh) 279 { 280 if (NULL != pkwh->job) 281 { 282 GNUNET_CURL_job_cancel (pkwh->job); 283 pkwh->job = NULL; 284 } 285 TALER_curl_easy_post_finished (&pkwh->post_ctx); 286 GNUNET_free (pkwh->url); 287 GNUNET_free (pkwh->base_url); 288 GNUNET_free (pkwh); 289 } 290 291 292 /* end of exchange_api_post-kyc-wallet.c */