cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | README | LICENSE

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:
Msrc/lib/CMakeLists.txt | 2+-
Asrc/lib/bank_api_get_accounts.c | 323+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/bank_api_get_accounts.h | 152+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/bank_api_get_config.c | 1-
Msrc/lib/bank_api_get_config.h | 5+++++
Msrc/taler-digitizer.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/taler/taler_digitizer_service.h | 26+++++++++++++++++++++++---
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