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