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: 62.0 % 187 116
Test Date: 2026-06-14 14:19:22 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           13 :     struct TALER_EXCHANGE_AccountLimit ala[GNUNET_NZL (limit_length)];
     145              :     size_t i;
     146              :     json_t *limit;
     147              : 
     148           42 :     json_array_foreach (limits, i, limit)
     149              :     {
     150           29 :       struct TALER_EXCHANGE_AccountLimit *al = &ala[i];
     151              :       struct GNUNET_JSON_Specification ispec[] = {
     152           29 :         GNUNET_JSON_spec_mark_optional (
     153              :           GNUNET_JSON_spec_bool ("soft_limit",
     154              :                                  &al->soft_limit),
     155              :           NULL),
     156           29 :         GNUNET_JSON_spec_relative_time ("timeframe",
     157              :                                         &al->timeframe),
     158           29 :         TALER_JSON_spec_kycte ("operation_type",
     159              :                                &al->operation_type),
     160           29 :         TALER_JSON_spec_amount_any ("threshold",
     161              :                                     &al->threshold),
     162           29 :         GNUNET_JSON_spec_end ()
     163              :       };
     164              : 
     165           29 :       al->soft_limit = false;
     166           29 :       if (GNUNET_OK !=
     167           29 :           GNUNET_JSON_parse (limit,
     168              :                              ispec,
     169              :                              NULL, NULL))
     170              :       {
     171            0 :         GNUNET_break_op (0);
     172            0 :         return GNUNET_SYSERR;
     173              :       }
     174              :     }
     175           13 :     status->limits = ala;
     176           13 :     status->limits_length = limit_length;
     177           13 :     gkch->cb (gkch->cb_cls,
     178              :               res);
     179              :   }
     180              :   else
     181              :   {
     182            1 :     gkch->cb (gkch->cb_cls,
     183              :               res);
     184              :   }
     185           14 :   GNUNET_JSON_parse_free (spec);
     186           14 :   return GNUNET_OK;
     187              : }
     188              : 
     189              : 
     190              : /**
     191              :  * Function called when we're done processing the
     192              :  * HTTP GET /kyc-check/$H_NORMALIZED_PAYTO request.
     193              :  *
     194              :  * @param cls the `struct TALER_EXCHANGE_GetKycCheckHandle`
     195              :  * @param response_code HTTP response code, 0 on error
     196              :  * @param response parsed JSON result, NULL on error
     197              :  */
     198              : static void
     199           14 : handle_kyc_check_finished (void *cls,
     200              :                            long response_code,
     201              :                            const void *response)
     202              : {
     203           14 :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch = cls;
     204           14 :   const json_t *j = response;
     205           14 :   struct TALER_EXCHANGE_GetKycCheckResponse ks = {
     206              :     .hr.reply = j,
     207           14 :     .hr.http_status = (unsigned int) response_code
     208              :   };
     209              : 
     210           14 :   gkch->job = NULL;
     211           14 :   switch (response_code)
     212              :   {
     213            0 :   case 0:
     214            0 :     ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     215            0 :     break;
     216            3 :   case MHD_HTTP_OK:
     217              :     {
     218            3 :       if (GNUNET_OK !=
     219            3 :           parse_account_status (gkch,
     220              :                                 j,
     221              :                                 &ks,
     222              :                                 &ks.details.ok))
     223              :       {
     224            0 :         GNUNET_break_op (0);
     225            0 :         ks.hr.http_status = 0;
     226            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     227            0 :         break;
     228              :       }
     229            3 :       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     230           14 :       return;
     231              :     }
     232           11 :   case MHD_HTTP_ACCEPTED:
     233              :     {
     234           11 :       if (GNUNET_OK !=
     235           11 :           parse_account_status (gkch,
     236              :                                 j,
     237              :                                 &ks,
     238              :                                 &ks.details.accepted))
     239              :       {
     240            0 :         GNUNET_break_op (0);
     241            0 :         ks.hr.http_status = 0;
     242            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     243            0 :         break;
     244              :       }
     245           11 :       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     246           11 :       return;
     247              :     }
     248            0 :   case MHD_HTTP_NO_CONTENT:
     249            0 :     break;
     250            0 :   case MHD_HTTP_BAD_REQUEST:
     251            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     252            0 :     break;
     253            0 :   case MHD_HTTP_FORBIDDEN:
     254              :     {
     255              :       struct GNUNET_JSON_Specification spec[] = {
     256            0 :         GNUNET_JSON_spec_fixed_auto (
     257              :           "expected_account_pub",
     258              :           &ks.details.forbidden.expected_account_pub),
     259            0 :         TALER_JSON_spec_ec ("code",
     260              :                             &ks.hr.ec),
     261            0 :         GNUNET_JSON_spec_end ()
     262              :       };
     263              : 
     264            0 :       if (GNUNET_OK !=
     265            0 :           GNUNET_JSON_parse (j,
     266              :                              spec,
     267              :                              NULL, NULL))
     268              :       {
     269            0 :         GNUNET_break_op (0);
     270            0 :         ks.hr.http_status = 0;
     271            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     272            0 :         break;
     273              :       }
     274            0 :       break;
     275              :     }
     276            0 :   case MHD_HTTP_NOT_FOUND:
     277            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     278            0 :     break;
     279            0 :   case MHD_HTTP_CONFLICT:
     280            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     281            0 :     break;
     282            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     283            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     284            0 :     break;
     285            0 :   default:
     286              :     /* unexpected response code */
     287            0 :     GNUNET_break_op (0);
     288            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     289            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     290              :                 "Unexpected response code %u/%d for exchange kyc_check\n",
     291              :                 (unsigned int) response_code,
     292              :                 (int) ks.hr.ec);
     293            0 :     break;
     294              :   }
     295            0 :   if (NULL != gkch->cb)
     296              :   {
     297            0 :     gkch->cb (gkch->cb_cls,
     298              :               &ks);
     299            0 :     gkch->cb = NULL;
     300              :   }
     301            0 :   TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     302              : }
     303              : 
     304              : 
     305              : struct TALER_EXCHANGE_GetKycCheckHandle *
     306           14 : TALER_EXCHANGE_get_kyc_check_create (
     307              :   struct GNUNET_CURL_Context *ctx,
     308              :   const char *url,
     309              :   const struct TALER_NormalizedPaytoHashP *h_payto,
     310              :   const union TALER_AccountPrivateKeyP *pk)
     311              : {
     312              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch;
     313              : 
     314           14 :   gkch = GNUNET_new (struct TALER_EXCHANGE_GetKycCheckHandle);
     315           14 :   gkch->ctx = ctx;
     316           14 :   gkch->base_url = GNUNET_strdup (url);
     317           14 :   gkch->h_payto = *h_payto;
     318           14 :   gkch->account_priv = *pk;
     319           14 :   return gkch;
     320              : }
     321              : 
     322              : 
     323              : enum GNUNET_GenericReturnValue
     324           14 : TALER_EXCHANGE_get_kyc_check_set_options_ (
     325              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
     326              :   unsigned int num_options,
     327              :   const struct TALER_EXCHANGE_GetKycCheckOptionValue *options)
     328              : {
     329           42 :   for (unsigned int i = 0; i < num_options; i++)
     330              :   {
     331           42 :     const struct TALER_EXCHANGE_GetKycCheckOptionValue *opt = &options[i];
     332              : 
     333           42 :     switch (opt->option)
     334              :     {
     335           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_END:
     336           14 :       return GNUNET_OK;
     337            0 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_KNOWN_RULE_GEN:
     338            0 :       gkch->known_rule_gen = opt->details.known_rule_gen;
     339            0 :       break;
     340           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_LPT:
     341           14 :       gkch->lpt = opt->details.lpt;
     342           14 :       break;
     343           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_TIMEOUT:
     344           14 :       gkch->timeout = opt->details.timeout;
     345           14 :       break;
     346            0 :     default:
     347            0 :       GNUNET_break (0);
     348            0 :       return GNUNET_SYSERR;
     349              :     }
     350              :   }
     351            0 :   return GNUNET_OK;
     352              : }
     353              : 
     354              : 
     355              : enum TALER_ErrorCode
     356           14 : TALER_EXCHANGE_get_kyc_check_start (
     357              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
     358              :   TALER_EXCHANGE_GetKycCheckCallback cb,
     359              :   TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls)
     360              : {
     361              :   CURL *eh;
     362              :   char arg_str[128];
     363              :   char timeout_ms[32];
     364              :   char lpt_str[32];
     365              :   char krg_str[32];
     366           14 :   struct curl_slist *job_headers = NULL;
     367              :   unsigned long long tms;
     368              : 
     369           14 :   gkch->cb = cb;
     370           14 :   gkch->cb_cls = cb_cls;
     371              :   {
     372              :     char *hps;
     373              : 
     374           14 :     hps = GNUNET_STRINGS_data_to_string_alloc (
     375           14 :       &gkch->h_payto,
     376              :       sizeof (gkch->h_payto));
     377           14 :     GNUNET_snprintf (arg_str,
     378              :                      sizeof (arg_str),
     379              :                      "kyc-check/%s",
     380              :                      hps);
     381           14 :     GNUNET_free (hps);
     382              :   }
     383           28 :   tms = gkch->timeout.rel_value_us
     384           14 :         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
     385           14 :   GNUNET_snprintf (timeout_ms,
     386              :                    sizeof (timeout_ms),
     387              :                    "%llu",
     388              :                    tms);
     389           14 :   GNUNET_snprintf (krg_str,
     390              :                    sizeof (krg_str),
     391              :                    "%llu",
     392           14 :                    (unsigned long long) gkch->known_rule_gen);
     393           14 :   GNUNET_snprintf (lpt_str,
     394              :                    sizeof (lpt_str),
     395              :                    "%d",
     396           14 :                    (int) gkch->lpt);
     397              :   gkch->url
     398           42 :     = TALER_url_join (
     399           14 :         gkch->base_url,
     400              :         arg_str,
     401              :         "timeout_ms",
     402           14 :         GNUNET_TIME_relative_is_zero (gkch->timeout)
     403              :         ? NULL
     404              :         : timeout_ms,
     405              :         "min_rule",
     406           14 :         0 == gkch->known_rule_gen
     407              :         ? NULL
     408              :         : krg_str,
     409              :         "lpt",
     410           14 :         TALER_EXCHANGE_KLPT_NONE == gkch->lpt
     411              :         ? NULL
     412              :         : lpt_str,
     413              :         NULL);
     414           14 :   if (NULL == gkch->url)
     415            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     416           14 :   eh = TALER_EXCHANGE_curl_easy_get_ (gkch->url);
     417           14 :   if (NULL == eh)
     418              :   {
     419            0 :     GNUNET_break (0);
     420            0 :     GNUNET_free (gkch->url);
     421            0 :     gkch->url = NULL;
     422            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     423              :   }
     424           14 :   if (0 != tms)
     425              :   {
     426           14 :     GNUNET_break (CURLE_OK ==
     427              :                   curl_easy_setopt (eh,
     428              :                                     CURLOPT_TIMEOUT_MS,
     429              :                                     (long) (tms + 500L)));
     430              :   }
     431              :   {
     432              :     union TALER_AccountPublicKeyP account_pub;
     433              :     union TALER_AccountSignatureP account_sig;
     434              :     char *sig_hdr;
     435              :     char *pub_hdr;
     436              :     char *hdr;
     437              : 
     438           14 :     GNUNET_CRYPTO_eddsa_key_get_public (
     439           14 :       &gkch->account_priv.reserve_priv.eddsa_priv,
     440              :       &account_pub.reserve_pub.eddsa_pub);
     441           14 :     TALER_account_kyc_auth_sign (&gkch->account_priv,
     442              :                                  &account_sig);
     443              : 
     444           14 :     sig_hdr = GNUNET_STRINGS_data_to_string_alloc (
     445              :       &account_sig,
     446              :       sizeof (account_sig));
     447           14 :     GNUNET_asprintf (&hdr,
     448              :                      "%s: %s",
     449              :                      TALER_HTTP_HEADER_ACCOUNT_OWNER_SIGNATURE,
     450              :                      sig_hdr);
     451           14 :     GNUNET_free (sig_hdr);
     452           14 :     job_headers = curl_slist_append (job_headers,
     453              :                                      hdr);
     454           14 :     GNUNET_free (hdr);
     455              : 
     456           14 :     pub_hdr = GNUNET_STRINGS_data_to_string_alloc (
     457              :       &account_pub,
     458              :       sizeof (account_pub));
     459           14 :     GNUNET_asprintf (&hdr,
     460              :                      "%s: %s",
     461              :                      TALER_HTTP_HEADER_ACCOUNT_OWNER_PUBKEY,
     462              :                      pub_hdr);
     463           14 :     GNUNET_free (pub_hdr);
     464           14 :     job_headers = curl_slist_append (job_headers,
     465              :                                      hdr);
     466           14 :     GNUNET_free (hdr);
     467           14 :     if (NULL == job_headers)
     468              :     {
     469            0 :       GNUNET_break (0);
     470            0 :       curl_easy_cleanup (eh);
     471            0 :       GNUNET_free (gkch->url);
     472            0 :       gkch->url = NULL;
     473            0 :       return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     474              :     }
     475              :   }
     476              :   gkch->job
     477           14 :     = GNUNET_CURL_job_add2 (gkch->ctx,
     478              :                             eh,
     479              :                             job_headers,
     480              :                             &handle_kyc_check_finished,
     481              :                             gkch);
     482           14 :   curl_slist_free_all (job_headers);
     483           14 :   if (NULL == gkch->job)
     484              :   {
     485            0 :     GNUNET_free (gkch->url);
     486            0 :     gkch->url = NULL;
     487            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     488              :   }
     489           14 :   return TALER_EC_NONE;
     490              : }
     491              : 
     492              : 
     493              : void
     494           14 : TALER_EXCHANGE_get_kyc_check_cancel (
     495              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch)
     496              : {
     497           14 :   if (NULL != gkch->job)
     498              :   {
     499            0 :     GNUNET_CURL_job_cancel (gkch->job);
     500            0 :     gkch->job = NULL;
     501              :   }
     502           14 :   GNUNET_free (gkch->url);
     503           14 :   GNUNET_free (gkch->base_url);
     504           14 :   GNUNET_free (gkch);
     505           14 : }
     506              : 
     507              : 
     508              : /* end of exchange_api_get-kyc-check-H_NORMALIZED_PAYTO.c */
        

Generated by: LCOV version 2.0-1