commit 19f38bbd194336f66944121614700e4e71525e72
parent e9f963afc6563066f6f4567e3785d54b20dc00f1
Author: Tellenbach Reto <tellr1@bfh.ch>
Date: Fri, 5 Jun 2026 00:06:27 +0200
[wip] Tapler-api: authorization header missing
Diffstat:
7 files changed, 558 insertions(+), 7 deletions(-)
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
@@ -1,5 +1,5 @@
# Add files
-add_library(bank bank_api_get_config.c bank_api_curl_defaults.c)
+add_library(bank bank_api_get_config.c bank_api_curl_defaults.c bank_api_get_accounts.c)
find_package(CURL REQUIRED)
pkg_check_modules(GNUNET REQUIRED gnunetutil)
diff --git a/src/lib/bank_api_get_accounts.c b/src/lib/bank_api_get_accounts.c
@@ -0,0 +1,323 @@
+/*
+ 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_accounts.c
+ * @brief implements the Taler Bank API "GET accounts/$USERNAME" handler
+ * @author Reto Tellenbach
+ */
+
+#include <microhttpd.h>
+#include "taler/taler_json_lib.h"
+#include "taler/taler_bank_service.h"
+#include "bank_api_get_accounts.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_GetAccountsHandle
+{
+ /**
+ * The context of this handle
+ */
+ struct GNUNET_CURL_Context *ctx;
+
+ /**
+ * Function to call with the ,
+ * NULL if this has already been done.
+ */
+ TALER_BANK_AccountsCallback accounts_cb;
+
+ /**
+ * Closure to pass to
+ */
+ void *accounts_cb_cls;
+
+ /**
+ * Data for the request to get the accounts/$USERNAME 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 accounts/$USERNAME 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_config_json (const json_t *resp_obj,
+ struct TALER_BANK_AccountInformation *vi)
+{
+ const json_t **channels_pack;
+ json_t *cnt;
+ bool contact_missing;
+ bool tan_channels_missing;
+
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("balance",
+ &vi->balance),
+ TALER_JSON_spec_amount_any ("debit_threshold",
+ &vi->debit_threshold),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_array_const ("tan_channels",
+ channels_pack),
+ &tan_channels_missing),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_json ("contact_data",
+ &cnt),
+ &contact_missing),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("status",
+ &vi->status),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_uint32 ("conversion_rate_class_id",
+ &vi->conversion_rate_class_id),
+ 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;
+ }
+ if(!contact_missing)
+ {
+ struct GNUNET_JSON_Specification spec_contact[] = {
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string("email",
+ &vi->contact_data.email),
+ NULL),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("phone",
+ &vi->contact_data.phone_number),
+ NULL),
+ GNUNET_JSON_spec_end ()
+ };
+ if (GNUNET_OK != GNUNET_JSON_parse (cnt,
+ spec_contact,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+ return TALER_EC_GENERIC_JSON_INVALID;
+ }
+ }
+ if (!tan_channels_missing)
+ {
+ size_t idx;
+ json_t *val;
+ json_array_foreach (*channels_pack,
+ idx,
+ val)
+ {
+ if (idx < TMH_TCS_OPTIONS_COUNT)
+ {
+ vi->tan_channels[idx] = GNUNET_strdup (json_string_value (val));
+ }
+ }
+ }
+
+ return TALER_EC_NONE;
+}
+
+
+/**
+ * Callback used when http reply arived to a /accounts/$USERNAME request.
+ *
+ * @param cls the `struct TALER_BANK_GetAccountsHandle`
+ * @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_GetAccountsHandle *account = cls;
+ const json_t *resp_obj = gresp_obj;
+
+ struct TALER_BANK_AccountsResponse ar = {
+ .hr.response = resp_obj,
+ .hr.http_status = (unsigned int)response_code
+ };
+
+ account->job = NULL; //job was successfull, curl job cancel not needed anymore in cleanup
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received config from URL `%s' with status %ld.\n",
+ account->job_url,
+ response_code);
+
+ switch (response_code)
+ {
+ case 0:
+ GNUNET_break_op (0);
+ ar.hr.ec = TALER_EC_INVALID;
+ break;
+ case MHD_HTTP_OK:
+ if (NULL == resp_obj)
+ {
+ GNUNET_break_op (0);
+ ar.hr.http_status = 0;
+ ar.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ }
+ ar.hr.ec = decode_config_json (resp_obj,
+ &ar.details.ok.acc);
+ if (TALER_EC_NONE != ar.hr.ec)
+ {
+ GNUNET_break_op (0);
+ ar.hr.http_status = 0;
+ break;
+ }
+ break;
+ case MHD_HTTP_UNAUTHORIZED:
+ ar.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ ar.hr.hint = TALER_JSON_get_error_hint (resp_obj);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Invalid or missing credentials %u/%d\n",
+ (unsigned int) response_code,
+ (int) ar.hr.ec);
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ ar.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ ar.hr.hint = TALER_JSON_get_error_hint (resp_obj);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Missing rights %u/%d\n",
+ (unsigned int) response_code,
+ (int) ar.hr.ec);
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ ar.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ ar.hr.hint = TALER_JSON_get_error_hint (resp_obj);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "The account pointed by $USERNAME was not found %u/%d\n",
+ (unsigned int) response_code,
+ (int) ar.hr.ec);
+ break;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ ar.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ ar.hr.hint = TALER_JSON_get_error_hint (resp_obj);
+ break;
+ default:
+ ar.hr.ec = TALER_JSON_get_error_code (resp_obj);
+ ar.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) ar.hr.ec);
+ break;
+ }
+
+ account->accounts_cb (account->accounts_cb_cls, &ar);
+ TALER_BANK_get_accounts_cancel(account);
+}
+
+
+struct TALER_BANK_GetAccountsHandle *
+TALER_BANK_get_accounts ( struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *username,
+ TALER_BANK_AccountsCallback accounts_cb,
+ void *accounts_cb_cls)
+{
+ struct TALER_BANK_GetAccountsHandle *account;
+ CURL *eh;
+
+ account = GNUNET_new(struct TALER_BANK_GetAccountsHandle);
+ account->accounts_cb = accounts_cb;
+ account->accounts_cb_cls = accounts_cb_cls;
+ account->ctx = ctx;
+ account->job_url = TALER_url_join(url,
+ "accounts",
+ username,
+ NULL);
+ if(NULL == account->job_url)
+ {
+ GNUNET_break(0);
+ GNUNET_free(account);
+ return NULL;
+ }
+ GNUNET_log( GNUNET_ERROR_TYPE_INFO,
+ "Requesting bank account information with URL `%s'.\n",
+ account->job_url);
+ eh = TALER_BANK_curl_easy_get_(account->job_url);
+ if(NULL == eh)
+ {
+ GNUNET_break(0);
+ TALER_BANK_get_accounts_cancel(account);
+ return NULL;
+ }
+ account->job = GNUNET_CURL_job_add(account->ctx,
+ eh,
+ &response_cb,
+ account);
+ if(NULL == account->job)
+ {
+ GNUNET_break(0);
+ TALER_BANK_get_accounts_cancel(account);
+ return NULL;
+ }
+
+ return account;
+}
+
+
+
+void
+TALER_BANK_get_accounts_cancel ( struct TALER_BANK_GetAccountsHandle *account)
+{
+ if(NULL != account->job)
+ {
+ GNUNET_CURL_job_cancel(account->job);
+ account->job = NULL;
+ }
+ GNUNET_free(account->job_url);
+ GNUNET_free(account);
+}
diff --git a/src/lib/bank_api_get_accounts.h b/src/lib/bank_api_get_accounts.h
@@ -0,0 +1,152 @@
+/*
+ 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_accounts.h
+ * @brief implements the Taler Bank API "GET accounts/$USERNAME" handler
+ * @author Reto Tellenbach
+ */
+#ifndef BANK_API_GET_ACCOUNTS_H
+#define BANK_API_GET_ACCOUNTS_H
+
+#include "../taler/taler_digitizer_service.h"
+
+
+/**
+ * @brief Information we get from the bank about itself
+ * and is needed for the digitizer
+ */
+struct TALER_BANK_AccountInformation
+{
+ /**
+ * Available balance on the account.
+ */
+ struct TALER_Amount balance;
+
+ /**
+ * Number indicating the max debit allowed
+ * for the requesting user.
+ */
+ struct TALER_Amount debit_threshold;
+
+ /**
+ * aray of tan channels
+ * posible channel @e TEH_TanChannelOptions
+ * enabled channels for 2FA
+ * can be empty
+ */
+ char *tan_channels[TMH_TCS_OPTIONS_COUNT];
+
+ /**
+ * Addresses where to send the TAN for transactions.
+ */
+ struct TALER_BANK_ChallengeContactData contact_data;
+
+ /**
+ * 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;
+
+ /**
+ * Conversion rate class of the user
+ * Only present if conversion is activated on the server
+ *
+ */
+ uint32_t conversion_rate_class_id;
+};
+
+/**
+ * Response details for a accounts/$USERNAME request
+ */
+struct TALER_BANK_AccountsResponse
+{
+
+ /**
+ * 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_AccountInformation acc;
+
+ } ok;
+
+ } details;
+
+};
+
+
+/**
+ * Function called with information about the bank.
+ *
+ * @param cls closure
+ * @param vr response data
+ */
+typedef void
+(*TALER_BANK_AccountsCallback) (
+ void *cls,
+ const struct TALER_BANK_AccountsResponse *vr);
+
+
+/**
+ * Handle for the get config request.
+ */
+struct TALER_BANK_GetAccountsHandle;
+
+
+/**
+ * Obtain bank account inforamtion
+ * @param ctx curl context
+ * @param url bank url
+ * @param username bank account name
+ * @param accounts_cb callback
+ * @param accounts_cb_cls callback context
+ * @return NULL on failure
+ */
+struct TALER_BANK_GetAccountsHandle *
+TALER_BANK_get_accounts ( struct GNUNET_CURL_Context *ctx,
+ const char *url,
+ const char *username,
+ TALER_BANK_AccountsCallback accounts_cb,
+ void *accounts_cb_cls);
+
+
+/**
+ * Cancel accounts request. Frees if neccessary
+ * @param account handle
+ */
+void TALER_BANK_get_accounts_cancel ( struct TALER_BANK_GetAccountsHandle *account);
+
+#endif
diff --git a/src/lib/bank_api_get_config.c b/src/lib/bank_api_get_config.c
@@ -25,7 +25,6 @@
#include <microhttpd.h>
#include "bank_api_get_config.h"
-#include "bank_api_curl_defaults.h"
#include "taler/taler_json_lib.h"
/**
diff --git a/src/lib/bank_api_get_config.h b/src/lib/bank_api_get_config.h
@@ -26,8 +26,13 @@
#ifndef BANK_API_GET_CONFIG_H
#define BANK_API_GET_CONFIG_H
+#include "bank_api_curl_defaults.h"
#include "../taler/taler_digitizer_service.h"
+/**
+ * Handle for the get config request.
+ */
+struct TALER_BANK_GetConfigHandle;
/**
* @brief Information we get from the bank about itself. and is needed for the digitizer
diff --git a/src/taler-digitizer.c b/src/taler-digitizer.c
@@ -27,6 +27,7 @@
#include <gnunet/gnunet_util_lib.h>
#include "taler_digitizer_util.h"
#include "lib/bank_api_get_config.h"
+#include "lib/bank_api_get_accounts.h"
#include "digitizer_display.h"
@@ -52,6 +53,11 @@ static int enable_diagnostics;
static char *cfg_bank_base_url;
/**
+ * Taler bank account username
+ */
+static char *cfg_bank_account_username;
+
+/**
* Taler exchange backend url read from configuration file
*/
static char *cfg_exchange_base_url;
@@ -155,6 +161,11 @@ static struct GNUNET_CURL_RescheduleContext *reschedule_ctx;
*/
static struct TALER_BANK_GetConfigHandle *get_config_handle;
+/**
+ * Handle for get_accounts request
+ */
+static struct TALER_BANK_GetAccountsHandle *get_accounts_handle;
+
enum
DIGITIZER_states
@@ -325,6 +336,23 @@ on_get_config_done (void *cls,
}
+ state = DIGITIZER_STATE_IDLE;
+ GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
+ return;
+}
+
+
+static void
+on_get_accounts_done(void *cls,
+ const struct TALER_BANK_GetAccountsHandle *vr)
+{
+ TALER_LOG_DEBUG ("Callback of accounts/$USERNAME\n");
+
+ (void) cls;
+ get_accounts_handle = NULL;
+
+ ///LOGIC HERE
+
state = DIGITIZER_STATE_SCAN_QR;
GNUNET_SCHEDULER_add_now(state_controller_task,NULL);
return;
@@ -361,7 +389,8 @@ shutdown_task (void *cls)
}
/**
- *
+ * Initialising state
+ * Init: Digitizer config, Bank config, Screen task, Touch task
*/
static void Init_state_task(void *cls)
{
@@ -732,7 +761,8 @@ static void Init_state_task(void *cls)
}
/**
- *
+ * Terminal Error state
+ * When error accures due to config issues, or unrecoverable errors
*/
static void TerminalError_state_task(void *cls)
{
@@ -743,6 +773,23 @@ static void TerminalError_state_task(void *cls)
}
/**
+ * Idle state
+ * prepare for new withdrawal process and
+ * check for start signal from UI
+ */
+static void Idle_state_task(void *cls)
+{
+ (void)cls;
+ cfg_bank
+
+ get_accounts_handle = TALER_BANK_get_accounts (curl_ctx,
+ cfg_bank_base_url,
+ cfg_bank_account_username,
+ &on_get_accounts_done,
+ NULL);
+}
+
+/**
*
*/
static void state_controller_task(void *cls)
@@ -756,6 +803,11 @@ static void state_controller_task(void *cls)
GNUNET_SCHEDULER_add_now(Init_state_task,cfg);
return;
}
+ case DIGITIZER_STATE_IDLE:
+ {
+ GNUNET_SCHEDULER_add_now(Idle_state_task,NULL);
+ return;
+ }
case DIGITIZER_STATE_TERMINAL_ERROR:
{
GNUNET_SCHEDULER_add_now(TerminalError_state_task,NULL);
diff --git a/src/taler/taler_digitizer_service.h b/src/taler/taler_digitizer_service.h
@@ -26,10 +26,7 @@
#ifndef _TALER_DIGITIZER_SERVICE_H
#define _TALER_DIGITIZER_SERVICE_H
-
-#include <taler/taler_bank_service.h>
#include <taler/taler_util.h>
-#include "../lib/bank_api_get_config.h"
/**
* How compatible are the protocol version of the bank and this
@@ -130,5 +127,28 @@ struct TALER_BANK_HttpResponse
};
+/**
+ * Possible TanChannel.
+ * current options v12:
+ * - "SMS"
+ * - "EMAIL"
+ */
+enum TEH_TanChannelOptions
+{
+ TMH_TCS_SMS,
+ TMH_TCS_EMAIL,
+ TMH_TCS_OPTIONS_COUNT,
+};
+
+/**
+ * Contact Data
+ */
+struct TALER_BANK_ChallengeContactData
+{
+ const char *email;
+
+ const char *phone_number;
+};
+
#endif
\ No newline at end of file