exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

exchange_api_get-aml-OFFICER_PUB-decisions.c (21024B)


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