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 (12119B)


      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 <microhttpd.h> /* just for HTTP status codes */
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <gnunet/gnunet_curl_lib.h>
     25 #include "taler/taler_json_lib.h"
     26 #include "taler/exchange/get-kyc-info-ACCESS_TOKEN.h"
     27 #include "taler/taler_signatures.h"
     28 #include "exchange_api_curl_defaults.h"
     29 
     30 
     31 /**
     32  * @brief A GET /kyc-info/$AT handle
     33  */
     34 struct TALER_EXCHANGE_GetKycInfoHandle
     35 {
     36 
     37   /**
     38    * The base URL for this request.
     39    */
     40   char *base_url;
     41 
     42   /**
     43    * The full URL for this request, set during _start.
     44    */
     45   char *url;
     46 
     47   /**
     48    * Handle for the request.
     49    */
     50   struct GNUNET_CURL_Job *job;
     51 
     52   /**
     53    * Function to call with the result.
     54    */
     55   TALER_EXCHANGE_GetKycInfoCallback cb;
     56 
     57   /**
     58    * Closure for @e cb.
     59    */
     60   TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE *cb_cls;
     61 
     62   /**
     63    * Reference to the execution context.
     64    */
     65   struct GNUNET_CURL_Context *ctx;
     66 
     67   /**
     68    * Access token for the KYC process.
     69    */
     70   struct TALER_AccountAccessTokenP token;
     71 
     72   /**
     73    * ETag from a previous response for conditional requests.
     74    * Borrowed pointer, not owned.
     75    */
     76   const char *if_none_match;
     77 
     78   /**
     79    * Long polling timeout.
     80    */
     81   struct GNUNET_TIME_Relative timeout;
     82 
     83 };
     84 
     85 
     86 /**
     87  * Parse the provided kyc-info data from the "200 OK" response.
     88  *
     89  * @param[in,out] lh handle (callback may be zero'ed out)
     90  * @param json json reply with the data
     91  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     92  */
     93 static enum GNUNET_GenericReturnValue
     94 parse_kyc_info_ok (struct TALER_EXCHANGE_GetKycInfoHandle *lh,
     95                    const json_t *json)
     96 {
     97   const json_t *jrequirements = NULL;
     98   const json_t *jvoluntary_checks = NULL;
     99   struct TALER_EXCHANGE_GetKycInfoResponse lr = {
    100     .hr.reply = json,
    101     .hr.http_status = MHD_HTTP_OK
    102   };
    103   struct GNUNET_JSON_Specification spec[] = {
    104     GNUNET_JSON_spec_array_const ("requirements",
    105                                   &jrequirements),
    106     GNUNET_JSON_spec_bool ("is_and_combinator",
    107                            &lr.details.ok.is_and_combinator),
    108     GNUNET_JSON_spec_mark_optional (
    109       GNUNET_JSON_spec_object_const ("voluntary_checks",
    110                                      &jvoluntary_checks),
    111       NULL),
    112     GNUNET_JSON_spec_end ()
    113   };
    114 
    115   if (GNUNET_OK !=
    116       GNUNET_JSON_parse (json,
    117                          spec,
    118                          NULL, NULL))
    119   {
    120     GNUNET_break_op (0);
    121     return GNUNET_SYSERR;
    122   }
    123 
    124   lr.details.ok.vci_length = json_object_size (jvoluntary_checks);
    125   lr.details.ok.requirements_length = json_array_size (jrequirements);
    126 
    127   {
    128     struct TALER_EXCHANGE_VoluntaryCheckInformation vci[
    129       GNUNET_NZL (lr.details.ok.vci_length)];
    130     struct TALER_EXCHANGE_RequirementInformation requirements[
    131       GNUNET_NZL (lr.details.ok.requirements_length)];
    132     const char *name;
    133     const json_t *jreq;
    134     const json_t *jvc;
    135     size_t off;
    136 
    137     memset (vci,
    138             0,
    139             sizeof (vci));
    140     memset (requirements,
    141             0,
    142             sizeof (requirements));
    143 
    144     json_array_foreach ((json_t *) jrequirements, off, jreq)
    145     {
    146       struct TALER_EXCHANGE_RequirementInformation *req = &requirements[off];
    147       struct GNUNET_JSON_Specification ispec[] = {
    148         GNUNET_JSON_spec_string ("form",
    149                                  &req->form),
    150         GNUNET_JSON_spec_string ("description",
    151                                  &req->description),
    152         GNUNET_JSON_spec_mark_optional (
    153           GNUNET_JSON_spec_object_const ("description_i18n",
    154                                          &req->description_i18n),
    155           NULL),
    156         GNUNET_JSON_spec_mark_optional (
    157           GNUNET_JSON_spec_string ("id",
    158                                    &req->id),
    159           NULL),
    160         GNUNET_JSON_spec_end ()
    161       };
    162 
    163       if (GNUNET_OK !=
    164           GNUNET_JSON_parse (jreq,
    165                              ispec,
    166                              NULL, NULL))
    167       {
    168         GNUNET_break_op (0);
    169         return GNUNET_SYSERR;
    170       }
    171     }
    172     GNUNET_assert (off == lr.details.ok.requirements_length);
    173 
    174     off = 0;
    175     json_object_foreach ((json_t *) jvoluntary_checks, name, jvc)
    176     {
    177       struct TALER_EXCHANGE_VoluntaryCheckInformation *vc = &vci[off++];
    178       struct GNUNET_JSON_Specification ispec[] = {
    179         GNUNET_JSON_spec_string ("description",
    180                                  &vc->description),
    181         GNUNET_JSON_spec_mark_optional (
    182           GNUNET_JSON_spec_object_const ("description_i18n",
    183                                          &vc->description_i18n),
    184           NULL),
    185         GNUNET_JSON_spec_end ()
    186       };
    187 
    188       vc->name = name;
    189       if (GNUNET_OK !=
    190           GNUNET_JSON_parse (jvc,
    191                              ispec,
    192                              NULL, NULL))
    193       {
    194         GNUNET_break_op (0);
    195         return GNUNET_SYSERR;
    196       }
    197     }
    198     GNUNET_assert (off == lr.details.ok.vci_length);
    199 
    200     lr.details.ok.vci = vci;
    201     lr.details.ok.requirements = requirements;
    202     lh->cb (lh->cb_cls,
    203             &lr);
    204     lh->cb = NULL;
    205     return GNUNET_OK;
    206   }
    207 }
    208 
    209 
    210 /**
    211  * Function called when we're done processing the
    212  * HTTP GET /kyc-info/$AT request.
    213  *
    214  * @param cls the `struct TALER_EXCHANGE_GetKycInfoHandle`
    215  * @param response_code HTTP response code, 0 on error
    216  * @param response parsed JSON result, NULL on error
    217  */
    218 static void
    219 handle_kyc_info_finished (void *cls,
    220                           long response_code,
    221                           const void *response)
    222 {
    223   struct TALER_EXCHANGE_GetKycInfoHandle *lh = cls;
    224   const json_t *j = response;
    225   struct TALER_EXCHANGE_GetKycInfoResponse lr = {
    226     .hr.reply = j,
    227     .hr.http_status = (unsigned int) response_code
    228   };
    229 
    230   lh->job = NULL;
    231   switch (response_code)
    232   {
    233   case 0:
    234     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    235     break;
    236   case MHD_HTTP_OK:
    237     if (GNUNET_OK !=
    238         parse_kyc_info_ok (lh,
    239                            j))
    240     {
    241       GNUNET_break_op (0);
    242       lr.hr.http_status = 0;
    243       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    244       break;
    245     }
    246     GNUNET_assert (NULL == lh->cb);
    247     TALER_EXCHANGE_get_kyc_info_cancel (lh);
    248     return;
    249   case MHD_HTTP_NO_CONTENT:
    250     break;
    251   case MHD_HTTP_NOT_MODIFIED:
    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   if (NULL != gkih->if_none_match)
    395   {
    396     char *hdr;
    397 
    398     GNUNET_asprintf (&hdr,
    399                      "%s: %s",
    400                      MHD_HTTP_HEADER_IF_NONE_MATCH,
    401                      gkih->if_none_match);
    402     job_headers = curl_slist_append (job_headers,
    403                                      hdr);
    404     GNUNET_free (hdr);
    405   }
    406   gkih->job = GNUNET_CURL_job_add2 (gkih->ctx,
    407                                     eh,
    408                                     job_headers,
    409                                     &handle_kyc_info_finished,
    410                                     gkih);
    411   curl_slist_free_all (job_headers);
    412   if (NULL == gkih->job)
    413   {
    414     GNUNET_free (gkih->url);
    415     gkih->url = NULL;
    416     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    417   }
    418   return TALER_EC_NONE;
    419 }
    420 
    421 
    422 void
    423 TALER_EXCHANGE_get_kyc_info_cancel (
    424   struct TALER_EXCHANGE_GetKycInfoHandle *gkih)
    425 {
    426   if (NULL != gkih->job)
    427   {
    428     GNUNET_CURL_job_cancel (gkih->job);
    429     gkih->job = NULL;
    430   }
    431   GNUNET_free (gkih->url);
    432   GNUNET_free (gkih->base_url);
    433   GNUNET_free (gkih);
    434 }
    435 
    436 
    437 /* end of exchange_api_get-kyc-info-ACCESS_TOKEN.c */