merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 32e12126c6c7cbc3b329f8611cde593ee150bfc7
parent eb87ea594acc2e2dd8b02cb4bf8cf86802185d1c
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 23 Sep 2025 11:31:18 +0200

fix completely cursed POST donau instance logic (see #10442)

Diffstat:
Msrc/backend/taler-merchant-httpd_post-orders-ID-pay.c | 3++-
Msrc/backend/taler-merchant-httpd_private-post-donau-instance.c | 177++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/backend/taler-merchant-httpd_private-post-donau-instance.h | 8++++++++
3 files changed, 118 insertions(+), 70 deletions(-)

diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -4364,6 +4364,7 @@ phase_parse_wallet_data (struct PayContext *pc) /* Check if the needed data is present for the given donau URL */ { enum GNUNET_DB_QueryStatus qs; + qs = TMH_db->lookup_order_charity ( TMH_db->cls, pc->hc->instance->settings.id, @@ -4407,7 +4408,7 @@ phase_parse_wallet_data (struct PayContext *pc) pc->parse_wallet_data.donau_keys = DONAU_keys_from_json (donau_keys_json); json_decref (donau_keys_json); - if (! pc->parse_wallet_data.donau_keys) + if (NULL == pc->parse_wallet_data.donau_keys) { GNUNET_break_op (0); resume_pay_with_error (pc, diff --git a/src/backend/taler-merchant-httpd_private-post-donau-instance.c b/src/backend/taler-merchant-httpd_private-post-donau-instance.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2024 Taler Systems SA + Copyright (C) 2024, 2025 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 @@ -18,8 +18,8 @@ * @brief implementation of POST /donau * @author Bohdan Potuzhnyi * @author Vlada Svirsh + * @author Christian Grothoff */ - #include "platform.h" #include <jansson.h> #include "donau/donau_service.h" @@ -65,16 +65,30 @@ struct PostDonauCtx uint64_t charity_id; /** - * Data that comes back from Donau + * Handle returned by DONAU_charities_get(); needed to cancel on + * connection abort, etc. */ - struct DONAU_Charity charity; + struct DONAU_CharityGetHandle *get_handle; /** - * Handle returned by DONAU_charities_get(); needed to cancel on - * connection abort, etc. */ - struct DONAU_CharityGetHandle *get_handle; + * Response to return. + */ + struct MHD_Response *response; + + /** + * HTTP status for @e response. + */ + unsigned int http_status; + + /** + * #GNUNET_YES if we are suspended, + * #GNUNET_NO if not, + * #GNUNET_SYSERR on shutdown + */ + enum GNUNET_GenericReturnValue suspended; }; + /** * Head of active pay context DLL. */ @@ -86,6 +100,22 @@ static struct PostDonauCtx *pdc_head; static struct PostDonauCtx *pdc_tail; +void +TMH_force_post_donau_resume () +{ + for (struct PostDonauCtx *pdc = pdc_head; + NULL != pdc; + pdc = pdc->next) + { + if (GNUNET_YES == pdc->suspended) + { + pdc->suspended = GNUNET_SYSERR; + MHD_resume_connection (pdc->connection); + } + } +} + + /** * Callback for DONAU_charities_get() to handle the response. * @@ -100,32 +130,31 @@ donau_charity_get_cb (void *cls, enum GNUNET_DB_QueryStatus qs; pdc->get_handle = NULL; - if (NULL == pdc->connection) - return; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Processing DONAU charity get response"); /* Anything but 200 => propagate Donau’s response. */ if (MHD_HTTP_OK != gcr->hr.http_status) { - TALER_MHD_reply_with_error (pdc->connection, - gcr->hr.http_status, - gcr->hr.ec, - gcr->hr.hint); + pdc->http_status = MHD_HTTP_BAD_GATEWAY; + pdc->response = TALER_MHD_MAKE_JSON_PACK ( + TALER_MHD_PACK_EC (gcr->hr.ec), + GNUNET_JSON_pack_uint64 ("donau_http_status", + gcr->hr.http_status)); + pdc->suspended = GNUNET_NO; MHD_resume_connection (cls); TALER_MHD_daemon_trigger (); return; } - pdc->charity = gcr->details.ok.charity; - - if (GNUNET_memcmp (&pdc->charity.charity_pub.eddsa_pub, + if (GNUNET_memcmp (&gcr->details.ok.charity.charity_pub.eddsa_pub, &pdc->hc->instance->merchant_pub.eddsa_pub)) { - TALER_MHD_reply_with_error (pdc->connection, - MHD_HTTP_CONFLICT, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "Merchant public key of this instance and charity public key received " - "from DONAU for this charity_id do not match"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Charity key at donau does not match our merchant key\n"); + pdc->http_status = MHD_HTTP_CONFLICT; + pdc->response = TALER_MHD_make_error ( + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "charity_pub != merchant_pub"); MHD_resume_connection (pdc->connection); TALER_MHD_daemon_trigger (); return; @@ -133,22 +162,31 @@ donau_charity_get_cb (void *cls, qs = TMH_db->insert_donau_instance (TMH_db->cls, pdc->donau_url, - &pdc->charity, + &gcr->details.ok.charity, pdc->charity_id); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - TALER_MHD_reply_with_error (pdc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "insert_donau_instance: Failed to insert Donau instance"); + GNUNET_break (0); + pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + pdc->response = TALER_MHD_make_error ( + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_donau_instance"); break; case GNUNET_DB_STATUS_SOFT_ERROR: - TALER_MHD_reply_with_error (pdc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_SOFT_FAILURE, - "insert_donau_instance: Database operation failed"); + GNUNET_break (0); + pdc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + pdc->response = TALER_MHD_make_error ( + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_donau_instance"); + break; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* presumably idempotent, no need to notify, but still respond */ + pdc->http_status = MHD_HTTP_NO_CONTENT; + pdc->response = MHD_create_response_from_buffer_static (0, + NULL); + TALER_MHD_add_global_headers (pdc->response, + false); break; case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: { @@ -160,14 +198,15 @@ donau_charity_get_cb (void *cls, &es, pdc->donau_url, strlen (pdc->donau_url) + 1); - TALER_MHD_reply_static (pdc->connection, - MHD_HTTP_NO_CONTENT, - NULL, - NULL, - 0); + pdc->http_status = MHD_HTTP_NO_CONTENT; + pdc->response = MHD_create_response_from_buffer_static (0, + NULL); + TALER_MHD_add_global_headers (pdc->response, + false); break; } } + pdc->suspended = GNUNET_NO; MHD_resume_connection (pdc->connection); TALER_MHD_daemon_trigger (); } @@ -184,8 +223,10 @@ post_donau_cleanup (void *cls) struct PostDonauCtx *pdc = cls; if (pdc->get_handle) + { DONAU_charity_get_cancel (pdc->get_handle); - + pdc->get_handle = NULL; + } GNUNET_CONTAINER_DLL_remove (pdc_head, pdc_tail, pdc); @@ -207,54 +248,42 @@ TMH_private_post_donau_instance (const struct TMH_RequestHandler *rh, struct TMH_HandlerContext *hc) { struct PostDonauCtx *pdc = hc->ctx; - const char *donau_url = NULL; - uint64_t charity_id = -1; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("donau_url", - &donau_url), - GNUNET_JSON_spec_uint64 ("charity_id", - &charity_id), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - TALER_MHD_parse_json_data (connection, - hc->request_body, - spec)) - { - return MHD_NO; - } - if ( (NULL == donau_url) || - (-1 == charity_id) ) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "Missing donau_url or charity_id"); - } if (NULL == pdc) { pdc = GNUNET_new (struct PostDonauCtx); pdc->connection = connection; pdc->hc = hc; - pdc->donau_url = donau_url; - pdc->charity_id = charity_id; hc->ctx = pdc; hc->cc = &post_donau_cleanup; GNUNET_CONTAINER_DLL_insert (pdc_head, pdc_tail, pdc); + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("donau_url", + &pdc->donau_url), + GNUNET_JSON_spec_uint64 ("charity_id", + &pdc->charity_id), + GNUNET_JSON_spec_end () + }; + if (GNUNET_OK != + TALER_MHD_parse_json_data (connection, + hc->request_body, + spec)) + { + GNUNET_break_op (0); + return MHD_NO; + } + } pdc->get_handle = DONAU_charity_get (TMH_curl_ctx, - donau_url, - charity_id, + pdc->donau_url, + pdc->charity_id, NULL, /* bearer */ &donau_charity_get_cb, pdc); - if (NULL == pdc->get_handle) { GNUNET_break (0); @@ -264,8 +293,18 @@ TMH_private_post_donau_instance (const struct TMH_RequestHandler *rh, TALER_EC_GENERIC_ALLOCATION_FAILURE, "Failed to initiate Donau lookup"); } + pdc->suspended = GNUNET_YES; MHD_suspend_connection (connection); return MHD_YES; } - return MHD_YES; + + if (NULL != pdc->response) + { + GNUNET_break (GNUNET_NO == pdc->suspended); + return MHD_queue_response (pdc->connection, + pdc->http_status, + pdc->response); + } + GNUNET_break (GNUNET_SYSERR == pdc->suspended); + return MHD_NO; } diff --git a/src/backend/taler-merchant-httpd_private-post-donau-instance.h b/src/backend/taler-merchant-httpd_private-post-donau-instance.h @@ -25,6 +25,14 @@ #include "taler-merchant-httpd.h" + +/** + * Resume all connections suspended on Donau-interaction during shutdown. + */ +void +TMH_force_post_donau_resume (void); + + /** * Handle a POST "/donau" request. *