exchange_api_get-aml-OFFICER_PUB-kyc-statistics-NAMES.c (12474B)
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 GNUNET_NZL (lr.details.ok.statistics_length)]; 138 json_t *obj; 139 size_t idx; 140 141 memset (statistics, 142 0, 143 sizeof (statistics)); 144 lr.details.ok.statistics = statistics; 145 json_array_foreach (jstatistics, idx, obj) 146 { 147 struct TALER_EXCHANGE_GetAmlKycStatisticsEventCounter *ec 148 = &statistics[idx]; 149 struct GNUNET_JSON_Specification ispec[] = { 150 GNUNET_JSON_spec_string ("event", 151 &ec->name), 152 GNUNET_JSON_spec_uint64 ("counter", 153 &ec->counter), 154 GNUNET_JSON_spec_end () 155 }; 156 157 if (GNUNET_OK != 158 GNUNET_JSON_parse (obj, 159 ispec, 160 NULL, 161 NULL)) 162 { 163 GNUNET_break_op (0); 164 return GNUNET_SYSERR; 165 } 166 } 167 aksh->cb (aksh->cb_cls, 168 &lr); 169 aksh->cb = NULL; 170 } 171 return GNUNET_OK; 172 } 173 174 175 /** 176 * Function called when we're done processing the 177 * HTTP GET /aml/$OFFICER_PUB/kyc-statistics/$NAMES request. 178 * 179 * @param cls the `struct TALER_EXCHANGE_GetAmlKycStatisticsHandle` 180 * @param response_code HTTP response code, 0 on error 181 * @param response parsed JSON result, NULL on error 182 */ 183 static void 184 handle_get_aml_kyc_statistics_finished (void *cls, 185 long response_code, 186 const void *response) 187 { 188 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh = cls; 189 const json_t *j = response; 190 struct TALER_EXCHANGE_GetAmlKycStatisticsResponse lr = { 191 .hr.reply = j, 192 .hr.http_status = (unsigned int) response_code 193 }; 194 195 aksh->job = NULL; 196 switch (response_code) 197 { 198 case 0: 199 lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 200 break; 201 case MHD_HTTP_OK: 202 if (GNUNET_OK != 203 parse_stats_ok_new (aksh, 204 j)) 205 { 206 GNUNET_break_op (0); 207 lr.hr.http_status = 0; 208 lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 209 break; 210 } 211 GNUNET_assert (NULL == aksh->cb); 212 TALER_EXCHANGE_get_aml_kyc_statistics_cancel (aksh); 213 return; 214 case MHD_HTTP_NO_CONTENT: 215 break; 216 case MHD_HTTP_BAD_REQUEST: 217 lr.hr.ec = TALER_JSON_get_error_code (j); 218 lr.hr.hint = TALER_JSON_get_error_hint (j); 219 /* This should never happen, either us or the exchange is buggy 220 (or API version conflict); just pass JSON reply to the application */ 221 break; 222 case MHD_HTTP_FORBIDDEN: 223 lr.hr.ec = TALER_JSON_get_error_code (j); 224 lr.hr.hint = TALER_JSON_get_error_hint (j); 225 break; 226 case MHD_HTTP_NOT_FOUND: 227 lr.hr.ec = TALER_JSON_get_error_code (j); 228 lr.hr.hint = TALER_JSON_get_error_hint (j); 229 break; 230 case MHD_HTTP_URI_TOO_LONG: 231 lr.hr.ec = TALER_JSON_get_error_code (j); 232 lr.hr.hint = TALER_JSON_get_error_hint (j); 233 break; 234 case MHD_HTTP_INTERNAL_SERVER_ERROR: 235 lr.hr.ec = TALER_JSON_get_error_code (j); 236 lr.hr.hint = TALER_JSON_get_error_hint (j); 237 /* Server had an internal issue; we should retry, but this API 238 leaves this to the application */ 239 break; 240 default: 241 /* unexpected response code */ 242 GNUNET_break_op (0); 243 lr.hr.ec = TALER_JSON_get_error_code (j); 244 lr.hr.hint = TALER_JSON_get_error_hint (j); 245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 246 "Unexpected response code %u/%d for GET KYC statistics\n", 247 (unsigned int) response_code, 248 (int) lr.hr.ec); 249 break; 250 } 251 if (NULL != aksh->cb) 252 aksh->cb (aksh->cb_cls, 253 &lr); 254 TALER_EXCHANGE_get_aml_kyc_statistics_cancel (aksh); 255 } 256 257 258 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle * 259 TALER_EXCHANGE_get_aml_kyc_statistics_create ( 260 struct GNUNET_CURL_Context *ctx, 261 const char *url, 262 const struct TALER_AmlOfficerPrivateKeyP *officer_priv, 263 const char *names) 264 { 265 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh; 266 267 aksh = GNUNET_new (struct TALER_EXCHANGE_GetAmlKycStatisticsHandle); 268 aksh->ctx = ctx; 269 aksh->base_url = GNUNET_strdup (url); 270 aksh->officer_priv = *officer_priv; 271 GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, 272 &aksh->officer_pub.eddsa_pub); 273 aksh->names = GNUNET_strdup (names); 274 /* Default: no start date filter, no end date filter */ 275 aksh->options.start_date = GNUNET_TIME_UNIT_ZERO_TS; 276 aksh->options.end_date = GNUNET_TIME_UNIT_FOREVER_TS; 277 return aksh; 278 } 279 280 281 enum GNUNET_GenericReturnValue 282 TALER_EXCHANGE_get_aml_kyc_statistics_set_options_ ( 283 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh, 284 unsigned int num_options, 285 const struct TALER_EXCHANGE_GetAmlKycStatisticsOptionValue *options) 286 { 287 for (unsigned int i = 0; i < num_options; i++) 288 { 289 const struct TALER_EXCHANGE_GetAmlKycStatisticsOptionValue *opt 290 = &options[i]; 291 292 switch (opt->option) 293 { 294 case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_END: 295 return GNUNET_OK; 296 case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_START_DATE: 297 aksh->options.start_date = opt->details.start_date; 298 break; 299 case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_END_DATE: 300 aksh->options.end_date = opt->details.end_date; 301 break; 302 } 303 } 304 return GNUNET_OK; 305 } 306 307 308 enum TALER_ErrorCode 309 TALER_EXCHANGE_get_aml_kyc_statistics_start ( 310 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh, 311 TALER_EXCHANGE_GetAmlKycStatisticsCallback cb, 312 TALER_EXCHANGE_GET_AML_KYC_STATISTICS_RESULT_CLOSURE *cb_cls) 313 { 314 struct TALER_AmlOfficerSignatureP officer_sig; 315 CURL *eh; 316 char sd_str[32]; 317 char ed_str[32]; 318 const char *sd = NULL; 319 const char *ed = NULL; 320 321 if (NULL != aksh->job) 322 { 323 GNUNET_break (0); 324 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 325 } 326 aksh->cb = cb; 327 aksh->cb_cls = cb_cls; 328 TALER_officer_aml_query_sign (&aksh->officer_priv, 329 &officer_sig); 330 if (! GNUNET_TIME_absolute_is_zero (aksh->options.start_date.abs_time)) 331 { 332 unsigned long long sec; 333 334 sec = (unsigned long long) GNUNET_TIME_timestamp_to_s ( 335 aksh->options.start_date); 336 GNUNET_snprintf (sd_str, 337 sizeof (sd_str), 338 "%llu", 339 sec); 340 sd = sd_str; 341 } 342 if (! GNUNET_TIME_absolute_is_never (aksh->options.end_date.abs_time)) 343 { 344 unsigned long long sec; 345 346 sec = (unsigned long long) GNUNET_TIME_timestamp_to_s ( 347 aksh->options.end_date); 348 GNUNET_snprintf (ed_str, 349 sizeof (ed_str), 350 "%llu", 351 sec); 352 ed = ed_str; 353 } 354 { 355 char pub_str[sizeof (aksh->officer_pub) * 2]; 356 char arg_str[sizeof (aksh->officer_pub) * 2 + 32]; 357 char *end; 358 359 end = GNUNET_STRINGS_data_to_string ( 360 &aksh->officer_pub, 361 sizeof (aksh->officer_pub), 362 pub_str, 363 sizeof (pub_str)); 364 *end = '\0'; 365 GNUNET_snprintf (arg_str, 366 sizeof (arg_str), 367 "aml/%s/kyc-statistics/%s", 368 pub_str, 369 aksh->names); 370 aksh->url = TALER_url_join (aksh->base_url, 371 arg_str, 372 "start_date", 373 sd, 374 "end_date", 375 ed, 376 NULL); 377 } 378 if (NULL == aksh->url) 379 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 380 eh = TALER_EXCHANGE_curl_easy_get_ (aksh->url); 381 if (NULL == eh) 382 { 383 GNUNET_free (aksh->url); 384 aksh->url = NULL; 385 return TALER_EC_GENERIC_ALLOCATION_FAILURE; 386 } 387 { 388 struct curl_slist *job_headers = NULL; 389 char *hdr; 390 char sig_str[sizeof (officer_sig) * 2]; 391 char *end; 392 393 end = GNUNET_STRINGS_data_to_string ( 394 &officer_sig, 395 sizeof (officer_sig), 396 sig_str, 397 sizeof (sig_str)); 398 *end = '\0'; 399 GNUNET_asprintf (&hdr, 400 "%s: %s", 401 TALER_AML_OFFICER_SIGNATURE_HEADER, 402 sig_str); 403 job_headers = curl_slist_append (NULL, 404 hdr); 405 GNUNET_free (hdr); 406 aksh->job = GNUNET_CURL_job_add2 (aksh->ctx, 407 eh, 408 job_headers, 409 &handle_get_aml_kyc_statistics_finished, 410 aksh); 411 curl_slist_free_all (job_headers); 412 } 413 if (NULL == aksh->job) 414 { 415 GNUNET_free (aksh->url); 416 aksh->url = NULL; 417 return TALER_EC_GENERIC_ALLOCATION_FAILURE; 418 } 419 return TALER_EC_NONE; 420 } 421 422 423 void 424 TALER_EXCHANGE_get_aml_kyc_statistics_cancel ( 425 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh) 426 { 427 if (NULL != aksh->job) 428 { 429 GNUNET_CURL_job_cancel (aksh->job); 430 aksh->job = NULL; 431 } 432 GNUNET_free (aksh->url); 433 GNUNET_free (aksh->base_url); 434 GNUNET_free (aksh->names); 435 GNUNET_free (aksh); 436 } 437 438 439 /* end of exchange_api_get-aml-OFFICER_PUB-kyc-statistics-NAMES.c */