LCOV - code coverage report
Current view: top level - lib - exchange_api_get-kyc-check-H_NORMALIZED_PAYTO.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 61.5 % 192 118
Test Date: 2026-10-29 19:05:17 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2021-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-kyc-check-H_NORMALIZED_PAYTO.c
      19              :  * @brief Implementation of the /kyc-check request
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"  /* UNNECESSARY? */
      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"  /* UNNECESSARY? */
      27              : #include "taler/taler_json_lib.h"
      28              : #include "taler/exchange/get-kyc-check-H_NORMALIZED_PAYTO.h"
      29              : #include "taler/taler_signatures.h"
      30              : #include "exchange_api_curl_defaults.h"
      31              : 
      32              : 
      33              : /**
      34              :  * @brief A GET /kyc-check/$H_NORMALIZED_PAYTO handle
      35              :  */
      36              : struct TALER_EXCHANGE_GetKycCheckHandle
      37              : {
      38              : 
      39              :   /**
      40              :    * The base URL for this request.
      41              :    */
      42              :   char *base_url;
      43              : 
      44              :   /**
      45              :    * The full URL for this request, set during _start.
      46              :    */
      47              :   char *url;
      48              : 
      49              :   /**
      50              :    * Handle for the request.
      51              :    */
      52              :   struct GNUNET_CURL_Job *job;
      53              : 
      54              :   /**
      55              :    * Function to call with the result.
      56              :    */
      57              :   TALER_EXCHANGE_GetKycCheckCallback cb;
      58              : 
      59              :   /**
      60              :    * Closure for @e cb.
      61              :    */
      62              :   TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls;
      63              : 
      64              :   /**
      65              :    * Reference to the execution context.
      66              :    */
      67              :   struct GNUNET_CURL_Context *ctx;
      68              : 
      69              :   /**
      70              :    * Hash of the payto URI we are checking.
      71              :    */
      72              :   struct TALER_NormalizedPaytoHashP h_payto;
      73              : 
      74              :   /**
      75              :    * Private key to authorize the request.
      76              :    */
      77              :   union TALER_AccountPrivateKeyP account_priv;
      78              : 
      79              :   /**
      80              :    * Long polling target.
      81              :    */
      82              :   enum TALER_EXCHANGE_KycLongPollTarget lpt;
      83              : 
      84              :   /**
      85              :    * Latest known rule generation (for long polling).
      86              :    */
      87              :   uint64_t known_rule_gen;
      88              : 
      89              :   /**
      90              :    * Long polling timeout.
      91              :    */
      92              :   struct GNUNET_TIME_Relative timeout;
      93              : 
      94              : };
      95              : 
      96              : 
      97              : /**
      98              :  * Parse an account KYC status from JSON and invoke the callback.
      99              :  *
     100              :  * @param[in,out] gkch handle
     101              :  * @param j JSON to parse
     102              :  * @param res response to fill
     103              :  * @param status account status field within @a res to fill
     104              :  * @return #GNUNET_OK on success
     105              :  */
     106              : static enum GNUNET_GenericReturnValue
     107           14 : parse_account_status (
     108              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
     109              :   const json_t *j,
     110              :   struct TALER_EXCHANGE_GetKycCheckResponse *res,
     111              :   struct TALER_EXCHANGE_AccountKycStatus *status)
     112              : {
     113           14 :   const json_t *limits = NULL;
     114              :   struct GNUNET_JSON_Specification spec[] = {
     115           14 :     GNUNET_JSON_spec_bool ("aml_review",
     116              :                            &status->aml_review),
     117           14 :     GNUNET_JSON_spec_uint64 ("rule_gen",
     118              :                              &status->rule_gen),
     119           14 :     GNUNET_JSON_spec_fixed_auto ("access_token",
     120              :                                  &status->access_token),
     121           14 :     GNUNET_JSON_spec_mark_optional (
     122              :       GNUNET_JSON_spec_string ("tos_required",
     123              :                                &status->tos_required),
     124              :       NULL),
     125           14 :     GNUNET_JSON_spec_mark_optional (
     126              :       GNUNET_JSON_spec_array_const ("limits",
     127              :                                     &limits),
     128              :       NULL),
     129           14 :     GNUNET_JSON_spec_end ()
     130              :   };
     131              : 
     132           14 :   if (GNUNET_OK !=
     133           14 :       GNUNET_JSON_parse (j,
     134              :                          spec,
     135              :                          NULL, NULL))
     136              :   {
     137            0 :     GNUNET_break_op (0);
     138            0 :     return GNUNET_SYSERR;
     139              :   }
     140           28 :   if ( (NULL != limits) &&
     141           14 :        (0 != json_array_size (limits)) )
     142           13 :   {
     143           13 :     size_t limit_length = json_array_size (limits);
     144              :     struct TALER_EXCHANGE_AccountLimit *ala;
     145              :     size_t i;
     146              :     json_t *limit;
     147              : 
     148           13 :     if (limit_length >
     149              :         GNUNET_MAX_MALLOC_CHECKED / sizeof (*ala))
     150              :     {
     151            0 :       GNUNET_break_op (0);
     152            0 :       return GNUNET_SYSERR;
     153              :     }
     154           13 :     ala = GNUNET_new_array (limit_length,
     155              :                             struct TALER_EXCHANGE_AccountLimit);
     156           42 :     json_array_foreach (limits, i, limit)
     157              :     {
     158           29 :       struct TALER_EXCHANGE_AccountLimit *al = &ala[i];
     159              :       struct GNUNET_JSON_Specification ispec[] = {
     160           29 :         GNUNET_JSON_spec_mark_optional (
     161              :           GNUNET_JSON_spec_bool ("soft_limit",
     162              :                                  &al->soft_limit),
     163              :           NULL),
     164           29 :         GNUNET_JSON_spec_relative_time ("timeframe",
     165              :                                         &al->timeframe),
     166           29 :         TALER_JSON_spec_kycte ("operation_type",
     167              :                                &al->operation_type),
     168           29 :         TALER_JSON_spec_amount_any ("threshold",
     169              :                                     &al->threshold),
     170           29 :         GNUNET_JSON_spec_end ()
     171              :       };
     172              : 
     173           29 :       al->soft_limit = false;
     174           29 :       if (GNUNET_OK !=
     175           29 :           GNUNET_JSON_parse (limit,
     176              :                              ispec,
     177              :                              NULL, NULL))
     178              :       {
     179            0 :         GNUNET_break_op (0);
     180            0 :         GNUNET_free (ala);
     181            0 :         return GNUNET_SYSERR;
     182              :       }
     183              :     }
     184           13 :     status->limits = ala;
     185           13 :     status->limits_length = limit_length;
     186           13 :     gkch->cb (gkch->cb_cls,
     187              :               res);
     188           13 :     GNUNET_free (ala);
     189              :   }
     190              :   else
     191              :   {
     192            1 :     gkch->cb (gkch->cb_cls,
     193              :               res);
     194              :   }
     195           14 :   GNUNET_JSON_parse_free (spec);
     196           14 :   return GNUNET_OK;
     197              : }
     198              : 
     199              : 
     200              : /**
     201              :  * Function called when we're done processing the
     202              :  * HTTP GET /kyc-check/$H_NORMALIZED_PAYTO request.
     203              :  *
     204              :  * @param cls the `struct TALER_EXCHANGE_GetKycCheckHandle`
     205              :  * @param response_code HTTP response code, 0 on error
     206              :  * @param response parsed JSON result, NULL on error
     207              :  */
     208              : static void
     209           14 : handle_kyc_check_finished (void *cls,
     210              :                            long response_code,
     211              :                            const void *response)
     212              : {
     213           14 :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch = cls;
     214           14 :   const json_t *j = response;
     215           14 :   struct TALER_EXCHANGE_GetKycCheckResponse ks = {
     216              :     .hr.reply = j,
     217           14 :     .hr.http_status = (unsigned int) response_code
     218              :   };
     219              : 
     220           14 :   gkch->job = NULL;
     221           14 :   switch (response_code)
     222              :   {
     223            0 :   case 0:
     224            0 :     ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     225            0 :     break;
     226            3 :   case MHD_HTTP_OK:
     227              :     {
     228            3 :       if (GNUNET_OK !=
     229            3 :           parse_account_status (gkch,
     230              :                                 j,
     231              :                                 &ks,
     232              :                                 &ks.details.ok))
     233              :       {
     234            0 :         GNUNET_break_op (0);
     235            0 :         ks.hr.http_status = 0;
     236            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     237            0 :         break;
     238              :       }
     239            3 :       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     240           14 :       return;
     241              :     }
     242           11 :   case MHD_HTTP_ACCEPTED:
     243              :     {
     244           11 :       if (GNUNET_OK !=
     245           11 :           parse_account_status (gkch,
     246              :                                 j,
     247              :                                 &ks,
     248              :                                 &ks.details.accepted))
     249              :       {
     250            0 :         GNUNET_break_op (0);
     251            0 :         ks.hr.http_status = 0;
     252            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     253            0 :         break;
     254              :       }
     255           11 :       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     256           11 :       return;
     257              :     }
     258            0 :   case MHD_HTTP_NO_CONTENT:
     259            0 :     break;
     260            0 :   case MHD_HTTP_BAD_REQUEST:
     261            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     262            0 :     break;
     263            0 :   case MHD_HTTP_FORBIDDEN:
     264              :     {
     265              :       struct GNUNET_JSON_Specification spec[] = {
     266            0 :         GNUNET_JSON_spec_fixed_auto (
     267              :           "expected_account_pub",
     268              :           &ks.details.forbidden.expected_account_pub),
     269            0 :         TALER_JSON_spec_ec ("code",
     270              :                             &ks.hr.ec),
     271            0 :         GNUNET_JSON_spec_end ()
     272              :       };
     273              : 
     274            0 :       if (GNUNET_OK !=
     275            0 :           GNUNET_JSON_parse (j,
     276              :                              spec,
     277              :                              NULL, NULL))
     278              :       {
     279            0 :         GNUNET_break_op (0);
     280            0 :         ks.hr.http_status = 0;
     281            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     282            0 :         break;
     283              :       }
     284            0 :       break;
     285              :     }
     286            0 :   case MHD_HTTP_NOT_FOUND:
     287            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     288            0 :     break;
     289            0 :   case MHD_HTTP_CONFLICT:
     290            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     291            0 :     break;
     292            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     293            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     294            0 :     break;
     295            0 :   default:
     296              :     /* unexpected response code */
     297            0 :     GNUNET_break_op (0);
     298            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     299            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     300              :                 "Unexpected response code %u/%d for exchange kyc_check\n",
     301              :                 (unsigned int) response_code,
     302              :                 (int) ks.hr.ec);
     303            0 :     break;
     304              :   }
     305            0 :   if (NULL != gkch->cb)
     306              :   {
     307            0 :     gkch->cb (gkch->cb_cls,
     308              :               &ks);
     309            0 :     gkch->cb = NULL;
     310              :   }
     311            0 :   TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     312              : }
     313              : 
     314              : 
     315              : struct TALER_EXCHANGE_GetKycCheckHandle *
     316           14 : TALER_EXCHANGE_get_kyc_check_create (
     317              :   struct GNUNET_CURL_Context *ctx,
     318              :   const char *url,
     319              :   const struct TALER_NormalizedPaytoHashP *h_payto,
     320              :   const union TALER_AccountPrivateKeyP *pk)
     321              : {
     322              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch;
     323              : 
     324           14 :   gkch = GNUNET_new (struct TALER_EXCHANGE_GetKycCheckHandle);
     325           14 :   gkch->ctx = ctx;
     326           14 :   gkch->base_url = GNUNET_strdup (url);
     327           14 :   gkch->h_payto = *h_payto;
     328           14 :   gkch->account_priv = *pk;
     329           14 :   return gkch;
     330              : }
     331              : 
     332              : 
     333              : enum GNUNET_GenericReturnValue
     334           14 : TALER_EXCHANGE_get_kyc_check_set_options_ (
     335              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
     336              :   unsigned int num_options,
     337              :   const struct TALER_EXCHANGE_GetKycCheckOptionValue *options)
     338              : {
     339           42 :   for (unsigned int i = 0; i < num_options; i++)
     340              :   {
     341           42 :     const struct TALER_EXCHANGE_GetKycCheckOptionValue *opt = &options[i];
     342              : 
     343           42 :     switch (opt->option)
     344              :     {
     345           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_END:
     346           14 :       return GNUNET_OK;
     347            0 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_KNOWN_RULE_GEN:
     348            0 :       gkch->known_rule_gen = opt->details.known_rule_gen;
     349            0 :       break;
     350           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_LPT:
     351           14 :       gkch->lpt = opt->details.lpt;
     352           14 :       break;
     353           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_TIMEOUT:
     354           14 :       gkch->timeout = opt->details.timeout;
     355           14 :       break;
     356            0 :     default:
     357            0 :       GNUNET_break (0);
     358            0 :       return GNUNET_SYSERR;
     359              :     }
     360              :   }
     361            0 :   return GNUNET_OK;
     362              : }
     363              : 
     364              : 
     365              : enum TALER_ErrorCode
     366           14 : TALER_EXCHANGE_get_kyc_check_start (
     367              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
     368              :   TALER_EXCHANGE_GetKycCheckCallback cb,
     369              :   TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls)
     370              : {
     371              :   CURL *eh;
     372              :   char arg_str[128];
     373              :   char timeout_ms[32];
     374              :   char lpt_str[32];
     375              :   char krg_str[32];
     376           14 :   struct curl_slist *job_headers = NULL;
     377              :   unsigned long long tms;
     378              : 
     379           14 :   gkch->cb = cb;
     380           14 :   gkch->cb_cls = cb_cls;
     381              :   {
     382              :     char *hps;
     383              : 
     384           14 :     hps = GNUNET_STRINGS_data_to_string_alloc (
     385           14 :       &gkch->h_payto,
     386              :       sizeof (gkch->h_payto));
     387           14 :     GNUNET_snprintf (arg_str,
     388              :                      sizeof (arg_str),
     389              :                      "kyc-check/%s",
     390              :                      hps);
     391           14 :     GNUNET_free (hps);
     392              :   }
     393           28 :   tms = gkch->timeout.rel_value_us
     394           14 :         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
     395           14 :   GNUNET_snprintf (timeout_ms,
     396              :                    sizeof (timeout_ms),
     397              :                    "%llu",
     398              :                    tms);
     399           14 :   GNUNET_snprintf (krg_str,
     400              :                    sizeof (krg_str),
     401              :                    "%llu",
     402           14 :                    (unsigned long long) gkch->known_rule_gen);
     403           14 :   GNUNET_snprintf (lpt_str,
     404              :                    sizeof (lpt_str),
     405              :                    "%d",
     406           14 :                    (int) gkch->lpt);
     407              :   gkch->url
     408           42 :     = TALER_url_join (
     409           14 :         gkch->base_url,
     410              :         arg_str,
     411              :         "timeout_ms",
     412           14 :         GNUNET_TIME_relative_is_zero (gkch->timeout)
     413              :         ? NULL
     414              :         : timeout_ms,
     415              :         "min_rule",
     416           14 :         0 == gkch->known_rule_gen
     417              :         ? NULL
     418              :         : krg_str,
     419              :         "lpt",
     420           14 :         TALER_EXCHANGE_KLPT_NONE == gkch->lpt
     421              :         ? NULL
     422              :         : lpt_str,
     423              :         NULL);
     424           14 :   if (NULL == gkch->url)
     425            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     426           14 :   eh = TALER_EXCHANGE_curl_easy_get_ (gkch->url);
     427           14 :   if (NULL == eh)
     428              :   {
     429            0 :     GNUNET_break (0);
     430            0 :     GNUNET_free (gkch->url);
     431            0 :     gkch->url = NULL;
     432            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     433              :   }
     434           14 :   if (0 != tms)
     435              :   {
     436           14 :     GNUNET_break (CURLE_OK ==
     437              :                   curl_easy_setopt (eh,
     438              :                                     CURLOPT_TIMEOUT_MS,
     439              :                                     (long) (tms + 500L)));
     440              :   }
     441              :   {
     442              :     union TALER_AccountPublicKeyP account_pub;
     443              :     union TALER_AccountSignatureP account_sig;
     444              :     char *sig_hdr;
     445              :     char *pub_hdr;
     446              :     char *hdr;
     447              : 
     448           14 :     GNUNET_CRYPTO_eddsa_key_get_public (
     449           14 :       &gkch->account_priv.reserve_priv.eddsa_priv,
     450              :       &account_pub.reserve_pub.eddsa_pub);
     451           14 :     TALER_account_kyc_auth_sign (&gkch->account_priv,
     452              :                                  &account_sig);
     453              : 
     454           14 :     sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
     455              :       &account_sig,
     456              :       sizeof (account_sig));
     457           14 :     GNUNET_asprintf (&hdr,
     458              :                      "%s: %s",
     459              :                      TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE,
     460              :                      sig_hdr);
     461           14 :     GNUNET_free (sig_hdr);
     462           14 :     job_headers = curl_slist_append (job_headers,
     463              :                                      hdr);
     464           14 :     GNUNET_free (hdr);
     465              : 
     466           14 :     pub_hdr = GNUNET_STRINGS_data_to_string_alloc (
     467              :       &account_pub,
     468              :       sizeof (account_pub));
     469           14 :     GNUNET_asprintf (&hdr,
     470              :                      "%s: %s",
     471              :                      TALER_HTTP_HEADER_ACCOUNT_OWNER_PUBKEY,
     472              :                      pub_hdr);
     473           14 :     GNUNET_free (pub_hdr);
     474           14 :     job_headers = curl_slist_append (job_headers,
     475              :                                      hdr);
     476           14 :     GNUNET_free (hdr);
     477           14 :     if (NULL == job_headers)
     478              :     {
     479            0 :       GNUNET_break (0);
     480            0 :       curl_easy_cleanup (eh);
     481            0 :       GNUNET_free (gkch->url);
     482            0 :       gkch->url = NULL;
     483            0 :       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     484              :     }
     485              :   }
     486              :   gkch->job
     487           14 :     = GNUNET_CURL_job_add2 (gkch->ctx,
     488              :                             eh,
     489              :                             job_headers,
     490              :                             &handle_kyc_check_finished,
     491              :                             gkch);
     492           14 :   curl_slist_free_all (job_headers);
     493           14 :   if (NULL == gkch->job)
     494              :   {
     495            0 :     GNUNET_free (gkch->url);
     496            0 :     gkch->url = NULL;
     497            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     498              :   }
     499           14 :   return TALER_EC_NONE;
     500              : }
     501              : 
     502              : 
     503              : void
     504           14 : TALER_EXCHANGE_get_kyc_check_cancel (
     505              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch)
     506              : {
     507           14 :   if (NULL != gkch->job)
     508              :   {
     509            0 :     GNUNET_CURL_job_cancel (gkch->job);
     510            0 :     gkch->job = NULL;
     511              :   }
     512           14 :   GNUNET_free (gkch->url);
     513           14 :   GNUNET_free (gkch->base_url);
     514           14 :   GNUNET_free (gkch);
     515           14 : }
     516              : 
     517              : 
     518              : /* end of exchange_api_get-kyc-check-H_NORMALIZED_PAYTO.c */
        

Generated by: LCOV version 2.0-1