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

Generated by: LCOV version 2.0-1