LCOV - code coverage report
Current view: top level - lib - exchange_api_get-kyc-info-ACCESS_TOKEN.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 52.2 % 161 84
Test Date: 2026-03-10 12:10:57 Functions: 83.3 % 6 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2015-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-info-ACCESS_TOKEN.c
      19              :  * @brief Implementation of the /kyc-info/$AT request
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "taler/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/taler_exchange_service.h"
      27              : #include "taler/taler_json_lib.h"
      28              : #include "taler/taler-exchange/get-kyc-info-ACCESS_TOKEN.h"
      29              : #include "taler/taler_signatures.h"
      30              : #include "exchange_api_curl_defaults.h"
      31              : 
      32              : 
      33              : /**
      34              :  * @brief A GET /kyc-info/$AT handle
      35              :  */
      36              : struct TALER_EXCHANGE_GetKycInfoHandle
      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_GetKycInfoCallback cb;
      58              : 
      59              :   /**
      60              :    * Closure for @e cb.
      61              :    */
      62              :   TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE *cb_cls;
      63              : 
      64              :   /**
      65              :    * Reference to the execution context.
      66              :    */
      67              :   struct GNUNET_CURL_Context *ctx;
      68              : 
      69              :   /**
      70              :    * Access token for the KYC process.
      71              :    */
      72              :   struct TALER_AccountAccessTokenP token;
      73              : 
      74              :   /**
      75              :    * ETag from a previous response for conditional requests.
      76              :    * Borrowed pointer, not owned.
      77              :    */
      78              :   const char *if_none_match;
      79              : 
      80              :   /**
      81              :    * Long polling timeout.
      82              :    */
      83              :   struct GNUNET_TIME_Relative timeout;
      84              : 
      85              : };
      86              : 
      87              : 
      88              : /**
      89              :  * Parse the provided kyc-info data from the "200 OK" response.
      90              :  *
      91              :  * @param[in,out] lh handle (callback may be zero'ed out)
      92              :  * @param json json reply with the data
      93              :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
      94              :  */
      95              : static enum GNUNET_GenericReturnValue
      96           11 : parse_kyc_info_ok (struct TALER_EXCHANGE_GetKycInfoHandle *lh,
      97              :                    const json_t *json)
      98              : {
      99           11 :   const json_t *jrequirements = NULL;
     100           11 :   const json_t *jvoluntary_checks = NULL;
     101           11 :   struct TALER_EXCHANGE_GetKycInfoResponse lr = {
     102              :     .hr.reply = json,
     103              :     .hr.http_status = MHD_HTTP_OK
     104              :   };
     105              :   struct GNUNET_JSON_Specification spec[] = {
     106           11 :     GNUNET_JSON_spec_array_const ("requirements",
     107              :                                   &jrequirements),
     108           11 :     GNUNET_JSON_spec_bool ("is_and_combinator",
     109              :                            &lr.details.ok.is_and_combinator),
     110           11 :     GNUNET_JSON_spec_mark_optional (
     111              :       GNUNET_JSON_spec_object_const ("voluntary_checks",
     112              :                                      &jvoluntary_checks),
     113              :       NULL),
     114           11 :     GNUNET_JSON_spec_end ()
     115              :   };
     116              : 
     117           11 :   if (GNUNET_OK !=
     118           11 :       GNUNET_JSON_parse (json,
     119              :                          spec,
     120              :                          NULL, NULL))
     121              :   {
     122            0 :     GNUNET_break_op (0);
     123            0 :     return GNUNET_SYSERR;
     124              :   }
     125              : 
     126           11 :   lr.details.ok.vci_length = json_object_size (jvoluntary_checks);
     127           11 :   lr.details.ok.requirements_length = json_array_size (jrequirements);
     128              : 
     129           11 :   {
     130           11 :     struct TALER_EXCHANGE_VoluntaryCheckInformation vci[
     131           11 :       GNUNET_NZL (lr.details.ok.vci_length)];
     132           11 :     struct TALER_EXCHANGE_RequirementInformation requirements[
     133           11 :       GNUNET_NZL (lr.details.ok.requirements_length)];
     134              :     const char *name;
     135              :     const json_t *jreq;
     136              :     const json_t *jvc;
     137              :     size_t off;
     138              : 
     139           11 :     memset (vci,
     140              :             0,
     141              :             sizeof (vci));
     142           11 :     memset (requirements,
     143              :             0,
     144              :             sizeof (requirements));
     145              : 
     146           22 :     json_array_foreach ((json_t *) jrequirements, off, jreq)
     147              :     {
     148           11 :       struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off];
     149              :       struct GNUNET_JSON_Specification ispec[] = {
     150           11 :         GNUNET_JSON_spec_string ("form",
     151              :                                  &req->form),
     152           11 :         GNUNET_JSON_spec_string ("description",
     153              :                                  &req->description),
     154           11 :         GNUNET_JSON_spec_mark_optional (
     155              :           GNUNET_JSON_spec_object_const ("description_i18n",
     156              :                                          &req->description_i18n),
     157              :           NULL),
     158           11 :         GNUNET_JSON_spec_mark_optional (
     159              :           GNUNET_JSON_spec_string ("id",
     160              :                                    &req->id),
     161              :           NULL),
     162           11 :         GNUNET_JSON_spec_end ()
     163              :       };
     164              : 
     165           11 :       if (GNUNET_OK !=
     166           11 :           GNUNET_JSON_parse (jreq,
     167              :                              ispec,
     168              :                              NULL, NULL))
     169              :       {
     170            0 :         GNUNET_break_op (0);
     171            0 :         return GNUNET_SYSERR;
     172              :       }
     173              :     }
     174           11 :     GNUNET_assert (off == lr.details.ok.requirements_length);
     175              : 
     176           11 :     off = 0;
     177           11 :     json_object_foreach ((json_t *) jvoluntary_checks, name, jvc)
     178              :     {
     179            0 :       struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++];
     180              :       struct GNUNET_JSON_Specification ispec[] = {
     181            0 :         GNUNET_JSON_spec_string ("description",
     182              :                                  &vc->description),
     183            0 :         GNUNET_JSON_spec_mark_optional (
     184              :           GNUNET_JSON_spec_object_const ("description_i18n",
     185              :                                          &vc->description_i18n),
     186              :           NULL),
     187            0 :         GNUNET_JSON_spec_end ()
     188              :       };
     189              : 
     190            0 :       vc->name = name;
     191            0 :       if (GNUNET_OK !=
     192            0 :           GNUNET_JSON_parse (jvc,
     193              :                              ispec,
     194              :                              NULL, NULL))
     195              :       {
     196            0 :         GNUNET_break_op (0);
     197            0 :         return GNUNET_SYSERR;
     198              :       }
     199              :     }
     200           11 :     GNUNET_assert (off == lr.details.ok.vci_length);
     201              : 
     202           11 :     lr.details.ok.vci = vci;
     203           11 :     lr.details.ok.requirements = requirements;
     204           11 :     lh->cb (lh->cb_cls,
     205              :             &lr);
     206           11 :     lh->cb = NULL;
     207           11 :     return GNUNET_OK;
     208              :   }
     209              : }
     210              : 
     211              : 
     212              : /**
     213              :  * Function called when we're done processing the
     214              :  * HTTP GET /kyc-info/$AT request.
     215              :  *
     216              :  * @param cls the `struct TALER_EXCHANGE_GetKycInfoHandle`
     217              :  * @param response_code HTTP response code, 0 on error
     218              :  * @param response parsed JSON result, NULL on error
     219              :  */
     220              : static void
     221           11 : handle_kyc_info_finished (void *cls,
     222              :                           long response_code,
     223              :                           const void *response)
     224              : {
     225           11 :   struct TALER_EXCHANGE_GetKycInfoHandle *lh = cls;
     226           11 :   const json_t *j = response;
     227           11 :   struct TALER_EXCHANGE_GetKycInfoResponse lr = {
     228              :     .hr.reply = j,
     229           11 :     .hr.http_status = (unsigned int) response_code
     230              :   };
     231              : 
     232           11 :   lh->job = NULL;
     233           11 :   switch (response_code)
     234              :   {
     235            0 :   case 0:
     236            0 :     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     237            0 :     break;
     238           11 :   case MHD_HTTP_OK:
     239           11 :     if (GNUNET_OK !=
     240           11 :         parse_kyc_info_ok (lh,
     241              :                            j))
     242              :     {
     243            0 :       GNUNET_break_op (0);
     244            0 :       lr.hr.http_status = 0;
     245            0 :       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     246            0 :       break;
     247              :     }
     248           11 :     GNUNET_assert (NULL == lh->cb);
     249           11 :     TALER_EXCHANGE_get_kyc_info_cancel (lh);
     250           11 :     return;
     251            0 :   case MHD_HTTP_NO_CONTENT:
     252            0 :     break;
     253            0 :   case MHD_HTTP_BAD_REQUEST:
     254            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     255            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     256            0 :     break;
     257            0 :   case MHD_HTTP_FORBIDDEN:
     258            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     259            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     260            0 :     break;
     261            0 :   case MHD_HTTP_NOT_FOUND:
     262            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     263            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     264            0 :     break;
     265            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     266            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     267            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     268            0 :     break;
     269            0 :   default:
     270              :     /* unexpected response code */
     271            0 :     GNUNET_break_op (0);
     272            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     273            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     274            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     275              :                 "Unexpected response code %u/%d for exchange /kyc-info\n",
     276              :                 (unsigned int) response_code,
     277              :                 (int) lr.hr.ec);
     278            0 :     break;
     279              :   }
     280            0 :   if (NULL != lh->cb)
     281            0 :     lh->cb (lh->cb_cls,
     282              :             &lr);
     283            0 :   TALER_EXCHANGE_get_kyc_info_cancel (lh);
     284              : }
     285              : 
     286              : 
     287              : struct TALER_EXCHANGE_GetKycInfoHandle *
     288           11 : TALER_EXCHANGE_get_kyc_info_create (
     289              :   struct GNUNET_CURL_Context *ctx,
     290              :   const char *url,
     291              :   const struct TALER_AccountAccessTokenP *token)
     292              : {
     293              :   struct TALER_EXCHANGE_GetKycInfoHandle *lh;
     294              : 
     295           11 :   lh = GNUNET_new (struct TALER_EXCHANGE_GetKycInfoHandle);
     296           11 :   lh->ctx = ctx;
     297           11 :   lh->base_url = GNUNET_strdup (url);
     298           11 :   lh->token = *token;
     299           11 :   return lh;
     300              : }
     301              : 
     302              : 
     303              : enum GNUNET_GenericReturnValue
     304            0 : TALER_EXCHANGE_get_kyc_info_set_options_ (
     305              :   struct TALER_EXCHANGE_GetKycInfoHandle *gkih,
     306              :   unsigned int num_options,
     307              :   const struct TALER_EXCHANGE_GetKycInfoOptionValue *options)
     308              : {
     309            0 :   for (unsigned int i = 0; i < num_options; i++)
     310              :   {
     311            0 :     const struct TALER_EXCHANGE_GetKycInfoOptionValue *opt = &options[i];
     312              : 
     313            0 :     switch (opt->option)
     314              :     {
     315            0 :     case TALER_EXCHANGE_GET_KYC_INFO_OPTION_END:
     316            0 :       return GNUNET_OK;
     317            0 :     case TALER_EXCHANGE_GET_KYC_INFO_OPTION_IF_NONE_MATCH:
     318            0 :       gkih->if_none_match = opt->details.if_none_match;
     319            0 :       break;
     320            0 :     case TALER_EXCHANGE_GET_KYC_INFO_OPTION_TIMEOUT:
     321            0 :       gkih->timeout = opt->details.timeout;
     322            0 :       break;
     323            0 :     default:
     324            0 :       GNUNET_break (0);
     325            0 :       return GNUNET_SYSERR;
     326              :     }
     327              :   }
     328            0 :   return GNUNET_OK;
     329              : }
     330              : 
     331              : 
     332              : enum TALER_ErrorCode
     333           11 : TALER_EXCHANGE_get_kyc_info_start (
     334              :   struct TALER_EXCHANGE_GetKycInfoHandle *gkih,
     335              :   TALER_EXCHANGE_GetKycInfoCallback cb,
     336              :   TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE *cb_cls)
     337              : {
     338              :   CURL *eh;
     339              :   char arg_str[sizeof (struct TALER_AccountAccessTokenP) * 2 + 32];
     340              :   unsigned int tms;
     341           11 :   struct curl_slist *job_headers = NULL;
     342              : 
     343           11 :   gkih->cb = cb;
     344           11 :   gkih->cb_cls = cb_cls;
     345           22 :   tms = (unsigned int) (gkih->timeout.rel_value_us
     346           11 :                         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
     347              :   {
     348              :     char at_str[sizeof (struct TALER_AccountAccessTokenP) * 2];
     349              :     char *end;
     350              : 
     351           11 :     end = GNUNET_STRINGS_data_to_string (
     352           11 :       &gkih->token,
     353              :       sizeof (gkih->token),
     354              :       at_str,
     355              :       sizeof (at_str));
     356           11 :     *end = '\0';
     357           11 :     GNUNET_snprintf (arg_str,
     358              :                      sizeof (arg_str),
     359              :                      "kyc-info/%s",
     360              :                      at_str);
     361              :   }
     362              :   {
     363              :     char timeout_str[32];
     364              : 
     365           11 :     GNUNET_snprintf (timeout_str,
     366              :                      sizeof (timeout_str),
     367              :                      "%u",
     368              :                      tms);
     369           11 :     gkih->url = TALER_url_join (gkih->base_url,
     370              :                                 arg_str,
     371              :                                 "timeout_ms",
     372              :                                 (0 == tms)
     373              :                                 ? NULL
     374              :                                 : timeout_str,
     375              :                                 NULL);
     376              :   }
     377           11 :   if (NULL == gkih->url)
     378            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     379           11 :   eh = TALER_EXCHANGE_curl_easy_get_ (gkih->url);
     380           11 :   if (NULL == eh)
     381              :   {
     382            0 :     GNUNET_break (0);
     383            0 :     GNUNET_free (gkih->url);
     384            0 :     gkih->url = NULL;
     385            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     386              :   }
     387           11 :   if (0 != tms)
     388              :   {
     389            0 :     GNUNET_break (CURLE_OK ==
     390              :                   curl_easy_setopt (eh,
     391              :                                     CURLOPT_TIMEOUT_MS,
     392              :                                     (long) (tms + 100L)));
     393              :   }
     394           11 :   job_headers = curl_slist_append (job_headers,
     395              :                                    "Content-Type: application/json");
     396           11 :   if (NULL != gkih->if_none_match)
     397              :   {
     398              :     char *hdr;
     399              : 
     400            0 :     GNUNET_asprintf (&hdr,
     401              :                      "%s: %s",
     402              :                      MHD_HTTP_HEADER_IF_NONE_MATCH,
     403              :                      gkih->if_none_match);
     404            0 :     job_headers = curl_slist_append (job_headers,
     405              :                                      hdr);
     406            0 :     GNUNET_free (hdr);
     407              :   }
     408           11 :   gkih->job = GNUNET_CURL_job_add2 (gkih->ctx,
     409              :                                     eh,
     410              :                                     job_headers,
     411              :                                     &handle_kyc_info_finished,
     412              :                                     gkih);
     413           11 :   curl_slist_free_all (job_headers);
     414           11 :   if (NULL == gkih->job)
     415              :   {
     416            0 :     GNUNET_free (gkih->url);
     417            0 :     gkih->url = NULL;
     418            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     419              :   }
     420           11 :   return TALER_EC_NONE;
     421              : }
     422              : 
     423              : 
     424              : void
     425           11 : TALER_EXCHANGE_get_kyc_info_cancel (
     426              :   struct TALER_EXCHANGE_GetKycInfoHandle *gkih)
     427              : {
     428           11 :   if (NULL != gkih->job)
     429              :   {
     430            0 :     GNUNET_CURL_job_cancel (gkih->job);
     431            0 :     gkih->job = NULL;
     432              :   }
     433           11 :   GNUNET_free (gkih->url);
     434           11 :   GNUNET_free (gkih->base_url);
     435           11 :   GNUNET_free (gkih);
     436           11 : }
     437              : 
     438              : 
     439              : /* end of exchange_api_get-kyc-info-ACCESS_TOKEN.c */
        

Generated by: LCOV version 2.0-1