LCOV - code coverage report
Current view: top level - lib - exchange_api_kyc_info.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 81 144 56.2 %
Date: 2025-06-05 21:03:14 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2015-2023 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_info.c
      19             :  * @brief Implementation of the /kyc-info/$AT request
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <microhttpd.h> /* just for HTTP status codes */
      24             : #include <gnunet/gnunet_util_lib.h>
      25             : #include <gnunet/gnunet_curl_lib.h>
      26             : #include "taler_exchange_service.h"
      27             : #include "taler_json_lib.h"
      28             : #include "exchange_api_handle.h"
      29             : #include "taler_signatures.h"
      30             : #include "exchange_api_curl_defaults.h"
      31             : 
      32             : 
      33             : /**
      34             :  * @brief A /kyc-info Handle
      35             :  */
      36             : struct TALER_EXCHANGE_KycInfoHandle
      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_KycInfoCallback kyc_info_cb;
      53             : 
      54             :   /**
      55             :    * Closure for @e cb.
      56             :    */
      57             :   void *kyc_info_cb_cls;
      58             : 
      59             : };
      60             : 
      61             : 
      62             : /**
      63             :  * Parse the provided kyc_infoage data from the "200 OK" response
      64             :  * for one of the coins.
      65             :  *
      66             :  * @param[in,out] lh kyc_info handle (callback may be zero'ed out)
      67             :  * @param json json reply with the data for one coin
      68             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
      69             :  */
      70             : static enum GNUNET_GenericReturnValue
      71          11 : parse_kyc_info_ok (struct TALER_EXCHANGE_KycInfoHandle *lh,
      72             :                    const json_t *json)
      73             : {
      74          11 :   const json_t *jrequirements = NULL;
      75          11 :   const json_t *jvoluntary_checks = NULL;
      76          11 :   struct TALER_EXCHANGE_KycProcessClientInformation lr = {
      77             :     .hr.reply = json,
      78             :     .hr.http_status = MHD_HTTP_OK
      79             :   };
      80             :   struct GNUNET_JSON_Specification spec[] = {
      81          11 :     GNUNET_JSON_spec_array_const ("requirements",
      82             :                                   &jrequirements),
      83          11 :     GNUNET_JSON_spec_bool ("is_and_combinator",
      84             :                            &lr.details.ok.is_and_combinator),
      85          11 :     GNUNET_JSON_spec_mark_optional (
      86             :       GNUNET_JSON_spec_object_const ("voluntary_checks",
      87             :                                      &jvoluntary_checks),
      88             :       NULL),
      89          11 :     GNUNET_JSON_spec_end ()
      90             :   };
      91             : 
      92          11 :   if (GNUNET_OK !=
      93          11 :       GNUNET_JSON_parse (json,
      94             :                          spec,
      95             :                          NULL, NULL))
      96             :   {
      97           0 :     GNUNET_break_op (0);
      98           0 :     return GNUNET_SYSERR;
      99             :   }
     100             : 
     101             :   lr.details.ok.vci_length
     102          11 :     = (unsigned int) json_object_size (jvoluntary_checks);
     103          11 :   if ( ((size_t) lr.details.ok.vci_length)
     104          11 :        != json_object_size (jvoluntary_checks))
     105             :   {
     106           0 :     GNUNET_break_op (0);
     107           0 :     return GNUNET_SYSERR;
     108             :   }
     109             :   lr.details.ok.requirements_length
     110          11 :     = json_array_size (jrequirements);
     111          11 :   if ( ((size_t) lr.details.ok.requirements_length)
     112          11 :        != json_array_size (jrequirements))
     113             :   {
     114           0 :     GNUNET_break_op (0);
     115           0 :     return GNUNET_SYSERR;
     116             :   }
     117             : 
     118          11 :   {
     119          11 :     struct TALER_EXCHANGE_VoluntaryCheckInformation vci[
     120          11 :       GNUNET_NZL (lr.details.ok.vci_length)];
     121          11 :     struct TALER_EXCHANGE_RequirementInformation requirements[
     122          11 :       GNUNET_NZL (lr.details.ok.requirements_length)];
     123             :     const char *name;
     124             :     const json_t *jreq;
     125             :     const json_t *jvc;
     126             :     size_t off;
     127             : 
     128          11 :     memset (vci,
     129             :             0,
     130             :             sizeof (vci));
     131          11 :     memset (requirements,
     132             :             0,
     133             :             sizeof (requirements));
     134             : 
     135          22 :     json_array_foreach ((json_t *) jrequirements, off, jreq)
     136             :     {
     137          11 :       struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off];
     138             :       struct GNUNET_JSON_Specification ispec[] = {
     139          11 :         GNUNET_JSON_spec_string ("form",
     140             :                                  &req->form),
     141          11 :         GNUNET_JSON_spec_string ("description",
     142             :                                  &req->description),
     143          11 :         GNUNET_JSON_spec_mark_optional (
     144             :           GNUNET_JSON_spec_object_const ("description_i18n",
     145             :                                          &req->description_i18n),
     146             :           NULL),
     147          11 :         GNUNET_JSON_spec_mark_optional (
     148             :           GNUNET_JSON_spec_string ("id",
     149             :                                    &req->id),
     150             :           NULL),
     151          11 :         GNUNET_JSON_spec_end ()
     152             :       };
     153             : 
     154          11 :       if (GNUNET_OK !=
     155          11 :           GNUNET_JSON_parse (jreq,
     156             :                              ispec,
     157             :                              NULL, NULL))
     158             :       {
     159           0 :         GNUNET_break_op (0);
     160           0 :         return GNUNET_SYSERR;
     161             :       }
     162             :     }
     163          11 :     GNUNET_assert (off == lr.details.ok.requirements_length);
     164             : 
     165          11 :     off = 0;
     166          11 :     json_object_foreach ((json_t *) jvoluntary_checks, name, jvc)
     167             :     {
     168           0 :       struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++];
     169             :       struct GNUNET_JSON_Specification ispec[] = {
     170           0 :         GNUNET_JSON_spec_string ("description",
     171             :                                  &vc->description),
     172           0 :         GNUNET_JSON_spec_mark_optional (
     173             :           GNUNET_JSON_spec_object_const ("description_i18n",
     174             :                                          &vc->description_i18n),
     175             :           NULL),
     176           0 :         GNUNET_JSON_spec_end ()
     177             :       };
     178             : 
     179           0 :       vc->name = name;
     180           0 :       if (GNUNET_OK !=
     181           0 :           GNUNET_JSON_parse (jvc,
     182             :                              ispec,
     183             :                              NULL, NULL))
     184             :       {
     185           0 :         GNUNET_break_op (0);
     186           0 :         return GNUNET_SYSERR;
     187             :       }
     188             :     }
     189          11 :     GNUNET_assert (off == lr.details.ok.vci_length);
     190             : 
     191          11 :     lr.details.ok.vci = vci;
     192          11 :     lr.details.ok.requirements = requirements;
     193          11 :     lh->kyc_info_cb (lh->kyc_info_cb_cls,
     194             :                      &lr);
     195          11 :     lh->kyc_info_cb = NULL;
     196          11 :     return GNUNET_OK;
     197             :   }
     198             : }
     199             : 
     200             : 
     201             : /**
     202             :  * Function called when we're done processing the
     203             :  * HTTP /kyc-info/$AT request.
     204             :  *
     205             :  * @param cls the `struct TALER_EXCHANGE_KycInfoHandle`
     206             :  * @param response_code HTTP response code, 0 on error
     207             :  * @param response parsed JSON result, NULL on error
     208             :  */
     209             : static void
     210          11 : handle_kyc_info_finished (void *cls,
     211             :                           long response_code,
     212             :                           const void *response)
     213             : {
     214          11 :   struct TALER_EXCHANGE_KycInfoHandle *lh = cls;
     215          11 :   const json_t *j = response;
     216          11 :   struct TALER_EXCHANGE_KycProcessClientInformation lr = {
     217             :     .hr.reply = j,
     218          11 :     .hr.http_status = (unsigned int) response_code
     219             :   };
     220             : 
     221          11 :   lh->job = NULL;
     222          11 :   switch (response_code)
     223             :   {
     224           0 :   case 0:
     225           0 :     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     226           0 :     break;
     227          11 :   case MHD_HTTP_OK:
     228          11 :     if (GNUNET_OK !=
     229          11 :         parse_kyc_info_ok (lh,
     230             :                            j))
     231             :     {
     232           0 :       GNUNET_break_op (0);
     233           0 :       lr.hr.http_status = 0;
     234           0 :       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     235           0 :       break;
     236             :     }
     237          11 :     GNUNET_assert (NULL == lh->kyc_info_cb);
     238          11 :     TALER_EXCHANGE_kyc_info_cancel (lh);
     239          11 :     return;
     240           0 :   case MHD_HTTP_NO_CONTENT:
     241           0 :     break;
     242           0 :   case MHD_HTTP_BAD_REQUEST:
     243           0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     244           0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     245             :     /* This should never happen, either us or the exchange is buggy
     246             :        (or API version conflict); just pass JSON reply to the application */
     247           0 :     break;
     248           0 :   case MHD_HTTP_FORBIDDEN:
     249           0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     250           0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     251           0 :     break;
     252           0 :   case MHD_HTTP_NOT_FOUND:
     253           0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     254           0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     255             :     /* Nothing really to verify, exchange says this coin was not melted; we
     256             :        should pass the JSON reply to the application */
     257           0 :     break;
     258           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     259           0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     260           0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     261             :     /* Server had an internal issue; we should retry, but this API
     262             :        leaves this to the application */
     263           0 :     break;
     264           0 :   default:
     265             :     /* unexpected response code */
     266           0 :     GNUNET_break_op (0);
     267           0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     268           0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     269           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     270             :                 "Unexpected response code %u/%d for exchange /kyc-info\n",
     271             :                 (unsigned int) response_code,
     272             :                 (int) lr.hr.ec);
     273           0 :     break;
     274             :   }
     275           0 :   if (NULL != lh->kyc_info_cb)
     276           0 :     lh->kyc_info_cb (lh->kyc_info_cb_cls,
     277             :                      &lr);
     278           0 :   TALER_EXCHANGE_kyc_info_cancel (lh);
     279             : }
     280             : 
     281             : 
     282             : struct TALER_EXCHANGE_KycInfoHandle *
     283          11 : TALER_EXCHANGE_kyc_info (
     284             :   struct GNUNET_CURL_Context *ctx,
     285             :   const char *url,
     286             :   const struct TALER_AccountAccessTokenP *token,
     287             :   const char *if_none_match,
     288             :   struct GNUNET_TIME_Relative timeout,
     289             :   TALER_EXCHANGE_KycInfoCallback cb,
     290             :   void *cb_cls)
     291             : {
     292             :   struct TALER_EXCHANGE_KycInfoHandle *lh;
     293             :   CURL *eh;
     294             :   char arg_str[sizeof (struct TALER_AccountAccessTokenP) * 2 + 32];
     295          11 :   unsigned int tms
     296          11 :     = (unsigned int) timeout.rel_value_us
     297          11 :       / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
     298          11 :   struct curl_slist *job_headers = NULL;
     299             : 
     300             :   {
     301             :     char at_str[sizeof (struct TALER_AccountAccessTokenP) * 2];
     302             :     char *end;
     303             : 
     304          11 :     end = GNUNET_STRINGS_data_to_string (
     305             :       token,
     306             :       sizeof (*token),
     307             :       at_str,
     308             :       sizeof (at_str));
     309          11 :     *end = '\0';
     310          11 :     GNUNET_snprintf (arg_str,
     311             :                      sizeof (arg_str),
     312             :                      "kyc-info/%s",
     313             :                      at_str);
     314             :   }
     315          11 :   lh = GNUNET_new (struct TALER_EXCHANGE_KycInfoHandle);
     316          11 :   lh->kyc_info_cb = cb;
     317          11 :   lh->kyc_info_cb_cls = cb_cls;
     318             :   {
     319             :     char timeout_str[32];
     320             : 
     321          11 :     GNUNET_snprintf (timeout_str,
     322             :                      sizeof (timeout_str),
     323             :                      "%u",
     324             :                      tms);
     325          11 :     lh->url = TALER_url_join (url,
     326             :                               arg_str,
     327             :                               "timeout_ms",
     328             :                               (0 == tms)
     329             :                               ? NULL
     330             :                               : timeout_str,
     331             :                               NULL);
     332             :   }
     333          11 :   if (NULL == lh->url)
     334             :   {
     335           0 :     GNUNET_free (lh);
     336           0 :     return NULL;
     337             :   }
     338          11 :   eh = TALER_EXCHANGE_curl_easy_get_ (lh->url);
     339          11 :   if (NULL == eh)
     340             :   {
     341           0 :     GNUNET_break (0);
     342           0 :     GNUNET_free (lh->url);
     343           0 :     GNUNET_free (lh);
     344           0 :     return NULL;
     345             :   }
     346          11 :   if (0 != tms)
     347             :   {
     348           0 :     GNUNET_break (CURLE_OK ==
     349             :                   curl_easy_setopt (eh,
     350             :                                     CURLOPT_TIMEOUT_MS,
     351             :                                     (long) (tms + 100L)));
     352             :   }
     353             : 
     354          11 :   job_headers = curl_slist_append (job_headers,
     355             :                                    "Content-Type: application/json");
     356          11 :   if (NULL != if_none_match)
     357             :   {
     358             :     char *hdr;
     359             : 
     360           0 :     GNUNET_asprintf (&hdr,
     361             :                      "%s: %s",
     362             :                      MHD_HTTP_HEADER_IF_NONE_MATCH,
     363             :                      if_none_match);
     364           0 :     job_headers = curl_slist_append (job_headers,
     365             :                                      hdr);
     366           0 :     GNUNET_free (hdr);
     367             :   }
     368          11 :   lh->job = GNUNET_CURL_job_add2 (ctx,
     369             :                                   eh,
     370             :                                   job_headers,
     371             :                                   &handle_kyc_info_finished,
     372             :                                   lh);
     373          11 :   curl_slist_free_all (job_headers);
     374          11 :   return lh;
     375             : }
     376             : 
     377             : 
     378             : void
     379          11 : TALER_EXCHANGE_kyc_info_cancel (struct TALER_EXCHANGE_KycInfoHandle *lh)
     380             : {
     381          11 :   if (NULL != lh->job)
     382             :   {
     383           0 :     GNUNET_CURL_job_cancel (lh->job);
     384           0 :     lh->job = NULL;
     385             :   }
     386             : 
     387          11 :   GNUNET_free (lh->url);
     388          11 :   GNUNET_free (lh);
     389          11 : }
     390             : 
     391             : 
     392             : /* end of exchange_api_kyc_info.c */

Generated by: LCOV version 1.16