LCOV - code coverage report
Current view: top level - lib - exchange_api_get-aml-OFFICER_PUB-decisions.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 74.0 % 258 191
Test Date: 2026-09-09 15:11:34 Functions: 100.0 % 8 8

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023, 2024, 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-decisions.c
      19              :  * @brief Implementation of the /aml/$OFFICER_PUB/decisions 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-aml-OFFICER_PUB-decisions.h"
      27              : #include "exchange_api_handle.h"
      28              : #include "taler/taler_signatures.h"
      29              : #include "exchange_api_curl_defaults.h"
      30              : 
      31              : #define DEBUG 0
      32              : 
      33              : /**
      34              :  * @brief A GET /aml/$OFFICER_PUB/decisions Handle
      35              :  */
      36              : struct TALER_EXCHANGE_GetAmlDecisionsHandle
      37              : {
      38              : 
      39              :   /**
      40              :    * The base URL of the exchange.
      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_GetAmlDecisionsCallback cb;
      58              : 
      59              :   /**
      60              :    * Closure for @e cb.
      61              :    */
      62              :   TALER_EXCHANGE_GET_AML_DECISIONS_RESULT_CLOSURE *cb_cls;
      63              : 
      64              :   /**
      65              :    * Reference to the execution context.
      66              :    */
      67              :   struct GNUNET_CURL_Context *ctx;
      68              : 
      69              :   /**
      70              :    * Public key of the AML officer.
      71              :    */
      72              :   struct TALER_AmlOfficerPublicKeyP officer_pub;
      73              : 
      74              :   /**
      75              :    * Private key of the AML officer (for signing).
      76              :    */
      77              :   struct TALER_AmlOfficerPrivateKeyP officer_priv;
      78              : 
      79              :   /**
      80              :    * Signature of the AML officer.
      81              :    */
      82              :   struct TALER_AmlOfficerSignatureP officer_sig;
      83              : 
      84              :   /**
      85              :    * Options for the request.
      86              :    */
      87              :   struct
      88              :   {
      89              :     /**
      90              :      * Limit on number of results (-20 by default).
      91              :      */
      92              :     int64_t limit;
      93              : 
      94              :     /**
      95              :      * Row offset threshold (INT64_MAX by default).
      96              :      */
      97              :     uint64_t offset;
      98              : 
      99              :     /**
     100              :      * Optional account filter; NULL if not set.
     101              :      */
     102              :     const struct TALER_NormalizedPaytoHashP *h_payto;
     103              : 
     104              :     /**
     105              :      * Filter for active decisions (YNA_ALL by default).
     106              :      */
     107              :     enum TALER_EXCHANGE_YesNoAll active;
     108              : 
     109              :     /**
     110              :      * Filter for investigation status (YNA_ALL by default).
     111              :      */
     112              :     enum TALER_EXCHANGE_YesNoAll investigation;
     113              :   } options;
     114              : 
     115              :   /**
     116              :    * Flat array of all KYC rules across all decisions (allocated during parse).
     117              :    */
     118              :   struct TALER_EXCHANGE_GetAmlDecisionsKycRule *all_rules;
     119              : 
     120              :   /**
     121              :    * Flat array of all measure string pointers across all rules (allocated during parse).
     122              :    */
     123              :   const char **all_mp;
     124              : 
     125              : };
     126              : 
     127              : 
     128              : /**
     129              :  * Parse the limits/rules object.
     130              :  *
     131              :  * @param[in,out] adgh handle (used for allocation tracking)
     132              :  * @param jlimits JSON object with legitimization rule set data
     133              :  * @param[out] limits where to write the parsed rule set
     134              :  * @param[in,out] rule_off current offset into adgh->all_rules (advanced)
     135              :  * @param[in,out] mp_off current offset into adgh->all_mp (advanced)
     136              :  * @return #GNUNET_OK on success
     137              :  */
     138              : static enum GNUNET_GenericReturnValue
     139            6 : parse_limits (
     140              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
     141              :   const json_t *jlimits,
     142              :   struct TALER_EXCHANGE_GetAmlDecisionsLegitimizationRuleSet *limits,
     143              :   size_t *rule_off,
     144              :   size_t *mp_off)
     145              : {
     146              :   const json_t *jrules;
     147              :   const json_t *jcustom_measures;
     148              :   struct GNUNET_JSON_Specification spec[] = {
     149            6 :     GNUNET_JSON_spec_timestamp ("expiration_time",
     150              :                                 &limits->expiration_time),
     151            6 :     GNUNET_JSON_spec_mark_optional (
     152              :       GNUNET_JSON_spec_string ("successor_measure",
     153              :                                &limits->successor_measure),
     154              :       NULL),
     155            6 :     GNUNET_JSON_spec_array_const ("rules",
     156              :                                   &jrules),
     157            6 :     GNUNET_JSON_spec_mark_optional (
     158              :       GNUNET_JSON_spec_object_const ("custom_measures",
     159              :                                      &jcustom_measures),
     160              :       NULL),
     161            6 :     GNUNET_JSON_spec_end ()
     162              :   };
     163              : 
     164            6 :   if (GNUNET_OK !=
     165            6 :       GNUNET_JSON_parse (jlimits,
     166              :                          spec,
     167              :                          NULL,
     168              :                          NULL))
     169              :   {
     170            0 :     GNUNET_break_op (0);
     171            0 :     return GNUNET_SYSERR;
     172              :   }
     173            6 :   limits->custom_measures = jcustom_measures;
     174              : 
     175              :   {
     176            6 :     size_t rule_count = json_array_size (jrules);
     177            6 :     size_t rule_start = *rule_off;
     178              : 
     179            6 :     limits->rules = &adgh->all_rules[rule_start];
     180            6 :     limits->rules_length = rule_count;
     181              : 
     182              :     {
     183              :       const json_t *jrule;
     184              :       size_t ridx;
     185              : 
     186           37 :       json_array_foreach ((json_t *) jrules, ridx, jrule)
     187              :       {
     188           31 :         struct TALER_EXCHANGE_GetAmlDecisionsKycRule *r
     189           31 :           = &adgh->all_rules[*rule_off];
     190              :         const json_t *jsmeasures;
     191              :         struct GNUNET_JSON_Specification rspec[] = {
     192           31 :           TALER_JSON_spec_kycte ("operation_type",
     193              :                                  &r->operation_type),
     194           31 :           GNUNET_JSON_spec_mark_optional (
     195              :             GNUNET_JSON_spec_string ("rule_name",
     196              :                                      &r->rule_name),
     197              :             NULL),
     198           31 :           TALER_JSON_spec_amount_any ("threshold",
     199              :                                       &r->threshold),
     200           31 :           GNUNET_JSON_spec_relative_time ("timeframe",
     201              :                                           &r->timeframe),
     202           31 :           GNUNET_JSON_spec_array_const ("measures",
     203              :                                         &jsmeasures),
     204           31 :           GNUNET_JSON_spec_mark_optional (
     205              :             GNUNET_JSON_spec_bool ("exposed",
     206              :                                    &r->exposed),
     207              :             NULL),
     208           31 :           GNUNET_JSON_spec_mark_optional (
     209              :             GNUNET_JSON_spec_bool ("is_and_combinator",
     210              :                                    &r->is_and_combinator),
     211              :             NULL),
     212           31 :           GNUNET_JSON_spec_int64 ("display_priority",
     213              :                                   &r->display_priority),
     214           31 :           GNUNET_JSON_spec_end ()
     215              :         };
     216              : 
     217           31 :         if (GNUNET_OK !=
     218           31 :             GNUNET_JSON_parse (jrule,
     219              :                                rspec,
     220              :                                NULL,
     221              :                                NULL))
     222              :         {
     223            0 :           GNUNET_break_op (0);
     224            0 :           return GNUNET_SYSERR;
     225              :         }
     226              : 
     227              :         {
     228           31 :           size_t mlen = json_array_size (jsmeasures);
     229           31 :           size_t mp_start = *mp_off;
     230              : 
     231           31 :           r->measures = &adgh->all_mp[mp_start];
     232           31 :           r->measures_length = mlen;
     233              : 
     234              :           {
     235              :             size_t midx;
     236              :             const json_t *jm;
     237              : 
     238           61 :             json_array_foreach (jsmeasures, midx, jm)
     239              :             {
     240           30 :               const char *sval = json_string_value (jm);
     241              : 
     242           30 :               if (NULL == sval)
     243              :               {
     244            0 :                 GNUNET_break_op (0);
     245            0 :                 return GNUNET_SYSERR;
     246              :               }
     247           30 :               adgh->all_mp[*mp_off] = sval;
     248           30 :               (*mp_off)++;
     249              :             }
     250              :           }
     251              :         }
     252              : 
     253           31 :         (*rule_off)++;
     254              :       }
     255              :     }
     256              :   }
     257              : 
     258            6 :   return GNUNET_OK;
     259              : }
     260              : 
     261              : 
     262              : /**
     263              :  * Parse AML decision records.
     264              :  *
     265              :  * @param[in,out] adgh handle (for allocations)
     266              :  * @param jrecords JSON array of decision records
     267              :  * @param records_ar_length length of @a records_ar
     268              :  * @param[out] records_ar caller-allocated array to fill
     269              :  * @return #GNUNET_OK on success
     270              :  */
     271              : static enum GNUNET_GenericReturnValue
     272            2 : parse_aml_decisions (
     273              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
     274              :   const json_t *jrecords,
     275              :   size_t records_ar_length,
     276              :   struct TALER_EXCHANGE_GetAmlDecisionsDecision *records_ar)
     277              : {
     278            2 :   size_t rule_off = 0;
     279            2 :   size_t mp_off = 0;
     280              :   const json_t *obj;
     281              :   size_t idx;
     282              : 
     283            8 :   json_array_foreach ((json_t *) jrecords, idx, obj)
     284              :   {
     285            6 :     struct TALER_EXCHANGE_GetAmlDecisionsDecision *decision = &records_ar[idx];
     286              :     const json_t *jlimits;
     287              :     struct GNUNET_JSON_Specification spec[] = {
     288            6 :       GNUNET_JSON_spec_fixed_auto ("h_payto",
     289              :                                    &decision->h_payto),
     290            6 :       GNUNET_JSON_spec_mark_optional (
     291              :         GNUNET_JSON_spec_string ("full_payto",
     292              :                                  &decision->full_payto),
     293              :         NULL),
     294            6 :       GNUNET_JSON_spec_mark_optional (
     295              :         GNUNET_JSON_spec_bool ("is_wallet",
     296              :                                &decision->is_wallet),
     297              :         NULL),
     298            6 :       GNUNET_JSON_spec_uint64 ("rowid",
     299              :                                &decision->rowid),
     300            6 :       GNUNET_JSON_spec_mark_optional (
     301              :         GNUNET_JSON_spec_string ("justification",
     302              :                                  &decision->justification),
     303              :         NULL),
     304            6 :       GNUNET_JSON_spec_mark_optional (
     305              :         GNUNET_JSON_spec_string ("new_measures",
     306              :                                  &decision->new_measures),
     307              :         NULL),
     308            6 :       GNUNET_JSON_spec_mark_optional (
     309            6 :         GNUNET_JSON_spec_fixed_auto ("decider_pub",
     310              :                                      &decision->decider_pub),
     311              :         NULL),
     312            6 :       GNUNET_JSON_spec_mark_optional (
     313              :         GNUNET_JSON_spec_string ("decider_name",
     314              :                                  &decision->decider_name),
     315              :         NULL),
     316            6 :       GNUNET_JSON_spec_mark_optional (
     317              :         GNUNET_JSON_spec_uint64 ("kyc_attributes_rowid",
     318              :                                  &decision->kyc_attributes_rowid),
     319              :         NULL),
     320            6 :       GNUNET_JSON_spec_timestamp ("decision_time",
     321              :                                   &decision->decision_time),
     322            6 :       GNUNET_JSON_spec_mark_optional (
     323              :         GNUNET_JSON_spec_object_const ("properties",
     324              :                                        &decision->properties),
     325              :         NULL),
     326            6 :       GNUNET_JSON_spec_object_const ("limits",
     327              :                                      &jlimits),
     328            6 :       GNUNET_JSON_spec_bool ("to_investigate",
     329              :                              &decision->to_investigate),
     330            6 :       GNUNET_JSON_spec_bool ("is_active",
     331              :                              &decision->is_active),
     332            6 :       GNUNET_JSON_spec_end ()
     333              :     };
     334              : 
     335            6 :     GNUNET_assert (idx < records_ar_length);
     336            6 :     if (GNUNET_OK !=
     337            6 :         GNUNET_JSON_parse (obj,
     338              :                            spec,
     339              :                            NULL,
     340              :                            NULL))
     341              :     {
     342            0 :       GNUNET_break_op (0);
     343            0 :       return GNUNET_SYSERR;
     344              :     }
     345              : 
     346            6 :     if (GNUNET_OK !=
     347            6 :         parse_limits (adgh,
     348              :                       jlimits,
     349              :                       &decision->limits,
     350              :                       &rule_off,
     351              :                       &mp_off))
     352              :     {
     353            0 :       GNUNET_break_op (0);
     354            0 :       return GNUNET_SYSERR;
     355              :     }
     356              :   }
     357            2 :   return GNUNET_OK;
     358              : }
     359              : 
     360              : 
     361              : /**
     362              :  * Parse the provided decision data from the "200 OK" response.
     363              :  *
     364              :  * @param[in,out] adgh handle (callback may be zero'ed out)
     365              :  * @param json json reply with the data
     366              :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     367              :  */
     368              : static enum GNUNET_GenericReturnValue
     369            2 : parse_get_aml_decisions_ok (struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
     370              :                             const json_t *json)
     371              : {
     372            2 :   struct TALER_EXCHANGE_GetAmlDecisionsResponse lr = {
     373              :     .hr.reply = json,
     374              :     .hr.http_status = MHD_HTTP_OK
     375              :   };
     376              :   const json_t *jrecords;
     377              :   struct GNUNET_JSON_Specification spec[] = {
     378            2 :     GNUNET_JSON_spec_array_const ("records",
     379              :                                   &jrecords),
     380            2 :     GNUNET_JSON_spec_end ()
     381              :   };
     382              : 
     383            2 :   if (GNUNET_OK !=
     384            2 :       GNUNET_JSON_parse (json,
     385              :                          spec,
     386              :                          NULL,
     387              :                          NULL))
     388              :   {
     389            0 :     GNUNET_break_op (0);
     390            0 :     return GNUNET_SYSERR;
     391              :   }
     392              : 
     393            2 :   lr.details.ok.records_length = json_array_size (jrecords);
     394              : 
     395              :   /* First pass: count total rules and measures across all records */
     396              :   {
     397            2 :     size_t total_rules = 0;
     398            2 :     size_t total_measures = 0;
     399              :     const json_t *obj;
     400              :     size_t idx;
     401              : 
     402            8 :     json_array_foreach ((json_t *) jrecords, idx, obj)
     403              :     {
     404            6 :       const json_t *jlimits = json_object_get (obj, "limits");
     405              :       const json_t *jrules;
     406              : 
     407            6 :       if (NULL == jlimits)
     408            0 :         continue;
     409            6 :       jrules = json_object_get (jlimits, "rules");
     410            6 :       if (NULL == jrules)
     411            0 :         continue;
     412            6 :       total_rules += json_array_size (jrules);
     413              : 
     414              :       {
     415              :         const json_t *jrule;
     416              :         size_t ridx;
     417              : 
     418           37 :         json_array_foreach ((json_t *) jrules, ridx, jrule)
     419              :         {
     420           31 :           const json_t *jmeasures = json_object_get (jrule, "measures");
     421              : 
     422           31 :           if (NULL != jmeasures)
     423           31 :             total_measures += json_array_size (jmeasures);
     424              :         }
     425              :       }
     426              :     }
     427              : 
     428            2 :     adgh->all_rules = GNUNET_new_array (
     429              :       GNUNET_NZL (total_rules),
     430              :       struct TALER_EXCHANGE_GetAmlDecisionsKycRule);
     431            2 :     adgh->all_mp = GNUNET_new_array (
     432              :       GNUNET_NZL (total_measures),
     433              :       const char *);
     434              :   }
     435              : 
     436            2 :   {
     437            2 :     struct TALER_EXCHANGE_GetAmlDecisionsDecision records[
     438            2 :       GNUNET_NZL (lr.details.ok.records_length)];
     439              :     enum GNUNET_GenericReturnValue ret;
     440              : 
     441            2 :     memset (records,
     442              :             0,
     443              :             sizeof (records));
     444            2 :     lr.details.ok.records = records;
     445            2 :     ret = parse_aml_decisions (adgh,
     446              :                                jrecords,
     447              :                                lr.details.ok.records_length,
     448              :                                records);
     449            2 :     if (GNUNET_OK == ret)
     450              :     {
     451            2 :       adgh->cb (adgh->cb_cls,
     452              :                 &lr);
     453            2 :       adgh->cb = NULL;
     454              :     }
     455            2 :     GNUNET_free (adgh->all_rules);
     456            2 :     adgh->all_rules = NULL;
     457            2 :     GNUNET_free (adgh->all_mp);
     458            2 :     adgh->all_mp = NULL;
     459            2 :     return ret;
     460              :   }
     461              : }
     462              : 
     463              : 
     464              : /**
     465              :  * Function called when we're done processing the
     466              :  * HTTP /aml/$OFFICER_PUB/decisions request.
     467              :  *
     468              :  * @param cls the `struct TALER_EXCHANGE_GetAmlDecisionsHandle`
     469              :  * @param response_code HTTP response code, 0 on error
     470              :  * @param response parsed JSON result, NULL on error
     471              :  */
     472              : static void
     473            4 : handle_get_aml_decisions_finished (void *cls,
     474              :                                    long response_code,
     475              :                                    const void *response)
     476              : {
     477            4 :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh = cls;
     478            4 :   const json_t *j = response;
     479            4 :   struct TALER_EXCHANGE_GetAmlDecisionsResponse lr = {
     480              :     .hr.reply = j,
     481            4 :     .hr.http_status = (unsigned int) response_code
     482              :   };
     483              : 
     484            4 :   adgh->job = NULL;
     485            4 :   switch (response_code)
     486              :   {
     487            0 :   case 0:
     488            0 :     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     489            0 :     break;
     490            2 :   case MHD_HTTP_OK:
     491            2 :     if (GNUNET_OK !=
     492            2 :         parse_get_aml_decisions_ok (adgh,
     493              :                                     j))
     494              :     {
     495            0 :       GNUNET_break_op (0);
     496            0 :       lr.hr.http_status = 0;
     497            0 :       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     498            0 :       break;
     499              :     }
     500            2 :     GNUNET_assert (NULL == adgh->cb);
     501            2 :     TALER_EXCHANGE_get_aml_decisions_cancel (adgh);
     502            2 :     return;
     503            1 :   case MHD_HTTP_NO_CONTENT:
     504            1 :     break;
     505            0 :   case MHD_HTTP_BAD_REQUEST:
     506              : #if DEBUG
     507              :     json_dumpf (j,
     508              :                 stderr,
     509              :                 JSON_INDENT (2));
     510              : #endif
     511            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     512            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     513            0 :     break;
     514            1 :   case MHD_HTTP_FORBIDDEN:
     515            1 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     516            1 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     517            1 :     break;
     518            0 :   case MHD_HTTP_NOT_FOUND:
     519            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     520            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     521            0 :     break;
     522            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     523            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     524            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     525            0 :     break;
     526            0 :   default:
     527              :     /* unexpected response code */
     528            0 :     GNUNET_break_op (0);
     529            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     530            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     531            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     532              :                 "Unexpected response code %u/%d for GET AML decisions\n",
     533              :                 (unsigned int) response_code,
     534              :                 (int) lr.hr.ec);
     535            0 :     break;
     536              :   }
     537            2 :   if (NULL != adgh->cb)
     538            2 :     adgh->cb (adgh->cb_cls,
     539              :               &lr);
     540            2 :   TALER_EXCHANGE_get_aml_decisions_cancel (adgh);
     541              : }
     542              : 
     543              : 
     544              : struct TALER_EXCHANGE_GetAmlDecisionsHandle *
     545            4 : TALER_EXCHANGE_get_aml_decisions_create (
     546              :   struct GNUNET_CURL_Context *ctx,
     547              :   const char *url,
     548              :   const struct TALER_AmlOfficerPrivateKeyP *officer_priv)
     549              : {
     550              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh;
     551              : 
     552            4 :   adgh = GNUNET_new (struct TALER_EXCHANGE_GetAmlDecisionsHandle);
     553            4 :   adgh->ctx = ctx;
     554            4 :   adgh->base_url = GNUNET_strdup (url);
     555            4 :   adgh->officer_priv = *officer_priv;
     556            4 :   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
     557              :                                       &adgh->officer_pub.eddsa_pub);
     558            4 :   adgh->options.limit = -20;
     559            4 :   adgh->options.offset = INT64_MAX;
     560            4 :   adgh->options.active = TALER_EXCHANGE_YNA_ALL;
     561            4 :   adgh->options.investigation = TALER_EXCHANGE_YNA_ALL;
     562            4 :   return adgh;
     563              : }
     564              : 
     565              : 
     566              : enum GNUNET_GenericReturnValue
     567            7 : TALER_EXCHANGE_get_aml_decisions_set_options_ (
     568              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
     569              :   unsigned int num_options,
     570              :   const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue options[])
     571              : {
     572           10 :   for (unsigned int i = 0; i < num_options; i++)
     573              :   {
     574           10 :     const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue *opt = &options[i];
     575              : 
     576           10 :     switch (opt->option)
     577              :     {
     578            3 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_END:
     579            3 :       return GNUNET_OK;
     580            0 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_LIMIT:
     581            0 :       adgh->options.limit = opt->details.limit;
     582            0 :       break;
     583            4 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_OFFSET:
     584            4 :       if (opt->details.offset > INT64_MAX)
     585              :       {
     586            4 :         GNUNET_break (0);
     587            4 :         return GNUNET_NO;
     588              :       }
     589            0 :       adgh->options.offset = opt->details.offset;
     590            0 :       break;
     591            3 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_H_PAYTO:
     592            3 :       adgh->options.h_payto = opt->details.h_payto;
     593            3 :       break;
     594            0 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_ACTIVE:
     595            0 :       adgh->options.active = opt->details.active;
     596            0 :       break;
     597            0 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_INVESTIGATION:
     598            0 :       adgh->options.investigation = opt->details.investigation;
     599            0 :       break;
     600            0 :     default:
     601            0 :       GNUNET_break (0);
     602            0 :       return GNUNET_SYSERR;
     603              :     }
     604              :   }
     605            0 :   return GNUNET_OK;
     606              : }
     607              : 
     608              : 
     609              : enum TALER_ErrorCode
     610            4 : TALER_EXCHANGE_get_aml_decisions_start (
     611              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
     612              :   TALER_EXCHANGE_GetAmlDecisionsCallback cb,
     613              :   TALER_EXCHANGE_GET_AML_DECISIONS_RESULT_CLOSURE *cb_cls)
     614              : {
     615              :   CURL *eh;
     616              :   char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32];
     617            4 :   struct curl_slist *job_headers = NULL;
     618              : 
     619            4 :   adgh->cb = cb;
     620            4 :   adgh->cb_cls = cb_cls;
     621              : 
     622              :   /* Build AML officer signature */
     623            4 :   TALER_officer_aml_query_sign (&adgh->officer_priv,
     624              :                                 &adgh->officer_sig);
     625              : 
     626              :   /* Build the path component: aml/{officer_pub}/decisions */
     627              :   {
     628              :     char pub_str[sizeof (adgh->officer_pub) * 2];
     629              :     char *end;
     630              : 
     631            4 :     end = GNUNET_STRINGS_data_to_string (
     632            4 :       &adgh->officer_pub,
     633              :       sizeof (adgh->officer_pub),
     634              :       pub_str,
     635              :       sizeof (pub_str));
     636            4 :     *end = '\0';
     637            4 :     GNUNET_snprintf (arg_str,
     638              :                      sizeof (arg_str),
     639              :                      "aml/%s/decisions",
     640              :                      pub_str);
     641              :   }
     642              : 
     643              :   /* Build URL with optional query parameters */
     644              :   {
     645              :     char limit_s[24];
     646              :     char offset_s[24];
     647              :     char payto_s[sizeof (*adgh->options.h_payto) * 2 + 1];
     648            4 :     int64_t limit = adgh->options.limit;
     649            4 :     uint64_t offset = adgh->options.offset;
     650            4 :     bool omit_limit = (-20 == limit);
     651            4 :     bool omit_offset = ( ( (limit < 0) && ((uint64_t) INT64_MAX == offset) ) ||
     652            0 :                          ( (limit > 0) && (0 == offset) ) );
     653              : 
     654            4 :     GNUNET_snprintf (limit_s,
     655              :                      sizeof (limit_s),
     656              :                      "%lld",
     657              :                      (long long) limit);
     658            4 :     GNUNET_snprintf (offset_s,
     659              :                      sizeof (offset_s),
     660              :                      "%llu",
     661              :                      (unsigned long long) offset);
     662              : 
     663            4 :     if (NULL != adgh->options.h_payto)
     664              :     {
     665              :       char *end;
     666              : 
     667            3 :       end = GNUNET_STRINGS_data_to_string (
     668            3 :         adgh->options.h_payto,
     669              :         sizeof (*adgh->options.h_payto),
     670              :         payto_s,
     671              :         sizeof (payto_s) - 1);
     672            3 :       *end = '\0';
     673              :     }
     674              : 
     675           12 :     adgh->url = TALER_url_join (
     676            4 :       adgh->base_url,
     677              :       arg_str,
     678              :       "limit",
     679              :       omit_limit ? NULL : limit_s,
     680              :       "offset",
     681              :       omit_offset ? NULL : offset_s,
     682              :       "h_payto",
     683            4 :       (NULL != adgh->options.h_payto) ? payto_s : NULL,
     684              :       "active",
     685            4 :       (TALER_EXCHANGE_YNA_ALL != adgh->options.active)
     686            0 :       ? TALER_yna_to_string (adgh->options.active)
     687              :       : NULL,
     688              :       "investigation",
     689            4 :       (TALER_EXCHANGE_YNA_ALL != adgh->options.investigation)
     690            0 :       ? TALER_yna_to_string (adgh->options.investigation)
     691              :       : NULL,
     692              :       NULL);
     693              :   }
     694              : 
     695            4 :   if (NULL == adgh->url)
     696            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     697              : 
     698            4 :   eh = TALER_EXCHANGE_curl_easy_get_ (adgh->url);
     699            4 :   if (NULL == eh)
     700              :   {
     701            0 :     GNUNET_break (0);
     702            0 :     GNUNET_free (adgh->url);
     703            0 :     adgh->url = NULL;
     704            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     705              :   }
     706              : 
     707              :   /* Build job headers with AML officer signature */
     708              :   {
     709              :     char *hdr;
     710              :     char sig_str[sizeof (adgh->officer_sig) * 2];
     711              :     char *end;
     712              : 
     713            4 :     end = GNUNET_STRINGS_data_to_string (
     714            4 :       &adgh->officer_sig,
     715              :       sizeof (adgh->officer_sig),
     716              :       sig_str,
     717              :       sizeof (sig_str));
     718            4 :     *end = '\0';
     719              : 
     720            4 :     GNUNET_asprintf (&hdr,
     721              :                      "%s: %s",
     722              :                      TALER_AML_OFFICER_SIGNATURE_HEADER,
     723              :                      sig_str);
     724            4 :     job_headers = curl_slist_append (NULL,
     725              :                                      hdr);
     726            4 :     GNUNET_free (hdr);
     727              :   }
     728              : 
     729            4 :   adgh->job = GNUNET_CURL_job_add2 (adgh->ctx,
     730              :                                     eh,
     731              :                                     job_headers,
     732              :                                     &handle_get_aml_decisions_finished,
     733              :                                     adgh);
     734            4 :   curl_slist_free_all (job_headers);
     735              : 
     736            4 :   if (NULL == adgh->job)
     737              :   {
     738            0 :     GNUNET_free (adgh->url);
     739            0 :     adgh->url = NULL;
     740            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     741              :   }
     742            4 :   return TALER_EC_NONE;
     743              : }
     744              : 
     745              : 
     746              : void
     747            4 : TALER_EXCHANGE_get_aml_decisions_cancel (
     748              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh)
     749              : {
     750            4 :   if (NULL != adgh->job)
     751              :   {
     752            0 :     GNUNET_CURL_job_cancel (adgh->job);
     753            0 :     adgh->job = NULL;
     754              :   }
     755            4 :   GNUNET_free (adgh->all_rules);
     756            4 :   GNUNET_free (adgh->all_mp);
     757            4 :   GNUNET_free (adgh->url);
     758            4 :   GNUNET_free (adgh->base_url);
     759            4 :   GNUNET_free (adgh);
     760            4 : }
     761              : 
     762              : 
     763              : /* end of exchange_api_get-aml-OFFICER_PUB-decisions.c */
        

Generated by: LCOV version 2.0-1