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