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.5 % 259 193
Test Date: 2026-10-29 19:05:17 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            2 : 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            2 :     GNUNET_JSON_spec_timestamp ("expiration_time",
     150              :                                 &limits->expiration_time),
     151            2 :     GNUNET_JSON_spec_mark_optional (
     152              :       GNUNET_JSON_spec_string ("successor_measure",
     153              :                                &limits->successor_measure),
     154              :       NULL),
     155            2 :     GNUNET_JSON_spec_array_const ("rules",
     156              :                                   &jrules),
     157            2 :     GNUNET_JSON_spec_mark_optional (
     158              :       GNUNET_JSON_spec_object_const ("custom_measures",
     159              :                                      &jcustom_measures),
     160              :       NULL),
     161            2 :     GNUNET_JSON_spec_end ()
     162              :   };
     163              : 
     164            2 :   if (GNUNET_OK !=
     165            2 :       GNUNET_JSON_parse (jlimits,
     166              :                          spec,
     167              :                          NULL,
     168              :                          NULL))
     169              :   {
     170            0 :     GNUNET_break_op (0);
     171            0 :     return GNUNET_SYSERR;
     172              :   }
     173            2 :   limits->custom_measures = jcustom_measures;
     174              : 
     175              :   {
     176            2 :     size_t rule_count = json_array_size (jrules);
     177            2 :     size_t rule_start = *rule_off;
     178              : 
     179            2 :     limits->rules = &adgh->all_rules[rule_start];
     180            2 :     limits->rules_length = rule_count;
     181              : 
     182              :     {
     183              :       const json_t *jrule;
     184              :       size_t ridx;
     185              : 
     186            9 :       json_array_foreach ((json_t *) jrules, ridx, jrule)
     187              :       {
     188            7 :         struct TALER_EXCHANGE_GetAmlDecisionsKycRule *r
     189            7 :           = &adgh->all_rules[*rule_off];
     190              :         const json_t *jsmeasures;
     191              :         struct GNUNET_JSON_Specification rspec[] = {
     192            7 :           TALER_JSON_spec_kycte ("operation_type",
     193              :                                  &r->operation_type),
     194            7 :           GNUNET_JSON_spec_mark_optional (
     195              :             GNUNET_JSON_spec_string ("rule_name",
     196              :                                      &r->rule_name),
     197              :             NULL),
     198            7 :           TALER_JSON_spec_amount_any ("threshold",
     199              :                                       &r->threshold),
     200            7 :           GNUNET_JSON_spec_relative_time ("timeframe",
     201              :                                           &r->timeframe),
     202            7 :           GNUNET_JSON_spec_array_const ("measures",
     203              :                                         &jsmeasures),
     204            7 :           GNUNET_JSON_spec_mark_optional (
     205              :             GNUNET_JSON_spec_bool ("exposed",
     206              :                                    &r->exposed),
     207              :             NULL),
     208            7 :           GNUNET_JSON_spec_mark_optional (
     209              :             GNUNET_JSON_spec_bool ("is_and_combinator",
     210              :                                    &r->is_and_combinator),
     211              :             NULL),
     212            7 :           GNUNET_JSON_spec_int64 ("display_priority",
     213              :                                   &r->display_priority),
     214            7 :           GNUNET_JSON_spec_end ()
     215              :         };
     216              : 
     217            7 :         if (GNUNET_OK !=
     218            7 :             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            7 :           size_t mlen = json_array_size (jsmeasures);
     229            7 :           size_t mp_start = *mp_off;
     230              : 
     231            7 :           r->measures = &adgh->all_mp[mp_start];
     232            7 :           r->measures_length = mlen;
     233              : 
     234              :           {
     235              :             size_t midx;
     236              :             const json_t *jm;
     237              : 
     238           13 :             json_array_foreach (jsmeasures, midx, jm)
     239              :             {
     240            6 :               const char *sval = json_string_value (jm);
     241              : 
     242            6 :               if (NULL == sval)
     243              :               {
     244            0 :                 GNUNET_break_op (0);
     245            0 :                 return GNUNET_SYSERR;
     246              :               }
     247            6 :               adgh->all_mp[*mp_off] = sval;
     248            6 :               (*mp_off)++;
     249              :             }
     250              :           }
     251              :         }
     252              : 
     253            7 :         (*rule_off)++;
     254              :       }
     255              :     }
     256              :   }
     257              : 
     258            2 :   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            4 :   json_array_foreach ((json_t *) jrecords, idx, obj)
     284              :   {
     285            2 :     struct TALER_EXCHANGE_GetAmlDecisionsDecision *decision = &records_ar[idx];
     286              :     const json_t *jlimits;
     287              :     struct GNUNET_JSON_Specification spec[] = {
     288            2 :       GNUNET_JSON_spec_fixed_auto ("h_payto",
     289              :                                    &decision->h_payto),
     290            2 :       GNUNET_JSON_spec_mark_optional (
     291              :         GNUNET_JSON_spec_string ("full_payto",
     292              :                                  &decision->full_payto),
     293              :         NULL),
     294            2 :       GNUNET_JSON_spec_mark_optional (
     295              :         GNUNET_JSON_spec_bool ("is_wallet",
     296              :                                &decision->is_wallet),
     297              :         NULL),
     298            2 :       GNUNET_JSON_spec_uint64 ("rowid",
     299              :                                &decision->rowid),
     300            2 :       GNUNET_JSON_spec_mark_optional (
     301              :         GNUNET_JSON_spec_string ("justification",
     302              :                                  &decision->justification),
     303              :         NULL),
     304            2 :       GNUNET_JSON_spec_mark_optional (
     305              :         GNUNET_JSON_spec_string ("new_measures",
     306              :                                  &decision->new_measures),
     307              :         NULL),
     308            2 :       GNUNET_JSON_spec_mark_optional (
     309            2 :         GNUNET_JSON_spec_fixed_auto ("decider_pub",
     310              :                                      &decision->decider_pub),
     311              :         NULL),
     312            2 :       GNUNET_JSON_spec_mark_optional (
     313              :         GNUNET_JSON_spec_string ("decider_name",
     314              :                                  &decision->decider_name),
     315              :         NULL),
     316            2 :       GNUNET_JSON_spec_mark_optional (
     317              :         GNUNET_JSON_spec_uint64 ("kyc_attributes_rowid",
     318              :                                  &decision->kyc_attributes_rowid),
     319              :         NULL),
     320            2 :       GNUNET_JSON_spec_timestamp ("decision_time",
     321              :                                   &decision->decision_time),
     322            2 :       GNUNET_JSON_spec_mark_optional (
     323              :         GNUNET_JSON_spec_object_const ("properties",
     324              :                                        &decision->properties),
     325              :         NULL),
     326            2 :       GNUNET_JSON_spec_object_const ("limits",
     327              :                                      &jlimits),
     328            2 :       GNUNET_JSON_spec_bool ("to_investigate",
     329              :                              &decision->to_investigate),
     330            2 :       GNUNET_JSON_spec_bool ("is_active",
     331              :                              &decision->is_active),
     332            2 :       GNUNET_JSON_spec_end ()
     333              :     };
     334              : 
     335            2 :     GNUNET_assert (idx < records_ar_length);
     336            2 :     if (GNUNET_OK !=
     337            2 :         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            2 :     if (GNUNET_OK !=
     347            2 :         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            2 :   if (lr.details.ok.records_length >
     395              :       GNUNET_MAX_MALLOC_CHECKED
     396              :       / sizeof (struct TALER_EXCHANGE_GetAmlDecisionsDecision))
     397              :   {
     398            0 :     GNUNET_break_op (0);
     399            0 :     return GNUNET_SYSERR;
     400              :   }
     401              : 
     402              :   /* First pass: count total rules and measures across all records */
     403              :   {
     404            2 :     size_t total_rules = 0;
     405            2 :     size_t total_measures = 0;
     406              :     const json_t *obj;
     407              :     size_t idx;
     408              : 
     409            4 :     json_array_foreach ((json_t *) jrecords, idx, obj)
     410              :     {
     411            2 :       const json_t *jlimits = json_object_get (obj, "limits");
     412              :       const json_t *jrules;
     413              : 
     414            2 :       if (NULL == jlimits)
     415            0 :         continue;
     416            2 :       jrules = json_object_get (jlimits, "rules");
     417            2 :       if (NULL == jrules)
     418            0 :         continue;
     419            2 :       total_rules += json_array_size (jrules);
     420              : 
     421              :       {
     422              :         const json_t *jrule;
     423              :         size_t ridx;
     424              : 
     425            9 :         json_array_foreach ((json_t *) jrules, ridx, jrule)
     426              :         {
     427            7 :           const json_t *jmeasures = json_object_get (jrule, "measures");
     428              : 
     429            7 :           if (NULL != jmeasures)
     430            7 :             total_measures += json_array_size (jmeasures);
     431              :         }
     432              :       }
     433              :     }
     434              : 
     435            2 :     adgh->all_rules = GNUNET_new_array (
     436              :       GNUNET_NZL (total_rules),
     437              :       struct TALER_EXCHANGE_GetAmlDecisionsKycRule);
     438            2 :     adgh->all_mp = GNUNET_new_array (
     439              :       GNUNET_NZL (total_measures),
     440              :       const char *);
     441              :   }
     442              : 
     443              :   {
     444              :     struct TALER_EXCHANGE_GetAmlDecisionsDecision *records;
     445              :     enum GNUNET_GenericReturnValue ret;
     446              : 
     447            2 :     records = GNUNET_new_array (
     448              :       GNUNET_NZL (lr.details.ok.records_length),
     449              :       struct TALER_EXCHANGE_GetAmlDecisionsDecision);
     450            2 :     lr.details.ok.records = records;
     451            2 :     ret = parse_aml_decisions (adgh,
     452              :                                jrecords,
     453              :                                lr.details.ok.records_length,
     454              :                                records);
     455            2 :     if (GNUNET_OK == ret)
     456              :     {
     457            2 :       adgh->cb (adgh->cb_cls,
     458              :                 &lr);
     459            2 :       adgh->cb = NULL;
     460              :     }
     461            2 :     GNUNET_free (records);
     462            2 :     GNUNET_free (adgh->all_rules);
     463            2 :     adgh->all_rules = NULL;
     464            2 :     GNUNET_free (adgh->all_mp);
     465            2 :     adgh->all_mp = NULL;
     466            2 :     return ret;
     467              :   }
     468              : }
     469              : 
     470              : 
     471              : /**
     472              :  * Function called when we're done processing the
     473              :  * HTTP /aml/$OFFICER_PUB/decisions request.
     474              :  *
     475              :  * @param cls the `struct TALER_EXCHANGE_GetAmlDecisionsHandle`
     476              :  * @param response_code HTTP response code, 0 on error
     477              :  * @param response parsed JSON result, NULL on error
     478              :  */
     479              : static void
     480            4 : handle_get_aml_decisions_finished (void *cls,
     481              :                                    long response_code,
     482              :                                    const void *response)
     483              : {
     484            4 :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh = cls;
     485            4 :   const json_t *j = response;
     486            4 :   struct TALER_EXCHANGE_GetAmlDecisionsResponse lr = {
     487              :     .hr.reply = j,
     488            4 :     .hr.http_status = (unsigned int) response_code
     489              :   };
     490              : 
     491            4 :   adgh->job = NULL;
     492            4 :   switch (response_code)
     493              :   {
     494            0 :   case 0:
     495            0 :     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     496            0 :     break;
     497            2 :   case MHD_HTTP_OK:
     498            2 :     if (GNUNET_OK !=
     499            2 :         parse_get_aml_decisions_ok (adgh,
     500              :                                     j))
     501              :     {
     502            0 :       GNUNET_break_op (0);
     503            0 :       lr.hr.http_status = 0;
     504            0 :       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     505            0 :       break;
     506              :     }
     507            2 :     GNUNET_assert (NULL == adgh->cb);
     508            2 :     TALER_EXCHANGE_get_aml_decisions_cancel (adgh);
     509            2 :     return;
     510            1 :   case MHD_HTTP_NO_CONTENT:
     511            1 :     break;
     512            0 :   case MHD_HTTP_BAD_REQUEST:
     513              : #if DEBUG
     514              :     json_dumpf (j,
     515              :                 stderr,
     516              :                 JSON_INDENT (2));
     517              : #endif
     518            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     519            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     520            0 :     break;
     521            1 :   case MHD_HTTP_FORBIDDEN:
     522            1 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     523            1 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     524            1 :     break;
     525            0 :   case MHD_HTTP_NOT_FOUND:
     526            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     527            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     528            0 :     break;
     529            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     530            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     531            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     532            0 :     break;
     533            0 :   default:
     534              :     /* unexpected response code */
     535            0 :     GNUNET_break_op (0);
     536            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     537            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     538            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     539              :                 "Unexpected response code %u/%d for GET AML decisions\n",
     540              :                 (unsigned int) response_code,
     541              :                 (int) lr.hr.ec);
     542            0 :     break;
     543              :   }
     544            2 :   if (NULL != adgh->cb)
     545            2 :     adgh->cb (adgh->cb_cls,
     546              :               &lr);
     547            2 :   TALER_EXCHANGE_get_aml_decisions_cancel (adgh);
     548              : }
     549              : 
     550              : 
     551              : struct TALER_EXCHANGE_GetAmlDecisionsHandle *
     552            4 : TALER_EXCHANGE_get_aml_decisions_create (
     553              :   struct GNUNET_CURL_Context *ctx,
     554              :   const char *url,
     555              :   const struct TALER_AmlOfficerPrivateKeyP *officer_priv)
     556              : {
     557              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh;
     558              : 
     559            4 :   adgh = GNUNET_new (struct TALER_EXCHANGE_GetAmlDecisionsHandle);
     560            4 :   adgh->ctx = ctx;
     561            4 :   adgh->base_url = GNUNET_strdup (url);
     562            4 :   adgh->officer_priv = *officer_priv;
     563            4 :   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
     564              :                                       &adgh->officer_pub.eddsa_pub);
     565            4 :   adgh->options.limit = -20;
     566            4 :   adgh->options.offset = INT64_MAX;
     567            4 :   adgh->options.active = TALER_EXCHANGE_YNA_ALL;
     568            4 :   adgh->options.investigation = TALER_EXCHANGE_YNA_ALL;
     569            4 :   return adgh;
     570              : }
     571              : 
     572              : 
     573              : enum GNUNET_GenericReturnValue
     574            7 : TALER_EXCHANGE_get_aml_decisions_set_options_ (
     575              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
     576              :   unsigned int num_options,
     577              :   const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue options[])
     578              : {
     579           18 :   for (unsigned int i = 0; i < num_options; i++)
     580              :   {
     581           18 :     const struct TALER_EXCHANGE_GetAmlDecisionsOptionValue *opt = &options[i];
     582              : 
     583           18 :     switch (opt->option)
     584              :     {
     585            7 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_END:
     586            7 :       return GNUNET_OK;
     587            4 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_LIMIT:
     588            4 :       adgh->options.limit = opt->details.limit;
     589            4 :       break;
     590            4 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_OFFSET:
     591            4 :       if (opt->details.offset > INT64_MAX)
     592              :       {
     593            0 :         GNUNET_break (0);
     594            0 :         return GNUNET_NO;
     595              :       }
     596            4 :       adgh->options.offset = opt->details.offset;
     597            4 :       break;
     598            3 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_H_PAYTO:
     599            3 :       adgh->options.h_payto = opt->details.h_payto;
     600            3 :       break;
     601            0 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_ACTIVE:
     602            0 :       adgh->options.active = opt->details.active;
     603            0 :       break;
     604            0 :     case TALER_EXCHANGE_GET_AML_DECISIONS_OPTION_INVESTIGATION:
     605            0 :       adgh->options.investigation = opt->details.investigation;
     606            0 :       break;
     607            0 :     default:
     608            0 :       GNUNET_break (0);
     609            0 :       return GNUNET_SYSERR;
     610              :     }
     611              :   }
     612            0 :   return GNUNET_OK;
     613              : }
     614              : 
     615              : 
     616              : enum TALER_ErrorCode
     617            4 : TALER_EXCHANGE_get_aml_decisions_start (
     618              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh,
     619              :   TALER_EXCHANGE_GetAmlDecisionsCallback cb,
     620              :   TALER_EXCHANGE_GET_AML_DECISIONS_RESULT_CLOSURE *cb_cls)
     621              : {
     622              :   CURL *eh;
     623              :   char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32];
     624            4 :   struct curl_slist *job_headers = NULL;
     625              : 
     626            4 :   adgh->cb = cb;
     627            4 :   adgh->cb_cls = cb_cls;
     628              : 
     629              :   /* Build AML officer signature */
     630            4 :   TALER_officer_aml_query_sign (&adgh->officer_priv,
     631              :                                 &adgh->officer_sig);
     632              : 
     633              :   /* Build the path component: aml/{officer_pub}/decisions */
     634              :   {
     635              :     char pub_str[sizeof (adgh->officer_pub) * 2];
     636              :     char *end;
     637              : 
     638            4 :     end = GNUNET_STRINGS_data_to_string (
     639            4 :       &adgh->officer_pub,
     640              :       sizeof (adgh->officer_pub),
     641              :       pub_str,
     642              :       sizeof (pub_str));
     643            4 :     *end = '\0';
     644            4 :     GNUNET_snprintf (arg_str,
     645              :                      sizeof (arg_str),
     646              :                      "aml/%s/decisions",
     647              :                      pub_str);
     648              :   }
     649              : 
     650              :   /* Build URL with optional query parameters */
     651              :   {
     652              :     char limit_s[24];
     653              :     char offset_s[24];
     654              :     char payto_s[sizeof (*adgh->options.h_payto) * 2 + 1];
     655            4 :     int64_t limit = adgh->options.limit;
     656            4 :     uint64_t offset = adgh->options.offset;
     657            4 :     bool omit_limit = (-20 == limit);
     658            4 :     bool omit_offset = ( ( (limit < 0) && ((uint64_t) INT64_MAX == offset) ) ||
     659            0 :                          ( (limit > 0) && (0 == offset) ) );
     660              : 
     661            4 :     GNUNET_snprintf (limit_s,
     662              :                      sizeof (limit_s),
     663              :                      "%lld",
     664              :                      (long long) limit);
     665            4 :     GNUNET_snprintf (offset_s,
     666              :                      sizeof (offset_s),
     667              :                      "%llu",
     668              :                      (unsigned long long) offset);
     669              : 
     670            4 :     if (NULL != adgh->options.h_payto)
     671              :     {
     672              :       char *end;
     673              : 
     674            3 :       end = GNUNET_STRINGS_data_to_string (
     675            3 :         adgh->options.h_payto,
     676              :         sizeof (*adgh->options.h_payto),
     677              :         payto_s,
     678              :         sizeof (payto_s) - 1);
     679            3 :       *end = '\0';
     680              :     }
     681              : 
     682           12 :     adgh->url = TALER_url_join (
     683            4 :       adgh->base_url,
     684              :       arg_str,
     685              :       "limit",
     686              :       omit_limit ? NULL : limit_s,
     687              :       "offset",
     688              :       omit_offset ? NULL : offset_s,
     689              :       "h_payto",
     690            4 :       (NULL != adgh->options.h_payto) ? payto_s : NULL,
     691              :       "active",
     692            4 :       (TALER_EXCHANGE_YNA_ALL != adgh->options.active)
     693            0 :       ? TALER_yna_to_string (adgh->options.active)
     694              :       : NULL,
     695              :       "investigation",
     696            4 :       (TALER_EXCHANGE_YNA_ALL != adgh->options.investigation)
     697            0 :       ? TALER_yna_to_string (adgh->options.investigation)
     698              :       : NULL,
     699              :       NULL);
     700              :   }
     701              : 
     702            4 :   if (NULL == adgh->url)
     703            0 :     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
     704              : 
     705            4 :   eh = TALER_EXCHANGE_curl_easy_get_ (adgh->url);
     706            4 :   if (NULL == eh)
     707              :   {
     708            0 :     GNUNET_break (0);
     709            0 :     GNUNET_free (adgh->url);
     710            0 :     adgh->url = NULL;
     711            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     712              :   }
     713              : 
     714              :   /* Build job headers with AML officer signature */
     715              :   {
     716              :     char *hdr;
     717              :     char sig_str[sizeof (adgh->officer_sig) * 2];
     718              :     char *end;
     719              : 
     720            4 :     end = GNUNET_STRINGS_data_to_string (
     721            4 :       &adgh->officer_sig,
     722              :       sizeof (adgh->officer_sig),
     723              :       sig_str,
     724              :       sizeof (sig_str));
     725            4 :     *end = '\0';
     726              : 
     727            4 :     GNUNET_asprintf (&hdr,
     728              :                      "%s: %s",
     729              :                      TALER_AML_OFFICER_SIGNATURE_HEADER,
     730              :                      sig_str);
     731            4 :     job_headers = curl_slist_append (NULL,
     732              :                                      hdr);
     733            4 :     GNUNET_free (hdr);
     734              :   }
     735              : 
     736            4 :   adgh->job = GNUNET_CURL_job_add2 (adgh->ctx,
     737              :                                     eh,
     738              :                                     job_headers,
     739              :                                     &handle_get_aml_decisions_finished,
     740              :                                     adgh);
     741            4 :   curl_slist_free_all (job_headers);
     742              : 
     743            4 :   if (NULL == adgh->job)
     744              :   {
     745            0 :     GNUNET_free (adgh->url);
     746            0 :     adgh->url = NULL;
     747            0 :     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
     748              :   }
     749            4 :   return TALER_EC_NONE;
     750              : }
     751              : 
     752              : 
     753              : void
     754            4 : TALER_EXCHANGE_get_aml_decisions_cancel (
     755              :   struct TALER_EXCHANGE_GetAmlDecisionsHandle *adgh)
     756              : {
     757            4 :   if (NULL != adgh->job)
     758              :   {
     759            0 :     GNUNET_CURL_job_cancel (adgh->job);
     760            0 :     adgh->job = NULL;
     761              :   }
     762            4 :   GNUNET_free (adgh->all_rules);
     763            4 :   GNUNET_free (adgh->all_mp);
     764            4 :   GNUNET_free (adgh->url);
     765            4 :   GNUNET_free (adgh->base_url);
     766            4 :   GNUNET_free (adgh);
     767            4 : }
     768              : 
     769              : 
     770              : /* end of exchange_api_get-aml-OFFICER_PUB-decisions.c */
        

Generated by: LCOV version 2.0-1