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