exchange_api_get-kyc-info-ACCESS_TOKEN.c (12609B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015-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-info-ACCESS_TOKEN.c 19 * @brief Implementation of the /kyc-info/$AT 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/get-kyc-info-ACCESS_TOKEN.h" 27 #include "taler/taler_signatures.h" 28 #include "exchange_api_curl_defaults.h" 29 30 31 /** 32 * @brief A GET /kyc-info/$AT handle 33 */ 34 struct TALER_EXCHANGE_GetKycInfoHandle 35 { 36 37 /** 38 * The base URL for this request. 39 */ 40 char *base_url; 41 42 /** 43 * The full URL for this request, set during _start. 44 */ 45 char *url; 46 47 /** 48 * Handle for the request. 49 */ 50 struct GNUNET_CURL_Job *job; 51 52 /** 53 * Function to call with the result. 54 */ 55 TALER_EXCHANGE_GetKycInfoCallback cb; 56 57 /** 58 * Closure for @e cb. 59 */ 60 TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE *cb_cls; 61 62 /** 63 * Reference to the execution context. 64 */ 65 struct GNUNET_CURL_Context *ctx; 66 67 /** 68 * Access token for the KYC process. 69 */ 70 struct TALER_AccountAccessTokenP token; 71 72 /** 73 * ETag from a previous response for conditional requests. 74 * Borrowed pointer, not owned. 75 */ 76 const char *if_none_match; 77 78 /** 79 * Long polling timeout. 80 */ 81 struct GNUNET_TIME_Relative timeout; 82 83 }; 84 85 86 /** 87 * Parse the provided kyc-info data from the "200 OK" response. 88 * 89 * @param[in,out] lh handle (callback may be zero'ed out) 90 * @param json json reply with the data 91 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 92 */ 93 static enum GNUNET_GenericReturnValue 94 parse_kyc_info_ok (struct TALER_EXCHANGE_GetKycInfoHandle *lh, 95 const json_t *json) 96 { 97 const json_t *jrequirements = NULL; 98 const json_t *jvoluntary_checks = NULL; 99 struct TALER_EXCHANGE_GetKycInfoResponse lr = { 100 .hr.reply = json, 101 .hr.http_status = MHD_HTTP_OK 102 }; 103 struct GNUNET_JSON_Specification spec[] = { 104 GNUNET_JSON_spec_array_const ("requirements", 105 &jrequirements), 106 GNUNET_JSON_spec_bool ("is_and_combinator", 107 &lr.details.ok.is_and_combinator), 108 GNUNET_JSON_spec_mark_optional ( 109 GNUNET_JSON_spec_object_const ("voluntary_checks", 110 &jvoluntary_checks), 111 NULL), 112 GNUNET_JSON_spec_end () 113 }; 114 115 if (GNUNET_OK != 116 GNUNET_JSON_parse (json, 117 spec, 118 NULL, NULL)) 119 { 120 GNUNET_break_op (0); 121 return GNUNET_SYSERR; 122 } 123 124 lr.details.ok.vci_length = json_object_size (jvoluntary_checks); 125 lr.details.ok.requirements_length = json_array_size (jrequirements); 126 127 { 128 struct TALER_EXCHANGE_VoluntaryCheckInformation *vci; 129 struct TALER_EXCHANGE_RequirementInformation *requirements; 130 const char *name; 131 const json_t *jreq; 132 const json_t *jvc; 133 size_t off; 134 135 if ( (lr.details.ok.vci_length > 136 GNUNET_MAX_MALLOC_CHECKED / sizeof (*vci)) || 137 (lr.details.ok.requirements_length > 138 GNUNET_MAX_MALLOC_CHECKED / sizeof (*requirements)) ) 139 { 140 GNUNET_break_op (0); 141 return GNUNET_SYSERR; 142 } 143 vci = GNUNET_new_array ( 144 GNUNET_NZL (lr.details.ok.vci_length), 145 struct TALER_EXCHANGE_VoluntaryCheckInformation); 146 requirements = GNUNET_new_array ( 147 GNUNET_NZL (lr.details.ok.requirements_length), 148 struct TALER_EXCHANGE_RequirementInformation); 149 json_array_foreach ((json_t *) jrequirements, off, jreq) 150 { 151 struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off]; 152 struct GNUNET_JSON_Specification ispec[] = { 153 GNUNET_JSON_spec_string ("form", 154 &req->form), 155 GNUNET_JSON_spec_string ("description", 156 &req->description), 157 GNUNET_JSON_spec_mark_optional ( 158 GNUNET_JSON_spec_object_const ("description_i18n", 159 &req->description_i18n), 160 NULL), 161 GNUNET_JSON_spec_mark_optional ( 162 GNUNET_JSON_spec_string ("id", 163 &req->id), 164 NULL), 165 GNUNET_JSON_spec_end () 166 }; 167 168 if (GNUNET_OK != 169 GNUNET_JSON_parse (jreq, 170 ispec, 171 NULL, NULL)) 172 { 173 GNUNET_break_op (0); 174 GNUNET_free (vci); 175 GNUNET_free (requirements); 176 return GNUNET_SYSERR; 177 } 178 } 179 GNUNET_assert (off == lr.details.ok.requirements_length); 180 181 off = 0; 182 json_object_foreach ((json_t *) jvoluntary_checks, name, jvc) 183 { 184 struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++]; 185 struct GNUNET_JSON_Specification ispec[] = { 186 GNUNET_JSON_spec_string ("description", 187 &vc->description), 188 GNUNET_JSON_spec_mark_optional ( 189 GNUNET_JSON_spec_object_const ("description_i18n", 190 &vc->description_i18n), 191 NULL), 192 GNUNET_JSON_spec_end () 193 }; 194 195 vc->name = name; 196 if (GNUNET_OK != 197 GNUNET_JSON_parse (jvc, 198 ispec, 199 NULL, NULL)) 200 { 201 GNUNET_break_op (0); 202 GNUNET_free (vci); 203 GNUNET_free (requirements); 204 return GNUNET_SYSERR; 205 } 206 } 207 GNUNET_assert (off == lr.details.ok.vci_length); 208 209 lr.details.ok.vci = vci; 210 lr.details.ok.requirements = requirements; 211 lh->cb (lh->cb_cls, 212 &lr); 213 lh->cb = NULL; 214 GNUNET_free (vci); 215 GNUNET_free (requirements); 216 return GNUNET_OK; 217 } 218 } 219 220 221 /** 222 * Function called when we're done processing the 223 * HTTP GET /kyc-info/$AT request. 224 * 225 * @param cls the `struct TALER_EXCHANGE_GetKycInfoHandle` 226 * @param response_code HTTP response code, 0 on error 227 * @param response parsed JSON result, NULL on error 228 */ 229 static void 230 handle_kyc_info_finished (void *cls, 231 long response_code, 232 const void *response) 233 { 234 struct TALER_EXCHANGE_GetKycInfoHandle *lh = cls; 235 const json_t *j = response; 236 struct TALER_EXCHANGE_GetKycInfoResponse lr = { 237 .hr.reply = j, 238 .hr.http_status = (unsigned int) response_code 239 }; 240 241 lh->job = NULL; 242 switch (response_code) 243 { 244 case 0: 245 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 246 break; 247 case MHD_HTTP_OK: 248 if (GNUNET_OK != 249 parse_kyc_info_ok (lh, 250 j)) 251 { 252 GNUNET_break_op (0); 253 lr.hr.http_status = 0; 254 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 255 break; 256 } 257 GNUNET_assert (NULL == lh->cb); 258 TALER_EXCHANGE_get_kyc_info_cancel (lh); 259 return; 260 case MHD_HTTP_NO_CONTENT: 261 break; 262 case MHD_HTTP_NOT_MODIFIED: 263 break; 264 case MHD_HTTP_BAD_REQUEST: 265 lr.hr.ec = TALER_JSON_get_error_code (j); 266 lr.hr.hint = TALER_JSON_get_error_hint (j); 267 break; 268 case MHD_HTTP_FORBIDDEN: 269 lr.hr.ec = TALER_JSON_get_error_code (j); 270 lr.hr.hint = TALER_JSON_get_error_hint (j); 271 break; 272 case MHD_HTTP_NOT_FOUND: 273 lr.hr.ec = TALER_JSON_get_error_code (j); 274 lr.hr.hint = TALER_JSON_get_error_hint (j); 275 break; 276 case MHD_HTTP_INTERNAL_SERVER_ERROR: 277 lr.hr.ec = TALER_JSON_get_error_code (j); 278 lr.hr.hint = TALER_JSON_get_error_hint (j); 279 break; 280 default: 281 /* unexpected response code */ 282 GNUNET_break_op (0); 283 lr.hr.ec = TALER_JSON_get_error_code (j); 284 lr.hr.hint = TALER_JSON_get_error_hint (j); 285 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 286 "Unexpected response code %u/%d for exchange /kyc-info\n", 287 (unsigned int) response_code, 288 (int) lr.hr.ec); 289 break; 290 } 291 if (NULL != lh->cb) 292 lh->cb (lh->cb_cls, 293 &lr); 294 TALER_EXCHANGE_get_kyc_info_cancel (lh); 295 } 296 297 298 struct TALER_EXCHANGE_GetKycInfoHandle * 299 TALER_EXCHANGE_get_kyc_info_create ( 300 struct GNUNET_CURL_Context *ctx, 301 const char *url, 302 const struct TALER_AccountAccessTokenP *token) 303 { 304 struct TALER_EXCHANGE_GetKycInfoHandle *lh; 305 306 lh = GNUNET_new (struct TALER_EXCHANGE_GetKycInfoHandle); 307 lh->ctx = ctx; 308 lh->base_url = GNUNET_strdup (url); 309 lh->token = *token; 310 return lh; 311 } 312 313 314 enum GNUNET_GenericReturnValue 315 TALER_EXCHANGE_get_kyc_info_set_options_ ( 316 struct TALER_EXCHANGE_GetKycInfoHandle *gkih, 317 unsigned int num_options, 318 const struct TALER_EXCHANGE_GetKycInfoOptionValue *options) 319 { 320 for (unsigned int i = 0; i < num_options; i++) 321 { 322 const struct TALER_EXCHANGE_GetKycInfoOptionValue *opt = &options[i]; 323 324 switch (opt->option) 325 { 326 case TALER_EXCHANGE_GET_KYC_INFO_OPTION_END: 327 return GNUNET_OK; 328 case TALER_EXCHANGE_GET_KYC_INFO_OPTION_IF_NONE_MATCH: 329 gkih->if_none_match = opt->details.if_none_match; 330 break; 331 case TALER_EXCHANGE_GET_KYC_INFO_OPTION_TIMEOUT: 332 gkih->timeout = opt->details.timeout; 333 break; 334 default: 335 GNUNET_break (0); 336 return GNUNET_SYSERR; 337 } 338 } 339 return GNUNET_OK; 340 } 341 342 343 enum TALER_ErrorCode 344 TALER_EXCHANGE_get_kyc_info_start ( 345 struct TALER_EXCHANGE_GetKycInfoHandle *gkih, 346 TALER_EXCHANGE_GetKycInfoCallback cb, 347 TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE *cb_cls) 348 { 349 CURL *eh; 350 char arg_str[sizeof (struct TALER_AccountAccessTokenP) * 2 + 32]; 351 unsigned int tms; 352 struct curl_slist *job_headers = NULL; 353 354 gkih->cb = cb; 355 gkih->cb_cls = cb_cls; 356 tms = (unsigned int) (gkih->timeout.rel_value_us 357 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us); 358 { 359 char at_str[sizeof (struct TALER_AccountAccessTokenP) * 2]; 360 char *end; 361 362 end = GNUNET_STRINGS_data_to_string ( 363 &gkih->token, 364 sizeof (gkih->token), 365 at_str, 366 sizeof (at_str)); 367 *end = '\0'; 368 GNUNET_snprintf (arg_str, 369 sizeof (arg_str), 370 "kyc-info/%s", 371 at_str); 372 } 373 { 374 char timeout_str[32]; 375 376 GNUNET_snprintf (timeout_str, 377 sizeof (timeout_str), 378 "%u", 379 tms); 380 gkih->url = TALER_url_join (gkih->base_url, 381 arg_str, 382 "timeout_ms", 383 (0 == tms) 384 ? NULL 385 : timeout_str, 386 NULL); 387 } 388 if (NULL == gkih->url) 389 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 390 eh = TALER_EXCHANGE_curl_easy_get_ (gkih->url); 391 if (NULL == eh) 392 { 393 GNUNET_break (0); 394 GNUNET_free (gkih->url); 395 gkih->url = NULL; 396 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 397 } 398 if (0 != tms) 399 { 400 GNUNET_break (CURLE_OK == 401 curl_easy_setopt (eh, 402 CURLOPT_TIMEOUT_MS, 403 (long) (tms + 100L))); 404 } 405 if (NULL != gkih->if_none_match) 406 { 407 char *hdr; 408 409 GNUNET_asprintf (&hdr, 410 "%s: %s", 411 MHD_HTTP_HEADER_IF_NONE_MATCH, 412 gkih->if_none_match); 413 job_headers = curl_slist_append (job_headers, 414 hdr); 415 GNUNET_free (hdr); 416 } 417 gkih->job = GNUNET_CURL_job_add2 (gkih->ctx, 418 eh, 419 job_headers, 420 &handle_kyc_info_finished, 421 gkih); 422 curl_slist_free_all (job_headers); 423 if (NULL == gkih->job) 424 { 425 GNUNET_free (gkih->url); 426 gkih->url = NULL; 427 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 428 } 429 return TALER_EC_NONE; 430 } 431 432 433 void 434 TALER_EXCHANGE_get_kyc_info_cancel ( 435 struct TALER_EXCHANGE_GetKycInfoHandle *gkih) 436 { 437 if (NULL != gkih->job) 438 { 439 GNUNET_CURL_job_cancel (gkih->job); 440 gkih->job = NULL; 441 } 442 GNUNET_free (gkih->url); 443 GNUNET_free (gkih->base_url); 444 GNUNET_free (gkih); 445 } 446 447 448 /* end of exchange_api_get-kyc-info-ACCESS_TOKEN.c */