exchange_api_kyc_info.c (11214B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015-2023 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_kyc_info.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 "exchange_api_handle.h" 29 #include "taler/taler_signatures.h" 30 #include "exchange_api_curl_defaults.h" 31 32 33 /** 34 * @brief A /kyc-info Handle 35 */ 36 struct TALER_EXCHANGE_KycInfoHandle 37 { 38 39 /** 40 * The url for this request. 41 */ 42 char *url; 43 44 /** 45 * Handle for the request. 46 */ 47 struct GNUNET_CURL_Job *job; 48 49 /** 50 * Function to call with the result. 51 */ 52 TALER_EXCHANGE_KycInfoCallback kyc_info_cb; 53 54 /** 55 * Closure for @e cb. 56 */ 57 void *kyc_info_cb_cls; 58 59 }; 60 61 62 /** 63 * Parse the provided kyc_infoage data from the "200 OK" response 64 * for one of the coins. 65 * 66 * @param[in,out] lh kyc_info handle (callback may be zero'ed out) 67 * @param json json reply with the data for one coin 68 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 69 */ 70 static enum GNUNET_GenericReturnValue 71 parse_kyc_info_ok (struct TALER_EXCHANGE_KycInfoHandle *lh, 72 const json_t *json) 73 { 74 const json_t *jrequirements = NULL; 75 const json_t *jvoluntary_checks = NULL; 76 struct TALER_EXCHANGE_KycProcessClientInformation lr = { 77 .hr.reply = json, 78 .hr.http_status = MHD_HTTP_OK 79 }; 80 struct GNUNET_JSON_Specification spec[] = { 81 GNUNET_JSON_spec_array_const ("requirements", 82 &jrequirements), 83 GNUNET_JSON_spec_bool ("is_and_combinator", 84 &lr.details.ok.is_and_combinator), 85 GNUNET_JSON_spec_mark_optional ( 86 GNUNET_JSON_spec_object_const ("voluntary_checks", 87 &jvoluntary_checks), 88 NULL), 89 GNUNET_JSON_spec_end () 90 }; 91 92 if (GNUNET_OK != 93 GNUNET_JSON_parse (json, 94 spec, 95 NULL, NULL)) 96 { 97 GNUNET_break_op (0); 98 return GNUNET_SYSERR; 99 } 100 101 lr.details.ok.vci_length 102 = (unsigned int) json_object_size (jvoluntary_checks); 103 if ( ((size_t) lr.details.ok.vci_length) 104 != json_object_size (jvoluntary_checks)) 105 { 106 GNUNET_break_op (0); 107 return GNUNET_SYSERR; 108 } 109 lr.details.ok.requirements_length 110 = json_array_size (jrequirements); 111 if ( ((size_t) lr.details.ok.requirements_length) 112 != json_array_size (jrequirements)) 113 { 114 GNUNET_break_op (0); 115 return GNUNET_SYSERR; 116 } 117 118 { 119 struct TALER_EXCHANGE_VoluntaryCheckInformation vci[ 120 GNUNET_NZL (lr.details.ok.vci_length)]; 121 struct TALER_EXCHANGE_RequirementInformation requirements[ 122 GNUNET_NZL (lr.details.ok.requirements_length)]; 123 const char *name; 124 const json_t *jreq; 125 const json_t *jvc; 126 size_t off; 127 128 memset (vci, 129 0, 130 sizeof (vci)); 131 memset (requirements, 132 0, 133 sizeof (requirements)); 134 135 json_array_foreach ((json_t *) jrequirements, off, jreq) 136 { 137 struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off]; 138 struct GNUNET_JSON_Specification ispec[] = { 139 GNUNET_JSON_spec_string ("form", 140 &req->form), 141 GNUNET_JSON_spec_string ("description", 142 &req->description), 143 GNUNET_JSON_spec_mark_optional ( 144 GNUNET_JSON_spec_object_const ("description_i18n", 145 &req->description_i18n), 146 NULL), 147 GNUNET_JSON_spec_mark_optional ( 148 GNUNET_JSON_spec_string ("id", 149 &req->id), 150 NULL), 151 GNUNET_JSON_spec_end () 152 }; 153 154 if (GNUNET_OK != 155 GNUNET_JSON_parse (jreq, 156 ispec, 157 NULL, NULL)) 158 { 159 GNUNET_break_op (0); 160 return GNUNET_SYSERR; 161 } 162 } 163 GNUNET_assert (off == lr.details.ok.requirements_length); 164 165 off = 0; 166 json_object_foreach ((json_t *) jvoluntary_checks, name, jvc) 167 { 168 struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++]; 169 struct GNUNET_JSON_Specification ispec[] = { 170 GNUNET_JSON_spec_string ("description", 171 &vc->description), 172 GNUNET_JSON_spec_mark_optional ( 173 GNUNET_JSON_spec_object_const ("description_i18n", 174 &vc->description_i18n), 175 NULL), 176 GNUNET_JSON_spec_end () 177 }; 178 179 vc->name = name; 180 if (GNUNET_OK != 181 GNUNET_JSON_parse (jvc, 182 ispec, 183 NULL, NULL)) 184 { 185 GNUNET_break_op (0); 186 return GNUNET_SYSERR; 187 } 188 } 189 GNUNET_assert (off == lr.details.ok.vci_length); 190 191 lr.details.ok.vci = vci; 192 lr.details.ok.requirements = requirements; 193 lh->kyc_info_cb (lh->kyc_info_cb_cls, 194 &lr); 195 lh->kyc_info_cb = NULL; 196 return GNUNET_OK; 197 } 198 } 199 200 201 /** 202 * Function called when we're done processing the 203 * HTTP /kyc-info/$AT request. 204 * 205 * @param cls the `struct TALER_EXCHANGE_KycInfoHandle` 206 * @param response_code HTTP response code, 0 on error 207 * @param response parsed JSON result, NULL on error 208 */ 209 static void 210 handle_kyc_info_finished (void *cls, 211 long response_code, 212 const void *response) 213 { 214 struct TALER_EXCHANGE_KycInfoHandle *lh = cls; 215 const json_t *j = response; 216 struct TALER_EXCHANGE_KycProcessClientInformation lr = { 217 .hr.reply = j, 218 .hr.http_status = (unsigned int) response_code 219 }; 220 221 lh->job = NULL; 222 switch (response_code) 223 { 224 case 0: 225 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 226 break; 227 case MHD_HTTP_OK: 228 if (GNUNET_OK != 229 parse_kyc_info_ok (lh, 230 j)) 231 { 232 GNUNET_break_op (0); 233 lr.hr.http_status = 0; 234 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 235 break; 236 } 237 GNUNET_assert (NULL == lh->kyc_info_cb); 238 TALER_EXCHANGE_kyc_info_cancel (lh); 239 return; 240 case MHD_HTTP_NO_CONTENT: 241 break; 242 case MHD_HTTP_BAD_REQUEST: 243 lr.hr.ec = TALER_JSON_get_error_code (j); 244 lr.hr.hint = TALER_JSON_get_error_hint (j); 245 /* This should never happen, either us or the exchange is buggy 246 (or API version conflict); just pass JSON reply to the application */ 247 break; 248 case MHD_HTTP_FORBIDDEN: 249 lr.hr.ec = TALER_JSON_get_error_code (j); 250 lr.hr.hint = TALER_JSON_get_error_hint (j); 251 break; 252 case MHD_HTTP_NOT_FOUND: 253 lr.hr.ec = TALER_JSON_get_error_code (j); 254 lr.hr.hint = TALER_JSON_get_error_hint (j); 255 /* Nothing really to verify, exchange says this coin was not melted; we 256 should pass the JSON reply to the application */ 257 break; 258 case MHD_HTTP_INTERNAL_SERVER_ERROR: 259 lr.hr.ec = TALER_JSON_get_error_code (j); 260 lr.hr.hint = TALER_JSON_get_error_hint (j); 261 /* Server had an internal issue; we should retry, but this API 262 leaves this to the application */ 263 break; 264 default: 265 /* unexpected response code */ 266 GNUNET_break_op (0); 267 lr.hr.ec = TALER_JSON_get_error_code (j); 268 lr.hr.hint = TALER_JSON_get_error_hint (j); 269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 270 "Unexpected response code %u/%d for exchange /kyc-info\n", 271 (unsigned int) response_code, 272 (int) lr.hr.ec); 273 break; 274 } 275 if (NULL != lh->kyc_info_cb) 276 lh->kyc_info_cb (lh->kyc_info_cb_cls, 277 &lr); 278 TALER_EXCHANGE_kyc_info_cancel (lh); 279 } 280 281 282 struct TALER_EXCHANGE_KycInfoHandle * 283 TALER_EXCHANGE_kyc_info ( 284 struct GNUNET_CURL_Context *ctx, 285 const char *url, 286 const struct TALER_AccountAccessTokenP *token, 287 const char *if_none_match, 288 struct GNUNET_TIME_Relative timeout, 289 TALER_EXCHANGE_KycInfoCallback cb, 290 void *cb_cls) 291 { 292 struct TALER_EXCHANGE_KycInfoHandle *lh; 293 CURL *eh; 294 char arg_str[sizeof (struct TALER_AccountAccessTokenP) * 2 + 32]; 295 unsigned int tms 296 = (unsigned int) timeout.rel_value_us 297 / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; 298 struct curl_slist *job_headers = NULL; 299 300 { 301 char at_str[sizeof (struct TALER_AccountAccessTokenP) * 2]; 302 char *end; 303 304 end = GNUNET_STRINGS_data_to_string ( 305 token, 306 sizeof (*token), 307 at_str, 308 sizeof (at_str)); 309 *end = '\0'; 310 GNUNET_snprintf (arg_str, 311 sizeof (arg_str), 312 "kyc-info/%s", 313 at_str); 314 } 315 lh = GNUNET_new (struct TALER_EXCHANGE_KycInfoHandle); 316 lh->kyc_info_cb = cb; 317 lh->kyc_info_cb_cls = cb_cls; 318 { 319 char timeout_str[32]; 320 321 GNUNET_snprintf (timeout_str, 322 sizeof (timeout_str), 323 "%u", 324 tms); 325 lh->url = TALER_url_join (url, 326 arg_str, 327 "timeout_ms", 328 (0 == tms) 329 ? NULL 330 : timeout_str, 331 NULL); 332 } 333 if (NULL == lh->url) 334 { 335 GNUNET_free (lh); 336 return NULL; 337 } 338 eh = TALER_EXCHANGE_curl_easy_get_ (lh->url); 339 if (NULL == eh) 340 { 341 GNUNET_break (0); 342 GNUNET_free (lh->url); 343 GNUNET_free (lh); 344 return NULL; 345 } 346 if (0 != tms) 347 { 348 GNUNET_break (CURLE_OK == 349 curl_easy_setopt (eh, 350 CURLOPT_TIMEOUT_MS, 351 (long) (tms + 100L))); 352 } 353 354 job_headers = curl_slist_append (job_headers, 355 "Content-Type: application/json"); 356 if (NULL != if_none_match) 357 { 358 char *hdr; 359 360 GNUNET_asprintf (&hdr, 361 "%s: %s", 362 MHD_HTTP_HEADER_IF_NONE_MATCH, 363 if_none_match); 364 job_headers = curl_slist_append (job_headers, 365 hdr); 366 GNUNET_free (hdr); 367 } 368 lh->job = GNUNET_CURL_job_add2 (ctx, 369 eh, 370 job_headers, 371 &handle_kyc_info_finished, 372 lh); 373 curl_slist_free_all (job_headers); 374 return lh; 375 } 376 377 378 void 379 TALER_EXCHANGE_kyc_info_cancel (struct TALER_EXCHANGE_KycInfoHandle *lh) 380 { 381 if (NULL != lh->job) 382 { 383 GNUNET_CURL_job_cancel (lh->job); 384 lh->job = NULL; 385 } 386 387 GNUNET_free (lh->url); 388 GNUNET_free (lh); 389 } 390 391 392 /* end of exchange_api_kyc_info.c */