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 (12550B)


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