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 (20348B)


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