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:
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);
}