exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

exchange_api_get-kyc-info-ACCESS_TOKEN.c (12272B)


      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 parse_kyc_info_ok (struct TALER_EXCHANGE_GetKycInfoHandle *lh,
     97                    const json_t *json)
     98 {
     99   const json_t *jrequirements = NULL;
    100   const json_t *jvoluntary_checks = NULL;
    101   struct TALER_EXCHANGE_GetKycInfoResponse lr = {
    102     .hr.reply = json,
    103     .hr.http_status = MHD_HTTP_OK
    104   };
    105   struct GNUNET_JSON_Specification spec[] = {
    106     GNUNET_JSON_spec_array_const ("requirements",
    107                                   &jrequirements),
    108     GNUNET_JSON_spec_bool ("is_and_combinator",
    109                            &lr.details.ok.is_and_combinator),
    110     GNUNET_JSON_spec_mark_optional (
    111       GNUNET_JSON_spec_object_const ("voluntary_checks",
    112                                      &jvoluntary_checks),
    113       NULL),
    114     GNUNET_JSON_spec_end ()
    115   };
    116 
    117   if (GNUNET_OK !=
    118       GNUNET_JSON_parse (json,
    119                          spec,
    120                          NULL, NULL))
    121   {
    122     GNUNET_break_op (0);
    123     return GNUNET_SYSERR;
    124   }
    125 
    126   lr.details.ok.vci_length = json_object_size (jvoluntary_checks);
    127   lr.details.ok.requirements_length = json_array_size (jrequirements);
    128 
    129   {
    130     struct TALER_EXCHANGE_VoluntaryCheckInformation vci[
    131       GNUNET_NZL (lr.details.ok.vci_length)];
    132     struct TALER_EXCHANGE_RequirementInformation requirements[
    133       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     memset (vci,
    140             0,
    141             sizeof (vci));
    142     memset (requirements,
    143             0,
    144             sizeof (requirements));
    145 
    146     json_array_foreach ((json_t *) jrequirements, off, jreq)
    147     {
    148       struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off];
    149       struct GNUNET_JSON_Specification ispec[] = {
    150         GNUNET_JSON_spec_string ("form",
    151                                  &req->form),
    152         GNUNET_JSON_spec_string ("description",
    153                                  &req->description),
    154         GNUNET_JSON_spec_mark_optional (
    155           GNUNET_JSON_spec_object_const ("description_i18n",
    156                                          &req->description_i18n),
    157           NULL),
    158         GNUNET_JSON_spec_mark_optional (
    159           GNUNET_JSON_spec_string ("id",
    160                                    &req->id),
    161           NULL),
    162         GNUNET_JSON_spec_end ()
    163       };
    164 
    165       if (GNUNET_OK !=
    166           GNUNET_JSON_parse (jreq,
    167                              ispec,
    168                              NULL, NULL))
    169       {
    170         GNUNET_break_op (0);
    171         return GNUNET_SYSERR;
    172       }
    173     }
    174     GNUNET_assert (off == lr.details.ok.requirements_length);
    175 
    176     off = 0;
    177     json_object_foreach ((json_t *) jvoluntary_checks, name, jvc)
    178     {
    179       struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++];
    180       struct GNUNET_JSON_Specification ispec[] = {
    181         GNUNET_JSON_spec_string ("description",
    182                                  &vc->description),
    183         GNUNET_JSON_spec_mark_optional (
    184           GNUNET_JSON_spec_object_const ("description_i18n",
    185                                          &vc->description_i18n),
    186           NULL),
    187         GNUNET_JSON_spec_end ()
    188       };
    189 
    190       vc->name = name;
    191       if (GNUNET_OK !=
    192           GNUNET_JSON_parse (jvc,
    193                              ispec,
    194                              NULL, NULL))
    195       {
    196         GNUNET_break_op (0);
    197         return GNUNET_SYSERR;
    198       }
    199     }
    200     GNUNET_assert (off == lr.details.ok.vci_length);
    201 
    202     lr.details.ok.vci = vci;
    203     lr.details.ok.requirements = requirements;
    204     lh->cb (lh->cb_cls,
    205             &lr);
    206     lh->cb = NULL;
    207     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 handle_kyc_info_finished (void *cls,
    222                           long response_code,
    223                           const void *response)
    224 {
    225   struct TALER_EXCHANGE_GetKycInfoHandle *lh = cls;
    226   const json_t *j = response;
    227   struct TALER_EXCHANGE_GetKycInfoResponse lr = {
    228     .hr.reply = j,
    229     .hr.http_status = (unsigned int) response_code
    230   };
    231 
    232   lh->job = NULL;
    233   switch (response_code)
    234   {
    235   case 0:
    236     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    237     break;
    238   case MHD_HTTP_OK:
    239     if (GNUNET_OK !=
    240         parse_kyc_info_ok (lh,
    241                            j))
    242     {
    243       GNUNET_break_op (0);
    244       lr.hr.http_status = 0;
    245       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    246       break;
    247     }
    248     GNUNET_assert (NULL == lh->cb);
    249     TALER_EXCHANGE_get_kyc_info_cancel (lh);
    250     return;
    251   case MHD_HTTP_NO_CONTENT:
    252     break;
    253   case MHD_HTTP_BAD_REQUEST:
    254     lr.hr.ec = TALER_JSON_get_error_code (j);
    255     lr.hr.hint = TALER_JSON_get_error_hint (j);
    256     break;
    257   case MHD_HTTP_FORBIDDEN:
    258     lr.hr.ec = TALER_JSON_get_error_code (j);
    259     lr.hr.hint = TALER_JSON_get_error_hint (j);
    260     break;
    261   case MHD_HTTP_NOT_FOUND:
    262     lr.hr.ec = TALER_JSON_get_error_code (j);
    263     lr.hr.hint = TALER_JSON_get_error_hint (j);
    264     break;
    265   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    266     lr.hr.ec = TALER_JSON_get_error_code (j);
    267     lr.hr.hint = TALER_JSON_get_error_hint (j);
    268     break;
    269   default:
    270     /* unexpected response code */
    271     GNUNET_break_op (0);
    272     lr.hr.ec = TALER_JSON_get_error_code (j);
    273     lr.hr.hint = TALER_JSON_get_error_hint (j);
    274     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     break;
    279   }
    280   if (NULL != lh->cb)
    281     lh->cb (lh->cb_cls,
    282             &lr);
    283   TALER_EXCHANGE_get_kyc_info_cancel (lh);
    284 }
    285 
    286 
    287 struct TALER_EXCHANGE_GetKycInfoHandle *
    288 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   lh = GNUNET_new (struct TALER_EXCHANGE_GetKycInfoHandle);
    296   lh->ctx = ctx;
    297   lh->base_url = GNUNET_strdup (url);
    298   lh->token = *token;
    299   return lh;
    300 }
    301 
    302 
    303 enum GNUNET_GenericReturnValue
    304 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   for (unsigned int i = 0; i < num_options; i++)
    310   {
    311     const struct TALER_EXCHANGE_GetKycInfoOptionValue *opt = &options[i];
    312 
    313     switch (opt->option)
    314     {
    315     case TALER_EXCHANGE_GET_KYC_INFO_OPTION_END:
    316       return GNUNET_OK;
    317     case TALER_EXCHANGE_GET_KYC_INFO_OPTION_IF_NONE_MATCH:
    318       gkih->if_none_match = opt->details.if_none_match;
    319       break;
    320     case TALER_EXCHANGE_GET_KYC_INFO_OPTION_TIMEOUT:
    321       gkih->timeout = opt->details.timeout;
    322       break;
    323     default:
    324       GNUNET_break (0);
    325       return GNUNET_SYSERR;
    326     }
    327   }
    328   return GNUNET_OK;
    329 }
    330 
    331 
    332 enum TALER_ErrorCode
    333 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   struct curl_slist *job_headers = NULL;
    342 
    343   gkih->cb = cb;
    344   gkih->cb_cls = cb_cls;
    345   tms = (unsigned int) (gkih->timeout.rel_value_us
    346                         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
    347   {
    348     char at_str[sizeof (struct TALER_AccountAccessTokenP) * 2];
    349     char *end;
    350 
    351     end = GNUNET_STRINGS_data_to_string (
    352       &gkih->token,
    353       sizeof (gkih->token),
    354       at_str,
    355       sizeof (at_str));
    356     *end = '\0';
    357     GNUNET_snprintf (arg_str,
    358                      sizeof (arg_str),
    359                      "kyc-info/%s",
    360                      at_str);
    361   }
    362   {
    363     char timeout_str[32];
    364 
    365     GNUNET_snprintf (timeout_str,
    366                      sizeof (timeout_str),
    367                      "%u",
    368                      tms);
    369     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   if (NULL == gkih->url)
    378     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    379   eh = TALER_EXCHANGE_curl_easy_get_ (gkih->url);
    380   if (NULL == eh)
    381   {
    382     GNUNET_break (0);
    383     GNUNET_free (gkih->url);
    384     gkih->url = NULL;
    385     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    386   }
    387   if (0 != tms)
    388   {
    389     GNUNET_break (CURLE_OK ==
    390                   curl_easy_setopt (eh,
    391                                     CURLOPT_TIMEOUT_MS,
    392                                     (long) (tms + 100L)));
    393   }
    394   job_headers = curl_slist_append (job_headers,
    395                                    "Content-Type: application/json");
    396   if (NULL != gkih->if_none_match)
    397   {
    398     char *hdr;
    399 
    400     GNUNET_asprintf (&hdr,
    401                      "%s: %s",
    402                      MHD_HTTP_HEADER_IF_NONE_MATCH,
    403                      gkih->if_none_match);
    404     job_headers = curl_slist_append (job_headers,
    405                                      hdr);
    406     GNUNET_free (hdr);
    407   }
    408   gkih->job = GNUNET_CURL_job_add2 (gkih->ctx,
    409                                     eh,
    410                                     job_headers,
    411                                     &handle_kyc_info_finished,
    412                                     gkih);
    413   curl_slist_free_all (job_headers);
    414   if (NULL == gkih->job)
    415   {
    416     GNUNET_free (gkih->url);
    417     gkih->url = NULL;
    418     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    419   }
    420   return TALER_EC_NONE;
    421 }
    422 
    423 
    424 void
    425 TALER_EXCHANGE_get_kyc_info_cancel (
    426   struct TALER_EXCHANGE_GetKycInfoHandle *gkih)
    427 {
    428   if (NULL != gkih->job)
    429   {
    430     GNUNET_CURL_job_cancel (gkih->job);
    431     gkih->job = NULL;
    432   }
    433   GNUNET_free (gkih->url);
    434   GNUNET_free (gkih->base_url);
    435   GNUNET_free (gkih);
    436 }
    437 
    438 
    439 /* end of exchange_api_get-kyc-info-ACCESS_TOKEN.c */