exchange_api_get-aml-OFFICER_PUB-kyc-statistics-NAMES.c (12730B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2023, 2024, 2025, 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-aml-OFFICER_PUB-kyc-statistics-NAMES.c 19 * @brief Implementation of the /aml/$OFFICER_PUB/kyc-statistics/$NAME 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 "exchange_api_handle.h" 27 #include "taler/taler_signatures.h" 28 #include "exchange_api_curl_defaults.h" 29 #include "taler/exchange/get-aml-OFFICER_PUB-kyc-statistics-NAMES.h" 30 31 32 /** 33 * @brief A GET /aml/$OFFICER_PUB/kyc-statistics/$NAMES Handle (new API) 34 */ 35 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle 36 { 37 38 /** 39 * The base URL of the exchange. 40 */ 41 char *base_url; 42 43 /** 44 * The full URL for this request. 45 */ 46 char *url; 47 48 /** 49 * Handle for the request. 50 */ 51 struct GNUNET_CURL_Job *job; 52 53 /** 54 * Function to call with the result. 55 */ 56 TALER_EXCHANGE_GetAmlKycStatisticsCallback cb; 57 58 /** 59 * Closure for @e cb. 60 */ 61 TALER_EXCHANGE_GET_AML_KYC_STATISTICS_RESULT_CLOSURE *cb_cls; 62 63 /** 64 * CURL context to use. 65 */ 66 struct GNUNET_CURL_Context *ctx; 67 68 /** 69 * Public key of the AML officer. 70 */ 71 struct TALER_AmlOfficerPublicKeyP officer_pub; 72 73 /** 74 * Private key of the AML officer. 75 */ 76 struct TALER_AmlOfficerPrivateKeyP officer_priv; 77 78 /** 79 * Space-separated list of event type names to count. 80 */ 81 char *names; 82 83 /** 84 * Options for this request. 85 */ 86 struct 87 { 88 /** 89 * Start date for statistics window. Zero means "from the beginning". 90 */ 91 struct GNUNET_TIME_Timestamp start_date; 92 93 /** 94 * End date for statistics window. #GNUNET_TIME_UNIT_FOREVER_ABS means "up to now". 95 */ 96 struct GNUNET_TIME_Timestamp end_date; 97 } options; 98 99 }; 100 101 102 /** 103 * Parse the provided statistics data from the "200 OK" response. 104 * 105 * @param[in,out] aksh handle (callback may be zero'ed out) 106 * @param json json reply 107 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error 108 */ 109 static enum GNUNET_GenericReturnValue 110 parse_stats_ok_new ( 111 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh, 112 const json_t *json) 113 { 114 struct TALER_EXCHANGE_GetAmlKycStatisticsResponse lr = { 115 .hr.reply = json, 116 .hr.http_status = MHD_HTTP_OK 117 }; 118 const json_t *jstatistics; 119 struct GNUNET_JSON_Specification spec[] = { 120 GNUNET_JSON_spec_array_const ("statistics", 121 &jstatistics), 122 GNUNET_JSON_spec_end () 123 }; 124 125 if (GNUNET_OK != 126 GNUNET_JSON_parse (json, 127 spec, 128 NULL, 129 NULL)) 130 { 131 GNUNET_break_op (0); 132 return GNUNET_SYSERR; 133 } 134 lr.details.ok.statistics_length = json_array_size (jstatistics); 135 { 136 struct TALER_EXCHANGE_GetAmlKycStatisticsEventCounter *statistics; 137 json_t *obj; 138 size_t idx; 139 140 if (lr.details.ok.statistics_length > 141 GNUNET_MAX_MALLOC_CHECKED / sizeof (*statistics)) 142 { 143 GNUNET_break_op (0); 144 return GNUNET_SYSERR; 145 } 146 statistics = GNUNET_new_array ( 147 GNUNET_NZL (lr.details.ok.statistics_length), 148 struct TALER_EXCHANGE_GetAmlKycStatisticsEventCounter); 149 lr.details.ok.statistics = statistics; 150 json_array_foreach (jstatistics, idx, obj) 151 { 152 struct TALER_EXCHANGE_GetAmlKycStatisticsEventCounter *ec 153 = &statistics[idx]; 154 struct GNUNET_JSON_Specification ispec[] = { 155 GNUNET_JSON_spec_string ("event", 156 &ec->name), 157 GNUNET_JSON_spec_uint64 ("counter", 158 &ec->counter), 159 GNUNET_JSON_spec_end () 160 }; 161 162 if (GNUNET_OK != 163 GNUNET_JSON_parse (obj, 164 ispec, 165 NULL, 166 NULL)) 167 { 168 GNUNET_break_op (0); 169 GNUNET_free (statistics); 170 return GNUNET_SYSERR; 171 } 172 } 173 aksh->cb (aksh->cb_cls, 174 &lr); 175 aksh->cb = NULL; 176 GNUNET_free (statistics); 177 } 178 return GNUNET_OK; 179 } 180 181 182 /** 183 * Function called when we're done processing the 184 * HTTP GET /aml/$OFFICER_PUB/kyc-statistics/$NAMES request. 185 * 186 * @param cls the `struct TALER_EXCHANGE_GetAmlKycStatisticsHandle` 187 * @param response_code HTTP response code, 0 on error 188 * @param response parsed JSON result, NULL on error 189 */ 190 static void 191 handle_get_aml_kyc_statistics_finished (void *cls, 192 long response_code, 193 const void *response) 194 { 195 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh = cls; 196 const json_t *j = response; 197 struct TALER_EXCHANGE_GetAmlKycStatisticsResponse lr = { 198 .hr.reply = j, 199 .hr.http_status = (unsigned int) response_code 200 }; 201 202 aksh->job = NULL; 203 switch (response_code) 204 { 205 case 0: 206 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 207 break; 208 case MHD_HTTP_OK: 209 if (GNUNET_OK != 210 parse_stats_ok_new (aksh, 211 j)) 212 { 213 GNUNET_break_op (0); 214 lr.hr.http_status = 0; 215 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 216 break; 217 } 218 GNUNET_assert (NULL == aksh->cb); 219 TALER_EXCHANGE_get_aml_kyc_statistics_cancel (aksh); 220 return; 221 case MHD_HTTP_NO_CONTENT: 222 break; 223 case MHD_HTTP_BAD_REQUEST: 224 lr.hr.ec = TALER_JSON_get_error_code (j); 225 lr.hr.hint = TALER_JSON_get_error_hint (j); 226 /* This should never happen, either us or the exchange is buggy 227 (or API version conflict); just pass JSON reply to the application */ 228 break; 229 case MHD_HTTP_FORBIDDEN: 230 lr.hr.ec = TALER_JSON_get_error_code (j); 231 lr.hr.hint = TALER_JSON_get_error_hint (j); 232 break; 233 case MHD_HTTP_NOT_FOUND: 234 lr.hr.ec = TALER_JSON_get_error_code (j); 235 lr.hr.hint = TALER_JSON_get_error_hint (j); 236 break; 237 case MHD_HTTP_URI_TOO_LONG: 238 lr.hr.ec = TALER_JSON_get_error_code (j); 239 lr.hr.hint = TALER_JSON_get_error_hint (j); 240 break; 241 case MHD_HTTP_INTERNAL_SERVER_ERROR: 242 lr.hr.ec = TALER_JSON_get_error_code (j); 243 lr.hr.hint = TALER_JSON_get_error_hint (j); 244 /* Server had an internal issue; we should retry, but this API 245 leaves this to the application */ 246 break; 247 default: 248 /* unexpected response code */ 249 GNUNET_break_op (0); 250 lr.hr.ec = TALER_JSON_get_error_code (j); 251 lr.hr.hint = TALER_JSON_get_error_hint (j); 252 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 253 "Unexpected response code %u/%d for GET KYC statistics\n", 254 (unsigned int) response_code, 255 (int) lr.hr.ec); 256 break; 257 } 258 if (NULL != aksh->cb) 259 aksh->cb (aksh->cb_cls, 260 &lr); 261 TALER_EXCHANGE_get_aml_kyc_statistics_cancel (aksh); 262 } 263 264 265 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle * 266 TALER_EXCHANGE_get_aml_kyc_statistics_create ( 267 struct GNUNET_CURL_Context *ctx, 268 const char *url, 269 const struct TALER_AmlOfficerPrivateKeyP *officer_priv, 270 const char *names) 271 { 272 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh; 273 274 aksh = GNUNET_new (struct TALER_EXCHANGE_GetAmlKycStatisticsHandle); 275 aksh->ctx = ctx; 276 aksh->base_url = GNUNET_strdup (url); 277 aksh->officer_priv = *officer_priv; 278 GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, 279 &aksh->officer_pub.eddsa_pub); 280 aksh->names = GNUNET_strdup (names); 281 /* Default: no start date filter, no end date filter */ 282 aksh->options.start_date = GNUNET_TIME_UNIT_ZERO_TS; 283 aksh->options.end_date = GNUNET_TIME_UNIT_FOREVER_TS; 284 return aksh; 285 } 286 287 288 enum GNUNET_GenericReturnValue 289 TALER_EXCHANGE_get_aml_kyc_statistics_set_options_ ( 290 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh, 291 unsigned int num_options, 292 const struct TALER_EXCHANGE_GetAmlKycStatisticsOptionValue *options) 293 { 294 for (unsigned int i = 0; i < num_options; i++) 295 { 296 const struct TALER_EXCHANGE_GetAmlKycStatisticsOptionValue *opt 297 = &options[i]; 298 299 switch (opt->option) 300 { 301 case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_END: 302 return GNUNET_OK; 303 case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_START_DATE: 304 aksh->options.start_date = opt->details.start_date; 305 break; 306 case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_END_DATE: 307 aksh->options.end_date = opt->details.end_date; 308 break; 309 } 310 } 311 return GNUNET_OK; 312 } 313 314 315 enum TALER_ErrorCode 316 TALER_EXCHANGE_get_aml_kyc_statistics_start ( 317 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh, 318 TALER_EXCHANGE_GetAmlKycStatisticsCallback cb, 319 TALER_EXCHANGE_GET_AML_KYC_STATISTICS_RESULT_CLOSURE *cb_cls) 320 { 321 struct TALER_AmlOfficerSignatureP officer_sig; 322 CURL *eh; 323 char sd_str[32]; 324 char ed_str[32]; 325 const char *sd = NULL; 326 const char *ed = NULL; 327 328 if (NULL != aksh->job) 329 { 330 GNUNET_break (0); 331 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 332 } 333 aksh->cb = cb; 334 aksh->cb_cls = cb_cls; 335 TALER_officer_aml_query_sign (&aksh->officer_priv, 336 &officer_sig); 337 if (! GNUNET_TIME_absolute_is_zero (aksh->options.start_date.abs_time)) 338 { 339 unsigned long long sec; 340 341 sec = (unsigned long long) GNUNET_TIME_timestamp_to_s ( 342 aksh->options.start_date); 343 GNUNET_snprintf (sd_str, 344 sizeof (sd_str), 345 "%llu", 346 sec); 347 sd = sd_str; 348 } 349 if (! GNUNET_TIME_absolute_is_never (aksh->options.end_date.abs_time)) 350 { 351 unsigned long long sec; 352 353 sec = (unsigned long long) GNUNET_TIME_timestamp_to_s ( 354 aksh->options.end_date); 355 GNUNET_snprintf (ed_str, 356 sizeof (ed_str), 357 "%llu", 358 sec); 359 ed = ed_str; 360 } 361 { 362 char pub_str[sizeof (aksh->officer_pub) * 2]; 363 char arg_str[sizeof (aksh->officer_pub) * 2 + 32]; 364 char *end; 365 366 end = GNUNET_STRINGS_data_to_string ( 367 &aksh->officer_pub, 368 sizeof (aksh->officer_pub), 369 pub_str, 370 sizeof (pub_str)); 371 *end = '\0'; 372 GNUNET_snprintf (arg_str, 373 sizeof (arg_str), 374 "aml/%s/kyc-statistics/%s", 375 pub_str, 376 aksh->names); 377 aksh->url = TALER_url_join (aksh->base_url, 378 arg_str, 379 "start_date", 380 sd, 381 "end_date", 382 ed, 383 NULL); 384 } 385 if (NULL == aksh->url) 386 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 387 eh = TALER_EXCHANGE_curl_easy_get_ (aksh->url); 388 if (NULL == eh) 389 { 390 GNUNET_free (aksh->url); 391 aksh->url = NULL; 392 return TALER_EC_GENERIC_ALLOCATION_FAILURE; 393 } 394 { 395 struct curl_slist *job_headers = NULL; 396 char *hdr; 397 char sig_str[sizeof (officer_sig) * 2]; 398 char *end; 399 400 end = GNUNET_STRINGS_data_to_string ( 401 &officer_sig, 402 sizeof (officer_sig), 403 sig_str, 404 sizeof (sig_str)); 405 *end = '\0'; 406 GNUNET_asprintf (&hdr, 407 "%s: %s", 408 TALER_AML_OFFICER_SIGNATURE_HEADER, 409 sig_str); 410 job_headers = curl_slist_append (NULL, 411 hdr); 412 GNUNET_free (hdr); 413 aksh->job = GNUNET_CURL_job_add2 (aksh->ctx, 414 eh, 415 job_headers, 416 &handle_get_aml_kyc_statistics_finished, 417 aksh); 418 curl_slist_free_all (job_headers); 419 } 420 if (NULL == aksh->job) 421 { 422 GNUNET_free (aksh->url); 423 aksh->url = NULL; 424 return TALER_EC_GENERIC_ALLOCATION_FAILURE; 425 } 426 return TALER_EC_NONE; 427 } 428 429 430 void 431 TALER_EXCHANGE_get_aml_kyc_statistics_cancel ( 432 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh) 433 { 434 if (NULL != aksh->job) 435 { 436 GNUNET_CURL_job_cancel (aksh->job); 437 aksh->job = NULL; 438 } 439 GNUNET_free (aksh->url); 440 GNUNET_free (aksh->base_url); 441 GNUNET_free (aksh->names); 442 GNUNET_free (aksh); 443 } 444 445 446 /* end of exchange_api_get-aml-OFFICER_PUB-kyc-statistics-NAMES.c */