LCOV - code coverage report
Current view: top level - lib - exchange_api_kyc_check.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 58.9 % 141 83
Test Date: 2025-12-28 14:06:02 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2021-2024 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_kyc_check.c
      19              :  * @brief Implementation of the /kyc-check request
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "taler/platform.h"
      23              : #include <microhttpd.h> /* just for HTTP check 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              : 
      32              : 
      33              : /**
      34              :  * @brief A ``/kyc-check`` handle
      35              :  */
      36              : struct TALER_EXCHANGE_KycCheckHandle
      37              : {
      38              : 
      39              :   /**
      40              :    * The url for this request.
      41              :    */
      42              :   char *url;
      43              : 
      44              :   /**
      45              :    * Handle for the request.
      46              :    */
      47              :   struct GNUNET_CURL_Job *job;
      48              : 
      49              :   /**
      50              :    * Function to call with the result.
      51              :    */
      52              :   TALER_EXCHANGE_KycStatusCallback cb;
      53              : 
      54              :   /**
      55              :    * Closure for @e cb.
      56              :    */
      57              :   void *cb_cls;
      58              : 
      59              : };
      60              : 
      61              : 
      62              : static enum GNUNET_GenericReturnValue
      63           14 : parse_account_status (
      64              :   struct TALER_EXCHANGE_KycCheckHandle *kch,
      65              :   const json_t *j,
      66              :   struct TALER_EXCHANGE_KycStatus *ks,
      67              :   struct TALER_EXCHANGE_AccountKycStatus *status)
      68              : {
      69           14 :   const json_t *limits = NULL;
      70              :   struct GNUNET_JSON_Specification spec[] = {
      71           14 :     GNUNET_JSON_spec_bool ("aml_review",
      72              :                            &status->aml_review),
      73           14 :     GNUNET_JSON_spec_uint64 ("rule_gen",
      74              :                              &status->rule_gen),
      75           14 :     GNUNET_JSON_spec_fixed_auto ("access_token",
      76              :                                  &status->access_token),
      77           14 :     GNUNET_JSON_spec_mark_optional (
      78              :       GNUNET_JSON_spec_array_const ("limits",
      79              :                                     &limits),
      80              :       NULL),
      81           14 :     GNUNET_JSON_spec_end ()
      82              :   };
      83              : 
      84           14 :   if (GNUNET_OK !=
      85           14 :       GNUNET_JSON_parse (j,
      86              :                          spec,
      87              :                          NULL, NULL))
      88              :   {
      89            0 :     GNUNET_break_op (0);
      90            0 :     return GNUNET_SYSERR;
      91              :   }
      92           28 :   if ( (NULL != limits) &&
      93           14 :        (0 != json_array_size (limits)) )
      94           13 :   {
      95           13 :     size_t limit_length = json_array_size (limits);
      96           13 :     struct TALER_EXCHANGE_AccountLimit ala[GNUNET_NZL (limit_length)];
      97              :     size_t i;
      98              :     json_t *limit;
      99              : 
     100           42 :     json_array_foreach (limits, i, limit)
     101              :     {
     102           29 :       struct TALER_EXCHANGE_AccountLimit *al = &ala[i];
     103              :       struct GNUNET_JSON_Specification ispec[] = {
     104           29 :         GNUNET_JSON_spec_mark_optional (
     105              :           GNUNET_JSON_spec_bool ("soft_limit",
     106              :                                  &al->soft_limit),
     107              :           NULL),
     108           29 :         GNUNET_JSON_spec_relative_time ("timeframe",
     109              :                                         &al->timeframe),
     110           29 :         TALER_JSON_spec_kycte ("operation_type",
     111              :                                &al->operation_type),
     112           29 :         TALER_JSON_spec_amount_any ("threshold",
     113              :                                     &al->threshold),
     114           29 :         GNUNET_JSON_spec_end ()
     115              :       };
     116              : 
     117           29 :       al->soft_limit = false;
     118           29 :       if (GNUNET_OK !=
     119           29 :           GNUNET_JSON_parse (limit,
     120              :                              ispec,
     121              :                              NULL, NULL))
     122              :       {
     123            0 :         GNUNET_break_op (0);
     124            0 :         return GNUNET_SYSERR;
     125              :       }
     126              :     }
     127           13 :     status->limits = ala;
     128           13 :     status->limits_length = limit_length;
     129           13 :     kch->cb (kch->cb_cls,
     130              :              ks);
     131              :   }
     132              :   else
     133              :   {
     134            1 :     kch->cb (kch->cb_cls,
     135              :              ks);
     136              :   }
     137           14 :   GNUNET_JSON_parse_free (spec);
     138           14 :   return GNUNET_OK;
     139              : }
     140              : 
     141              : 
     142              : /**
     143              :  * Function called when we're done processing the
     144              :  * HTTP /kyc-check request.
     145              :  *
     146              :  * @param cls the `struct TALER_EXCHANGE_KycCheckHandle`
     147              :  * @param response_code HTTP response code, 0 on error
     148              :  * @param response parsed JSON result, NULL on error
     149              :  */
     150              : static void
     151           14 : handle_kyc_check_finished (void *cls,
     152              :                            long response_code,
     153              :                            const void *response)
     154              : {
     155           14 :   struct TALER_EXCHANGE_KycCheckHandle *kch = cls;
     156           14 :   const json_t *j = response;
     157           14 :   struct TALER_EXCHANGE_KycStatus ks = {
     158              :     .hr.reply = j,
     159           14 :     .hr.http_status = (unsigned int) response_code
     160              :   };
     161              : 
     162           14 :   kch->job = NULL;
     163           14 :   switch (response_code)
     164              :   {
     165            0 :   case 0:
     166            0 :     ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     167            0 :     break;
     168            3 :   case MHD_HTTP_OK:
     169              :     {
     170            3 :       if (GNUNET_OK !=
     171            3 :           parse_account_status (kch,
     172              :                                 j,
     173              :                                 &ks,
     174              :                                 &ks.details.ok))
     175              :       {
     176            0 :         GNUNET_break_op (0);
     177            0 :         ks.hr.http_status = 0;
     178            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     179            0 :         break;
     180              :       }
     181            3 :       TALER_EXCHANGE_kyc_check_cancel (kch);
     182           14 :       return;
     183              :     }
     184           11 :   case MHD_HTTP_ACCEPTED:
     185              :     {
     186           11 :       if (GNUNET_OK !=
     187           11 :           parse_account_status (kch,
     188              :                                 j,
     189              :                                 &ks,
     190              :                                 &ks.details.accepted))
     191              :       {
     192            0 :         GNUNET_break_op (0);
     193            0 :         ks.hr.http_status = 0;
     194            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     195            0 :         break;
     196              :       }
     197           11 :       TALER_EXCHANGE_kyc_check_cancel (kch);
     198           11 :       return;
     199              :     }
     200            0 :   case MHD_HTTP_NO_CONTENT:
     201            0 :     break;
     202            0 :   case MHD_HTTP_BAD_REQUEST:
     203            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     204              :     /* This should never happen, either us or the exchange is buggy
     205              :        (or API version conflict); just pass JSON reply to the application */
     206            0 :     break;
     207            0 :   case MHD_HTTP_FORBIDDEN:
     208              :     {
     209              :       struct GNUNET_JSON_Specification spec[] = {
     210            0 :         GNUNET_JSON_spec_fixed_auto (
     211              :           "expected_account_pub",
     212              :           &ks.details.forbidden.expected_account_pub),
     213            0 :         TALER_JSON_spec_ec ("code",
     214              :                             &ks.hr.ec),
     215            0 :         GNUNET_JSON_spec_end ()
     216              :       };
     217              : 
     218            0 :       if (GNUNET_OK !=
     219            0 :           GNUNET_JSON_parse (j,
     220              :                              spec,
     221              :                              NULL, NULL))
     222              :       {
     223            0 :         GNUNET_break_op (0);
     224            0 :         ks.hr.http_status = 0;
     225            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     226            0 :         break;
     227              :       }
     228            0 :       break;
     229              :     }
     230            0 :   case MHD_HTTP_NOT_FOUND:
     231            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     232            0 :     break;
     233            0 :   case MHD_HTTP_CONFLICT:
     234            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     235            0 :     break;
     236            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     237            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     238              :     /* Server had an internal issue; we should retry, but this API
     239              :        leaves this to the application */
     240            0 :     break;
     241            0 :   default:
     242              :     /* unexpected response code */
     243            0 :     GNUNET_break_op (0);
     244            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     245            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     246              :                 "Unexpected response code %u/%d for exchange kyc_check\n",
     247              :                 (unsigned int) response_code,
     248              :                 (int) ks.hr.ec);
     249            0 :     break;
     250              :   }
     251            0 :   kch->cb (kch->cb_cls,
     252              :            &ks);
     253            0 :   TALER_EXCHANGE_kyc_check_cancel (kch);
     254              : }
     255              : 
     256              : 
     257              : struct TALER_EXCHANGE_KycCheckHandle *
     258           14 : TALER_EXCHANGE_kyc_check (
     259              :   struct GNUNET_CURL_Context *ctx,
     260              :   const char *url,
     261              :   const struct TALER_NormalizedPaytoHashP *h_payto,
     262              :   const union TALER_AccountPrivateKeyP *account_priv,
     263              :   uint64_t known_rule_gen,
     264              :   enum TALER_EXCHANGE_KycLongPollTarget lpt,
     265              :   struct GNUNET_TIME_Relative timeout,
     266              :   TALER_EXCHANGE_KycStatusCallback cb,
     267              :   void *cb_cls)
     268              : {
     269              :   struct TALER_EXCHANGE_KycCheckHandle *kch;
     270              :   CURL *eh;
     271              :   char arg_str[128];
     272              :   char timeout_ms[32];
     273              :   char lpt_str[32];
     274              :   char krg_str[32];
     275           14 :   struct curl_slist *job_headers = NULL;
     276              :   unsigned long long tms;
     277              : 
     278              :   {
     279              :     char *hps;
     280              : 
     281           14 :     hps = GNUNET_STRINGS_data_to_string_alloc (
     282              :       h_payto,
     283              :       sizeof (*h_payto));
     284           14 :     GNUNET_snprintf (arg_str,
     285              :                      sizeof (arg_str),
     286              :                      "kyc-check/%s",
     287              :                      hps);
     288           14 :     GNUNET_free (hps);
     289              :   }
     290           28 :   tms = timeout.rel_value_us
     291           14 :         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
     292           14 :   GNUNET_snprintf (timeout_ms,
     293              :                    sizeof (timeout_ms),
     294              :                    "%llu",
     295              :                    tms);
     296           14 :   GNUNET_snprintf (krg_str,
     297              :                    sizeof (krg_str),
     298              :                    "%llu",
     299              :                    (unsigned long long) known_rule_gen);
     300           14 :   GNUNET_snprintf (lpt_str,
     301              :                    sizeof (lpt_str),
     302              :                    "%d",
     303              :                    (int) lpt);
     304           14 :   kch = GNUNET_new (struct TALER_EXCHANGE_KycCheckHandle);
     305           14 :   kch->cb = cb;
     306           14 :   kch->cb_cls = cb_cls;
     307              :   kch->url
     308           28 :     = TALER_url_join (
     309              :         url,
     310              :         arg_str,
     311              :         "timeout_ms",
     312           14 :         GNUNET_TIME_relative_is_zero (timeout)
     313              :         ? NULL
     314              :         : timeout_ms,
     315              :         "min_rule",
     316              :         0 == known_rule_gen
     317              :         ? NULL
     318              :         : krg_str,
     319              :         "lpt",
     320              :         TALER_EXCHANGE_KLPT_NONE == lpt
     321              :         ? NULL
     322              :         : lpt_str,
     323              :         NULL);
     324           14 :   if (NULL == kch->url)
     325              :   {
     326            0 :     GNUNET_free (kch);
     327            0 :     return NULL;
     328              :   }
     329           14 :   eh = TALER_EXCHANGE_curl_easy_get_ (kch->url);
     330           14 :   if (NULL == eh)
     331              :   {
     332            0 :     GNUNET_break (0);
     333            0 :     GNUNET_free (kch->url);
     334            0 :     GNUNET_free (kch);
     335            0 :     return NULL;
     336              :   }
     337           14 :   if (0 != tms)
     338              :   {
     339           14 :     GNUNET_break (CURLE_OK ==
     340              :                   curl_easy_setopt (eh,
     341              :                                     CURLOPT_TIMEOUT_MS,
     342              :                                     (long) (tms + 500L)));
     343              :   }
     344              :   job_headers
     345           14 :     = curl_slist_append (
     346              :         job_headers,
     347              :         "Content-Type: application/json");
     348              :   {
     349              :     union TALER_AccountSignatureP account_sig;
     350              :     char *sig_hdr;
     351              :     char *hdr;
     352              : 
     353           14 :     TALER_account_kyc_auth_sign (account_priv,
     354              :                                  &account_sig);
     355              : 
     356           14 :     sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
     357              :       &account_sig,
     358              :       sizeof (account_sig));
     359           14 :     GNUNET_asprintf (&hdr,
     360              :                      "%s: %s",
     361              :                      TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE,
     362              :                      sig_hdr);
     363           14 :     GNUNET_free (sig_hdr);
     364           14 :     job_headers = curl_slist_append (job_headers,
     365              :                                      hdr);
     366           14 :     GNUNET_free (hdr);
     367           14 :     if (NULL == job_headers)
     368              :     {
     369            0 :       GNUNET_break (0);
     370            0 :       curl_easy_cleanup (eh);
     371            0 :       return NULL;
     372              :     }
     373              :   }
     374              :   kch->job
     375           14 :     = GNUNET_CURL_job_add2 (ctx,
     376              :                             eh,
     377              :                             job_headers,
     378              :                             &handle_kyc_check_finished,
     379              :                             kch);
     380           14 :   curl_slist_free_all (job_headers);
     381           14 :   return kch;
     382              : }
     383              : 
     384              : 
     385              : void
     386           14 : TALER_EXCHANGE_kyc_check_cancel (struct TALER_EXCHANGE_KycCheckHandle *kch)
     387              : {
     388           14 :   if (NULL != kch->job)
     389              :   {
     390            0 :     GNUNET_CURL_job_cancel (kch->job);
     391            0 :     kch->job = NULL;
     392              :   }
     393           14 :   GNUNET_free (kch->url);
     394           14 :   GNUNET_free (kch);
     395           14 : }
     396              : 
     397              : 
     398              : /* end of exchange_api_kyc_check.c */
        

Generated by: LCOV version 2.0-1