exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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 */