exchange

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

commit 3ee66c46d4bbd837f780624db52aa2a6eb30928a
parent 643dbfe5eb4c2aabeb12236f7a427efe4c389895
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  4 Apr 2026 10:08:35 +0200

fix API style for purse deletion API

Diffstat:
Msrc/include/taler/taler-exchange/delete-purses-PURSE_PUB.h | 81++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/lib/exchange_api_delete-purses-PURSE_PUB.c | 164+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/testing/testing_api_cmd_purse_delete.c | 38++++++++++++++++++++++++++------------
3 files changed, 173 insertions(+), 110 deletions(-)

diff --git a/src/include/taler/taler-exchange/delete-purses-PURSE_PUB.h b/src/include/taler/taler-exchange/delete-purses-PURSE_PUB.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2025 Taler Systems SA + Copyright (C) 2014-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 @@ -24,9 +24,34 @@ #include <taler/taler-exchange/common.h> /** + * Handle for an operation to DELETE /purses/$PURSE_PUB. + */ +struct TALER_EXCHANGE_DeletePursesHandle; + + +/** + * Set up DELETE /purses/$PURSE_PUB operation. + * Note that you must explicitly start the operation after setup. + * + * Asks the exchange to delete a purse. Will only succeed if + * the purse was not yet merged and did not yet time out. + * + * @param ctx CURL context + * @param url exchange base URL + * @param purse_priv private key of the purse + * @return handle to operation, NULL upon error + */ +struct TALER_EXCHANGE_DeletePursesHandle * +TALER_EXCHANGE_delete_purses_create ( + struct GNUNET_CURL_Context *ctx, + const char *url, + const struct TALER_PurseContractPrivateKeyP *purse_priv); + + +/** * Response generated for a purse deletion request. */ -struct TALER_EXCHANGE_PurseDeleteResponse +struct TALER_EXCHANGE_DeletePursesResponse { /** * Full HTTP response. @@ -35,6 +60,14 @@ struct TALER_EXCHANGE_PurseDeleteResponse }; +#ifndef TALER_EXCHANGE_DELETE_PURSES_RESULT_CLOSURE +/** + * Type of the closure used by + * the #TALER_EXCHANGE_DeletePursesCallback. + */ +#define TALER_EXCHANGE_DELETE_PURSES_RESULT_CLOSURE void +#endif /* TALER_EXCHANGE_DELETE_PURSES_RESULT_CLOSURE */ + /** * Function called with information about the deletion * of a purse. @@ -43,44 +76,36 @@ struct TALER_EXCHANGE_PurseDeleteResponse * @param pdr HTTP response data */ typedef void -(*TALER_EXCHANGE_PurseDeleteCallback) ( - void *cls, - const struct TALER_EXCHANGE_PurseDeleteResponse *pdr); - - -/** - * @brief Handle for a DELETE /purses/$PID request. - */ -struct TALER_EXCHANGE_PurseDeleteHandle; +(*TALER_EXCHANGE_DeletePursesCallback) ( + TALER_EXCHANGE_DELETE_PURSES_RESULT_CLOSURE *cls, + const struct TALER_EXCHANGE_DeletePursesResponse *pdr); /** - * Asks the exchange to delete a purse. Will only succeed if - * the purse was not yet merged and did not yet time out. + * Start DELETE /purses/$PURSE_PUB operation. * - * @param ctx CURL context - * @param url exchange base URL - * @param purse_priv private key of the purse + * @param[in,out] dph operation to start * @param cb function to call with the exchange's result * @param cb_cls closure for @a cb - * @return the request handle; NULL upon error + * @return status code, #TALER_EC_NONE on success */ -struct TALER_EXCHANGE_PurseDeleteHandle * -TALER_EXCHANGE_purse_delete ( - struct GNUNET_CURL_Context *ctx, - const char *url, - const struct TALER_PurseContractPrivateKeyP *purse_priv, - TALER_EXCHANGE_PurseDeleteCallback cb, - void *cb_cls); +enum TALER_ErrorCode +TALER_EXCHANGE_delete_purses_start ( + struct TALER_EXCHANGE_DeletePursesHandle *dph, + TALER_EXCHANGE_DeletePursesCallback cb, + TALER_EXCHANGE_DELETE_PURSES_RESULT_CLOSURE *cb_cls); /** - * Cancel #TALER_EXCHANGE_purse_delete() operation. + * Cancel DELETE /purses/$PURSE_PUB operation. This function must not be + * called by clients after the TALER_EXCHANGE_DeletePursesCallback has been + * invoked (as in those cases it'll be called internally by the + * implementation already). * - * @param pdh handle of the operation to cancel + * @param[in] dph operation to cancel */ void -TALER_EXCHANGE_purse_delete_cancel ( - struct TALER_EXCHANGE_PurseDeleteHandle *pdh); +TALER_EXCHANGE_delete_purses_cancel ( + struct TALER_EXCHANGE_DeletePursesHandle *dph); #endif diff --git a/src/lib/exchange_api_delete-purses-PURSE_PUB.c b/src/lib/exchange_api_delete-purses-PURSE_PUB.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-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 @@ -17,7 +17,6 @@ /** * @file lib/exchange_api_delete-purses-PURSE_PUB.c * @brief Implementation of the client to delete a purse - * into an account * @author Christian Grothoff */ #include "taler/platform.h" @@ -35,13 +34,18 @@ /** - * @brief A purse delete with deposit handle + * @brief A purse delete handle */ -struct TALER_EXCHANGE_PurseDeleteHandle +struct TALER_EXCHANGE_DeletePursesHandle { /** - * The url for this request. + * The base url for this request. + */ + char *base_url; + + /** + * The full url for this request, set during _start. */ char *url; @@ -53,17 +57,27 @@ struct TALER_EXCHANGE_PurseDeleteHandle /** * Function to call with the result. */ - TALER_EXCHANGE_PurseDeleteCallback cb; + TALER_EXCHANGE_DeletePursesCallback cb; /** * Closure for @a cb. */ - void *cb_cls; + TALER_EXCHANGE_DELETE_PURSES_RESULT_CLOSURE *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; /** * Header with the purse_sig. */ struct curl_slist *xhdr; + + /** + * Public key of the purse (derived from private key in _create). + */ + struct TALER_PurseContractPublicKeyP purse_pub; }; @@ -71,7 +85,7 @@ struct TALER_EXCHANGE_PurseDeleteHandle * Function called when we're done processing the * HTTP DELETE /purse/$PID request. * - * @param cls the `struct TALER_EXCHANGE_PurseDeleteHandle` + * @param cls the `struct TALER_EXCHANGE_DeletePursesHandle` * @param response_code HTTP response code, 0 on error * @param response parsed JSON result, NULL on error */ @@ -80,14 +94,14 @@ handle_purse_delete_finished (void *cls, long response_code, const void *response) { - struct TALER_EXCHANGE_PurseDeleteHandle *pdh = cls; + struct TALER_EXCHANGE_DeletePursesHandle *dph = cls; const json_t *j = response; - struct TALER_EXCHANGE_PurseDeleteResponse dr = { + struct TALER_EXCHANGE_DeletePursesResponse dr = { .hr.reply = j, .hr.http_status = (unsigned int) response_code }; - pdh->job = NULL; + dph->job = NULL; switch (response_code) { case 0: @@ -129,60 +143,32 @@ handle_purse_delete_finished (void *cls, dr.hr.ec = TALER_JSON_get_error_code (j); dr.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u/%d for exchange deposit\n", + "Unexpected response code %u/%d for exchange purse delete\n", (unsigned int) response_code, dr.hr.ec); GNUNET_break_op (0); break; } - pdh->cb (pdh->cb_cls, + dph->cb (dph->cb_cls, &dr); - TALER_EXCHANGE_purse_delete_cancel (pdh); + TALER_EXCHANGE_delete_purses_cancel (dph); } -struct TALER_EXCHANGE_PurseDeleteHandle * -TALER_EXCHANGE_purse_delete ( +struct TALER_EXCHANGE_DeletePursesHandle * +TALER_EXCHANGE_delete_purses_create ( struct GNUNET_CURL_Context *ctx, const char *url, - const struct TALER_PurseContractPrivateKeyP *purse_priv, - TALER_EXCHANGE_PurseDeleteCallback cb, - void *cb_cls) + const struct TALER_PurseContractPrivateKeyP *purse_priv) { - struct TALER_EXCHANGE_PurseDeleteHandle *pdh; - CURL *eh; - struct TALER_PurseContractPublicKeyP purse_pub; + struct TALER_EXCHANGE_DeletePursesHandle *dph; struct TALER_PurseContractSignatureP purse_sig; - char arg_str[sizeof (purse_pub) * 2 + 32]; - pdh = GNUNET_new (struct TALER_EXCHANGE_PurseDeleteHandle); - pdh->cb = cb; - pdh->cb_cls = cb_cls; + dph = GNUNET_new (struct TALER_EXCHANGE_DeletePursesHandle); + dph->ctx = ctx; + dph->base_url = GNUNET_strdup (url); GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv->eddsa_priv, - &purse_pub.eddsa_pub); - { - char pub_str[sizeof (purse_pub) * 2]; - char *end; - - end = GNUNET_STRINGS_data_to_string (&purse_pub, - sizeof (purse_pub), - pub_str, - sizeof (pub_str)); - *end = '\0'; - GNUNET_snprintf (arg_str, - sizeof (arg_str), - "purses/%s", - pub_str); - } - pdh->url = TALER_url_join (url, - arg_str, - NULL); - if (NULL == pdh->url) - { - GNUNET_break (0); - GNUNET_free (pdh); - return NULL; - } + &dph->purse_pub.eddsa_pub); TALER_wallet_purse_delete_sign (purse_priv, &purse_sig); { @@ -196,18 +182,53 @@ TALER_EXCHANGE_purse_delete ( "Taler-Purse-Signature: %s", delete_str); GNUNET_free (delete_str); - pdh->xhdr = curl_slist_append (NULL, + dph->xhdr = curl_slist_append (NULL, xhdr); GNUNET_free (xhdr); } - eh = TALER_EXCHANGE_curl_easy_get_ (pdh->url); + return dph; +} + + +enum TALER_ErrorCode +TALER_EXCHANGE_delete_purses_start ( + struct TALER_EXCHANGE_DeletePursesHandle *dph, + TALER_EXCHANGE_DeletePursesCallback cb, + TALER_EXCHANGE_DELETE_PURSES_RESULT_CLOSURE *cb_cls) +{ + CURL *eh; + char arg_str[sizeof (dph->purse_pub) * 2 + 32]; + + dph->cb = cb; + dph->cb_cls = cb_cls; + { + char pub_str[sizeof (dph->purse_pub) * 2]; + char *end; + + end = GNUNET_STRINGS_data_to_string (&dph->purse_pub, + sizeof (dph->purse_pub), + pub_str, + sizeof (pub_str)); + *end = '\0'; + GNUNET_snprintf (arg_str, + sizeof (arg_str), + "purses/%s", + pub_str); + } + dph->url = TALER_url_join (dph->base_url, + arg_str, + NULL); + if (NULL == dph->url) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not construct request URL.\n"); + return TALER_EC_GENERIC_CONFIGURATION_INVALID; + } + eh = TALER_EXCHANGE_curl_easy_get_ (dph->url); if (NULL == eh) { GNUNET_break (0); - curl_slist_free_all (pdh->xhdr); - GNUNET_free (pdh->url); - GNUNET_free (pdh); - return NULL; + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; } GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, @@ -215,29 +236,32 @@ TALER_EXCHANGE_purse_delete ( MHD_HTTP_METHOD_DELETE)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "URL for purse delete: `%s'\n", - pdh->url); - pdh->job = GNUNET_CURL_job_add2 (ctx, + dph->url); + dph->job = GNUNET_CURL_job_add2 (dph->ctx, eh, - pdh->xhdr, + dph->xhdr, &handle_purse_delete_finished, - pdh); - return pdh; + dph); + if (NULL == dph->job) + return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; + return TALER_EC_NONE; } void -TALER_EXCHANGE_purse_delete_cancel ( - struct TALER_EXCHANGE_PurseDeleteHandle *pdh) +TALER_EXCHANGE_delete_purses_cancel ( + struct TALER_EXCHANGE_DeletePursesHandle *dph) { - if (NULL != pdh->job) + if (NULL != dph->job) { - GNUNET_CURL_job_cancel (pdh->job); - pdh->job = NULL; + GNUNET_CURL_job_cancel (dph->job); + dph->job = NULL; } - curl_slist_free_all (pdh->xhdr); - GNUNET_free (pdh->url); - GNUNET_free (pdh); + curl_slist_free_all (dph->xhdr); + GNUNET_free (dph->url); + GNUNET_free (dph->base_url); + GNUNET_free (dph); } -/* end of exchange_api_purse_delete.c */ +/* end of exchange_api_delete-purses-PURSE_PUB.c */ diff --git a/src/testing/testing_api_cmd_purse_delete.c b/src/testing/testing_api_cmd_purse_delete.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2020 Taler Systems SA + Copyright (C) 2020-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 @@ -38,7 +38,7 @@ struct PurseDeleteState /** * Purse delete handle while operation is running. */ - struct TALER_EXCHANGE_PurseDeleteHandle *pdh; + struct TALER_EXCHANGE_DeletePursesHandle *dph; /** * Our interpreter. @@ -67,11 +67,11 @@ struct PurseDeleteState */ static void purse_delete_cb (void *cls, - const struct TALER_EXCHANGE_PurseDeleteResponse *pdr) + const struct TALER_EXCHANGE_DeletePursesResponse *pdr) { struct PurseDeleteState *pds = cls; - pds->pdh = NULL; + pds->dph = NULL; if (pds->expected_response_code != pdr->hr.http_status) { TALER_TESTING_unexpected_status (pds->is, @@ -124,18 +124,32 @@ purse_delete_run (void *cls, return; } pds->is = is; - pds->pdh = TALER_EXCHANGE_purse_delete ( + pds->dph = TALER_EXCHANGE_delete_purses_create ( TALER_TESTING_interpreter_get_context (is), exchange_url, - purse_priv, - &purse_delete_cb, - pds); - if (NULL == pds->pdh) + purse_priv); + if (NULL == pds->dph) { GNUNET_break (0); TALER_TESTING_interpreter_fail (is); return; } + { + enum TALER_ErrorCode ec; + + ec = TALER_EXCHANGE_delete_purses_start ( + pds->dph, + &purse_delete_cb, + pds); + if (TALER_EC_NONE != ec) + { + GNUNET_break (0); + TALER_EXCHANGE_delete_purses_cancel (pds->dph); + pds->dph = NULL; + TALER_TESTING_interpreter_fail (is); + return; + } + } } @@ -152,12 +166,12 @@ purse_delete_cleanup (void *cls, { struct PurseDeleteState *pds = cls; - if (NULL != pds->pdh) + if (NULL != pds->dph) { TALER_TESTING_command_incomplete (pds->is, cmd->label); - TALER_EXCHANGE_purse_delete_cancel (pds->pdh); - pds->pdh = NULL; + TALER_EXCHANGE_delete_purses_cancel (pds->dph); + pds->dph = NULL; } GNUNET_free (pds); }