LCOV - code coverage report
Current view: top level - lib - exchange_api_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 167 0
Test Date: 2026-04-14 15:39:31 Functions: 0.0 % 7 0

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023, 2024, 2025, 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-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c
      19              :  * @brief Implementation of the /aml/$OFFICER_PUB/attributes 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 "exchange_api_handle.h"
      27              : #include "taler/taler_signatures.h"
      28              : #include "exchange_api_curl_defaults.h"
      29              : #include \
      30              :   "taler/exchange/get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.h"
      31              : 
      32              : 
      33              : /**
      34              :  * @brief A GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO Handle
      35              :  */
      36              : struct TALER_EXCHANGE_GetAmlAttributesHandle
      37              : {
      38              : 
      39              :   /**
      40              :    * Base URL of the exchange.
      41              :    */
      42              :   char *base_url;
      43              : 
      44              :   /**
      45              :    * The url for this request.
      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_GetAmlAttributesCallback cb;
      58              : 
      59              :   /**
      60              :    * Closure for @e cb.
      61              :    */
      62              :   TALER_EXCHANGE_GET_AML_ATTRIBUTES_RESULT_CLOSURE *cb_cls;
      63              : 
      64              :   /**
      65              :    * CURL context to use.
      66              :    */
      67              :   struct GNUNET_CURL_Context *ctx;
      68              : 
      69              :   /**
      70              :    * Public key of the AML officer (computed from officer_priv in _create).
      71              :    */
      72              :   struct TALER_AmlOfficerPublicKeyP officer_pub;
      73              : 
      74              :   /**
      75              :    * Private key of the AML officer (stored for signing in _start).
      76              :    */
      77              :   struct TALER_AmlOfficerPrivateKeyP officer_priv;
      78              : 
      79              :   /**
      80              :    * Hash of the normalized payto URI for the account.
      81              :    */
      82              :   struct TALER_NormalizedPaytoHashP h_payto;
      83              : 
      84              :   /**
      85              :    * Options set for this request.
      86              :    */
      87              :   struct
      88              :   {
      89              :     /**
      90              :      * Limit on the number of results (negative = before offset, positive = after).
      91              :      * Default: -20.
      92              :      */
      93              :     int64_t limit;
      94              : 
      95              :     /**
      96              :      * Row offset threshold. Default: UINT64_MAX.
      97              :      */
      98              :     uint64_t offset;
      99              :   } options;
     100              : 
     101              : };
     102              : 
     103              : 
     104              : /**
     105              :  * Parse AML attribute collection events from a JSON array.
     106              :  *
     107              :  * @param jdetails JSON array with AML attribute collection events
     108              :  * @param[out] detail_ar where to write the results
     109              :  * @return #GNUNET_OK on success
     110              :  */
     111              : static enum GNUNET_GenericReturnValue
     112            0 : parse_attributes (
     113              :   const json_t *jdetails,
     114              :   struct TALER_EXCHANGE_GetAmlAttributesCollectionEvent *detail_ar)
     115              : {
     116              :   json_t *obj;
     117              :   size_t idx;
     118              : 
     119            0 :   json_array_foreach (jdetails, idx, obj)
     120              :   {
     121            0 :     struct TALER_EXCHANGE_GetAmlAttributesCollectionEvent *detail
     122            0 :       = &detail_ar[idx];
     123            0 :     bool by_aml_officer = false;
     124              :     struct GNUNET_JSON_Specification spec[] = {
     125            0 :       GNUNET_JSON_spec_uint64 ("rowid",
     126              :                                &detail->rowid),
     127            0 :       GNUNET_JSON_spec_bool ("by_aml_officer",
     128              :                              &by_aml_officer),
     129            0 :       GNUNET_JSON_spec_mark_optional (
     130              :         GNUNET_JSON_spec_object_const ("attributes",
     131              :                                        &detail->attributes),
     132              :         NULL),
     133            0 :       GNUNET_JSON_spec_timestamp ("collection_time",
     134              :                                   &detail->collection_time),
     135            0 :       GNUNET_JSON_spec_end ()
     136              :     };
     137              : 
     138            0 :     detail->by_aml_officer = false;
     139            0 :     detail->attributes = NULL;
     140            0 :     if (GNUNET_OK !=
     141            0 :         GNUNET_JSON_parse (obj,
     142              :                            spec,
     143              :                            NULL,
     144              :                            NULL))
     145              :     {
     146            0 :       GNUNET_break_op (0);
     147            0 :       return GNUNET_SYSERR;
     148              :     }
     149            0 :     detail->by_aml_officer = by_aml_officer;
     150              :   }
     151            0 :   return GNUNET_OK;
     152              : }
     153              : 
     154              : 
     155              : /**
     156              :  * Parse the provided data from the "200 OK" response.
     157              :  *
     158              :  * @param[in,out] aagh handle (callback may be zero'ed out)
     159              :  * @param json json reply with the data
     160              :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     161              :  */
     162              : static enum GNUNET_GenericReturnValue
     163            0 : parse_get_aml_attributes_ok (
     164              :   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh,
     165              :   const json_t *json)
     166              : {
     167            0 :   struct TALER_EXCHANGE_GetAmlAttributesResponse lr = {
     168              :     .hr.reply = json,
     169              :     .hr.http_status = MHD_HTTP_OK
     170              :   };
     171              :   const json_t *jdetails;
     172              :   struct GNUNET_JSON_Specification spec[] = {
     173            0 :     GNUNET_JSON_spec_array_const ("details",
     174              :                                   &jdetails),
     175            0 :     GNUNET_JSON_spec_end ()
     176              :   };
     177              : 
     178            0 :   if (GNUNET_OK !=
     179            0 :       GNUNET_JSON_parse (json,
     180              :                          spec,
     181              :                          NULL,
     182              :                          NULL))
     183              :   {
     184            0 :     GNUNET_break_op (0);
     185            0 :     return GNUNET_SYSERR;
     186              :   }
     187            0 :   lr.details.ok.details_length = json_array_size (jdetails);
     188            0 :   {
     189            0 :     struct TALER_EXCHANGE_GetAmlAttributesCollectionEvent details[
     190            0 :       GNUNET_NZL (lr.details.ok.details_length)];
     191              : 
     192            0 :     memset (details,
     193              :             0,
     194              :             sizeof (details));
     195            0 :     lr.details.ok.details = details;
     196            0 :     if (GNUNET_OK !=
     197            0 :         parse_attributes (jdetails,
     198              :                           details))
     199              :     {
     200            0 :       GNUNET_break_op (0);
     201            0 :       return GNUNET_SYSERR;
     202              :     }
     203            0 :     aagh->cb (aagh->cb_cls,
     204              :               &lr);
     205            0 :     aagh->cb = NULL;
     206              :   }
     207            0 :   return GNUNET_OK;
     208              : }
     209              : 
     210              : 
     211              : /**
     212              :  * Function called when we're done processing the
     213              :  * HTTP GET /aml/$OFFICER_PUB/attributes/$H_NORMALIZED_PAYTO request (new API).
     214              :  *
     215              :  * @param cls the `struct TALER_EXCHANGE_GetAmlAttributesHandle`
     216              :  * @param response_code HTTP response code, 0 on error
     217              :  * @param response parsed JSON result, NULL on error
     218              :  */
     219              : static void
     220            0 : handle_get_aml_attributes_finished (void *cls,
     221              :                                     long response_code,
     222              :                                     const void *response)
     223              : {
     224            0 :   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh = cls;
     225            0 :   const json_t *j = response;
     226            0 :   struct TALER_EXCHANGE_GetAmlAttributesResponse lr = {
     227              :     .hr.reply = j,
     228            0 :     .hr.http_status = (unsigned int) response_code
     229              :   };
     230              : 
     231            0 :   aagh->job = NULL;
     232            0 :   switch (response_code)
     233              :   {
     234            0 :   case 0:
     235            0 :     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     236            0 :     break;
     237            0 :   case MHD_HTTP_OK:
     238            0 :     if (GNUNET_OK !=
     239            0 :         parse_get_aml_attributes_ok (aagh,
     240              :                                      j))
     241              :     {
     242            0 :       GNUNET_break_op (0);
     243            0 :       lr.hr.http_status = 0;
     244            0 :       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     245            0 :       break;
     246              :     }
     247            0 :     GNUNET_assert (NULL == aagh->cb);
     248            0 :     TALER_EXCHANGE_get_aml_attributes_cancel (aagh);
     249            0 :     return;
     250            0 :   case MHD_HTTP_NO_CONTENT:
     251            0 :     break;
     252            0 :   case MHD_HTTP_NOT_IMPLEMENTED:
     253            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     254            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     255            0 :     break;
     256            0 :   case MHD_HTTP_BAD_REQUEST:
     257            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     258            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     259              :     /* This should never happen, either us or the exchange is buggy
     260              :        (or API version conflict); just pass JSON reply to the application */
     261            0 :     break;
     262            0 :   case MHD_HTTP_FORBIDDEN:
     263            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     264            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     265            0 :     break;
     266            0 :   case MHD_HTTP_NOT_FOUND:
     267            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     268            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     269            0 :     break;
     270            0 :   case MHD_HTTP_NOT_ACCEPTABLE:
     271            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     272            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     273            0 :     break;
     274            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     275            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     276            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     277              :     /* Server had an internal issue; we should retry, but this API
     278              :        leaves this to the application */
     279            0 :     break;
     280            0 :   default:
     281              :     /* unexpected response code */
     282            0 :     GNUNET_break_op (0);
     283            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     284            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     285            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     286              :                 "Unexpected response code %u/%d for get AML attributes\n",
     287              :                 (unsigned int) response_code,
     288              :                 (int) lr.hr.ec);
     289            0 :     break;
     290              :   }
     291            0 :   if (NULL != aagh->cb)
     292            0 :     aagh->cb (aagh->cb_cls,
     293              :               &lr);
     294            0 :   TALER_EXCHANGE_get_aml_attributes_cancel (aagh);
     295              : }
     296              : 
     297              : 
     298              : struct TALER_EXCHANGE_GetAmlAttributesHandle *
     299            0 : TALER_EXCHANGE_get_aml_attributes_create (
     300              :   struct GNUNET_CURL_Context *ctx,
     301              :   const char *url,
     302              :   const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
     303              :   const struct TALER_NormalizedPaytoHashP *h_payto)
     304              : {
     305              :   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh;
     306              : 
     307            0 :   aagh = GNUNET_new (struct TALER_EXCHANGE_GetAmlAttributesHandle);
     308            0 :   aagh->ctx = ctx;
     309            0 :   aagh->base_url = GNUNET_strdup (url);
     310            0 :   aagh->h_payto = *h_payto;
     311            0 :   aagh->officer_priv = *officer_priv;
     312            0 :   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
     313              :                                       &aagh->officer_pub.eddsa_pub);
     314            0 :   aagh->options.limit = -20;
     315            0 :   aagh->options.offset = INT64_MAX;
     316            0 :   return aagh;
     317              : }
     318              : 
     319              : 
     320              : enum GNUNET_GenericReturnValue
     321            0 : TALER_EXCHANGE_get_aml_attributes_set_options_ (
     322              :   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh,
     323              :   unsigned int num_options,
     324              :   const struct TALER_EXCHANGE_GetAmlAttributesOptionValue *options)
     325              : {
     326            0 :   for (unsigned int i = 0; i < num_options; i++)
     327              :   {
     328            0 :     const struct TALER_EXCHANGE_GetAmlAttributesOptionValue *opt = &options[i];
     329              : 
     330            0 :     switch (opt->option)
     331              :     {
     332            0 :     case TALER_EXCHANGE_GET_AML_ATTRIBUTES_OPTION_END:
     333            0 :       return GNUNET_OK;
     334            0 :     case TALER_EXCHANGE_GET_AML_ATTRIBUTES_OPTION_LIMIT:
     335            0 :       aagh->options.limit = opt->details.limit;
     336            0 :       break;
     337            0 :     case TALER_EXCHANGE_GET_AML_ATTRIBUTES_OPTION_OFFSET:
     338            0 :       if (opt->details.offset > INT64_MAX)
     339              :       {
     340            0 :         GNUNET_break (0);
     341            0 :         return GNUNET_NO;
     342              :       }
     343            0 :       aagh->options.offset = opt->details.offset;
     344            0 :       break;
     345              :     }
     346              :   }
     347            0 :   return GNUNET_OK;
     348              : }
     349              : 
     350              : 
     351              : enum TALER_ErrorCode
     352            0 : TALER_EXCHANGE_get_aml_attributes_start (
     353              :   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh,
     354              :   TALER_EXCHANGE_GetAmlAttributesCallback cb,
     355              :   TALER_EXCHANGE_GET_AML_ATTRIBUTES_RESULT_CLOSURE *cb_cls)
     356              : {
     357              :   struct TALER_AmlOfficerSignatureP officer_sig;
     358              :   char arg_str[sizeof (aagh->officer_pub) * 2
     359              :                + sizeof (aagh->h_payto) * 2
     360              :                + 32];
     361              :   CURL *eh;
     362              : 
     363            0 :   if (NULL != aagh->job)
     364              :   {
     365            0 :     GNUNET_break (0);
     366            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     367              :   }
     368            0 :   aagh->cb = cb;
     369            0 :   aagh->cb_cls = cb_cls;
     370            0 :   TALER_officer_aml_query_sign (&aagh->officer_priv,
     371              :                                 &officer_sig);
     372              :   {
     373              :     char payto_s[sizeof (aagh->h_payto) * 2];
     374              :     char pub_str[sizeof (aagh->officer_pub) * 2];
     375              :     char *end;
     376              : 
     377            0 :     end = GNUNET_STRINGS_data_to_string (
     378            0 :       &aagh->h_payto,
     379              :       sizeof (aagh->h_payto),
     380              :       payto_s,
     381              :       sizeof (payto_s));
     382            0 :     *end = '\0';
     383            0 :     end = GNUNET_STRINGS_data_to_string (
     384            0 :       &aagh->officer_pub,
     385              :       sizeof (aagh->officer_pub),
     386              :       pub_str,
     387              :       sizeof (pub_str));
     388            0 :     *end = '\0';
     389            0 :     GNUNET_snprintf (arg_str,
     390              :                      sizeof (arg_str),
     391              :                      "aml/%s/attributes/%s",
     392              :                      pub_str,
     393              :                      payto_s);
     394              :   }
     395              :   {
     396              :     char limit_s[24];
     397              :     char offset_s[24];
     398              : 
     399            0 :     GNUNET_snprintf (limit_s,
     400              :                      sizeof (limit_s),
     401              :                      "%lld",
     402            0 :                      (long long) aagh->options.limit);
     403            0 :     GNUNET_snprintf (offset_s,
     404              :                      sizeof (offset_s),
     405              :                      "%llu",
     406            0 :                      (unsigned long long) aagh->options.offset);
     407            0 :     aagh->url = TALER_url_join (aagh->base_url,
     408              :                                 arg_str,
     409              :                                 "limit",
     410              :                                 limit_s,
     411              :                                 "offset",
     412              :                                 offset_s,
     413              :                                 NULL);
     414              :   }
     415            0 :   if (NULL == aagh->url)
     416            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     417            0 :   eh = TALER_EXCHANGE_curl_easy_get_ (aagh->url);
     418            0 :   if (NULL == eh)
     419              :   {
     420            0 :     GNUNET_free (aagh->url);
     421            0 :     aagh->url = NULL;
     422            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     423              :   }
     424              :   {
     425            0 :     struct curl_slist *job_headers = NULL;
     426              :     char *hdr;
     427              :     char sig_str[sizeof (officer_sig) * 2];
     428              :     char *end;
     429              : 
     430            0 :     end = GNUNET_STRINGS_data_to_string (
     431              :       &officer_sig,
     432              :       sizeof (officer_sig),
     433              :       sig_str,
     434              :       sizeof (sig_str));
     435            0 :     *end = '\0';
     436            0 :     GNUNET_asprintf (&hdr,
     437              :                      "%s: %s",
     438              :                      TALER_AML_OFFICER_SIGNATURE_HEADER,
     439              :                      sig_str);
     440            0 :     job_headers = curl_slist_append (NULL,
     441              :                                      hdr);
     442            0 :     GNUNET_free (hdr);
     443            0 :     aagh->job = GNUNET_CURL_job_add2 (aagh->ctx,
     444              :                                       eh,
     445              :                                       job_headers,
     446              :                                       &handle_get_aml_attributes_finished,
     447              :                                       aagh);
     448            0 :     curl_slist_free_all (job_headers);
     449              :   }
     450            0 :   if (NULL == aagh->job)
     451              :   {
     452            0 :     GNUNET_free (aagh->url);
     453            0 :     aagh->url = NULL;
     454            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     455              :   }
     456            0 :   return TALER_EC_NONE;
     457              : }
     458              : 
     459              : 
     460              : void
     461            0 : TALER_EXCHANGE_get_aml_attributes_cancel (
     462              :   struct TALER_EXCHANGE_GetAmlAttributesHandle *aagh)
     463              : {
     464            0 :   if (NULL != aagh->job)
     465              :   {
     466            0 :     GNUNET_CURL_job_cancel (aagh->job);
     467            0 :     aagh->job = NULL;
     468              :   }
     469            0 :   GNUNET_free (aagh->url);
     470            0 :   GNUNET_free (aagh->base_url);
     471            0 :   GNUNET_free (aagh);
     472            0 : }
     473              : 
     474              : 
     475              : /* end of exchange_api_lookup_kyc_attributes.c */
        

Generated by: LCOV version 2.0-1