exchange

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

commit 694cd42c3f4fdae4dc0a2261cc7da88e12e2a62f
parent c36cde50657a2ce89d79b832208587dac72e7fff
Author: Florian Dold <dold@taler.net>
Date:   Mon, 31 Aug 2026 22:39:16 +0200

exchange: add AML form PDF rendering endpoint

Diffstat:
Msrc/exchange/meson.build | 1+
Msrc/exchange/taler-exchange-httpd.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/exchange/taler-exchange-httpd_get-config.h | 2+-
Asrc/exchange/taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchange/taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.h | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/include/taler/exchange/meson.build | 1+
Asrc/include/taler/exchange/post-aml-OFFICER_PUB-render-form.h | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/taler/taler_exchange_service.h | 1+
Msrc/lib/exchange_api_handle.c | 2+-
Asrc/lib/exchange_api_post-aml-OFFICER_PUB-render-form.c | 227+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/meson.build | 2+-
11 files changed, 717 insertions(+), 45 deletions(-)

diff --git a/src/exchange/meson.build b/src/exchange/meson.build @@ -167,6 +167,7 @@ taler_exchange_httpd_SOURCES = [ 'taler-exchange-httpd_get-aml-OFFICER_PUB-accounts.c', 'taler-exchange-httpd_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.c', 'taler-exchange-httpd_post-aml-OFFICER_PUB-decision.c', + 'taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.c', 'taler-exchange-httpd_get-aml-OFFICER_PUB-decisions.c', 'taler-exchange-httpd_get-aml-OFFICER_PUB-legitimizations.c', 'taler-exchange-httpd_get-aml-OFFICER_PUB-kyc-statistics-NAMES.c', diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -36,6 +36,7 @@ #include \ "taler-exchange-httpd_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.h" #include "taler-exchange-httpd_post-aml-OFFICER_PUB-decision.h" +#include "taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.h" #include "taler-exchange-httpd_get-aml-OFFICER_PUB-legitimizations.h" #include "taler-exchange-httpd_get-aml-OFFICER_PUB-kyc-statistics-NAMES.h" #include "taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.h" @@ -434,6 +435,59 @@ typedef enum MHD_Result /** + * Verify the generic AML officer authentication signature. + * + * @param rc request context + * @param officer_pub public key claimed by the request + * @param[out] mhd_ret response result on failure + * @return true if authentication succeeded + */ +static bool +verify_aml_query_signature ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + enum MHD_Result *mhd_ret) +{ + const char *sig_hdr; + struct TALER_AmlOfficerSignatureP officer_sig; + + 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_break_op (0); + *mhd_ret = TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, + TALER_AML_OFFICER_SIGNATURE_HEADER); + return false; + } + if (GNUNET_OK != + TALER_officer_aml_query_verify (officer_pub, + &officer_sig)) + { + GNUNET_break_op (0); + *mhd_ret = TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, + sig_hdr); + return false; + } + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + return true; +} + + +/** * Handle a "/aml/$OFFICER_PUB/$OP" POST request. Parses the "officer_pub" * EdDSA key of the officer and demultiplexes based on $OP. * @@ -460,16 +514,33 @@ handle_post_aml (struct TEH_RequestContext *rc, */ AmlOpPostHandler handler; + /** + * Whether a read-only officer may use this operation. + */ + bool allow_read_only; + + /** + * Whether the generic AML query signature header is required. + */ + bool query_auth; + } h[] = { { .op = "decision", .handler = &TEH_handler_post_aml_decision }, { + .op = "render-form", + .handler = &TEH_handler_post_aml_render_form, + .allow_read_only = true, + .query_auth = true + }, + { .op = NULL, .handler = NULL }, }; + unsigned int hi; if (GNUNET_OK != GNUNET_STRINGS_string_to_data (args[0], @@ -484,6 +555,23 @@ handle_post_aml (struct TEH_RequestContext *rc, args[0]); } + for (hi = 0; NULL != h[hi].op; hi++) + if (0 == strcmp (h[hi].op, + args[1])) + break; + if (NULL == h[hi].op) + return r404 (rc->connection, + args[1]); + if (h[hi].query_auth) + { + enum MHD_Result ret; + + if (! verify_aml_query_signature (rc, + &officer_pub, + &ret)) + return ret; + } + { enum GNUNET_DB_QueryStatus qs; bool ro; @@ -509,7 +597,8 @@ handle_post_aml (struct TEH_RequestContext *rc, case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } - if (ro) + if (ro && + (! h[hi].allow_read_only)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (rc->connection, @@ -518,15 +607,9 @@ handle_post_aml (struct TEH_RequestContext *rc, "read-only"); } } - - for (unsigned int i = 0; NULL != h[i].op; i++) - if (0 == strcmp (h[i].op, - args[1])) - return h[i].handler (rc, - &officer_pub, - root); - return r404 (rc->connection, - args[1]); + return h[hi].handler (rc, + &officer_pub, + root); } @@ -641,39 +724,12 @@ handle_get_aml (struct TEH_RequestContext *rc, args[0]); } { - const char *sig_hdr; - struct TALER_AmlOfficerSignatureP officer_sig; + enum MHD_Result ret; - 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_break_op (0); - return TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED, - TALER_AML_OFFICER_SIGNATURE_HEADER); - } - if (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_FORBIDDEN, - TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID, - sig_hdr); - } - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + if (! verify_aml_query_signature (rc, + &officer_pub, + &ret)) + return ret; } { @@ -2788,6 +2844,7 @@ do_shutdown (void *cls) TEH_kyc_start_cleanup (); TEH_aml_decision_cleanup (); TEH_handler_aml_attributes_get_cleanup (); + TEH_handler_post_aml_render_form_cleanup (); TALER_KYCLOGIC_kyc_done (); TALER_MHD_daemons_destroy (); TEH_keys_finished (); diff --git a/src/exchange/taler-exchange-httpd_get-config.h b/src/exchange/taler-exchange-httpd_get-config.h @@ -41,7 +41,7 @@ * * Returned via both /config and /keys endpoints. */ -#define EXCHANGE_PROTOCOL_VERSION "40:0:6" +#define EXCHANGE_PROTOCOL_VERSION "41:0:7" /** diff --git a/src/exchange/taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.c b/src/exchange/taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.c @@ -0,0 +1,227 @@ +/* + This file is part of TALER + Copyright (C) 2026 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. +*/ +/** + * @file taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.c + * @brief Render submitted AML form data as PDF + */ +#include <gnunet/gnunet_util_lib.h> +#include <jansson.h> +#include <microhttpd.h> +#include "taler/taler_mhd_lib.h" +#include "taler-exchange-httpd.h" +#include "taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.h" + + +/** + * State of an asynchronous form rendering request. + */ +struct RenderContext +{ + struct RenderContext *next; + struct RenderContext *prev; + struct TEH_RequestContext *rc; + struct TALER_MHD_TypstContext *tc; + struct MHD_Response *response; + unsigned int http_status; + json_t *data; + struct TALER_MHD_TypstDocument doc; +}; + + +/** + * Requests currently suspended while Typst runs. + */ +static struct RenderContext *render_head; +static struct RenderContext *render_tail; + + +void +TEH_handler_post_aml_render_form_cleanup (void) +{ + struct RenderContext *rctx; + + while (NULL != (rctx = render_head)) + { + GNUNET_CONTAINER_DLL_remove (render_head, + render_tail, + rctx); + MHD_resume_connection (rctx->rc->connection); + } +} + + +/** + * Release a request context. + * + * @param[in] rc request being cleaned up + */ +static void +free_render_context (struct TEH_RequestContext *rc) +{ + struct RenderContext *rctx = rc->rh_ctx; + + if (NULL != rctx->tc) + { + TALER_MHD_typst_cancel (rctx->tc); + rctx->tc = NULL; + } + if (NULL != rctx->response) + { + MHD_destroy_response (rctx->response); + rctx->response = NULL; + } + json_decref (rctx->data); + GNUNET_free (rctx); +} + + +/** + * Receive the result of the Typst operation. + * + * @param cls a `struct RenderContext *` + * @param tr result from Typst + */ +static void +render_finished (void *cls, + const struct TALER_MHD_TypstResponse *tr) +{ + struct RenderContext *rctx = cls; + + rctx->tc = NULL; + GNUNET_CONTAINER_DLL_remove (render_head, + render_tail, + rctx); + MHD_resume_connection (rctx->rc->connection); + TALER_MHD_daemon_trigger (); + if (TALER_EC_NONE != tr->ec) + { + rctx->http_status = TALER_ErrorCode_get_http_status (tr->ec); + rctx->response = TALER_MHD_make_error (tr->ec, + tr->details.hint); + return; + } + rctx->response = TALER_MHD_response_from_pdf_file (tr->details.filename); + if (NULL == rctx->response) + { + rctx->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + rctx->response = TALER_MHD_make_error ( + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "failed to open rendered PDF"); + return; + } + rctx->http_status = MHD_HTTP_OK; +} + + +enum MHD_Result +TEH_handler_post_aml_render_form ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const json_t *root) +{ + struct RenderContext *rctx = rc->rh_ctx; + const char *form_id; + const json_t *form_version; + + (void) officer_pub; + if (NULL != rctx) + { + if (NULL == rctx->response) + { + GNUNET_break (0); + return MHD_NO; + } + return MHD_queue_response (rc->connection, + rctx->http_status, + rctx->response); + } + if (! json_is_object (root)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "request body must be a JSON object"); + } + form_id = json_string_value (json_object_get (root, + "FORM_ID")); + if (NULL == form_id) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "FORM_ID"); + } + form_version = json_object_get (root, + "FORM_VERSION"); + if ( (NULL != form_version) && + ( (! json_is_integer (form_version)) || + (0 > json_integer_value (form_version)) ) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "FORM_VERSION"); + } + + rctx = GNUNET_new (struct RenderContext); + rctx->rc = rc; + rctx->data = json_deep_copy (root); + if (NULL == rctx->data) + { + GNUNET_free (rctx); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + "failed to copy form data"); + } + if (NULL != TEH_global_pdf_form_data) + GNUNET_assert (0 == + json_object_update_missing (rctx->data, + TEH_global_pdf_form_data)); + rctx->doc.form_name = json_string_value (json_object_get (rctx->data, + "FORM_ID")); + rctx->doc.form_version = NULL; + rctx->doc.data = rctx->data; + rc->rh_ctx = rctx; + rc->rh_cleaner = &free_render_context; + + rctx->tc = TALER_MHD_typst (TALER_EXCHANGE_project_data (), + TEH_cfg, + true, + "exchange", + 1, + &rctx->doc, + &render_finished, + rctx); + if (NULL == rctx->tc) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Client requested form PDF, but Typst is unavailable\n"); + return TALER_MHD_reply_with_error ( + rc->connection, + MHD_HTTP_NOT_IMPLEMENTED, + TALER_EC_EXCHANGE_GENERIC_NO_TYPST_OR_PDFTK, + NULL); + } + GNUNET_CONTAINER_DLL_insert (render_head, + render_tail, + rctx); + MHD_suspend_connection (rc->connection); + return MHD_YES; +} + + +/* end of taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.c */ diff --git a/src/exchange/taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.h b/src/exchange/taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + Copyright (C) 2026 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. +*/ +/** + * @file taler-exchange-httpd_post-aml-OFFICER_PUB-render-form.h + * @brief Handle POST /aml/$OFFICER_PUB/render-form requests + */ +#ifndef TALER_EXCHANGE_HTTPD_POST_AML_OFFICER_PUB_RENDER_FORM_H +#define TALER_EXCHANGE_HTTPD_POST_AML_OFFICER_PUB_RENDER_FORM_H + +#include <microhttpd.h> +#include "taler-exchange-httpd.h" + + +/** + * Render submitted AML form data as a PDF. + * + * @param rc request context + * @param officer_pub authenticated AML officer + * @param root submitted form data + * @return MHD result code + */ +enum MHD_Result +TEH_handler_post_aml_render_form ( + struct TEH_RequestContext *rc, + const struct TALER_AmlOfficerPublicKeyP *officer_pub, + const json_t *root); + + +/** + * Stop all in-flight form rendering requests. + */ +void +TEH_handler_post_aml_render_form_cleanup (void); + +#endif diff --git a/src/include/taler/exchange/meson.build b/src/include/taler/exchange/meson.build @@ -21,6 +21,7 @@ talerexchange_headers = [ 'get-reserves-RESERVE_PUB-history.h', 'get-transfers-WTID.h', 'post-aml-OFFICER_PUB-decision.h', + 'post-aml-OFFICER_PUB-render-form.h', 'post-auditors-AUDITOR_PUB-H_DENOM_PUB.h', 'post-batch-deposit.h', 'post-blinding-prepare.h', diff --git a/src/include/taler/exchange/post-aml-OFFICER_PUB-render-form.h b/src/include/taler/exchange/post-aml-OFFICER_PUB-render-form.h @@ -0,0 +1,117 @@ +/* + This file is part of TALER + Copyright (C) 2026 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. +*/ +/** + * @file include/taler/exchange/post-aml-OFFICER_PUB-render-form.h + * @brief C interface for POST /aml/$OFFICER_PUB/render-form + */ +#ifndef _TALER_EXCHANGE_POST_AML_OFFICER_PUB_RENDER_FORM_H +#define _TALER_EXCHANGE_POST_AML_OFFICER_PUB_RENDER_FORM_H + +#include <taler/exchange/common.h> + + +/** + * Handle for a POST /aml/$OFFICER_PUB/render-form operation. + */ +struct TALER_EXCHANGE_PostAmlRenderFormHandle; + + +/** + * Response from POST /aml/$OFFICER_PUB/render-form. + */ +struct TALER_EXCHANGE_PostAmlRenderFormResponse +{ + /** + * HTTP response information. + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on the HTTP status. + */ + union + { + /** + * Details for an HTTP 200 response. + */ + struct + { + /** + * PDF bytes. Borrowed and only valid during the callback. + */ + const void *pdf; + + /** + * Number of bytes in @e pdf. + */ + size_t pdf_size; + } ok; + } details; +}; + + +#ifndef TALER_EXCHANGE_POST_AML_RENDER_FORM_RESULT_CLOSURE +#define TALER_EXCHANGE_POST_AML_RENDER_FORM_RESULT_CLOSURE void +#endif + + +/** + * Callback for POST /aml/$OFFICER_PUB/render-form. + * + * @param cls closure + * @param response exchange response + */ +typedef void +(*TALER_EXCHANGE_PostAmlRenderFormCallback)( + TALER_EXCHANGE_POST_AML_RENDER_FORM_RESULT_CLOSURE *cls, + const struct TALER_EXCHANGE_PostAmlRenderFormResponse *response); + + +/** + * Set up a form rendering operation. The operation must be started explicitly. + * + * @param ctx CURL context + * @param url exchange base URL + * @param officer_priv private key of the officer + * @param form completed form data; must be a JSON object containing FORM_ID + * @return operation handle, NULL on invalid arguments + */ +struct TALER_EXCHANGE_PostAmlRenderFormHandle * +TALER_EXCHANGE_post_aml_render_form_create ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv, + const json_t *form); + + +/** + * Start POST /aml/$OFFICER_PUB/render-form. + * + * @param[in,out] parfh operation handle + * @param cb result callback + * @param cb_cls closure for @a cb + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TALER_EXCHANGE_post_aml_render_form_start ( + struct TALER_EXCHANGE_PostAmlRenderFormHandle *parfh, + TALER_EXCHANGE_PostAmlRenderFormCallback cb, + TALER_EXCHANGE_POST_AML_RENDER_FORM_RESULT_CLOSURE *cb_cls); + + +/** + * Cancel POST /aml/$OFFICER_PUB/render-form. + * + * @param[in] parfh operation handle + */ +void +TALER_EXCHANGE_post_aml_render_form_cancel ( + struct TALER_EXCHANGE_PostAmlRenderFormHandle *parfh); + +#endif diff --git a/src/include/taler/taler_exchange_service.h b/src/include/taler/taler_exchange_service.h @@ -65,6 +65,7 @@ #include <taler/exchange/get-aml-OFFICER_PUB-measures.h> #include <taler/exchange/get-aml-OFFICER_PUB-kyc-statistics-NAMES.h> #include <taler/exchange/post-aml-OFFICER_PUB-decision.h> +#include <taler/exchange/post-aml-OFFICER_PUB-render-form.h> #include <taler/exchange/post-management-partners.h> #include <taler/exchange/post-management-auditors.h> #include <taler/exchange/post-management-auditors-AUDITOR_PUB-disable.h> diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c @@ -35,7 +35,7 @@ * Which version of the Taler protocol is implemented * by this library? Used to determine compatibility. */ -#define EXCHANGE_PROTOCOL_CURRENT 39 +#define EXCHANGE_PROTOCOL_CURRENT 41 /** * How many versions are we backwards compatible with? diff --git a/src/lib/exchange_api_post-aml-OFFICER_PUB-render-form.c b/src/lib/exchange_api_post-aml-OFFICER_PUB-render-form.c @@ -0,0 +1,227 @@ +/* + This file is part of TALER + Copyright (C) 2026 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. +*/ +/** + * @file lib/exchange_api_post-aml-OFFICER_PUB-render-form.c + * @brief Implementation of POST /aml/$OFFICER_PUB/render-form + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include "taler/taler_curl_lib.h" +#include "taler/taler_json_lib.h" +#include "taler/taler_util.h" +#include "taler/exchange/post-aml-OFFICER_PUB-render-form.h" +#include "exchange_api_curl_defaults.h" + + +struct TALER_EXCHANGE_PostAmlRenderFormHandle +{ + struct GNUNET_CURL_Context *ctx; + struct GNUNET_CURL_Job *job; + struct TALER_CURL_PostContext post_ctx; + char *base_url; + char *url; + struct TALER_AmlOfficerPrivateKeyP officer_priv; + struct TALER_AmlOfficerPublicKeyP officer_pub; + json_t *form; + TALER_EXCHANGE_PostAmlRenderFormCallback cb; + TALER_EXCHANGE_POST_AML_RENDER_FORM_RESULT_CLOSURE *cb_cls; +}; + + +/** + * Complete a raw PDF request. + */ +static void +handle_render_form_finished (void *cls, + long response_code, + const void *body, + size_t body_size) +{ + struct TALER_EXCHANGE_PostAmlRenderFormHandle *parfh = cls; + json_t *json = NULL; + struct TALER_EXCHANGE_PostAmlRenderFormResponse response = { + .hr.http_status = (unsigned int) response_code + }; + + parfh->job = NULL; + if (MHD_HTTP_OK == response_code) + { + response.details.ok.pdf = body; + response.details.ok.pdf_size = body_size; + } + else + { + json_error_t error; + + if ( (NULL != body) && + (0 != body_size) ) + json = json_loadb (body, + body_size, + JSON_REJECT_DUPLICATES, + &error); + response.hr.reply = json; + if (0 == response_code) + { + response.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + response.hr.hint = "server offline?"; + } + else + { + response.hr.ec = TALER_JSON_get_error_code (json); + response.hr.hint = TALER_JSON_get_error_hint (json); + } + } + if (NULL != parfh->cb) + { + parfh->cb (parfh->cb_cls, + &response); + parfh->cb = NULL; + } + if (NULL != json) + json_decref (json); + TALER_EXCHANGE_post_aml_render_form_cancel (parfh); +} + + +struct TALER_EXCHANGE_PostAmlRenderFormHandle * +TALER_EXCHANGE_post_aml_render_form_create ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_AmlOfficerPrivateKeyP *officer_priv, + const json_t *form) +{ + struct TALER_EXCHANGE_PostAmlRenderFormHandle *parfh; + + if ( (NULL == ctx) || + (NULL == url) || + (NULL == officer_priv) || + (! json_is_object (form)) || + (! json_is_string (json_object_get (form, + "FORM_ID"))) ) + { + GNUNET_break (0); + return NULL; + } + parfh = GNUNET_new (struct TALER_EXCHANGE_PostAmlRenderFormHandle); + parfh->ctx = ctx; + parfh->base_url = GNUNET_strdup (url); + parfh->officer_priv = *officer_priv; + GNUNET_CRYPTO_eddsa_key_get_public (&officer_priv->eddsa_priv, + &parfh->officer_pub.eddsa_pub); + parfh->form = json_deep_copy (form); + if (NULL == parfh->form) + { + TALER_EXCHANGE_post_aml_render_form_cancel (parfh); + return NULL; + } + return parfh; +} + + +enum TALER_ErrorCode +TALER_EXCHANGE_post_aml_render_form_start ( + struct TALER_EXCHANGE_PostAmlRenderFormHandle *parfh, + TALER_EXCHANGE_PostAmlRenderFormCallback cb, + TALER_EXCHANGE_POST_AML_RENDER_FORM_RESULT_CLOSURE *cb_cls) +{ + struct TALER_AmlOfficerSignatureP officer_sig; + char pub_str[sizeof (parfh->officer_pub) * 2]; + char sig_str[sizeof (officer_sig) * 2]; + char *path; + char *header; + char *end; + CURL *eh; + struct curl_slist *headers; + + if (NULL != parfh->job) + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + parfh->cb = cb; + parfh->cb_cls = cb_cls; + TALER_officer_aml_query_sign (&parfh->officer_priv, + &officer_sig); + end = GNUNET_STRINGS_data_to_string (&parfh->officer_pub, + sizeof (parfh->officer_pub), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_asprintf (&path, + "aml/%s/render-form", + pub_str); + parfh->url = TALER_url_join (parfh->base_url, + path, + NULL); + GNUNET_free (path); + if (NULL == parfh->url) + return TALER_EC_GENERIC_CONFIGURATION_INVALID; + eh = TALER_EXCHANGE_curl_easy_get_ (parfh->url); + if ( (NULL == eh) || + (GNUNET_OK != + TALER_curl_easy_post (&parfh->post_ctx, + eh, + parfh->form)) ) + { + if (NULL != eh) + curl_easy_cleanup (eh); + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + end = GNUNET_STRINGS_data_to_string (&officer_sig, + sizeof (officer_sig), + sig_str, + sizeof (sig_str)); + *end = '\0'; + GNUNET_asprintf (&header, + "%s: %s", + TALER_AML_OFFICER_SIGNATURE_HEADER, + sig_str); + headers = curl_slist_append (parfh->post_ctx.headers, + header); + GNUNET_free (header); + if (NULL == headers) + { + curl_easy_cleanup (eh); + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + parfh->post_ctx.headers = headers; + parfh->job = GNUNET_CURL_job_add_raw (parfh->ctx, + eh, + parfh->post_ctx.headers, + &handle_render_form_finished, + parfh); + if (NULL == parfh->job) + { + curl_easy_cleanup (eh); + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + } + return TALER_EC_NONE; +} + + +void +TALER_EXCHANGE_post_aml_render_form_cancel ( + struct TALER_EXCHANGE_PostAmlRenderFormHandle *parfh) +{ + if (NULL == parfh) + return; + if (NULL != parfh->job) + { + GNUNET_CURL_job_cancel (parfh->job); + parfh->job = NULL; + } + TALER_curl_easy_post_finished (&parfh->post_ctx); + if (NULL != parfh->form) + json_decref (parfh->form); + GNUNET_free (parfh->url); + GNUNET_free (parfh->base_url); + GNUNET_free (parfh); +} + + +/* end of exchange_api_post-aml-OFFICER_PUB-render-form.c */ diff --git a/src/lib/meson.build b/src/lib/meson.build @@ -27,6 +27,7 @@ libtalerexchange_la_SOURCES = [ 'exchange_api_curl_defaults.c', 'exchange_api_handle.c', 'exchange_api_post-aml-OFFICER_PUB-decision.c', + 'exchange_api_post-aml-OFFICER_PUB-render-form.c', 'exchange_api_post-auditors-AUDITOR_PUB-H_DENOM_PUB.c', 'exchange_api_post-batch-deposit.c', 'exchange_api_post-blinding-prepare.c', @@ -146,4 +147,3 @@ test( suite: ['stefan', 'installcheck'], ) -