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


      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 ("name",
    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_INTERNAL_SERVER_ERROR:
    229     lr.hr.ec = TALER_JSON_get_error_code (j);
    230     lr.hr.hint = TALER_JSON_get_error_hint (j);
    231     /* Server had an internal issue; we should retry, but this API
    232        leaves this to the application */
    233     break;
    234   default:
    235     /* unexpected response code */
    236     GNUNET_break_op (0);
    237     lr.hr.ec = TALER_JSON_get_error_code (j);
    238     lr.hr.hint = TALER_JSON_get_error_hint (j);
    239     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    240                 "Unexpected response code %u/%d for GET KYC statistics\n",
    241                 (unsigned int) response_code,
    242                 (int) lr.hr.ec);
    243     break;
    244   }
    245   if (NULL != aksh->cb)
    246     aksh->cb (aksh->cb_cls,
    247               &lr);
    248   TALER_EXCHANGE_get_aml_kyc_statistics_cancel (aksh);
    249 }
    250 
    251 
    252 struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *
    253 TALER_EXCHANGE_get_aml_kyc_statistics_create (
    254   struct GNUNET_CURL_Context *ctx,
    255   const char *url,
    256   const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
    257   const char *names)
    258 {
    259   struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh;
    260 
    261   aksh = GNUNET_new (struct TALER_EXCHANGE_GetAmlKycStatisticsHandle);
    262   aksh->ctx = ctx;
    263   aksh->base_url = GNUNET_strdup (url);
    264   aksh->officer_priv = *officer_priv;
    265   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
    266                                       &aksh->officer_pub.eddsa_pub);
    267   aksh->names = GNUNET_strdup (names);
    268   /* Default: no start date filter, no end date filter */
    269   aksh->options.start_date = GNUNET_TIME_UNIT_ZERO_TS;
    270   aksh->options.end_date = GNUNET_TIME_UNIT_FOREVER_TS;
    271   return aksh;
    272 }
    273 
    274 
    275 enum GNUNET_GenericReturnValue
    276 TALER_EXCHANGE_get_aml_kyc_statistics_set_options_ (
    277   struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh,
    278   unsigned int num_options,
    279   const struct TALER_EXCHANGE_GetAmlKycStatisticsOptionValue *options)
    280 {
    281   for (unsigned int i = 0; i < num_options; i++)
    282   {
    283     const struct TALER_EXCHANGE_GetAmlKycStatisticsOptionValue *opt
    284       = &options[i];
    285 
    286     switch (opt->option)
    287     {
    288     case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_END:
    289       return GNUNET_OK;
    290     case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_START_DATE:
    291       aksh->options.start_date = opt->details.start_date;
    292       break;
    293     case TALER_EXCHANGE_GET_AML_KYC_STATISTICS_OPTION_END_DATE:
    294       aksh->options.end_date = opt->details.end_date;
    295       break;
    296     }
    297   }
    298   return GNUNET_OK;
    299 }
    300 
    301 
    302 enum TALER_ErrorCode
    303 TALER_EXCHANGE_get_aml_kyc_statistics_start (
    304   struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh,
    305   TALER_EXCHANGE_GetAmlKycStatisticsCallback cb,
    306   TALER_EXCHANGE_GET_AML_KYC_STATISTICS_RESULT_CLOSURE *cb_cls)
    307 {
    308   struct TALER_AmlOfficerSignatureP officer_sig;
    309   CURL *eh;
    310   char sd_str[32];
    311   char ed_str[32];
    312   const char *sd = NULL;
    313   const char *ed = NULL;
    314 
    315   if (NULL != aksh->job)
    316   {
    317     GNUNET_break (0);
    318     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    319   }
    320   aksh->cb = cb;
    321   aksh->cb_cls = cb_cls;
    322   TALER_officer_aml_query_sign (&aksh->officer_priv,
    323                                 &officer_sig);
    324   if (! GNUNET_TIME_absolute_is_zero (aksh->options.start_date.abs_time))
    325   {
    326     unsigned long long sec;
    327 
    328     sec = (unsigned long long) GNUNET_TIME_timestamp_to_s (
    329       aksh->options.start_date);
    330     GNUNET_snprintf (sd_str,
    331                      sizeof (sd_str),
    332                      "%llu",
    333                      sec);
    334     sd = sd_str;
    335   }
    336   if (! GNUNET_TIME_absolute_is_never (aksh->options.end_date.abs_time))
    337   {
    338     unsigned long long sec;
    339 
    340     sec = (unsigned long long) GNUNET_TIME_timestamp_to_s (
    341       aksh->options.end_date);
    342     GNUNET_snprintf (ed_str,
    343                      sizeof (ed_str),
    344                      "%llu",
    345                      sec);
    346     ed = ed_str;
    347   }
    348   {
    349     char pub_str[sizeof (aksh->officer_pub) * 2];
    350     char arg_str[sizeof (aksh->officer_pub) * 2 + 32];
    351     char *end;
    352 
    353     end = GNUNET_STRINGS_data_to_string (
    354       &aksh->officer_pub,
    355       sizeof (aksh->officer_pub),
    356       pub_str,
    357       sizeof (pub_str));
    358     *end = '\0';
    359     GNUNET_snprintf (arg_str,
    360                      sizeof (arg_str),
    361                      "aml/%s/kyc-statistics/%s",
    362                      pub_str,
    363                      aksh->names);
    364     aksh->url = TALER_url_join (aksh->base_url,
    365                                 arg_str,
    366                                 "start_date",
    367                                 sd,
    368                                 "end_date",
    369                                 ed,
    370                                 NULL);
    371   }
    372   if (NULL == aksh->url)
    373     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    374   eh = TALER_EXCHANGE_curl_easy_get_ (aksh->url);
    375   if (NULL == eh)
    376   {
    377     GNUNET_free (aksh->url);
    378     aksh->url = NULL;
    379     return TALER_EC_GENERIC_ALLOCATION_FAILURE;
    380   }
    381   {
    382     struct curl_slist *job_headers = NULL;
    383     char *hdr;
    384     char sig_str[sizeof (officer_sig) * 2];
    385     char *end;
    386 
    387     end = GNUNET_STRINGS_data_to_string (
    388       &officer_sig,
    389       sizeof (officer_sig),
    390       sig_str,
    391       sizeof (sig_str));
    392     *end = '\0';
    393     GNUNET_asprintf (&hdr,
    394                      "%s: %s",
    395                      TALER_AML_OFFICER_SIGNATURE_HEADER,
    396                      sig_str);
    397     job_headers = curl_slist_append (NULL,
    398                                      hdr);
    399     GNUNET_free (hdr);
    400     job_headers = curl_slist_append (job_headers,
    401                                      "Content-type: application/json");
    402     aksh->job = GNUNET_CURL_job_add2 (aksh->ctx,
    403                                       eh,
    404                                       job_headers,
    405                                       &handle_get_aml_kyc_statistics_finished,
    406                                       aksh);
    407     curl_slist_free_all (job_headers);
    408   }
    409   if (NULL == aksh->job)
    410   {
    411     GNUNET_free (aksh->url);
    412     aksh->url = NULL;
    413     return TALER_EC_GENERIC_ALLOCATION_FAILURE;
    414   }
    415   return TALER_EC_NONE;
    416 }
    417 
    418 
    419 void
    420 TALER_EXCHANGE_get_aml_kyc_statistics_cancel (
    421   struct TALER_EXCHANGE_GetAmlKycStatisticsHandle *aksh)
    422 {
    423   if (NULL != aksh->job)
    424   {
    425     GNUNET_CURL_job_cancel (aksh->job);
    426     aksh->job = NULL;
    427   }
    428   GNUNET_free (aksh->url);
    429   GNUNET_free (aksh->base_url);
    430   GNUNET_free (aksh->names);
    431   GNUNET_free (aksh);
    432 }
    433 
    434 
    435 /* end of exchange_api_get-aml-OFFICER_PUB-kyc-statistics-NAMES.c */