exchange_api_get-kyc-check-H_NORMALIZED_PAYTO.c (14017B)
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_get-kyc-check-H_NORMALIZED_PAYTO.c 19 * @brief Implementation of the /kyc-check request 20 * @author Christian Grothoff 21 */ 22 #include "platform.h" /* UNNECESSARY? */ 23 #include <microhttpd.h> /* just for HTTP check codes */ 24 #include <gnunet/gnunet_util_lib.h> 25 #include <gnunet/gnunet_curl_lib.h> 26 #include "taler/taler_exchange_service.h" /* UNNECESSARY? */ 27 #include "taler/taler_json_lib.h" 28 #include "taler/exchange/get-kyc-check-H_NORMALIZED_PAYTO.h" 29 #include "taler/taler_signatures.h" 30 #include "exchange_api_curl_defaults.h" 31 32 33 /** 34 * @brief A GET /kyc-check/$H_NORMALIZED_PAYTO handle 35 */ 36 struct TALER_EXCHANGE_GetKycCheckHandle 37 { 38 39 /** 40 * The base URL for this request. 41 */ 42 char *base_url; 43 44 /** 45 * The full URL for this request, set during _start. 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_GetKycCheckCallback cb; 58 59 /** 60 * Closure for @e cb. 61 */ 62 TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls; 63 64 /** 65 * Reference to the execution context. 66 */ 67 struct GNUNET_CURL_Context *ctx; 68 69 /** 70 * Hash of the payto URI we are checking. 71 */ 72 struct TALER_NormalizedPaytoHashP h_payto; 73 74 /** 75 * Private key to authorize the request. 76 */ 77 union TALER_AccountPrivateKeyP account_priv; 78 79 /** 80 * Long polling target. 81 */ 82 enum TALER_EXCHANGE_KycLongPollTarget lpt; 83 84 /** 85 * Latest known rule generation (for long polling). 86 */ 87 uint64_t known_rule_gen; 88 89 /** 90 * Long polling timeout. 91 */ 92 struct GNUNET_TIME_Relative timeout; 93 94 }; 95 96 97 /** 98 * Parse an account KYC status from JSON and invoke the callback. 99 * 100 * @param[in,out] gkch handle 101 * @param j JSON to parse 102 * @param res response to fill 103 * @param status account status field within @a res to fill 104 * @return #GNUNET_OK on success 105 */ 106 static enum GNUNET_GenericReturnValue 107 parse_account_status ( 108 struct TALER_EXCHANGE_GetKycCheckHandle *gkch, 109 const json_t *j, 110 struct TALER_EXCHANGE_GetKycCheckResponse *res, 111 struct TALER_EXCHANGE_AccountKycStatus *status) 112 { 113 const json_t *limits = NULL; 114 struct GNUNET_JSON_Specification spec[] = { 115 GNUNET_JSON_spec_bool ("aml_review", 116 &status->aml_review), 117 GNUNET_JSON_spec_uint64 ("rule_gen", 118 &status->rule_gen), 119 GNUNET_JSON_spec_fixed_auto ("access_token", 120 &status->access_token), 121 GNUNET_JSON_spec_mark_optional ( 122 GNUNET_JSON_spec_string ("tos_required", 123 &status->tos_required), 124 NULL), 125 GNUNET_JSON_spec_mark_optional ( 126 GNUNET_JSON_spec_array_const ("limits", 127 &limits), 128 NULL), 129 GNUNET_JSON_spec_end () 130 }; 131 132 if (GNUNET_OK != 133 GNUNET_JSON_parse (j, 134 spec, 135 NULL, NULL)) 136 { 137 GNUNET_break_op (0); 138 return GNUNET_SYSERR; 139 } 140 if ( (NULL != limits) && 141 (0 != json_array_size (limits)) ) 142 { 143 size_t limit_length = json_array_size (limits); 144 struct TALER_EXCHANGE_AccountLimit *ala; 145 size_t i; 146 json_t *limit; 147 148 if (limit_length > 149 GNUNET_MAX_MALLOC_CHECKED / sizeof (*ala)) 150 { 151 GNUNET_break_op (0); 152 return GNUNET_SYSERR; 153 } 154 ala = GNUNET_new_array (limit_length, 155 struct TALER_EXCHANGE_AccountLimit); 156 json_array_foreach (limits, i, limit) 157 { 158 struct TALER_EXCHANGE_AccountLimit *al = &ala[i]; 159 struct GNUNET_JSON_Specification ispec[] = { 160 GNUNET_JSON_spec_mark_optional ( 161 GNUNET_JSON_spec_bool ("soft_limit", 162 &al->soft_limit), 163 NULL), 164 GNUNET_JSON_spec_relative_time ("timeframe", 165 &al->timeframe), 166 TALER_JSON_spec_kycte ("operation_type", 167 &al->operation_type), 168 TALER_JSON_spec_amount_any ("threshold", 169 &al->threshold), 170 GNUNET_JSON_spec_end () 171 }; 172 173 al->soft_limit = false; 174 if (GNUNET_OK != 175 GNUNET_JSON_parse (limit, 176 ispec, 177 NULL, NULL)) 178 { 179 GNUNET_break_op (0); 180 GNUNET_free (ala); 181 return GNUNET_SYSERR; 182 } 183 } 184 status->limits = ala; 185 status->limits_length = limit_length; 186 gkch->cb (gkch->cb_cls, 187 res); 188 GNUNET_free (ala); 189 } 190 else 191 { 192 gkch->cb (gkch->cb_cls, 193 res); 194 } 195 GNUNET_JSON_parse_free (spec); 196 return GNUNET_OK; 197 } 198 199 200 /** 201 * Function called when we're done processing the 202 * HTTP GET /kyc-check/$H_NORMALIZED_PAYTO request. 203 * 204 * @param cls the `struct TALER_EXCHANGE_GetKycCheckHandle` 205 * @param response_code HTTP response code, 0 on error 206 * @param response parsed JSON result, NULL on error 207 */ 208 static void 209 handle_kyc_check_finished (void *cls, 210 long response_code, 211 const void *response) 212 { 213 struct TALER_EXCHANGE_GetKycCheckHandle *gkch = cls; 214 const json_t *j = response; 215 struct TALER_EXCHANGE_GetKycCheckResponse ks = { 216 .hr.reply = j, 217 .hr.http_status = (unsigned int) response_code 218 }; 219 220 gkch->job = NULL; 221 switch (response_code) 222 { 223 case 0: 224 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 225 break; 226 case MHD_HTTP_OK: 227 { 228 if (GNUNET_OK != 229 parse_account_status (gkch, 230 j, 231 &ks, 232 &ks.details.ok)) 233 { 234 GNUNET_break_op (0); 235 ks.hr.http_status = 0; 236 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 237 break; 238 } 239 TALER_EXCHANGE_get_kyc_check_cancel (gkch); 240 return; 241 } 242 case MHD_HTTP_ACCEPTED: 243 { 244 if (GNUNET_OK != 245 parse_account_status (gkch, 246 j, 247 &ks, 248 &ks.details.accepted)) 249 { 250 GNUNET_break_op (0); 251 ks.hr.http_status = 0; 252 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 253 break; 254 } 255 TALER_EXCHANGE_get_kyc_check_cancel (gkch); 256 return; 257 } 258 case MHD_HTTP_NO_CONTENT: 259 break; 260 case MHD_HTTP_BAD_REQUEST: 261 ks.hr.ec = TALER_JSON_get_error_code (j); 262 break; 263 case MHD_HTTP_FORBIDDEN: 264 { 265 struct GNUNET_JSON_Specification spec[] = { 266 GNUNET_JSON_spec_fixed_auto ( 267 "expected_account_pub", 268 &ks.details.forbidden.expected_account_pub), 269 TALER_JSON_spec_ec ("code", 270 &ks.hr.ec), 271 GNUNET_JSON_spec_end () 272 }; 273 274 if (GNUNET_OK != 275 GNUNET_JSON_parse (j, 276 spec, 277 NULL, NULL)) 278 { 279 GNUNET_break_op (0); 280 ks.hr.http_status = 0; 281 ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 282 break; 283 } 284 break; 285 } 286 case MHD_HTTP_NOT_FOUND: 287 ks.hr.ec = TALER_JSON_get_error_code (j); 288 break; 289 case MHD_HTTP_CONFLICT: 290 ks.hr.ec = TALER_JSON_get_error_code (j); 291 break; 292 case MHD_HTTP_INTERNAL_SERVER_ERROR: 293 ks.hr.ec = TALER_JSON_get_error_code (j); 294 break; 295 default: 296 /* unexpected response code */ 297 GNUNET_break_op (0); 298 ks.hr.ec = TALER_JSON_get_error_code (j); 299 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 300 "Unexpected response code %u/%d for exchange kyc_check\n", 301 (unsigned int) response_code, 302 (int) ks.hr.ec); 303 break; 304 } 305 if (NULL != gkch->cb) 306 { 307 gkch->cb (gkch->cb_cls, 308 &ks); 309 gkch->cb = NULL; 310 } 311 TALER_EXCHANGE_get_kyc_check_cancel (gkch); 312 } 313 314 315 struct TALER_EXCHANGE_GetKycCheckHandle * 316 TALER_EXCHANGE_get_kyc_check_create ( 317 struct GNUNET_CURL_Context *ctx, 318 const char *url, 319 const struct TALER_NormalizedPaytoHashP *h_payto, 320 const union TALER_AccountPrivateKeyP *pk) 321 { 322 struct TALER_EXCHANGE_GetKycCheckHandle *gkch; 323 324 gkch = GNUNET_new (struct TALER_EXCHANGE_GetKycCheckHandle); 325 gkch->ctx = ctx; 326 gkch->base_url = GNUNET_strdup (url); 327 gkch->h_payto = *h_payto; 328 gkch->account_priv = *pk; 329 return gkch; 330 } 331 332 333 enum GNUNET_GenericReturnValue 334 TALER_EXCHANGE_get_kyc_check_set_options_ ( 335 struct TALER_EXCHANGE_GetKycCheckHandle *gkch, 336 unsigned int num_options, 337 const struct TALER_EXCHANGE_GetKycCheckOptionValue *options) 338 { 339 for (unsigned int i = 0; i < num_options; i++) 340 { 341 const struct TALER_EXCHANGE_GetKycCheckOptionValue *opt = &options[i]; 342 343 switch (opt->option) 344 { 345 case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_END: 346 return GNUNET_OK; 347 case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_KNOWN_RULE_GEN: 348 gkch->known_rule_gen = opt->details.known_rule_gen; 349 break; 350 case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_LPT: 351 gkch->lpt = opt->details.lpt; 352 break; 353 case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_TIMEOUT: 354 gkch->timeout = opt->details.timeout; 355 break; 356 default: 357 GNUNET_break (0); 358 return GNUNET_SYSERR; 359 } 360 } 361 return GNUNET_OK; 362 } 363 364 365 enum TALER_ErrorCode 366 TALER_EXCHANGE_get_kyc_check_start ( 367 struct TALER_EXCHANGE_GetKycCheckHandle *gkch, 368 TALER_EXCHANGE_GetKycCheckCallback cb, 369 TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls) 370 { 371 CURL *eh; 372 char arg_str[128]; 373 char timeout_ms[32]; 374 char lpt_str[32]; 375 char krg_str[32]; 376 struct curl_slist *job_headers = NULL; 377 unsigned long long tms; 378 379 gkch->cb = cb; 380 gkch->cb_cls = cb_cls; 381 { 382 char *hps; 383 384 hps = GNUNET_STRINGS_data_to_string_alloc ( 385 &gkch->h_payto, 386 sizeof (gkch->h_payto)); 387 GNUNET_snprintf (arg_str, 388 sizeof (arg_str), 389 "kyc-check/%s", 390 hps); 391 GNUNET_free (hps); 392 } 393 tms = gkch->timeout.rel_value_us 394 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; 395 GNUNET_snprintf (timeout_ms, 396 sizeof (timeout_ms), 397 "%llu", 398 tms); 399 GNUNET_snprintf (krg_str, 400 sizeof (krg_str), 401 "%llu", 402 (unsigned long long) gkch->known_rule_gen); 403 GNUNET_snprintf (lpt_str, 404 sizeof (lpt_str), 405 "%d", 406 (int) gkch->lpt); 407 gkch->url 408 = TALER_url_join ( 409 gkch->base_url, 410 arg_str, 411 "timeout_ms", 412 GNUNET_TIME_relative_is_zero (gkch->timeout) 413 ? NULL 414 : timeout_ms, 415 "min_rule", 416 0 == gkch->known_rule_gen 417 ? NULL 418 : krg_str, 419 "lpt", 420 TALER_EXCHANGE_KLPT_NONE == gkch->lpt 421 ? NULL 422 : lpt_str, 423 NULL); 424 if (NULL == gkch->url) 425 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 426 eh = TALER_EXCHANGE_curl_easy_get_ (gkch->url); 427 if (NULL == eh) 428 { 429 GNUNET_break (0); 430 GNUNET_free (gkch->url); 431 gkch->url = NULL; 432 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 433 } 434 if (0 != tms) 435 { 436 GNUNET_break (CURLE_OK == 437 curl_easy_setopt (eh, 438 CURLOPT_TIMEOUT_MS, 439 (long) (tms + 500L))); 440 } 441 { 442 union TALER_AccountPublicKeyP account_pub; 443 union TALER_AccountSignatureP account_sig; 444 char *sig_hdr; 445 char *pub_hdr; 446 char *hdr; 447 448 GNUNET_CRYPTO_eddsa_key_get_public ( 449 &gkch->account_priv.reserve_priv.eddsa_priv, 450 &account_pub.reserve_pub.eddsa_pub); 451 TALER_account_kyc_auth_sign (&gkch->account_priv, 452 &account_sig); 453 454 sig_hdr = GNUNET_STRINGS_data_to_string_alloc ( 455 &account_sig, 456 sizeof (account_sig)); 457 GNUNET_asprintf (&hdr, 458 "%s: %s", 459 TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE, 460 sig_hdr); 461 GNUNET_free (sig_hdr); 462 job_headers = curl_slist_append (job_headers, 463 hdr); 464 GNUNET_free (hdr); 465 466 pub_hdr = GNUNET_STRINGS_data_to_string_alloc ( 467 &account_pub, 468 sizeof (account_pub)); 469 GNUNET_asprintf (&hdr, 470 "%s: %s", 471 TALER_HTTP_HEADER_ACCOUNT_OWNER_PUBKEY, 472 pub_hdr); 473 GNUNET_free (pub_hdr); 474 job_headers = curl_slist_append (job_headers, 475 hdr); 476 GNUNET_free (hdr); 477 if (NULL == job_headers) 478 { 479 GNUNET_break (0); 480 curl_easy_cleanup (eh); 481 GNUNET_free (gkch->url); 482 gkch->url = NULL; 483 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 484 } 485 } 486 gkch->job 487 = GNUNET_CURL_job_add2 (gkch->ctx, 488 eh, 489 job_headers, 490 &handle_kyc_check_finished, 491 gkch); 492 curl_slist_free_all (job_headers); 493 if (NULL == gkch->job) 494 { 495 GNUNET_free (gkch->url); 496 gkch->url = NULL; 497 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 498 } 499 return TALER_EC_NONE; 500 } 501 502 503 void 504 TALER_EXCHANGE_get_kyc_check_cancel ( 505 struct TALER_EXCHANGE_GetKycCheckHandle *gkch) 506 { 507 if (NULL != gkch->job) 508 { 509 GNUNET_CURL_job_cancel (gkch->job); 510 gkch->job = NULL; 511 } 512 GNUNET_free (gkch->url); 513 GNUNET_free (gkch->base_url); 514 GNUNET_free (gkch); 515 } 516 517 518 /* end of exchange_api_get-kyc-check-H_NORMALIZED_PAYTO.c */