LCOV - code coverage report
Current view: top level - lib - exchange_api_lookup_aml_decisions.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 69.8 % 199 139
Test Date: 2025-12-28 14:06:02 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023, 2024 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_lookup_aml_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 "exchange_api_handle.h"
      29              : #include "taler/taler_signatures.h"
      30              : #include "exchange_api_curl_defaults.h"
      31              : 
      32              : 
      33              : /**
      34              :  * @brief A GET /aml/$OFFICER_PUB/decisions Handle
      35              :  */
      36              : struct TALER_EXCHANGE_LookupAmlDecisions
      37              : {
      38              : 
      39              :   /**
      40              :    * The url for this request.
      41              :    */
      42              :   char *url;
      43              : 
      44              :   /**
      45              :    * Handle for the request.
      46              :    */
      47              :   struct GNUNET_CURL_Job *job;
      48              : 
      49              :   /**
      50              :    * Function to call with the result.
      51              :    */
      52              :   TALER_EXCHANGE_LookupAmlDecisionsCallback decisions_cb;
      53              : 
      54              :   /**
      55              :    * Closure for @e cb.
      56              :    */
      57              :   void *decisions_cb_cls;
      58              : 
      59              :   /**
      60              :    * HTTP headers for the job.
      61              :    */
      62              :   struct curl_slist *job_headers;
      63              : 
      64              :   /**
      65              :    * Array with measure information.
      66              :    */
      67              :   struct TALER_EXCHANGE_MeasureInformation *mip;
      68              : 
      69              :   /**
      70              :    * Array with rule information.
      71              :    */
      72              :   struct TALER_EXCHANGE_KycRule *rp;
      73              : 
      74              :   /**
      75              :    * Array with all the measures (of all the rules!).
      76              :    */
      77              :   const char **mp;
      78              : };
      79              : 
      80              : 
      81              : /**
      82              :  * Parse AML limits array.
      83              :  *
      84              :  * @param[in,out] lh handle to use for allocations
      85              :  * @param jlimits JSON array with AML rules
      86              :  * @param[out] ds where to write the result
      87              :  * @return #GNUNET_OK on success
      88              :  */
      89              : static enum GNUNET_GenericReturnValue
      90            2 : parse_limits (struct TALER_EXCHANGE_LookupAmlDecisions *lh,
      91              :               const json_t *jlimits,
      92              :               struct TALER_EXCHANGE_AmlDecision *ds)
      93              : {
      94            2 :   struct TALER_EXCHANGE_LegitimizationRuleSet *limits
      95              :     = &ds->limits;
      96              :   const json_t *jrules;
      97              :   const json_t *jmeasures;
      98              :   size_t mip_len;
      99              :   size_t rule_len;
     100              :   size_t total;
     101              :   struct GNUNET_JSON_Specification spec[] = {
     102            2 :     GNUNET_JSON_spec_timestamp ("expiration_time",
     103              :                                 &limits->expiration_time),
     104            2 :     GNUNET_JSON_spec_mark_optional (
     105              :       GNUNET_JSON_spec_string ("successor_measure",
     106              :                                &limits->successor_measure),
     107              :       NULL),
     108            2 :     GNUNET_JSON_spec_array_const ("rules",
     109              :                                   &jrules),
     110            2 :     GNUNET_JSON_spec_object_const ("custom_measures",
     111              :                                    &jmeasures),
     112            2 :     GNUNET_JSON_spec_end ()
     113              :   };
     114              : 
     115            2 :   if (GNUNET_OK !=
     116            2 :       GNUNET_JSON_parse (jlimits,
     117              :                          spec,
     118              :                          NULL,
     119              :                          NULL))
     120              :   {
     121            0 :     GNUNET_break_op (0);
     122            0 :     return GNUNET_SYSERR;
     123              :   }
     124              : 
     125            2 :   mip_len = json_object_size (jmeasures);
     126            2 :   lh->mip = GNUNET_new_array (mip_len,
     127              :                               struct TALER_EXCHANGE_MeasureInformation);
     128            2 :   limits->measures = lh->mip;
     129            2 :   limits->measures_length = mip_len;
     130              : 
     131              :   {
     132              :     const char *measure_name;
     133              :     const json_t *jmeasure;
     134              : 
     135            2 :     json_object_foreach ((json_t*) jmeasures,
     136              :                          measure_name,
     137              :                          jmeasure)
     138              :     {
     139            0 :       struct TALER_EXCHANGE_MeasureInformation *mi
     140            0 :         = &lh->mip[--mip_len];
     141              :       struct GNUNET_JSON_Specification ispec[] = {
     142            0 :         GNUNET_JSON_spec_string ("check_name",
     143              :                                  &mi->check_name),
     144            0 :         GNUNET_JSON_spec_mark_optional (
     145              :           GNUNET_JSON_spec_string ("prog_name",
     146              :                                    &mi->prog_name),
     147              :           NULL),
     148            0 :         GNUNET_JSON_spec_mark_optional (
     149              :           GNUNET_JSON_spec_object_const ("context",
     150              :                                          &mi->context),
     151              :           NULL),
     152            0 :         GNUNET_JSON_spec_end ()
     153              :       };
     154              : 
     155            0 :       if (GNUNET_OK !=
     156            0 :           GNUNET_JSON_parse (jmeasure,
     157              :                              ispec,
     158              :                              NULL,
     159              :                              NULL))
     160              :       {
     161            0 :         GNUNET_break_op (0);
     162            0 :         return GNUNET_SYSERR;
     163              :       }
     164            0 :       mi->measure_name = measure_name;
     165              :     }
     166              :   }
     167              : 
     168            2 :   total = 0;
     169              : 
     170              :   {
     171              :     const json_t *rule;
     172              :     size_t idx;
     173              : 
     174            9 :     json_array_foreach ((json_t *) jrules,
     175              :                         idx,
     176              :                         rule)
     177              :     {
     178            7 :       total += json_array_size (json_object_get (rule,
     179              :                                                  "measures"));
     180              :     }
     181              :   }
     182              : 
     183            2 :   rule_len = json_array_size (jrules);
     184            2 :   lh->rp = GNUNET_new_array (rule_len,
     185              :                              struct TALER_EXCHANGE_KycRule);
     186            2 :   lh->mp = GNUNET_new_array (total,
     187              :                              const char *);
     188              : 
     189              :   {
     190              :     const json_t *rule;
     191              :     size_t idx;
     192              : 
     193            9 :     json_array_foreach ((json_t *) jrules,
     194              :                         idx,
     195              :                         rule)
     196              :     {
     197              :       const json_t *smeasures;
     198            7 :       struct TALER_EXCHANGE_KycRule *r
     199            7 :         = &lh->rp[--rule_len];
     200              :       struct GNUNET_JSON_Specification ispec[] = {
     201            7 :         TALER_JSON_spec_kycte ("operation_type",
     202              :                                &r->operation_type),
     203            7 :         TALER_JSON_spec_amount_any ("threshold",
     204              :                                     &r->threshold),
     205            7 :         GNUNET_JSON_spec_relative_time ("timeframe",
     206              :                                         &r->timeframe),
     207            7 :         GNUNET_JSON_spec_array_const ("measures",
     208              :                                       &smeasures),
     209            7 :         GNUNET_JSON_spec_mark_optional (
     210              :           GNUNET_JSON_spec_bool ("exposed",
     211              :                                  &r->exposed),
     212              :           NULL),
     213            7 :         GNUNET_JSON_spec_mark_optional (
     214              :           GNUNET_JSON_spec_bool ("is_and_combinator",
     215              :                                  &r->is_and_combinator),
     216              :           NULL),
     217            7 :         GNUNET_JSON_spec_uint32 ("display_priority",
     218              :                                  &r->display_priority),
     219            7 :         GNUNET_JSON_spec_end ()
     220              :       };
     221              :       size_t mlen;
     222              : 
     223            7 :       if (GNUNET_OK !=
     224            7 :           GNUNET_JSON_parse (rule,
     225              :                              ispec,
     226              :                              NULL,
     227              :                              NULL))
     228              :       {
     229            0 :         GNUNET_break_op (0);
     230            0 :         return GNUNET_SYSERR;
     231              :       }
     232              : 
     233            7 :       mlen = json_array_size (smeasures);
     234            7 :       GNUNET_assert (mlen <= total);
     235            7 :       total -= mlen;
     236              : 
     237              :       {
     238              :         size_t midx;
     239              :         const json_t *smeasure;
     240              : 
     241           13 :         json_array_foreach (smeasures,
     242              :                             midx,
     243              :                             smeasure)
     244              :         {
     245            6 :           const char *sval = json_string_value (smeasure);
     246              : 
     247            6 :           if (NULL == sval)
     248              :           {
     249            0 :             GNUNET_break_op (0);
     250            0 :             return GNUNET_SYSERR;
     251              :           }
     252            6 :           lh->mp[total + midx] = sval;
     253            6 :           if (0 == strcasecmp (sval,
     254              :                                "verboten"))
     255            6 :             r->verboten = true;
     256              :         }
     257              :       }
     258            7 :       r->measures = &lh->mp[total];
     259            7 :       r->measures_length = r->verboten ? 0 : total;
     260              :     }
     261              :   }
     262            2 :   return GNUNET_OK;
     263              : }
     264              : 
     265              : 
     266              : /**
     267              :  * Parse AML decision summary array.
     268              :  *
     269              :  * @param[in,out] lh handle to use for allocations
     270              :  * @param decisions JSON array with AML decision summaries
     271              :  * @param[out] decision_ar where to write the result
     272              :  * @return #GNUNET_OK on success
     273              :  */
     274              : static enum GNUNET_GenericReturnValue
     275            2 : parse_aml_decisions (
     276              :   struct TALER_EXCHANGE_LookupAmlDecisions *lh,
     277              :   const json_t *decisions,
     278              :   struct TALER_EXCHANGE_AmlDecision *decision_ar)
     279              : {
     280              :   json_t *obj;
     281              :   size_t idx;
     282              : 
     283            4 :   json_array_foreach (decisions, idx, obj)
     284              :   {
     285            2 :     struct TALER_EXCHANGE_AmlDecision *decision = &decision_ar[idx];
     286              :     const json_t *jlimits;
     287              :     struct GNUNET_JSON_Specification spec[] = {
     288            2 :       GNUNET_JSON_spec_fixed_auto ("h_payto",
     289              :                                    &decision->h_payto),
     290            2 :       GNUNET_JSON_spec_uint64 ("rowid",
     291              :                                &decision->rowid),
     292            2 :       GNUNET_JSON_spec_mark_optional (
     293              :         GNUNET_JSON_spec_string ("justification",
     294              :                                  &decision->justification),
     295              :         NULL),
     296            2 :       GNUNET_JSON_spec_timestamp ("decision_time",
     297              :                                   &decision->decision_time),
     298            2 :       GNUNET_JSON_spec_mark_optional (
     299              :         GNUNET_JSON_spec_object_const ("properties",
     300              :                                        &decision->jproperties),
     301              :         NULL),
     302            2 :       GNUNET_JSON_spec_object_const ("limits",
     303              :                                      &jlimits),
     304            2 :       GNUNET_JSON_spec_bool ("to_investigate",
     305              :                              &decision->to_investigate),
     306            2 :       GNUNET_JSON_spec_bool ("is_active",
     307              :                              &decision->is_active),
     308            2 :       GNUNET_JSON_spec_end ()
     309              :     };
     310              : 
     311            2 :     if (GNUNET_OK !=
     312            2 :         GNUNET_JSON_parse (obj,
     313              :                            spec,
     314              :                            NULL,
     315              :                            NULL))
     316              :     {
     317            0 :       GNUNET_break_op (0);
     318            0 :       return GNUNET_SYSERR;
     319              :     }
     320            2 :     if (GNUNET_OK !=
     321            2 :         parse_limits (lh,
     322              :                       jlimits,
     323              :                       decision))
     324              :     {
     325            0 :       GNUNET_break_op (0);
     326            0 :       return GNUNET_SYSERR;
     327              :     }
     328              :   }
     329            2 :   return GNUNET_OK;
     330              : }
     331              : 
     332              : 
     333              : /**
     334              :  * Parse the provided decision data from the "200 OK" response.
     335              :  *
     336              :  * @param[in,out] lh handle (callback may be zero'ed out)
     337              :  * @param json json reply with the data for one coin
     338              :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
     339              :  */
     340              : static enum GNUNET_GenericReturnValue
     341            2 : parse_decisions_ok (struct TALER_EXCHANGE_LookupAmlDecisions *lh,
     342              :                     const json_t *json)
     343              : {
     344            2 :   struct TALER_EXCHANGE_AmlDecisionsResponse lr = {
     345              :     .hr.reply = json,
     346              :     .hr.http_status = MHD_HTTP_OK
     347              :   };
     348              :   const json_t *records;
     349              :   struct GNUNET_JSON_Specification spec[] = {
     350            2 :     GNUNET_JSON_spec_array_const ("records",
     351              :                                   &records),
     352            2 :     GNUNET_JSON_spec_end ()
     353              :   };
     354              : 
     355            2 :   if (GNUNET_OK !=
     356            2 :       GNUNET_JSON_parse (json,
     357              :                          spec,
     358              :                          NULL,
     359              :                          NULL))
     360              :   {
     361            0 :     GNUNET_break_op (0);
     362            0 :     return GNUNET_SYSERR;
     363              :   }
     364              :   lr.details.ok.decisions_length
     365            2 :     = json_array_size (records);
     366            2 :   {
     367            2 :     struct TALER_EXCHANGE_AmlDecision decisions[
     368            2 :       GNUNET_NZL (lr.details.ok.decisions_length)];
     369            2 :     enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
     370              : 
     371            2 :     memset (decisions,
     372              :             0,
     373              :             sizeof (decisions));
     374            2 :     lr.details.ok.decisions = decisions;
     375            2 :     ret = parse_aml_decisions (lh,
     376              :                                records,
     377              :                                decisions);
     378            2 :     if (GNUNET_OK == ret)
     379              :     {
     380            2 :       lh->decisions_cb (lh->decisions_cb_cls,
     381              :                         &lr);
     382            2 :       lh->decisions_cb = NULL;
     383              :     }
     384            2 :     GNUNET_free (lh->mip);
     385            2 :     GNUNET_free (lh->rp);
     386            2 :     GNUNET_free (lh->mp);
     387            2 :     return ret;
     388              :   }
     389              : }
     390              : 
     391              : 
     392              : /**
     393              :  * Function called when we're done processing the
     394              :  * HTTP /aml/$OFFICER_PUB/decisions request.
     395              :  *
     396              :  * @param cls the `struct TALER_EXCHANGE_LookupAmlDecisions`
     397              :  * @param response_code HTTP response code, 0 on error
     398              :  * @param response parsed JSON result, NULL on error
     399              :  */
     400              : static void
     401            4 : handle_lookup_finished (void *cls,
     402              :                         long response_code,
     403              :                         const void *response)
     404              : {
     405            4 :   struct TALER_EXCHANGE_LookupAmlDecisions *lh = cls;
     406            4 :   const json_t *j = response;
     407            4 :   struct TALER_EXCHANGE_AmlDecisionsResponse lr = {
     408              :     .hr.reply = j,
     409            4 :     .hr.http_status = (unsigned int) response_code
     410              :   };
     411              : 
     412            4 :   lh->job = NULL;
     413            4 :   switch (response_code)
     414              :   {
     415            0 :   case 0:
     416            0 :     lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     417            0 :     break;
     418            2 :   case MHD_HTTP_OK:
     419            2 :     if (GNUNET_OK !=
     420            2 :         parse_decisions_ok (lh,
     421              :                             j))
     422              :     {
     423            0 :       GNUNET_break_op (0);
     424            0 :       lr.hr.http_status = 0;
     425            0 :       lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     426            0 :       break;
     427              :     }
     428            2 :     GNUNET_assert (NULL == lh->decisions_cb);
     429            2 :     TALER_EXCHANGE_lookup_aml_decisions_cancel (lh);
     430            2 :     return;
     431            1 :   case MHD_HTTP_NO_CONTENT:
     432            1 :     break;
     433            0 :   case MHD_HTTP_BAD_REQUEST:
     434            0 :     json_dumpf (j,
     435              :                 stderr,
     436              :                 JSON_INDENT (2));
     437            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     438            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     439              :     /* This should never happen, either us or the exchange is buggy
     440              :        (or API version conflict); just pass JSON reply to the application */
     441            0 :     break;
     442            1 :   case MHD_HTTP_FORBIDDEN:
     443            1 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     444            1 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     445              :     /* Nothing really to verify, exchange says this coin was not melted; we
     446              :        should pass the JSON reply to the application */
     447            1 :     break;
     448            0 :   case MHD_HTTP_NOT_FOUND:
     449            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     450            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     451              :     /* Nothing really to verify, exchange says this coin was not melted; we
     452              :        should pass the JSON reply to the application */
     453            0 :     break;
     454            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     455            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     456            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     457              :     /* Server had an internal issue; we should retry, but this API
     458              :        leaves this to the application */
     459            0 :     break;
     460            0 :   default:
     461              :     /* unexpected response code */
     462            0 :     GNUNET_break_op (0);
     463            0 :     lr.hr.ec = TALER_JSON_get_error_code (j);
     464            0 :     lr.hr.hint = TALER_JSON_get_error_hint (j);
     465            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     466              :                 "Unexpected response code %u/%d for lookup AML decisions\n",
     467              :                 (unsigned int) response_code,
     468              :                 (int) lr.hr.ec);
     469            0 :     break;
     470              :   }
     471            2 :   if (NULL != lh->decisions_cb)
     472            2 :     lh->decisions_cb (lh->decisions_cb_cls,
     473              :                       &lr);
     474            2 :   TALER_EXCHANGE_lookup_aml_decisions_cancel (lh);
     475              : }
     476              : 
     477              : 
     478              : struct TALER_EXCHANGE_LookupAmlDecisions *
     479            4 : TALER_EXCHANGE_lookup_aml_decisions (
     480              :   struct GNUNET_CURL_Context *ctx,
     481              :   const char *exchange_url,
     482              :   const struct TALER_NormalizedPaytoHashP *h_payto,
     483              :   enum TALER_EXCHANGE_YesNoAll investigation_only,
     484              :   enum TALER_EXCHANGE_YesNoAll active_only,
     485              :   uint64_t offset,
     486              :   int64_t limit,
     487              :   const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
     488              :   TALER_EXCHANGE_LookupAmlDecisionsCallback cb,
     489              :   void *cb_cls)
     490              : {
     491              :   struct TALER_EXCHANGE_LookupAmlDecisions *lh;
     492              :   CURL *eh;
     493              :   struct TALER_AmlOfficerPublicKeyP officer_pub;
     494              :   struct TALER_AmlOfficerSignatureP officer_sig;
     495              :   char arg_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2
     496              :                + 32];
     497              : 
     498            4 :   GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv,
     499              :                                       &officer_pub.eddsa_pub);
     500            4 :   TALER_officer_aml_query_sign (officer_priv,
     501              :                                 &officer_sig);
     502              :   {
     503              :     char pub_str[sizeof (officer_pub) * 2];
     504              :     char *end;
     505              : 
     506            4 :     end = GNUNET_STRINGS_data_to_string (
     507              :       &officer_pub,
     508              :       sizeof (officer_pub),
     509              :       pub_str,
     510              :       sizeof (pub_str));
     511            4 :     *end = '\0';
     512            4 :     GNUNET_snprintf (arg_str,
     513              :                      sizeof (arg_str),
     514              :                      "aml/%s/decisions",
     515              :                      pub_str);
     516              :   }
     517            4 :   lh = GNUNET_new (struct TALER_EXCHANGE_LookupAmlDecisions);
     518            4 :   lh->decisions_cb = cb;
     519            4 :   lh->decisions_cb_cls = cb_cls;
     520              :   {
     521              :     char limit_s[24];
     522              :     char offset_s[24];
     523              :     char payto_s[sizeof (*h_payto) * 2];
     524              :     char *end;
     525              : 
     526            4 :     if (NULL != h_payto)
     527              :     {
     528            3 :       end = GNUNET_STRINGS_data_to_string (
     529              :         h_payto,
     530              :         sizeof (*h_payto),
     531              :         payto_s,
     532              :         sizeof (payto_s));
     533            3 :       *end = '\0';
     534              :     }
     535            4 :     GNUNET_snprintf (limit_s,
     536              :                      sizeof (limit_s),
     537              :                      "%lld",
     538              :                      (long long) limit);
     539            4 :     GNUNET_snprintf (offset_s,
     540              :                      sizeof (offset_s),
     541              :                      "%llu",
     542              :                      (unsigned long long) offset);
     543            8 :     lh->url = TALER_url_join (
     544              :       exchange_url,
     545              :       arg_str,
     546              :       "limit",
     547              :       limit_s,
     548              :       "offset",
     549            4 :       ( ( (limit < 0) && (UINT64_MAX == offset) ) ||
     550            0 :         ( (limit > 0) && (0 == offset) ) )
     551              :       ? NULL
     552              :       : offset_s,
     553              :       "h_payto",
     554              :       NULL != h_payto
     555              :       ? payto_s
     556              :       : NULL,
     557              :       "active",
     558              :       TALER_EXCHANGE_YNA_ALL != active_only
     559            0 :       ? TALER_yna_to_string (active_only)
     560              :       : NULL,
     561              :       "investigation",
     562              :       TALER_EXCHANGE_YNA_ALL != investigation_only
     563            0 :       ? TALER_yna_to_string (investigation_only)
     564              :       : NULL,
     565              :       NULL);
     566              :   }
     567            4 :   if (NULL == lh->url)
     568              :   {
     569            0 :     GNUNET_free (lh);
     570            0 :     return NULL;
     571              :   }
     572            4 :   eh = TALER_EXCHANGE_curl_easy_get_ (lh->url);
     573            4 :   if (NULL == eh)
     574              :   {
     575            0 :     GNUNET_break (0);
     576            0 :     GNUNET_free (lh->url);
     577            0 :     GNUNET_free (lh);
     578            0 :     return NULL;
     579              :   }
     580              :   {
     581              :     char *hdr;
     582              :     char sig_str[sizeof (officer_sig) * 2];
     583              :     char *end;
     584              : 
     585            4 :     end = GNUNET_STRINGS_data_to_string (
     586              :       &officer_sig,
     587              :       sizeof (officer_sig),
     588              :       sig_str,
     589              :       sizeof (sig_str));
     590            4 :     *end = '\0';
     591              : 
     592            4 :     GNUNET_asprintf (&hdr,
     593              :                      "%s: %s",
     594              :                      TALER_AML_OFFICER_SIGNATURE_HEADER,
     595              :                      sig_str);
     596            4 :     lh->job_headers = curl_slist_append (NULL,
     597              :                                          hdr);
     598            4 :     GNUNET_free (hdr);
     599            4 :     lh->job_headers = curl_slist_append (lh->job_headers,
     600              :                                          "Content-type: application/json");
     601            8 :     lh->job = GNUNET_CURL_job_add2 (ctx,
     602              :                                     eh,
     603            4 :                                     lh->job_headers,
     604              :                                     &handle_lookup_finished,
     605              :                                     lh);
     606              :   }
     607            4 :   return lh;
     608              : }
     609              : 
     610              : 
     611              : void
     612            4 : TALER_EXCHANGE_lookup_aml_decisions_cancel (
     613              :   struct TALER_EXCHANGE_LookupAmlDecisions *lh)
     614              : {
     615            4 :   if (NULL != lh->job)
     616              :   {
     617            0 :     GNUNET_CURL_job_cancel (lh->job);
     618            0 :     lh->job = NULL;
     619              :   }
     620            4 :   curl_slist_free_all (lh->job_headers);
     621            4 :   GNUNET_free (lh->url);
     622            4 :   GNUNET_free (lh);
     623            4 : }
     624              : 
     625              : 
     626              : /* end of exchange_api_lookup_aml_decisions.c */
        

Generated by: LCOV version 2.0-1