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-03-10 12:10:57 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 "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 "taler/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_array_const ("limits",
     123              :                                     &limits),
     124              :       NULL),
     125           14 :     GNUNET_JSON_spec_end ()
     126              :   };
     127              : 
     128           14 :   if (GNUNET_OK !=
     129           14 :       GNUNET_JSON_parse (j,
     130              :                          spec,
     131              :                          NULL, NULL))
     132              :   {
     133            0 :     GNUNET_break_op (0);
     134            0 :     return GNUNET_SYSERR;
     135              :   }
     136           28 :   if ( (NULL != limits) &&
     137           14 :        (0 != json_array_size (limits)) )
     138           13 :   {
     139           13 :     size_t limit_length = json_array_size (limits);
     140           13 :     struct TALER_EXCHANGE_AccountLimit ala[GNUNET_NZL (limit_length)];
     141              :     size_t i;
     142              :     json_t *limit;
     143              : 
     144           42 :     json_array_foreach (limits, i, limit)
     145              :     {
     146           29 :       struct TALER_EXCHANGE_AccountLimit *al = &ala[i];
     147              :       struct GNUNET_JSON_Specification ispec[] = {
     148           29 :         GNUNET_JSON_spec_mark_optional (
     149              :           GNUNET_JSON_spec_bool ("soft_limit",
     150              :                                  &al->soft_limit),
     151              :           NULL),
     152           29 :         GNUNET_JSON_spec_relative_time ("timeframe",
     153              :                                         &al->timeframe),
     154           29 :         TALER_JSON_spec_kycte ("operation_type",
     155              :                                &al->operation_type),
     156           29 :         TALER_JSON_spec_amount_any ("threshold",
     157              :                                     &al->threshold),
     158           29 :         GNUNET_JSON_spec_end ()
     159              :       };
     160              : 
     161           29 :       al->soft_limit = false;
     162           29 :       if (GNUNET_OK !=
     163           29 :           GNUNET_JSON_parse (limit,
     164              :                              ispec,
     165              :                              NULL, NULL))
     166              :       {
     167            0 :         GNUNET_break_op (0);
     168            0 :         return GNUNET_SYSERR;
     169              :       }
     170              :     }
     171           13 :     status->limits = ala;
     172           13 :     status->limits_length = limit_length;
     173           13 :     gkch->cb (gkch->cb_cls,
     174              :               res);
     175              :   }
     176              :   else
     177              :   {
     178            1 :     gkch->cb (gkch->cb_cls,
     179              :               res);
     180              :   }
     181           14 :   GNUNET_JSON_parse_free (spec);
     182           14 :   return GNUNET_OK;
     183              : }
     184              : 
     185              : 
     186              : /**
     187              :  * Function called when we're done processing the
     188              :  * HTTP GET /kyc-check/$H_NORMALIZED_PAYTO request.
     189              :  *
     190              :  * @param cls the `struct TALER_EXCHANGE_GetKycCheckHandle`
     191              :  * @param response_code HTTP response code, 0 on error
     192              :  * @param response parsed JSON result, NULL on error
     193              :  */
     194              : static void
     195           14 : handle_kyc_check_finished (void *cls,
     196              :                            long response_code,
     197              :                            const void *response)
     198              : {
     199           14 :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch = cls;
     200           14 :   const json_t *j = response;
     201           14 :   struct TALER_EXCHANGE_GetKycCheckResponse ks = {
     202              :     .hr.reply = j,
     203           14 :     .hr.http_status = (unsigned int) response_code
     204              :   };
     205              : 
     206           14 :   gkch->job = NULL;
     207           14 :   switch (response_code)
     208              :   {
     209            0 :   case 0:
     210            0 :     ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     211            0 :     break;
     212            3 :   case MHD_HTTP_OK:
     213              :     {
     214            3 :       if (GNUNET_OK !=
     215            3 :           parse_account_status (gkch,
     216              :                                 j,
     217              :                                 &ks,
     218              :                                 &ks.details.ok))
     219              :       {
     220            0 :         GNUNET_break_op (0);
     221            0 :         ks.hr.http_status = 0;
     222            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     223            0 :         break;
     224              :       }
     225            3 :       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     226           14 :       return;
     227              :     }
     228           11 :   case MHD_HTTP_ACCEPTED:
     229              :     {
     230           11 :       if (GNUNET_OK !=
     231           11 :           parse_account_status (gkch,
     232              :                                 j,
     233              :                                 &ks,
     234              :                                 &ks.details.accepted))
     235              :       {
     236            0 :         GNUNET_break_op (0);
     237            0 :         ks.hr.http_status = 0;
     238            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     239            0 :         break;
     240              :       }
     241           11 :       TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     242           11 :       return;
     243              :     }
     244            0 :   case MHD_HTTP_NO_CONTENT:
     245            0 :     break;
     246            0 :   case MHD_HTTP_BAD_REQUEST:
     247            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     248            0 :     break;
     249            0 :   case MHD_HTTP_FORBIDDEN:
     250              :     {
     251              :       struct GNUNET_JSON_Specification spec[] = {
     252            0 :         GNUNET_JSON_spec_fixed_auto (
     253              :           "expected_account_pub",
     254              :           &ks.details.forbidden.expected_account_pub),
     255            0 :         TALER_JSON_spec_ec ("code",
     256              :                             &ks.hr.ec),
     257            0 :         GNUNET_JSON_spec_end ()
     258              :       };
     259              : 
     260            0 :       if (GNUNET_OK !=
     261            0 :           GNUNET_JSON_parse (j,
     262              :                              spec,
     263              :                              NULL, NULL))
     264              :       {
     265            0 :         GNUNET_break_op (0);
     266            0 :         ks.hr.http_status = 0;
     267            0 :         ks.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     268            0 :         break;
     269              :       }
     270            0 :       break;
     271              :     }
     272            0 :   case MHD_HTTP_NOT_FOUND:
     273            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     274            0 :     break;
     275            0 :   case MHD_HTTP_CONFLICT:
     276            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     277            0 :     break;
     278            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     279            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     280            0 :     break;
     281            0 :   default:
     282              :     /* unexpected response code */
     283            0 :     GNUNET_break_op (0);
     284            0 :     ks.hr.ec = TALER_JSON_get_error_code (j);
     285            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     286              :                 "Unexpected response code %u/%d for exchange kyc_check\n",
     287              :                 (unsigned int) response_code,
     288              :                 (int) ks.hr.ec);
     289            0 :     break;
     290              :   }
     291            0 :   if (NULL != gkch->cb)
     292              :   {
     293            0 :     gkch->cb (gkch->cb_cls,
     294              :               &ks);
     295            0 :     gkch->cb = NULL;
     296              :   }
     297            0 :   TALER_EXCHANGE_get_kyc_check_cancel (gkch);
     298              : }
     299              : 
     300              : 
     301              : struct TALER_EXCHANGE_GetKycCheckHandle *
     302           14 : TALER_EXCHANGE_get_kyc_check_create (
     303              :   struct GNUNET_CURL_Context *ctx,
     304              :   const char *url,
     305              :   const struct TALER_NormalizedPaytoHashP *h_payto,
     306              :   const union TALER_AccountPrivateKeyP *pk)
     307              : {
     308              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch;
     309              : 
     310           14 :   gkch = GNUNET_new (struct TALER_EXCHANGE_GetKycCheckHandle);
     311           14 :   gkch->ctx = ctx;
     312           14 :   gkch->base_url = GNUNET_strdup (url);
     313           14 :   gkch->h_payto = *h_payto;
     314           14 :   gkch->account_priv = *pk;
     315           14 :   return gkch;
     316              : }
     317              : 
     318              : 
     319              : enum GNUNET_GenericReturnValue
     320           14 : TALER_EXCHANGE_get_kyc_check_set_options_ (
     321              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
     322              :   unsigned int num_options,
     323              :   const struct TALER_EXCHANGE_GetKycCheckOptionValue *options)
     324              : {
     325           42 :   for (unsigned int i = 0; i < num_options; i++)
     326              :   {
     327           42 :     const struct TALER_EXCHANGE_GetKycCheckOptionValue *opt = &options[i];
     328              : 
     329           42 :     switch (opt->option)
     330              :     {
     331           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_END:
     332           14 :       return GNUNET_OK;
     333            0 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_KNOWN_RULE_GEN:
     334            0 :       gkch->known_rule_gen = opt->details.known_rule_gen;
     335            0 :       break;
     336           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_LPT:
     337           14 :       gkch->lpt = opt->details.lpt;
     338           14 :       break;
     339           14 :     case TALER_EXCHANGE_GET_KYC_CHECK_OPTION_TIMEOUT:
     340           14 :       gkch->timeout = opt->details.timeout;
     341           14 :       break;
     342            0 :     default:
     343            0 :       GNUNET_break (0);
     344            0 :       return GNUNET_SYSERR;
     345              :     }
     346              :   }
     347            0 :   return GNUNET_OK;
     348              : }
     349              : 
     350              : 
     351              : enum TALER_ErrorCode
     352           14 : TALER_EXCHANGE_get_kyc_check_start (
     353              :   struct TALER_EXCHANGE_GetKycCheckHandle *gkch,
     354              :   TALER_EXCHANGE_GetKycCheckCallback cb,
     355              :   TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE *cb_cls)
     356              : {
     357              :   CURL *eh;
     358              :   char arg_str[128];
     359              :   char timeout_ms[32];
     360              :   char lpt_str[32];
     361              :   char krg_str[32];
     362           14 :   struct curl_slist *job_headers = NULL;
     363              :   unsigned long long tms;
     364              : 
     365           14 :   gkch->cb = cb;
     366           14 :   gkch->cb_cls = cb_cls;
     367              :   {
     368              :     char *hps;
     369              : 
     370           14 :     hps = GNUNET_STRINGS_data_to_string_alloc (
     371           14 :       &gkch->h_payto,
     372              :       sizeof (gkch->h_payto));
     373           14 :     GNUNET_snprintf (arg_str,
     374              :                      sizeof (arg_str),
     375              :                      "kyc-check/%s",
     376              :                      hps);
     377           14 :     GNUNET_free (hps);
     378              :   }
     379           28 :   tms = gkch->timeout.rel_value_us
     380           14 :         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
     381           14 :   GNUNET_snprintf (timeout_ms,
     382              :                    sizeof (timeout_ms),
     383              :                    "%llu",
     384              :                    tms);
     385           14 :   GNUNET_snprintf (krg_str,
     386              :                    sizeof (krg_str),
     387              :                    "%llu",
     388           14 :                    (unsigned long long) gkch->known_rule_gen);
     389           14 :   GNUNET_snprintf (lpt_str,
     390              :                    sizeof (lpt_str),
     391              :                    "%d",
     392           14 :                    (int) gkch->lpt);
     393              :   gkch->url
     394           42 :     = TALER_url_join (
     395           14 :         gkch->base_url,
     396              :         arg_str,
     397              :         "timeout_ms",
     398           14 :         GNUNET_TIME_relative_is_zero (gkch->timeout)
     399              :         ? NULL
     400              :         : timeout_ms,
     401              :         "min_rule",
     402           14 :         0 == gkch->known_rule_gen
     403              :         ? NULL
     404              :         : krg_str,
     405              :         "lpt",
     406           14 :         TALER_EXCHANGE_KLPT_NONE == gkch->lpt
     407              :         ? NULL
     408              :         : lpt_str,
     409              :         NULL);
     410           14 :   if (NULL == gkch->url)
     411            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     412           14 :   eh = TALER_EXCHANGE_curl_easy_get_ (gkch->url);
     413           14 :   if (NULL == eh)
     414              :   {
     415            0 :     GNUNET_break (0);
     416            0 :     GNUNET_free (gkch->url);
     417            0 :     gkch->url = NULL;
     418            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     419              :   }
     420           14 :   if (0 != tms)
     421              :   {
     422           14 :     GNUNET_break (CURLE_OK ==
     423              :                   curl_easy_setopt (eh,
     424              :                                     CURLOPT_TIMEOUT_MS,
     425              :                                     (long) (tms + 500L)));
     426              :   }
     427              :   job_headers
     428           14 :     = curl_slist_append (
     429              :         job_headers,
     430              :         "Content-Type: application/json");
     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