commit 01d76ab69fa5f99e3a66d6bd3e9e87699f3fd352
parent c3347d0e469b617a0cac1dbb2f8d9ec23f08f1a8
Author: Tellenbach Reto <tellr1@bfh.ch>
Date: Mon, 8 Jun 2026 00:48:57 +0200
[wip] Tapler-api: GET /withdrawals/ created, but longpoll options missing
Diffstat:
5 files changed, 449 insertions(+), 8 deletions(-)
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
@@ -3,7 +3,9 @@ add_library(bank
bank_api_get_config.c
bank_api_curl_defaults.c
bank_api_get_accounts.c
- bank_api_post_accounts_withdrawals.c)
+ bank_api_get_withdrawals.c
+ bank_api_post_accounts_withdrawals.c
+ bank_api_post_accounts_withdrawals_confirm.c)
add_library(common
api_common.c api_parse.c)
diff --git a/src/lib/bank_api_get_withdrawals.c b/src/lib/bank_api_get_withdrawals.c
@@ -0,0 +1,278 @@
+/*
+ This file is part of TALER cash2ecash
+ Copyright (C) 2026 GNUnet e.V.
+
+ This program 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 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ 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 this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+/**
+ * @file bank-lib/bank_api_get_withdrawals.c
+ * @brief implements the Taler Bank API "GET /withdrawals/$WITHDRAWAL_ID" handler
+ * @author Reto Tellenbach
+ */
+
+#include <microhttpd.h>
+#include "taler/taler_json_lib.h"
+#include "taler/taler_bank_service.h"
+#include "bank_api_get_withdrawals.h"
+#include "bank_api_curl_defaults.h"
+
+/**
+ * Log error related to CURL operations.
+ *
+ * @param type log level
+ * @param function which function failed to run
+ * @param code what was the curl error code
+ */
+#define CURL_STRERROR(type, function, code) \
+ GNUNET_log (type, \
+ "Curl function `%s' has failed at `%s:%d' with error: %s", \
+ function, __FILE__, __LINE__, curl_easy_strerror (code));
+
+/**
+ * Handle for the accounts request.
+ */
+struct TALER_BANK_GetWithdrawalHandle
+{
+ /**
+ * The context of this handle
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Function to call with the ,
+ * NULL if this has already been done.
+ */
+ TALER_BANK_WithdrawalCallback withdrawals_cb;
+
+ /**
+ * Closure to pass to
+ */
+ void *withdrawals_cb_cls;
+
+ /**
+ * Data for the request to get the /withdrawals/$WITHDRAWAL_ID of a bank,
+ * NULL once we are past stage #MHS_INIT.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * The whole request line
+ */
+ char *job_url;
+};
+
+
+/**
+ * Decode the JSON in @a resp_obj from the /withdrawals/$WITHDRAWAL_ID response
+ *
+ * @param[in] resp_obj JSON object to parse
+ * @param[in,out] vi where to store the results we decoded
+ * @param[out] vc where to store account info data
+ * @return #TALER_EC_NONE on success
+ */
+static enum TALER_ErrorCode
+decode_withdrawals_json (const json_t *resp_obj,
+ struct TALER_BANK_WithdrawalInfo *vi)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received body\n`%s'\n",
+ json_dumps(resp_obj, 0));
+
+
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("status",
+ &vi->status),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("amount",
+ &vi->amount),NULL),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount_any ("suggested_amount",
+ &vi->suggested_amount),NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_bool ("no_amount_to_wallet",
+ &vi->no_amount_to_wallet),NULL),
+ GNUNET_JSON_spec_string ("username",
+ &vi->username),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("selected_reserve_pub",
+ &vi->selected_reserve_pub),NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("selected_exchange_account",
+ &vi->selected_exchange_account),NULL),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (JSON_OBJECT != json_typeof (resp_obj))
+ {
+ GNUNET_break_op (0);
+ return TALER_EC_GENERIC_JSON_INVALID;
+ }
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (resp_obj,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return TALER_EC_GENERIC_JSON_INVALID;
+ }
+
+ return TALER_EC_NONE;
+}
+
+
+/**
+ * Callback used when http reply arived to a /withdrawals/$WITHDRAWAL_ID request.
+ *
+ * @param cls the `struct TALER_BANK_GetWithdrawalHandle`
+ * @param response_code HTTP response code or 0 on error
+ * @param gresp_obj JSON result, NULL on error, must be a `const json_t *`
+ */
+static void
+response_cb(void *cls,
+ long response_code,
+ const void *gresp_obj)
+{
+ struct TALER_BANK_GetWithdrawalHandle *withdrawal = cls;
+ const json_t *resp_obj = gresp_obj;
+
+ struct TALER_BANK_WithdrawalResponse wr = {
+ .hr.response = resp_obj,
+ .hr.http_status = (unsigned int)response_code
+ };
+
+ withdrawal->job = NULL; //job was successfull, curl job cancel not needed anymore in cleanup
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received withdrawal info from URL `%s' with status %ld.\n",
+ withdrawal->job_url,
+ response_code);
+
+ switch (response_code)
+ {
+ case 0:
+ GNUNET_break_op (0);
+ wr.hr.ec = TALER_EC_INVALID;
+ break;
+ case MHD_HTTP_OK:
+ if (NULL == resp_obj)
+ {
+ GNUNET_break_op (0);
+ wr.hr.http_status = 0;
+ wr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ wr.hr.ec = decode_withdrawals_json (resp_obj,
+ &wr.details.ok.acc);
+ if (TALER_EC_NONE != wr.hr.ec)
+ {
+ GNUNET_break_op (0);
+ wr.hr.http_status = 0;
+ break;
+ }
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ wr.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ wr.hr.hint = TALER_JSON_get_error_hint (resp_obj);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "The operation was not found %u/%d\n",
+ (unsigned int) response_code,
+ (int) wr.hr.ec);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ wr.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ wr.hr.hint = TALER_JSON_get_error_hint (resp_obj);
+ break;
+ default:
+ wr.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ wr.hr.hint = TALER_JSON_get_error_hint (resp_obj);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d\n",
+ (unsigned int) response_code,
+ (int) wr.hr.ec);
+ break;
+ }
+
+ withdrawal->withdrawals_cb (withdrawal->withdrawals_cb_cls, &wr);
+ TALER_BANK_get_withdrawal_cancel(withdrawal);
+}
+
+
+struct TALER_BANK_GetWithdrawalHandle *
+TALER_BANK_get_withdrawal ( struct GNUNET_CURL_Context *ctx,
+ const char *base_url,
+ const char *wopid,
+
+ TALER_BANK_WithdrawalCallback withdrawals_cb,
+ void *withdrawals_cb_cls)
+{
+ struct TALER_BANK_GetWithdrawalHandle *withdrawal;
+ char *url;
+ CURL *eh;
+
+ withdrawal = GNUNET_new(struct TALER_BANK_GetWithdrawalHandle);
+ withdrawal->withdrawals_cb = withdrawals_cb;
+ withdrawal->withdrawals_cb_cls = withdrawals_cb_cls;
+ withdrawal->ctx = ctx;
+ GNUNET_asprintf(&url,
+ "withdrawals/%s",
+ wopid);
+ withdrawal->job_url = TALER_url_join(base_url,
+ url,
+ NULL);
+ if(NULL == withdrawal->job_url)
+ {
+ GNUNET_break(0);
+ GNUNET_free(withdrawal);
+ return NULL;
+ }
+ GNUNET_log( GNUNET_ERROR_TYPE_INFO,
+ "Requesting bank account information with URL `%s'.\n",
+ withdrawal->job_url);
+ eh = TALER_BANK_curl_easy_get_(withdrawal->job_url);
+ if(NULL == eh)
+ {
+ GNUNET_break(0);
+ TALER_BANK_get_withdrawal_cancel(withdrawal);
+ return NULL;
+ }
+ withdrawal->job =
+ GNUNET_CURL_job_add(withdrawal->ctx,
+ eh,
+ &response_cb,
+ withdrawal);
+ if(NULL == withdrawal->job)
+ {
+ GNUNET_break(0);
+ TALER_BANK_get_withdrawal_cancel(withdrawal);
+ return NULL;
+ }
+
+ return withdrawal;
+}
+
+
+
+void
+TALER_BANK_get_withdrawals_cancel ( struct TALER_BANK_GetWithdrawalHandle *withdrawal)
+{
+ if(NULL != withdrawal->job)
+ {
+ GNUNET_CURL_job_cancel(withdrawal->job);
+ withdrawal->job = NULL;
+ }
+ GNUNET_free(withdrawal->job_url);
+ GNUNET_free(withdrawal);
+}
diff --git a/src/lib/bank_api_get_withdrawals.h b/src/lib/bank_api_get_withdrawals.h
@@ -0,0 +1,154 @@
+/*
+ This file is part of TALER cash2ecash
+ Copyright (C) 2026 GNUnet e.V.
+
+ This program 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 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ 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 this program. If not, see <https://www.gnu.org/licenses/>.
+*/
+/**
+ * @file lib/bank_api_get_withdrawals.h
+ * @brief implements the Taler Bank API "GET /withdrawals/$WITHDRAWAL_ID" handler
+ * @author Reto Tellenbach
+ */
+#ifndef BANK_API_GET_WITHDRAWALS_H
+#define BANK_API_GET_WITHDRAWALS_H
+
+#include "api_common.h"
+
+
+/**
+ * @brief Information about withrawal operation
+ */
+struct TALER_BANK_WithdrawalInfo
+{
+ /**
+ * Current status of the account
+ * active: the account can be used
+ * locked: the account can be used but cannot create new tokens
+ * deleted: the account has been deleted but is retained for compliance
+ */
+ const char *status;
+
+ /**
+ * Amount that will be withdrawn with this operation
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Suggestion for the amount to be withdrawn with this
+ * operation
+ */
+ struct TALER_Amount suggested_amount;
+
+ /**
+ * indication that no amount was set at creation.
+ * therfor amount must be set in confirm step
+ */
+ bool no_amount_to_wallet;
+
+ /**
+ * Bank account username
+ */
+ const char *username;
+
+ /**
+ * Reserve public key selected by the exchange,
+ * only non-null if status is selected or confirmed.
+ */
+ const char *selected_reserve_pub;
+
+ /**
+ * Exchange account selected by the wallet
+ * only non-null if status is selected or confirmed.
+ */
+ const char *selected_exchange_account;
+};
+
+/**
+ * Response details for a /withdrawals/$WITHDRAWAL_ID request
+ */
+struct TALER_BANK_WithdrawalResponse
+{
+
+ /**
+ * HTTP response
+ */
+ struct TALER_BANK_HttpResponse hr;
+
+ /**
+ * Details returned depending on the @e http_status.
+ */
+ union
+ {
+
+ /**
+ * Details if status was request was succesfull
+ */
+ struct
+ {
+
+ /**
+ * Config data returned by accounts/$USERNAME
+ */
+ struct TALER_BANK_WithdrawalInfo acc;
+
+ } ok;
+
+ } details;
+
+};
+
+
+/**
+ * Function called with information about the withdrawal operation
+ *
+ * @param cls closure
+ * @param vr response data
+ */
+typedef void
+(*TALER_BANK_WithdrawalCallback) (
+ void *cls,
+ const struct TALER_BANK_WithdrawalResponse *vr);
+
+
+/**
+ * Handle for the get withdrawal request.
+ */
+struct TALER_BANK_GetWithdrawalHandle;
+
+
+/**
+ * Obtain wopid inforamtion
+ * @param ctx curl context
+ * @param url bank url
+ * @param wopid withdrawal operation id
+ * @param withdrawal_cb callback
+ * @param withdrawal_cb_cls callback context
+ * @return NULL on failure
+ */
+struct TALER_BANK_GetWithdrawalHandle *
+TALER_BANK_get_withdrawal ( struct GNUNET_CURL_Context *ctx,
+ const char *base_url,
+ const char *wopid,
+ TALER_BANK_WithdrawalCallback withdrawals_cb,
+ void *withdrawals_cb_cls);
+
+
+/**
+ * Cancel withdrawal request. Frees if neccessary
+ * @param withdrawal handle
+ */
+void TALER_BANK_get_withdrawal_cancel ( struct TALER_BANK_GetWithdrawalHandle *withdrawal);
+
+#endif
diff --git a/src/lib/bank_api_post_accounts_withdrawals_confirm.c b/src/lib/bank_api_post_accounts_withdrawals_confirm.c
@@ -169,10 +169,17 @@ response_cb(void *cls,
{
GNUNET_break_op (0);
pacwr.hr.http_status = 0;
- pacwr.hr.ec = decode_challenge_json (resp_obj,
+ break;
+ }
+ pacwr.hr.ec = decode_challenge_json (resp_obj,
&pacwr.details.ok.challenge);
+ if (TALER_EC_NONE != pacwr.hr.ec)
+ {
+ GNUNET_break_op (0);
+ pacwr.hr.http_status = 0;
break;
}
+ break;
case MHD_HTTP_OK:
if (NULL == resp_obj)
{
@@ -235,7 +242,7 @@ response_cb(void *cls,
}
pcwh->woc_cb (pcwh->woc_cb_cls, &pacwr);
- TALER_BANK_post_withdrawal_create_cancel(pcwh);
+ TALER_BANK_post_withdrawal_confirm_cancel(pcwh);
}
/**
@@ -267,7 +274,7 @@ TALER_BANK_post_withdrawal_confirm_create ( struct GNUNET_CURL_Context *ctx,
{
GNUNET_free (usr);
GNUNET_break (0);
- TALER_BANK_post_withdrawal_create_cancel (pwc);
+ TALER_BANK_post_withdrawal_confirm_cancel (pwc);
return GNUNET_NO;
}
GNUNET_free (usr);
@@ -299,7 +306,7 @@ TALER_BANK_post_withdrawal_confirm ( struct TALER_BANK_PostWithdrawalConfirmHand
if(GNUNET_OK != DIGITIZER_setup_auth_(handle->easy_handle,handle->authorization))
{
GNUNET_break(0);
- TALER_BANK_post_withdrawal_create_cancel(handle);
+ TALER_BANK_post_withdrawal_confirm_cancel(handle);
return GNUNET_NO;
}
if(GNUNET_OK !=
@@ -308,7 +315,7 @@ TALER_BANK_post_withdrawal_confirm ( struct TALER_BANK_PostWithdrawalConfirmHand
req))
{
GNUNET_break(0);
- TALER_BANK_post_withdrawal_create_cancel(handle);
+ TALER_BANK_post_withdrawal_confirm_cancel(handle);
return GNUNET_NO;
}
@@ -324,7 +331,7 @@ TALER_BANK_post_withdrawal_confirm ( struct TALER_BANK_PostWithdrawalConfirmHand
if(NULL == handle->job)
{
GNUNET_break(0);
- TALER_BANK_post_withdrawal_create_cancel(handle);
+ TALER_BANK_post_withdrawal_confirm_cancel(handle);
return GNUNET_NO;
}
diff --git a/src/lib/bank_api_post_accounts_withdrawals_confirm.h b/src/lib/bank_api_post_accounts_withdrawals_confirm.h
@@ -51,7 +51,7 @@ struct TALER_BANK_ChallengeResponse
/**
* temporal json object for parsing
*/
- json_t blc;
+ json_t *blc;
};
/**