LCOV - code coverage report
Current view: top level - lib - exchange_api_aml_legitimizations_get.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 62.2 % 172 107
Test Date: 2025-12-28 14:06:02 Functions: 100.0 % 7 7

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2025 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_aml_legitimizations_get.c
      19              :  * @brief Implementation of the GET /aml/$OFFICER_PUB/legitimizations requests
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "taler/platform.h"
      23              : #include <jansson.h>
      24              : #include <microhttpd.h> /* just for HTTP status codes */
      25              : #include <gnunet/gnunet_util_lib.h>
      26              : #include <gnunet/gnunet_json_lib.h>
      27              : #include <gnunet/gnunet_curl_lib.h>
      28              : #include "taler/taler_exchange_service.h"
      29              : #include "taler/taler_json_lib.h"
      30              : #include "exchange_api_handle.h"
      31              : #include "taler/taler_signatures.h"
      32              : #include "exchange_api_curl_defaults.h"
      33              : 
      34              : 
      35              : /**
      36              :  * Handle for an operation to GET /aml/$OFFICER_PUB/legitimizations.
      37              :  */
      38              : struct TALER_EXCHANGE_AmlLegitimizationsGetHandle
      39              : {
      40              : 
      41              :   /**
      42              :    * The exchange base URL for this request.
      43              :    */
      44              :   char *exchange_base_url;
      45              : 
      46              :   /**
      47              :    * Our execution context.
      48              :    */
      49              :   struct GNUNET_CURL_Context *ctx;
      50              : 
      51              :   /**
      52              :    * Handle for the request.
      53              :    */
      54              :   struct GNUNET_CURL_Job *job;
      55              : 
      56              :   /**
      57              :    * Signature of the AML officer.
      58              :    */
      59              :   struct TALER_AmlOfficerSignatureP officer_sig;
      60              : 
      61              :   /**
      62              :    * Public key of the AML officer.
      63              :    */
      64              :   struct TALER_AmlOfficerPublicKeyP officer_pub;
      65              : 
      66              :   /**
      67              :    * Function to call with the result.
      68              :    */
      69              :   TALER_EXCHANGE_AmlLegitimizationsGetCallback cb;
      70              : 
      71              :   /**
      72              :    * Closure for @a cb.
      73              :    */
      74              :   TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *cb_cls;
      75              : 
      76              :   /**
      77              :    * The url for this request.
      78              :    */
      79              :   char *url;
      80              : 
      81              :   /**
      82              :    * HTTP headers for the job.
      83              :    */
      84              :   struct curl_slist *job_headers;
      85              : 
      86              :   /**
      87              :    * Request options.
      88              :    */
      89              :   struct
      90              :   {
      91              :     /**
      92              :      * Limit on number of results.
      93              :      */
      94              :     int64_t limit;
      95              : 
      96              :     /**
      97              :      * Row offset from which to return results.
      98              :      */
      99              :     uint64_t offset;
     100              : 
     101              :     /**
     102              :      * Hash of payto URI to filter by, NULL for no filter.
     103              :      */
     104              :     const struct TALER_NormalizedPaytoHashP *h_payto;
     105              : 
     106              :     /**
     107              :      * Activity filter.
     108              :      */
     109              :     enum TALER_EXCHANGE_YesNoAll active;
     110              : 
     111              :   } options;
     112              : 
     113              : };
     114              : 
     115              : 
     116              : /**
     117              :  * Parse a single measure details entry from JSON.
     118              :  *
     119              :  * @param md_json JSON object to parse
     120              :  * @param[out] md where to store the result
     121              :  * @return #GNUNET_OK on success
     122              :  */
     123              : static enum GNUNET_GenericReturnValue
     124            1 : parse_measure_details (
     125              :   const json_t *md_json,
     126              :   struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails *md)
     127              : {
     128              :   struct GNUNET_JSON_Specification spec[] = {
     129            1 :     GNUNET_JSON_spec_fixed_auto ("h_payto",
     130              :                                  &md->h_payto),
     131            1 :     GNUNET_JSON_spec_uint64 ("rowid",
     132              :                              &md->rowid),
     133            1 :     GNUNET_JSON_spec_timestamp ("start_time",
     134              :                                 &md->start_time),
     135            1 :     GNUNET_JSON_spec_object_const ("measures",
     136              :                                    &md->measures),
     137            1 :     GNUNET_JSON_spec_bool ("is_finished",
     138              :                            &md->is_finished),
     139            1 :     GNUNET_JSON_spec_end ()
     140              :   };
     141              : 
     142            1 :   if (GNUNET_OK !=
     143            1 :       GNUNET_JSON_parse (md_json,
     144              :                          spec,
     145              :                          NULL,
     146              :                          NULL))
     147              :   {
     148            0 :     GNUNET_break_op (0);
     149            0 :     return GNUNET_SYSERR;
     150              :   }
     151            1 :   return GNUNET_OK;
     152              : }
     153              : 
     154              : 
     155              : /**
     156              :  * We received an #MHD_HTTP_OK status code. Handle the JSON
     157              :  * response.
     158              :  *
     159              :  * @param algh handle of the request
     160              :  * @param j JSON response
     161              :  * @return #GNUNET_OK on success
     162              :  */
     163              : static enum GNUNET_GenericReturnValue
     164            1 : handle_aml_legitimizations_get_ok (
     165              :   struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh,
     166              :   const json_t *j)
     167              : {
     168            1 :   struct TALER_EXCHANGE_AmlLegitimizationsGetResult result = {
     169              :     .hr.reply = j,
     170              :     .hr.http_status = MHD_HTTP_OK
     171              :   };
     172              :   const json_t *measures_array;
     173              :   struct GNUNET_JSON_Specification spec[] = {
     174            1 :     GNUNET_JSON_spec_array_const ("measures",
     175              :                                   &measures_array),
     176            1 :     GNUNET_JSON_spec_end ()
     177              :   };
     178              :   struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails *measures;
     179              : 
     180            1 :   if (GNUNET_OK !=
     181            1 :       GNUNET_JSON_parse (j,
     182              :                          spec,
     183              :                          NULL,
     184              :                          NULL))
     185              :   {
     186            0 :     GNUNET_break_op (0);
     187            0 :     return GNUNET_SYSERR;
     188              :   }
     189              : 
     190            1 :   result.details.ok.measures_length = json_array_size (measures_array);
     191            1 :   if (0 == result.details.ok.measures_length)
     192              :   {
     193            0 :     measures = NULL;
     194              :   }
     195              :   else
     196              :   {
     197              :     measures
     198            1 :       = GNUNET_new_array (
     199              :           result.details.ok.measures_length,
     200              :           struct TALER_EXCHANGE_AmlLegitimizationsGetMeasureDetails);
     201              :   }
     202            2 :   for (size_t i = 0; i < result.details.ok.measures_length; i++)
     203              :   {
     204            1 :     const json_t *measure_json = json_array_get (measures_array,
     205              :                                                  i);
     206              : 
     207            1 :     if (GNUNET_OK !=
     208            1 :         parse_measure_details (measure_json,
     209            1 :                                &measures[i]))
     210              :     {
     211            0 :       GNUNET_free (measures);
     212            0 :       return GNUNET_SYSERR;
     213              :     }
     214              :   }
     215            1 :   result.details.ok.measures = measures;
     216            1 :   algh->cb (algh->cb_cls,
     217              :             &result);
     218            1 :   algh->cb = NULL;
     219            1 :   GNUNET_free (measures);
     220            1 :   return GNUNET_OK;
     221              : }
     222              : 
     223              : 
     224              : /**
     225              :  * Function called when we're done processing the
     226              :  * HTTP /aml/$OFFICER_PUB/legitimizations GET request.
     227              :  *
     228              :  * @param cls the `struct TALER_EXCHANGE_AmlLegitimizationsGetHandle`
     229              :  * @param response_code HTTP response code, 0 on error
     230              :  * @param response parsed JSON result, NULL on error
     231              :  */
     232              : static void
     233            1 : handle_aml_legitimizations_get_finished (void *cls,
     234              :                                          long response_code,
     235              :                                          const void *response)
     236              : {
     237            1 :   struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh = cls;
     238            1 :   const json_t *j = response;
     239            1 :   struct TALER_EXCHANGE_AmlLegitimizationsGetResult result = {
     240              :     .hr.reply = j,
     241            1 :     .hr.http_status = (unsigned int) response_code
     242              :   };
     243              : 
     244            1 :   algh->job = NULL;
     245            1 :   switch (response_code)
     246              :   {
     247            0 :   case 0:
     248            0 :     result.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     249            0 :     break;
     250            1 :   case MHD_HTTP_OK:
     251            1 :     if (GNUNET_OK !=
     252            1 :         handle_aml_legitimizations_get_ok (algh,
     253              :                                            j))
     254              :     {
     255            0 :       result.hr.http_status = 0;
     256            0 :       result.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     257              :     }
     258            1 :     break;
     259            0 :   case MHD_HTTP_NO_CONTENT:
     260              :     /* can happen */
     261            0 :     break;
     262            0 :   case MHD_HTTP_BAD_REQUEST:
     263              :     /* This should never happen, either us or the exchange is buggy
     264              :        (or API version conflict); just pass JSON reply to the application */
     265            0 :     result.hr.ec = TALER_JSON_get_error_code (j);
     266            0 :     result.hr.hint = TALER_JSON_get_error_hint (j);
     267            0 :     break;
     268            0 :   case MHD_HTTP_UNAUTHORIZED:
     269              :     /* Invalid officer credentials */
     270            0 :     result.hr.ec = TALER_JSON_get_error_code (j);
     271            0 :     result.hr.hint = TALER_JSON_get_error_hint (j);
     272            0 :     break;
     273            0 :   case MHD_HTTP_FORBIDDEN:
     274              :     /* Officer not authorized for this operation */
     275            0 :     result.hr.ec = TALER_JSON_get_error_code (j);
     276            0 :     result.hr.hint = TALER_JSON_get_error_hint (j);
     277            0 :     break;
     278            0 :   case MHD_HTTP_NOT_FOUND:
     279              :     /* Officer not found */
     280            0 :     result.hr.ec = TALER_JSON_get_error_code (j);
     281            0 :     result.hr.hint = TALER_JSON_get_error_hint (j);
     282            0 :     break;
     283            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     284              :     /* Server had an internal issue; we should retry, but this API
     285              :        leaves this to the application */
     286            0 :     result.hr.ec = TALER_JSON_get_error_code (j);
     287            0 :     result.hr.hint = TALER_JSON_get_error_hint (j);
     288            0 :     break;
     289            0 :   default:
     290              :     /* unexpected response code */
     291            0 :     GNUNET_break_op (0);
     292            0 :     result.hr.ec = TALER_JSON_get_error_code (j);
     293            0 :     result.hr.hint = TALER_JSON_get_error_hint (j);
     294            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     295              :                 "Unexpected response code %u/%d for GET %s\n",
     296              :                 (unsigned int) response_code,
     297              :                 (int) result.hr.ec,
     298              :                 algh->url);
     299            0 :     break;
     300              :   }
     301            1 :   if (NULL != algh->cb)
     302              :   {
     303            0 :     algh->cb (algh->cb_cls,
     304              :               &result);
     305            0 :     algh->cb = NULL;
     306              :   }
     307            1 :   TALER_EXCHANGE_aml_legitimizations_get_cancel (algh);
     308            1 : }
     309              : 
     310              : 
     311              : struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *
     312            1 : TALER_EXCHANGE_aml_legitimizations_get_create (
     313              :   struct GNUNET_CURL_Context *ctx,
     314              :   const char *exchange_base_url,
     315              :   const struct TALER_AmlOfficerPrivateKeyP *officer_priv)
     316              : {
     317              :   struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh;
     318              : 
     319            1 :   algh = GNUNET_new (struct TALER_EXCHANGE_AmlLegitimizationsGetHandle);
     320            1 :   algh->ctx = ctx;
     321            1 :   algh->exchange_base_url = GNUNET_strdup (exchange_base_url);
     322            1 :   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
     323              :                                       &algh->officer_pub.eddsa_pub);
     324            1 :   TALER_officer_aml_query_sign (officer_priv,
     325              :                                 &algh->officer_sig);
     326            1 :   algh->options.limit = -20; /* Default to last 20 entries */
     327            1 :   algh->options.offset = INT64_MAX; /* Default to maximum row id */
     328            1 :   algh->options.active = TALER_EXCHANGE_YNA_ALL; /* Default to all */
     329            1 :   return algh;
     330              : }
     331              : 
     332              : 
     333              : enum GNUNET_GenericReturnValue
     334            1 : TALER_EXCHANGE_aml_legitimizations_get_set_options_ (
     335              :   struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh,
     336              :   unsigned int num_options,
     337              :   const struct TALER_EXCHANGE_AmlLegitimizationsGetOptionValue *options)
     338              : {
     339            3 :   for (unsigned int i = 0; i < num_options; i++)
     340              :   {
     341            3 :     switch (options[i].option)
     342              :     {
     343            1 :     case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_END:
     344            1 :       return GNUNET_OK;
     345            0 :     case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_LIMIT:
     346            0 :       algh->options.limit = options[i].details.limit;
     347            0 :       break;
     348            0 :     case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_OFFSET:
     349            0 :       algh->options.offset = options[i].details.offset;
     350            0 :       break;
     351            1 :     case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_H_PAYTO:
     352            1 :       algh->options.h_payto = options[i].details.h_payto;
     353            1 :       break;
     354            1 :     case TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_OPTION_ACTIVE:
     355            1 :       algh->options.active = options[i].details.active;
     356            1 :       break;
     357            0 :     default:
     358            0 :       GNUNET_break (0);
     359            0 :       return GNUNET_NO;
     360              :     }
     361              :   }
     362            0 :   return GNUNET_OK;
     363              : }
     364              : 
     365              : 
     366              : enum TALER_EXCHANGE_AmlLegitimizationsGetStartError
     367            1 : TALER_EXCHANGE_aml_legitimizations_get_start (
     368              :   struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh,
     369              :   TALER_EXCHANGE_AmlLegitimizationsGetCallback cb,
     370              :   TALER_EXCHANGE__AML_LEGITIMIZATIONS_GET_RESULT_CLOSURE *cb_cls)
     371              : {
     372              :   char officer_pub_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2];
     373              :   char arg_str[sizeof (officer_pub_str) + 64];
     374              :   char limit_str[24];
     375              :   char offset_str[24];
     376              :   char paytoh_str[sizeof (struct TALER_NormalizedPaytoHashP) * 2];
     377              : 
     378            1 :   if (NULL != algh->job)
     379              :   {
     380            0 :     GNUNET_break (0);
     381            0 :     return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_AGAIN;
     382              :   }
     383            1 :   algh->cb = cb;
     384            1 :   algh->cb_cls = cb_cls;
     385            1 :   if (algh->options.offset > INT64_MAX)
     386              :   {
     387            0 :     GNUNET_break (0);
     388            0 :     algh->options.offset = INT64_MAX;
     389              :   }
     390              :   {
     391              :     char *end;
     392              : 
     393            1 :     end = GNUNET_STRINGS_data_to_string (
     394            1 :       &algh->officer_pub,
     395              :       sizeof (algh->officer_pub),
     396              :       officer_pub_str,
     397              :       sizeof (officer_pub_str));
     398            1 :     *end = '\0';
     399              :   }
     400            1 :   if (NULL != algh->options.h_payto)
     401              :   {
     402              :     char *end;
     403              : 
     404            1 :     end = GNUNET_STRINGS_data_to_string (
     405            1 :       algh->options.h_payto,
     406              :       sizeof (struct TALER_NormalizedPaytoHashP),
     407              :       paytoh_str,
     408              :       sizeof (paytoh_str));
     409            1 :     *end = '\0';
     410              :   }
     411              :   /* Build query parameters */
     412            1 :   GNUNET_snprintf (offset_str,
     413              :                    sizeof (offset_str),
     414              :                    "%llu",
     415            1 :                    (unsigned long long) algh->options.offset);
     416            1 :   GNUNET_snprintf (limit_str,
     417              :                    sizeof (limit_str),
     418              :                    "%lld",
     419            1 :                    (long long) algh->options.limit);
     420            1 :   GNUNET_snprintf (arg_str,
     421              :                    sizeof (arg_str),
     422              :                    "aml/%s/legitimizations",
     423              :                    officer_pub_str);
     424            2 :   algh->url = TALER_url_join (algh->exchange_base_url,
     425              :                               arg_str,
     426              :                               "limit",
     427              :                               limit_str,
     428              :                               "offset",
     429            1 :                               ( (algh->options.limit > 0) &&
     430            0 :                                 (0 == algh->options.offset) ) ||
     431            1 :                               ( (algh->options.limit <= 0) &&
     432            1 :                                 (INT64_MAX <= algh->options.offset) )
     433              :                               ? NULL
     434              :                               : offset_str,
     435              :                               "h_payto",
     436            1 :                               NULL == algh->options.h_payto
     437              :                               ? NULL
     438              :                               : paytoh_str,
     439              :                               "active",
     440            1 :                               TALER_EXCHANGE_YNA_ALL == algh->options.active
     441              :                               ? NULL
     442            1 :                               : TALER_yna_to_string (algh->options.active),
     443              :                               NULL);
     444            1 :   if (NULL == algh->url)
     445              :   {
     446            0 :     GNUNET_break (0);
     447            0 :     return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_INTERNAL;
     448              :   }
     449              : 
     450              :   {
     451              :     CURL *eh;
     452              : 
     453            1 :     eh = TALER_EXCHANGE_curl_easy_get_ (algh->url);
     454            1 :     if (NULL == eh)
     455              :     {
     456            0 :       GNUNET_break (0);
     457            0 :       GNUNET_free (algh->url);
     458            0 :       algh->url = NULL;
     459            0 :       return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_E_INTERNAL;
     460              :     }
     461              : 
     462              :     /* Add authentication header for AML officer */
     463              :     {
     464              :       char *hdr;
     465              :       char sig_str[sizeof (algh->officer_sig) * 2];
     466              :       char *end;
     467              : 
     468            1 :       end = GNUNET_STRINGS_data_to_string (
     469            1 :         &algh->officer_sig,
     470              :         sizeof (algh->officer_sig),
     471              :         sig_str,
     472              :         sizeof (sig_str));
     473            1 :       *end = '\0';
     474            1 :       GNUNET_asprintf (&hdr,
     475              :                        "%s: %s",
     476              :                        TALER_AML_OFFICER_SIGNATURE_HEADER,
     477              :                        sig_str);
     478            1 :       algh->job_headers = curl_slist_append (NULL,
     479              :                                              hdr);
     480            1 :       GNUNET_free (hdr);
     481              :     }
     482              :     algh->job
     483            2 :       = GNUNET_CURL_job_add2 (
     484              :           algh->ctx,
     485              :           eh,
     486            1 :           algh->job_headers,
     487              :           &handle_aml_legitimizations_get_finished,
     488              :           algh);
     489              :   }
     490            1 :   return TALER_EXCHANGE_AML_LEGITIMIZATIONS_GET_START_OK;
     491              : }
     492              : 
     493              : 
     494              : void
     495            1 : TALER_EXCHANGE_aml_legitimizations_get_cancel (
     496              :   struct TALER_EXCHANGE_AmlLegitimizationsGetHandle *algh)
     497              : {
     498            1 :   if (NULL != algh->job)
     499              :   {
     500            0 :     GNUNET_CURL_job_cancel (algh->job);
     501            0 :     algh->job = NULL;
     502              :   }
     503            1 :   curl_slist_free_all (algh->job_headers);
     504            1 :   GNUNET_free (algh->exchange_base_url);
     505            1 :   GNUNET_free (algh->url);
     506            1 :   GNUNET_free (algh);
     507            1 : }
     508              : 
     509              : 
     510              : /* end of exchange_api_aml_legitimizations_get.c */
        

Generated by: LCOV version 2.0-1