exchange_api_get-kyc-info-ACCESS_TOKEN.c (12119B)
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 GNUNET_NZL (lr.details.ok.vci_length)]; 130 struct TALER_EXCHANGE_RequirementInformation requirements[ 131 GNUNET_NZL (lr.details.ok.requirements_length)]; 132 const char *name; 133 const json_t *jreq; 134 const json_t *jvc; 135 size_t off; 136 137 memset (vci, 138 0, 139 sizeof (vci)); 140 memset (requirements, 141 0, 142 sizeof (requirements)); 143 144 json_array_foreach ((json_t *) jrequirements, off, jreq) 145 { 146 struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off]; 147 struct GNUNET_JSON_Specification ispec[] = { 148 GNUNET_JSON_spec_string ("form", 149 &req->form), 150 GNUNET_JSON_spec_string ("description", 151 &req->description), 152 GNUNET_JSON_spec_mark_optional ( 153 GNUNET_JSON_spec_object_const ("description_i18n", 154 &req->description_i18n), 155 NULL), 156 GNUNET_JSON_spec_mark_optional ( 157 GNUNET_JSON_spec_string ("id", 158 &req->id), 159 NULL), 160 GNUNET_JSON_spec_end () 161 }; 162 163 if (GNUNET_OK != 164 GNUNET_JSON_parse (jreq, 165 ispec, 166 NULL, NULL)) 167 { 168 GNUNET_break_op (0); 169 return GNUNET_SYSERR; 170 } 171 } 172 GNUNET_assert (off == lr.details.ok.requirements_length); 173 174 off = 0; 175 json_object_foreach ((json_t *) jvoluntary_checks, name, jvc) 176 { 177 struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++]; 178 struct GNUNET_JSON_Specification ispec[] = { 179 GNUNET_JSON_spec_string ("description", 180 &vc->description), 181 GNUNET_JSON_spec_mark_optional ( 182 GNUNET_JSON_spec_object_const ("description_i18n", 183 &vc->description_i18n), 184 NULL), 185 GNUNET_JSON_spec_end () 186 }; 187 188 vc->name = name; 189 if (GNUNET_OK != 190 GNUNET_JSON_parse (jvc, 191 ispec, 192 NULL, NULL)) 193 { 194 GNUNET_break_op (0); 195 return GNUNET_SYSERR; 196 } 197 } 198 GNUNET_assert (off == lr.details.ok.vci_length); 199 200 lr.details.ok.vci = vci; 201 lr.details.ok.requirements = requirements; 202 lh->cb (lh->cb_cls, 203 &lr); 204 lh->cb = NULL; 205 return GNUNET_OK; 206 } 207 } 208 209 210 /** 211 * Function called when we're done processing the 212 * HTTP GET /kyc-info/$AT request. 213 * 214 * @param cls the `struct TALER_EXCHANGE_GetKycInfoHandle` 215 * @param response_code HTTP response code, 0 on error 216 * @param response parsed JSON result, NULL on error 217 */ 218 static void 219 handle_kyc_info_finished (void *cls, 220 long response_code, 221 const void *response) 222 { 223 struct TALER_EXCHANGE_GetKycInfoHandle *lh = cls; 224 const json_t *j = response; 225 struct TALER_EXCHANGE_GetKycInfoResponse lr = { 226 .hr.reply = j, 227 .hr.http_status = (unsigned int) response_code 228 }; 229 230 lh->job = NULL; 231 switch (response_code) 232 { 233 case 0: 234 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 235 break; 236 case MHD_HTTP_OK: 237 if (GNUNET_OK != 238 parse_kyc_info_ok (lh, 239 j)) 240 { 241 GNUNET_break_op (0); 242 lr.hr.http_status = 0; 243 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 244 break; 245 } 246 GNUNET_assert (NULL == lh->cb); 247 TALER_EXCHANGE_get_kyc_info_cancel (lh); 248 return; 249 case MHD_HTTP_NO_CONTENT: 250 break; 251 case MHD_HTTP_NOT_MODIFIED: 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 if (NULL != gkih->if_none_match) 395 { 396 char *hdr; 397 398 GNUNET_asprintf (&hdr, 399 "%s: %s", 400 MHD_HTTP_HEADER_IF_NONE_MATCH, 401 gkih->if_none_match); 402 job_headers = curl_slist_append (job_headers, 403 hdr); 404 GNUNET_free (hdr); 405 } 406 gkih->job = GNUNET_CURL_job_add2 (gkih->ctx, 407 eh, 408 job_headers, 409 &handle_kyc_info_finished, 410 gkih); 411 curl_slist_free_all (job_headers); 412 if (NULL == gkih->job) 413 { 414 GNUNET_free (gkih->url); 415 gkih->url = NULL; 416 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 417 } 418 return TALER_EC_NONE; 419 } 420 421 422 void 423 TALER_EXCHANGE_get_kyc_info_cancel ( 424 struct TALER_EXCHANGE_GetKycInfoHandle *gkih) 425 { 426 if (NULL != gkih->job) 427 { 428 GNUNET_CURL_job_cancel (gkih->job); 429 gkih->job = NULL; 430 } 431 GNUNET_free (gkih->url); 432 GNUNET_free (gkih->base_url); 433 GNUNET_free (gkih); 434 } 435 436 437 /* end of exchange_api_get-kyc-info-ACCESS_TOKEN.c */