diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index ffcfc5e99..bf6b1b1a9 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -124,6 +124,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd.c taler-exchange-httpd.h \
taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \
taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \
+ taler-exchange-httpd_aml-decisions-get.c \
taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \
taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \
taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 7f49955dc..11b2d35fa 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -446,14 +446,14 @@ handle_get_aml (struct TEH_RequestContext *rc,
AmlOpGetHandler handler;
} h[] = {
-#if FIXME_AML_GET_DECISIONS_NOT_IMPLEMENTED
{
.op = "decisions",
- .handler = &TEH_handler_get_aml_decisions
+ .handler = &TEH_handler_aml_decisions_get
},
+#if FIXME_AML_GET_DECISIONS_NOT_IMPLEMENTED
{
.op = "decision",
- .handler = &TEH_handler_get_aml_decision
+ .handler = &TEH_handler_aml_decision_get
},
#endif
{
diff --git a/src/exchange/taler-exchange-httpd_aml-decision.h b/src/exchange/taler-exchange-httpd_aml-decision.h
index 8dd3bb3fc..e31cfdfad 100644
--- a/src/exchange/taler-exchange-httpd_aml-decision.h
+++ b/src/exchange/taler-exchange-httpd_aml-decision.h
@@ -26,7 +26,7 @@
/**
- * Handle an "/aml/$OFFICER_PUB/decision" request. Parses the decision
+ * Handle a POST "/aml/$OFFICER_PUB/decision" request. Parses the decision
* details, checks the signatures and if appropriately authorized executes
* the decision.
*
@@ -42,4 +42,20 @@ TEH_handler_post_aml_decision (
const json_t *root);
+/**
+ * Handle a GET "/aml/$OFFICER_PUB/decisions" request. Parses the request
+ * details, checks the signatures and if appropriately authorized returns
+ * the matching decisions.
+ *
+ * @param rc request context
+ * @param officer_pub public key of the AML officer who made the request
+ * @param args GET arguments (should be none)
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_aml_decisions_get (
+ struct TEH_RequestContext *rc,
+ const struct TALER_AmlOfficerPublicKeyP *officer_pub,
+ const char *const args[]);
+
#endif
diff --git a/src/exchange/taler-exchange-httpd_aml-decisions-get.c b/src/exchange/taler-exchange-httpd_aml-decisions-get.c
new file mode 100644
index 000000000..091e7a674
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_aml-decisions-get.c
@@ -0,0 +1,228 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see
+*/
+/**
+ * @file taler-exchange-httpd_aml-decisions-get.c
+ * @brief Return summary information about AML decisions
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include
+#include
+#include
+#include
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler_signatures.h"
+#include "taler-exchange-httpd.h"
+#include "taler_exchangedb_plugin.h"
+#include "taler-exchange-httpd_aml-decision.h"
+#include "taler-exchange-httpd_metrics.h"
+
+
+/**
+ * Maximum number of records we return per request.
+ */
+#define MAX_RECORDS 1024
+
+/**
+ * Return AML status.
+ *
+ * @param cls closure
+ * @param row_id current row in AML status table
+ * @param h_payto account for which the attribute data is stored
+ * @param threshold currently monthly threshold that would trigger an AML check
+ * @param status what is the current AML decision
+ */
+static void
+record_cb (
+ void *cls,
+ uint64_t row_id,
+ const struct TALER_PaytoHashP *h_payto,
+ const struct TALER_Amount *threshold,
+ enum TALER_AmlDecisionState status)
+{
+ json_t *records = cls;
+
+ GNUNET_assert (
+ 0 ==
+ json_array_append (
+ records,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_data_auto ("h_payto",
+ h_payto),
+ TALER_JSON_pack_amount ("threshold",
+ threshold),
+ GNUNET_JSON_pack_int64 ("current_state",
+ status),
+ GNUNET_JSON_pack_int64 ("rowid",
+ row_id)
+ )));
+}
+
+
+MHD_RESULT
+TEH_handler_aml_decisions_get (
+ struct TEH_RequestContext *rc,
+ const struct TALER_AmlOfficerPublicKeyP *officer_pub,
+ const char *const args[])
+{
+ enum GNUNET_GenericReturnValue res;
+ const char *sig_hdr;
+ struct TALER_AmlOfficerSignatureP officer_sig;
+ bool frozen = false;
+ bool pending = false;
+ bool normal = false;
+ int delta = -20;
+ unsigned long long start = INT64_MAX;
+
+ if (NULL != args[0])
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ args[0]);
+ }
+ sig_hdr = MHD_lookup_connection_value (rc->connection,
+ MHD_HEADER_KIND,
+ TALER_AML_OFFICER_SIGNATURE_HEADER);
+ if ( (NULL == sig_hdr) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (sig_hdr,
+ strlen (sig_hdr),
+ &officer_sig,
+ sizeof (officer_sig))) ||
+ (GNUNET_OK !=
+ TALER_officer_aml_query_verify (officer_pub,
+ &officer_sig)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID,
+ sig_hdr);
+ }
+ TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
+
+ {
+ const char *p;
+
+ p = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "frozen");
+ if (NULL != p)
+ frozen = (0 == strcasecmp (p,
+ "yes"));
+ p = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "pending");
+ if (NULL != p)
+ pending = (0 == strcasecmp (p,
+ "yes"));
+ p = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "normal");
+ if (NULL != p)
+ normal = (0 == strcasecmp (p,
+ "yes"));
+ p = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "start");
+ if (NULL != p)
+ {
+ char dummy;
+
+ if (1 != sscanf (p,
+ "%llu%c",
+ &start,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "start");
+ }
+ }
+ p = MHD_lookup_connection_value (rc->connection,
+ MHD_GET_ARGUMENT_KIND,
+ "delta");
+ if (NULL != p)
+ {
+ char dummy;
+
+ if (1 != sscanf (p,
+ "%d%c",
+ &delta,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "delta");
+ }
+ }
+ }
+
+ {
+ json_t *records;
+ enum GNUNET_DB_QueryStatus qs;
+ enum TALER_AmlDecisionState decision = 42; // FIXME!
+
+ records = json_array ();
+ GNUNET_assert (NULL != records);
+ if (INT_MIN == delta)
+ delta = INT_MIN + 1;
+ qs = TEH_plugin->select_aml_process (TEH_plugin->cls,
+ decision,
+ start,
+ delta > 0,
+ GNUNET_MIN (MAX_RECORDS,
+ delta > 0 ? delta :
+ -delta),
+ &record_cb,
+ records);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ json_decref (records);
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL);
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ return TALER_MHD_reply_static (
+ rc->connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_array_steal ("records",
+ records));
+ }
+}
+
+
+/* end of taler-exchange-httpd_deposits_get.c */
diff --git a/src/exchangedb/pg_select_aml_process.c b/src/exchangedb/pg_select_aml_process.c
index 2105308fc..c165e230b 100644
--- a/src/exchangedb/pg_select_aml_process.c
+++ b/src/exchangedb/pg_select_aml_process.c
@@ -112,6 +112,7 @@ TEH_PG_select_aml_process (
void *cls,
enum TALER_AmlDecisionState decision,
uint64_t row_off,
+ uint64_t limit,
bool forward,
TALER_EXCHANGEDB_AmlStatusCallback cb,
void *cb_cls)
@@ -120,6 +121,7 @@ TEH_PG_select_aml_process (
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint32 (&decision),
GNUNET_PQ_query_param_uint64 (&row_off),
+ GNUNET_PQ_query_param_uint64 (&limit),
GNUNET_PQ_query_param_end
};
struct AmlProcessResultContext ctx = {
@@ -144,7 +146,8 @@ TEH_PG_select_aml_process (
" FROM aml_status"
" WHERE aml_status_serial_id > $2"
" AND $1 = status & $1"
- " ORDER BY aml_status_serial_id INC");
+ " ORDER BY aml_status_serial_id INC"
+ " LIMIT $3");
PREPARE (pg,
"select_aml_process_dec",
"SELECT"
@@ -156,7 +159,8 @@ TEH_PG_select_aml_process (
" FROM aml_status"
" WHERE aml_status_serial_id < $2"
" AND $1 = status & $1"
- " ORDER BY aml_status_serial_id DESC");
+ " ORDER BY aml_status_serial_id DESC"
+ " LIMIT $3");
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
stmt,
params,
diff --git a/src/exchangedb/pg_select_aml_process.h b/src/exchangedb/pg_select_aml_process.h
index 019f58b26..648cace2e 100644
--- a/src/exchangedb/pg_select_aml_process.h
+++ b/src/exchangedb/pg_select_aml_process.h
@@ -32,6 +32,7 @@
* @param cls closure
* @param decision which decision states to filter by
* @param row_off offset to start from
+ * @param limit how many rows to return at most
* @param forward true to go forward in time, false to go backwards
* @param cb callback to invoke on each match
* @param cb_cls closure for @a cb
@@ -42,6 +43,7 @@ TEH_PG_select_aml_process (
void *cls,
enum TALER_AmlDecisionState decision,
uint64_t row_off,
+ uint64_t limit,
bool forward,
TALER_EXCHANGEDB_AmlStatusCallback cb,
void *cb_cls);
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 2f3008e2a..c6391647e 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -4381,7 +4381,7 @@ struct TALER_EXCHANGE_LookupAmlDecisions;
* Inform the exchange that an AML decision has been taken.
*
* @param ctx the context
- * @param url HTTP base URL for the exchange
+ * @param exchange_url HTTP base URL for the exchange
* @param start row number starting point (exclusive rowid)
* @param delta number of records to return, negative for descending, positive for ascending from start
* @param filter_frozen true to only return frozen accounts
@@ -4395,7 +4395,7 @@ struct TALER_EXCHANGE_LookupAmlDecisions;
struct TALER_EXCHANGE_LookupAmlDecisions *
TALER_EXCHANGE_lookup_aml_decisions (
struct GNUNET_CURL_Context *ctx,
- const char *url,
+ const char *exchange_url,
uint64_t start,
int delta,
bool filter_frozen,
@@ -4532,7 +4532,7 @@ struct TALER_EXCHANGE_LookupAmlDecision;
* Inform the exchange that an AML decision has been taken.
*
* @param ctx the context
- * @param url HTTP base URL for the exchange
+ * @param exchange_url HTTP base URL for the exchange
* @param h_payto which account to return the decision history for
* @param officer_priv private key of the deciding AML officer
* @param cb function to call with the exchange's result
@@ -4542,10 +4542,10 @@ struct TALER_EXCHANGE_LookupAmlDecision;
struct TALER_EXCHANGE_LookupAmlDecision *
TALER_EXCHANGE_lookup_aml_decision (
struct GNUNET_CURL_Context *ctx,
- const char *url,
+ const char *exchange_url,
const struct TALER_PaytoHashP *h_payto,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
- TALER_EXCHANGE_LookupAmlDecisionsCallback cb,
+ TALER_EXCHANGE_LookupAmlDecisionCallback cb,
void *cb_cls);
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index b1394b45e..3a8b88ae3 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -6693,6 +6693,7 @@ struct TALER_EXCHANGEDB_Plugin
void *cls,
enum TALER_AmlDecisionState decision,
uint64_t row_off,
+ uint64_t limit,
bool forward,
TALER_EXCHANGEDB_AmlStatusCallback cb,
void *cb_cls);
diff --git a/src/include/taler_util.h b/src/include/taler_util.h
index 5776d62c3..8192ed87c 100644
--- a/src/include/taler_util.h
+++ b/src/include/taler_util.h
@@ -32,7 +32,7 @@
* Version of the Taler API, in hex.
* Thus 0.8.4-1 = 0x00080401.
*/
-#define TALER_API_VERSION 0x00080401
+#define TALER_API_VERSION 0x00090200
/**
* Stringify operator.
@@ -80,6 +80,12 @@
} while (0)
+/**
+ * HTTP header with an AML officer signature to approve the inquiry.
+ * Used only in GET Requests.
+ */
+#define TALER_AML_OFFICER_SIGNATURE_HEADER "Taler-AML-Officer-Signature"
+
/**
* Log an error message at log-level 'level' that indicates
* a failure of the command 'cmd' with the message given
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 00b604acf..529a2d019 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -38,6 +38,8 @@ libtalerexchange_la_SOURCES = \
exchange_api_kyc_proof.c \
exchange_api_kyc_wallet.c \
exchange_api_link.c \
+ exchange_api_lookup_aml_decision.c \
+ exchange_api_lookup_aml_decisions.c \
exchange_api_management_add_partner.c \
exchange_api_management_auditor_disable.c \
exchange_api_management_auditor_enable.c \
diff --git a/src/lib/exchange_api_lookup_aml_decision.c b/src/lib/exchange_api_lookup_aml_decision.c
index c10919165..04b1d52aa 100644
--- a/src/lib/exchange_api_lookup_aml_decision.c
+++ b/src/lib/exchange_api_lookup_aml_decision.c
@@ -56,6 +56,10 @@ struct TALER_EXCHANGE_LookupAmlDecision
*/
void *decision_cb_cls;
+ /**
+ * HTTP headers for the job.
+ */
+ struct curl_slist *job_headers;
};
@@ -118,7 +122,7 @@ handle_lookup_finished (void *cls,
break;
}
GNUNET_assert (NULL == lh->decision_cb);
- TALER_EXCHANGE_link_cancel (lh);
+ TALER_EXCHANGE_lookup_aml_decision_cancel (lh);
return;
case MHD_HTTP_BAD_REQUEST:
lr.hr.ec = TALER_JSON_get_error_code (j);
@@ -152,14 +156,14 @@ handle_lookup_finished (void *cls,
if (NULL != lh->decision_cb)
lh->decision_cb (lh->decision_cb_cls,
&lr);
- TALER_EXCHANGE_link_cancel (lh);
+ TALER_EXCHANGE_lookup_aml_decision_cancel (lh);
}
struct TALER_EXCHANGE_LookupAmlDecision *
TALER_EXCHANGE_lookup_aml_decision (
struct GNUNET_CURL_Context *ctx,
- const char *url,
+ const char *exchange_url,
const struct TALER_PaytoHashP *h_payto,
const struct TALER_AmlOfficerPrivateKeyP *officer_priv,
TALER_EXCHANGE_LookupAmlDecisionCallback cb,
@@ -202,7 +206,7 @@ TALER_EXCHANGE_lookup_aml_decision (
lh = GNUNET_new (struct TALER_EXCHANGE_LookupAmlDecision);
lh->decision_cb = cb;
lh->decision_cb_cls = cb_cls;
- lh->url = TALER_URL_join (exchange_url,
+ lh->url = TALER_url_join (exchange_url,
arg_str,
NULL);
if (NULL == lh->url)
@@ -218,11 +222,33 @@ TALER_EXCHANGE_lookup_aml_decision (
GNUNET_free (lh);
return NULL;
}
- // FIXME: add authorization header to 'eh' based on officer_sig!
- lh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
- eh,
- &handle_lookup_finished,
- lh);
+ {
+ char *hdr;
+ char sig_str[sizeof (officer_sig) * 2];
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (
+ &officer_sig,
+ sizeof (officer_sig),
+ sig_str,
+ sizeof (sig_str));
+ *end = '\0';
+
+ GNUNET_asprintf (&hdr,
+ "%s: %s",
+ TALER_AML_OFFICER_SIGNATURE_HEADER,
+ sig_str);
+ lh->job_headers = curl_slist_append (NULL,
+ hdr);
+ GNUNET_free (hdr);
+ lh->job_headers = curl_slist_append (lh->job_headers,
+ "Content-type: application/json");
+ lh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ lh->job_headers,
+ &handle_lookup_finished,
+ lh);
+ }
return lh;
}
@@ -236,6 +262,7 @@ TALER_EXCHANGE_lookup_aml_decision_cancel (
GNUNET_CURL_job_cancel (lh->job);
lh->job = NULL;
}
+ curl_slist_free_all (lh->job_headers);
GNUNET_free (lh->url);
GNUNET_free (lh);
}
diff --git a/src/lib/exchange_api_lookup_aml_decisions.c b/src/lib/exchange_api_lookup_aml_decisions.c
index 00c2eaab9..308c0f969 100644
--- a/src/lib/exchange_api_lookup_aml_decisions.c
+++ b/src/lib/exchange_api_lookup_aml_decisions.c
@@ -56,6 +56,10 @@ struct TALER_EXCHANGE_LookupAmlDecisions
*/
void *decisions_cb_cls;
+ /**
+ * HTTP headers for the job.
+ */
+ struct curl_slist *job_headers;
};
@@ -118,7 +122,7 @@ handle_lookup_finished (void *cls,
break;
}
GNUNET_assert (NULL == lh->decisions_cb);
- TALER_EXCHANGE_link_cancel (lh);
+ TALER_EXCHANGE_lookup_aml_decisions_cancel (lh);
return;
case MHD_HTTP_BAD_REQUEST:
lr.hr.ec = TALER_JSON_get_error_code (j);
@@ -152,14 +156,14 @@ handle_lookup_finished (void *cls,
if (NULL != lh->decisions_cb)
lh->decisions_cb (lh->decisions_cb_cls,
&lr);
- TALER_EXCHANGE_link_cancel (lh);
+ TALER_EXCHANGE_lookup_aml_decisions_cancel (lh);
}
struct TALER_EXCHANGE_LookupAmlDecisions *
TALER_EXCHANGE_lookup_aml_decisions (
struct GNUNET_CURL_Context *ctx,
- const char *url,
+ const char *exchange_url,
uint64_t start,
int delta,
bool filter_frozen,
@@ -180,7 +184,7 @@ TALER_EXCHANGE_lookup_aml_decisions (
TALER_officer_aml_query_sign (officer_priv,
&officer_sig);
{
- char pub_str[sizeof (struct TALER_AmlOfficerPublicKeyP) * 2];
+ char pub_str[sizeof (officer_pub) * 2];
char *end;
end = GNUNET_STRINGS_data_to_string (
@@ -197,10 +201,10 @@ TALER_EXCHANGE_lookup_aml_decisions (
lh = GNUNET_new (struct TALER_EXCHANGE_LookupAmlDecisions);
lh->decisions_cb = cb;
lh->decisions_cb_cls = cb_cls;
- lh->url = TALER_URL_join (exchange_url,
+ lh->url = TALER_url_join (exchange_url,
arg_str,
"frozen",
- filter_fozen ? "yes" : NULL,
+ filter_frozen ? "yes" : NULL,
"pending",
filter_pending ? "yes" : NULL,
"normal",
@@ -219,11 +223,33 @@ TALER_EXCHANGE_lookup_aml_decisions (
GNUNET_free (lh);
return NULL;
}
- // FIXME: add authorization header to 'eh' based on officer_sig!
- lh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
- eh,
- &handle_lookup_finished,
- lh);
+ {
+ char *hdr;
+ char sig_str[sizeof (officer_sig) * 2];
+ char *end;
+
+ end = GNUNET_STRINGS_data_to_string (
+ &officer_sig,
+ sizeof (officer_sig),
+ sig_str,
+ sizeof (sig_str));
+ *end = '\0';
+
+ GNUNET_asprintf (&hdr,
+ "%s: %s",
+ TALER_AML_OFFICER_SIGNATURE_HEADER,
+ sig_str);
+ lh->job_headers = curl_slist_append (NULL,
+ hdr);
+ GNUNET_free (hdr);
+ lh->job_headers = curl_slist_append (lh->job_headers,
+ "Content-type: application/json");
+ lh->job = GNUNET_CURL_job_add2 (ctx,
+ eh,
+ lh->job_headers,
+ &handle_lookup_finished,
+ lh);
+ }
return lh;
}
@@ -237,6 +263,7 @@ TALER_EXCHANGE_lookup_aml_decisions_cancel (
GNUNET_CURL_job_cancel (lh->job);
lh->job = NULL;
}
+ curl_slist_free_all (lh->job_headers);
GNUNET_free (lh->url);
GNUNET_free (lh);
}