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-measures.c (20150B)


      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-measures.c
     19  * @brief Implementation of the GET /aml/$OFFICER_PUB/measures 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-measures.h"
     27 #include "exchange_api_handle.h"
     28 #include "taler/taler_signatures.h"
     29 #include "exchange_api_curl_defaults.h"
     30 
     31 
     32 /**
     33  * Scrap buffer of temporary arrays.
     34  */
     35 struct Scrap
     36 {
     37   /**
     38    * Kept in DLL.
     39    */
     40   struct Scrap *next;
     41 
     42   /**
     43    * Kept in DLL.
     44    */
     45   struct Scrap *prev;
     46 
     47   /**
     48    * Pointer to our allocation.
     49    */
     50   const char **ptr;
     51 };
     52 
     53 
     54 /**
     55  * @brief A GET /aml/$OFFICER_PUB/measures Handle
     56  */
     57 struct TALER_EXCHANGE_GetAmlMeasuresHandle
     58 {
     59 
     60   /**
     61    * The base URL of the exchange.
     62    */
     63   char *base_url;
     64 
     65   /**
     66    * The full URL for this request, set during _start.
     67    */
     68   char *url;
     69 
     70   /**
     71    * Handle for the request.
     72    */
     73   struct GNUNET_CURL_Job *job;
     74 
     75   /**
     76    * Function to call with the result.
     77    */
     78   TALER_EXCHANGE_GetAmlMeasuresCallback cb;
     79 
     80   /**
     81    * Closure for @e cb.
     82    */
     83   TALER_EXCHANGE_GET_AML_MEASURES_RESULT_CLOSURE *cb_cls;
     84 
     85   /**
     86    * Reference to the execution context.
     87    */
     88   struct GNUNET_CURL_Context *ctx;
     89 
     90   /**
     91    * Public key of the AML officer.
     92    */
     93   struct TALER_AmlOfficerPublicKeyP officer_pub;
     94 
     95   /**
     96    * Private key of the AML officer (for signing).
     97    */
     98   struct TALER_AmlOfficerPrivateKeyP officer_priv;
     99 
    100   /**
    101    * Head of scrap list.
    102    */
    103   struct Scrap *scrap_head;
    104 
    105   /**
    106    * Tail of scrap list.
    107    */
    108   struct Scrap *scrap_tail;
    109 };
    110 
    111 
    112 /**
    113  * Create array of length @a len in scrap book.
    114  *
    115  * @param[in,out] amh context for allocations
    116  * @param len length of array
    117  * @return scrap array
    118  */
    119 static const char **
    120 make_scrap (
    121   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh,
    122   unsigned int len)
    123 {
    124   struct Scrap *s = GNUNET_new (struct Scrap);
    125 
    126   s->ptr = GNUNET_new_array (len,
    127                              const char *);
    128   GNUNET_CONTAINER_DLL_insert (amh->scrap_head,
    129                                amh->scrap_tail,
    130                                s);
    131   return s->ptr;
    132 }
    133 
    134 
    135 /**
    136  * Free all scrap space.
    137  *
    138  * @param[in,out] amh scrap context
    139  */
    140 static void
    141 free_scrap (struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh)
    142 {
    143   struct Scrap *s;
    144 
    145   while (NULL != (s = amh->scrap_head))
    146   {
    147     GNUNET_CONTAINER_DLL_remove (amh->scrap_head,
    148                                  amh->scrap_tail,
    149                                  s);
    150     GNUNET_free (s->ptr);
    151     GNUNET_free (s);
    152   }
    153 }
    154 
    155 
    156 /**
    157  * Convert JSON array of strings to string array.
    158  *
    159  * @param j JSON array to convert
    160  * @param[out] a array to initialize
    161  * @return true on success
    162  */
    163 static bool
    164 j_to_a (const json_t *j,
    165         const char **a)
    166 {
    167   const json_t *e;
    168   size_t idx;
    169 
    170   json_array_foreach ((json_t *) j, idx, e)
    171   {
    172     if (NULL == (a[idx] = json_string_value (e)))
    173       return false;
    174   }
    175   return true;
    176 }
    177 
    178 
    179 /**
    180  * Parse AML root measures.
    181  *
    182  * @param jroots JSON object with measure data
    183  * @param[out] roots where to write the result
    184  * @return #GNUNET_OK on success
    185  */
    186 static enum GNUNET_GenericReturnValue
    187 parse_aml_roots (
    188   const json_t *jroots,
    189   struct TALER_EXCHANGE_GetAmlMeasuresMeasureInfo *roots)
    190 {
    191   const json_t *obj;
    192   const char *name;
    193   size_t idx = 0;
    194 
    195   json_object_foreach ((json_t *) jroots, name, obj)
    196   {
    197     struct TALER_EXCHANGE_GetAmlMeasuresMeasureInfo *root = &roots[idx++];
    198     struct GNUNET_JSON_Specification spec[] = {
    199       GNUNET_JSON_spec_string ("check_name",
    200                                &root->check_name),
    201       GNUNET_JSON_spec_mark_optional (
    202         GNUNET_JSON_spec_string ("prog_name",
    203                                  &root->prog_name),
    204         NULL),
    205       GNUNET_JSON_spec_mark_optional (
    206         GNUNET_JSON_spec_object_const ("context",
    207                                        &root->context),
    208         NULL),
    209       GNUNET_JSON_spec_mark_optional (
    210         GNUNET_JSON_spec_string ("operation_type",
    211                                  &root->operation_type),
    212         NULL),
    213       GNUNET_JSON_spec_mark_optional (
    214         GNUNET_JSON_spec_bool ("voluntary",
    215                                &root->voluntary),
    216         NULL),
    217       GNUNET_JSON_spec_end ()
    218     };
    219 
    220     if (GNUNET_OK !=
    221         GNUNET_JSON_parse (obj,
    222                            spec,
    223                            NULL,
    224                            NULL))
    225     {
    226       GNUNET_break_op (0);
    227       return GNUNET_SYSERR;
    228     }
    229     root->measure_name = name;
    230   }
    231   return GNUNET_OK;
    232 }
    233 
    234 
    235 /**
    236  * Parse AML programs.
    237  *
    238  * @param[in,out] amh context for allocations
    239  * @param jprogs JSON object with program data
    240  * @param[out] progs where to write the result
    241  * @return #GNUNET_OK on success
    242  */
    243 static enum GNUNET_GenericReturnValue
    244 parse_aml_programs (
    245   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh,
    246   const json_t *jprogs,
    247   struct TALER_EXCHANGE_GetAmlMeasuresProgramRequirement *progs)
    248 {
    249   const json_t *obj;
    250   const char *name;
    251   size_t idx = 0;
    252 
    253   json_object_foreach ((json_t *) jprogs, name, obj)
    254   {
    255     struct TALER_EXCHANGE_GetAmlMeasuresProgramRequirement *prog = &progs[idx++]
    256     ;
    257     const json_t *jcontext;
    258     const json_t *jinputs;
    259     struct GNUNET_JSON_Specification spec[] = {
    260       GNUNET_JSON_spec_string ("description",
    261                                &prog->description),
    262       GNUNET_JSON_spec_array_const ("context",
    263                                     &jcontext),
    264       GNUNET_JSON_spec_array_const ("inputs",
    265                                     &jinputs),
    266       GNUNET_JSON_spec_end ()
    267     };
    268     unsigned int len;
    269     const char **ptr;
    270 
    271     if (GNUNET_OK !=
    272         GNUNET_JSON_parse (obj,
    273                            spec,
    274                            NULL,
    275                            NULL))
    276     {
    277       GNUNET_break_op (0);
    278       return GNUNET_SYSERR;
    279     }
    280     prog->prog_name = name;
    281     prog->contexts_length = json_array_size (jcontext);
    282     prog->inputs_length = json_array_size (jinputs);
    283     len = (unsigned int) (prog->contexts_length + prog->inputs_length);
    284     if ( ((size_t) len) != prog->contexts_length + prog->inputs_length)
    285     {
    286       GNUNET_break_op (0);
    287       return GNUNET_SYSERR;
    288     }
    289     ptr = make_scrap (amh, len);
    290     prog->contexts = ptr;
    291     if (! j_to_a (jcontext, prog->contexts))
    292     {
    293       GNUNET_break_op (0);
    294       return GNUNET_SYSERR;
    295     }
    296     prog->inputs = &ptr[prog->contexts_length];
    297     if (! j_to_a (jinputs, prog->inputs))
    298     {
    299       GNUNET_break_op (0);
    300       return GNUNET_SYSERR;
    301     }
    302   }
    303   return GNUNET_OK;
    304 }
    305 
    306 
    307 /**
    308  * Parse KYC checks.
    309  *
    310  * @param[in,out] amh context for allocations
    311  * @param jchecks JSON object with check data
    312  * @param[out] checks where to write the result
    313  * @return #GNUNET_OK on success
    314  */
    315 static enum GNUNET_GenericReturnValue
    316 parse_aml_checks (
    317   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh,
    318   const json_t *jchecks,
    319   struct TALER_EXCHANGE_GetAmlMeasuresCheckInfo *checks)
    320 {
    321   const json_t *obj;
    322   const char *name;
    323   size_t idx = 0;
    324 
    325   json_object_foreach ((json_t *) jchecks, name, obj)
    326   {
    327     struct TALER_EXCHANGE_GetAmlMeasuresCheckInfo *check = &checks[idx++];
    328     const json_t *jrequires;
    329     const json_t *joutputs;
    330     struct GNUNET_JSON_Specification spec[] = {
    331       GNUNET_JSON_spec_string ("description",
    332                                &check->description),
    333       GNUNET_JSON_spec_mark_optional (
    334         GNUNET_JSON_spec_object_const ("description_i18n",
    335                                        &check->description_i18n),
    336         NULL),
    337       GNUNET_JSON_spec_array_const ("requires",
    338                                     &jrequires),
    339       GNUNET_JSON_spec_array_const ("outputs",
    340                                     &joutputs),
    341       GNUNET_JSON_spec_string ("fallback",
    342                                &check->fallback),
    343       GNUNET_JSON_spec_end ()
    344     };
    345     unsigned int len;
    346     const char **ptr;
    347 
    348     if (GNUNET_OK !=
    349         GNUNET_JSON_parse (obj,
    350                            spec,
    351                            NULL,
    352                            NULL))
    353     {
    354       GNUNET_break_op (0);
    355       return GNUNET_SYSERR;
    356     }
    357     check->check_name = name;
    358     check->requires_length = json_array_size (jrequires);
    359     check->outputs_length = json_array_size (joutputs);
    360     len = (unsigned int) (check->requires_length + check->outputs_length);
    361     if ( ((size_t) len) != check->requires_length + check->outputs_length)
    362     {
    363       GNUNET_break_op (0);
    364       return GNUNET_SYSERR;
    365     }
    366     ptr = make_scrap (amh, len);
    367     check->requires = ptr;
    368     if (! j_to_a (jrequires, check->requires))
    369     {
    370       GNUNET_break_op (0);
    371       return GNUNET_SYSERR;
    372     }
    373     check->outputs = &ptr[check->requires_length];
    374     if (! j_to_a (joutputs, check->outputs))
    375     {
    376       GNUNET_break_op (0);
    377       return GNUNET_SYSERR;
    378     }
    379   }
    380   return GNUNET_OK;
    381 }
    382 
    383 
    384 /**
    385  * Parse default KYC rules from the default_rules array.
    386  *
    387  * @param[in,out] amh context for allocations
    388  * @param jrules JSON array with rule data
    389  * @param[out] rules where to write the result
    390  * @return #GNUNET_OK on success
    391  */
    392 static enum GNUNET_GenericReturnValue
    393 parse_default_rules (
    394   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh,
    395   const json_t *jrules,
    396   struct TALER_EXCHANGE_GetAmlMeasuresKycRule *rules)
    397 {
    398   const json_t *obj;
    399   size_t idx;
    400 
    401   json_array_foreach ((json_t *) jrules, idx, obj)
    402   {
    403     struct TALER_EXCHANGE_GetAmlMeasuresKycRule *rule = &rules[idx];
    404     const json_t *jmeasures;
    405     struct GNUNET_JSON_Specification spec[] = {
    406       TALER_JSON_spec_kycte ("operation_type",
    407                              &rule->operation_type),
    408       GNUNET_JSON_spec_mark_optional (
    409         GNUNET_JSON_spec_string ("rule_name",
    410                                  &rule->rule_name),
    411         NULL),
    412       TALER_JSON_spec_amount_any ("threshold",
    413                                   &rule->threshold),
    414       GNUNET_JSON_spec_relative_time ("timeframe",
    415                                       &rule->timeframe),
    416       GNUNET_JSON_spec_array_const ("measures",
    417                                     &jmeasures),
    418       GNUNET_JSON_spec_mark_optional (
    419         GNUNET_JSON_spec_bool ("exposed",
    420                                &rule->exposed),
    421         NULL),
    422       GNUNET_JSON_spec_mark_optional (
    423         GNUNET_JSON_spec_bool ("is_and_combinator",
    424                                &rule->is_and_combinator),
    425         NULL),
    426       GNUNET_JSON_spec_int64 ("display_priority",
    427                               &rule->display_priority),
    428       GNUNET_JSON_spec_end ()
    429     };
    430 
    431     if (GNUNET_OK !=
    432         GNUNET_JSON_parse (obj,
    433                            spec,
    434                            NULL,
    435                            NULL))
    436     {
    437       GNUNET_break_op (0);
    438       return GNUNET_SYSERR;
    439     }
    440     rule->measures_length = json_array_size (jmeasures);
    441     {
    442       const char **ptr = make_scrap (amh,
    443                                      (unsigned int) rule->measures_length);
    444 
    445       rule->measures = ptr;
    446       if (! j_to_a (jmeasures,
    447                     ptr))
    448       {
    449         GNUNET_break_op (0);
    450         return GNUNET_SYSERR;
    451       }
    452     }
    453   }
    454   return GNUNET_OK;
    455 }
    456 
    457 
    458 /**
    459  * Parse the provided measures data from the "200 OK" response.
    460  *
    461  * @param[in,out] amh handle (callback may be zero'ed out)
    462  * @param json json reply with the data
    463  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
    464  */
    465 static enum GNUNET_GenericReturnValue
    466 parse_get_aml_measures_ok (struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh,
    467                            const json_t *json)
    468 {
    469   struct TALER_EXCHANGE_GetAmlMeasuresResponse lr = {
    470     .hr.reply = json,
    471     .hr.http_status = MHD_HTTP_OK
    472   };
    473   const json_t *jroots;
    474   const json_t *jprograms;
    475   const json_t *jchecks;
    476   const json_t *jdefault_rules = NULL;
    477   struct GNUNET_JSON_Specification spec[] = {
    478     GNUNET_JSON_spec_object_const ("roots",
    479                                    &jroots),
    480     GNUNET_JSON_spec_object_const ("programs",
    481                                    &jprograms),
    482     GNUNET_JSON_spec_object_const ("checks",
    483                                    &jchecks),
    484     GNUNET_JSON_spec_mark_optional (
    485       GNUNET_JSON_spec_array_const ("default_rules",
    486                                     &jdefault_rules),
    487       NULL),
    488     GNUNET_JSON_spec_end ()
    489   };
    490 
    491   if (GNUNET_OK !=
    492       GNUNET_JSON_parse (json,
    493                          spec,
    494                          NULL,
    495                          NULL))
    496   {
    497     GNUNET_break_op (0);
    498     return GNUNET_SYSERR;
    499   }
    500 
    501   lr.details.ok.roots_length = json_object_size (jroots);
    502   lr.details.ok.programs_length = json_object_size (jprograms);
    503   lr.details.ok.checks_length = json_object_size (jchecks);
    504   lr.details.ok.default_rules_length =
    505     (NULL != jdefault_rules) ? json_array_size (jdefault_rules) : 0;
    506 
    507   {
    508     struct TALER_EXCHANGE_GetAmlMeasuresMeasureInfo roots[
    509       GNUNET_NZL (lr.details.ok.roots_length)];
    510     struct TALER_EXCHANGE_GetAmlMeasuresProgramRequirement progs[
    511       GNUNET_NZL (lr.details.ok.programs_length)];
    512     struct TALER_EXCHANGE_GetAmlMeasuresCheckInfo checks[
    513       GNUNET_NZL (lr.details.ok.checks_length)];
    514     struct TALER_EXCHANGE_GetAmlMeasuresKycRule drules[
    515       GNUNET_NZL (lr.details.ok.default_rules_length)];
    516     enum GNUNET_GenericReturnValue ret;
    517 
    518     memset (roots, 0, sizeof (roots));
    519     memset (progs, 0, sizeof (progs));
    520     memset (checks, 0, sizeof (checks));
    521     memset (drules, 0, sizeof (drules));
    522     lr.details.ok.roots = roots;
    523     lr.details.ok.programs = progs;
    524     lr.details.ok.checks = checks;
    525     lr.details.ok.default_rules = drules;
    526 
    527     ret = parse_aml_roots (jroots, roots);
    528     if (GNUNET_OK == ret)
    529       ret = parse_aml_programs (amh, jprograms, progs);
    530     if (GNUNET_OK == ret)
    531       ret = parse_aml_checks (amh, jchecks, checks);
    532     if ( (GNUNET_OK == ret) && (NULL != jdefault_rules) )
    533       ret = parse_default_rules (amh, jdefault_rules, drules);
    534     if (GNUNET_OK == ret)
    535     {
    536       amh->cb (amh->cb_cls,
    537                &lr);
    538       amh->cb = NULL;
    539     }
    540     free_scrap (amh);
    541     return ret;
    542   }
    543 }
    544 
    545 
    546 /**
    547  * Function called when we're done processing the
    548  * HTTP GET /aml/$OFFICER_PUB/measures request.
    549  *
    550  * @param cls the `struct TALER_EXCHANGE_GetAmlMeasuresHandle`
    551  * @param response_code HTTP response code, 0 on error
    552  * @param response parsed JSON result, NULL on error
    553  */
    554 static void
    555 handle_get_aml_measures_finished (void *cls,
    556                                   long response_code,
    557                                   const void *response)
    558 {
    559   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh = cls;
    560   const json_t *j = response;
    561   struct TALER_EXCHANGE_GetAmlMeasuresResponse lr = {
    562     .hr.reply = j,
    563     .hr.http_status = (unsigned int) response_code
    564   };
    565 
    566   amh->job = NULL;
    567   switch (response_code)
    568   {
    569   case 0:
    570     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    571     break;
    572   case MHD_HTTP_OK:
    573     if (GNUNET_OK !=
    574         parse_get_aml_measures_ok (amh,
    575                                    j))
    576     {
    577       GNUNET_break_op (0);
    578       lr.hr.http_status = 0;
    579       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    580       break;
    581     }
    582     GNUNET_assert (NULL == amh->cb);
    583     TALER_EXCHANGE_get_aml_measures_cancel (amh);
    584     return;
    585   case MHD_HTTP_NO_CONTENT:
    586     break;
    587   case MHD_HTTP_BAD_REQUEST:
    588     lr.hr.ec = TALER_JSON_get_error_code (j);
    589     lr.hr.hint = TALER_JSON_get_error_hint (j);
    590     break;
    591   case MHD_HTTP_FORBIDDEN:
    592     lr.hr.ec = TALER_JSON_get_error_code (j);
    593     lr.hr.hint = TALER_JSON_get_error_hint (j);
    594     break;
    595   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    596     lr.hr.ec = TALER_JSON_get_error_code (j);
    597     lr.hr.hint = TALER_JSON_get_error_hint (j);
    598     break;
    599   default:
    600     /* unexpected response code */
    601     GNUNET_break_op (0);
    602     lr.hr.ec = TALER_JSON_get_error_code (j);
    603     lr.hr.hint = TALER_JSON_get_error_hint (j);
    604     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    605                 "Unexpected response code %u/%d for GET AML measures\n",
    606                 (unsigned int) response_code,
    607                 (int) lr.hr.ec);
    608     break;
    609   }
    610   if (NULL != amh->cb)
    611     amh->cb (amh->cb_cls,
    612              &lr);
    613   TALER_EXCHANGE_get_aml_measures_cancel (amh);
    614 }
    615 
    616 
    617 struct TALER_EXCHANGE_GetAmlMeasuresHandle *
    618 TALER_EXCHANGE_get_aml_measures_create (
    619   struct GNUNET_CURL_Context *ctx,
    620   const char *url,
    621   const struct TALER_AmlOfficerPrivateKeyP *officer_priv)
    622 {
    623   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh;
    624 
    625   amh = GNUNET_new (struct TALER_EXCHANGE_GetAmlMeasuresHandle);
    626   amh->ctx = ctx;
    627   amh->base_url = GNUNET_strdup (url);
    628   amh->officer_priv = *officer_priv;
    629   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
    630                                       &amh->officer_pub.eddsa_pub);
    631   return amh;
    632 }
    633 
    634 
    635 enum TALER_ErrorCode
    636 TALER_EXCHANGE_get_aml_measures_start (
    637   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh,
    638   TALER_EXCHANGE_GetAmlMeasuresCallback cb,
    639   TALER_EXCHANGE_GET_AML_MEASURES_RESULT_CLOSURE *cb_cls)
    640 {
    641   CURL *eh;
    642   struct TALER_AmlOfficerSignatureP officer_sig;
    643   char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2 + 32];
    644   struct curl_slist *job_headers = NULL;
    645 
    646   amh->cb = cb;
    647   amh->cb_cls = cb_cls;
    648 
    649   /* Build AML officer signature */
    650   TALER_officer_aml_query_sign (&amh->officer_priv,
    651                                 &officer_sig);
    652 
    653   /* Build the path component: aml/{officer_pub}/measures */
    654   {
    655     char pub_str[sizeof (amh->officer_pub) * 2];
    656     char *end;
    657 
    658     end = GNUNET_STRINGS_data_to_string (
    659       &amh->officer_pub,
    660       sizeof (amh->officer_pub),
    661       pub_str,
    662       sizeof (pub_str));
    663     *end = '\0';
    664     GNUNET_snprintf (arg_str,
    665                      sizeof (arg_str),
    666                      "aml/%s/measures",
    667                      pub_str);
    668   }
    669 
    670   amh->url = TALER_url_join (amh->base_url,
    671                              arg_str,
    672                              NULL);
    673   if (NULL == amh->url)
    674     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    675 
    676   eh = TALER_EXCHANGE_curl_easy_get_ (amh->url);
    677   if (NULL == eh)
    678   {
    679     GNUNET_break (0);
    680     GNUNET_free (amh->url);
    681     amh->url = NULL;
    682     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    683   }
    684 
    685   /* Build job headers with AML officer signature */
    686   {
    687     char *hdr;
    688     char sig_str[sizeof (officer_sig) * 2];
    689     char *end;
    690 
    691     end = GNUNET_STRINGS_data_to_string (
    692       &officer_sig,
    693       sizeof (officer_sig),
    694       sig_str,
    695       sizeof (sig_str));
    696     *end = '\0';
    697 
    698     GNUNET_asprintf (&hdr,
    699                      "%s: %s",
    700                      TALER_AML_OFFICER_SIGNATURE_HEADER,
    701                      sig_str);
    702     job_headers = curl_slist_append (NULL,
    703                                      hdr);
    704     GNUNET_free (hdr);
    705   }
    706 
    707   amh->job = GNUNET_CURL_job_add2 (amh->ctx,
    708                                    eh,
    709                                    job_headers,
    710                                    &handle_get_aml_measures_finished,
    711                                    amh);
    712   curl_slist_free_all (job_headers);
    713 
    714   if (NULL == amh->job)
    715   {
    716     GNUNET_free (amh->url);
    717     amh->url = NULL;
    718     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    719   }
    720   return TALER_EC_NONE;
    721 }
    722 
    723 
    724 void
    725 TALER_EXCHANGE_get_aml_measures_cancel (
    726   struct TALER_EXCHANGE_GetAmlMeasuresHandle *amh)
    727 {
    728   if (NULL != amh->job)
    729   {
    730     GNUNET_CURL_job_cancel (amh->job);
    731     amh->job = NULL;
    732   }
    733   free_scrap (amh);
    734   GNUNET_free (amh->url);
    735   GNUNET_free (amh->base_url);
    736   GNUNET_free (amh);
    737 }
    738 
    739 
    740 /* end of exchange_api_get-aml-OFFICER_PUB-measures.c */