summaryrefslogtreecommitdiff
path: root/src/bank-lib
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2023-09-21 00:01:45 +0200
committerChristian Grothoff <christian@grothoff.org>2023-09-21 00:01:45 +0200
commit48e58124fb61ded372f147d00d112f108c997f81 (patch)
tree21c406d2a952243416faf18e5ccf84d79b3b134b /src/bank-lib
parent7529ffb8b05bb63386630b3667d1e42db889f31a (diff)
downloadexchange-48e58124fb61ded372f147d00d112f108c997f81.tar.gz
exchange-48e58124fb61ded372f147d00d112f108c997f81.tar.bz2
exchange-48e58124fb61ded372f147d00d112f108c997f81.zip
major refactor of fakebank implementation
Diffstat (limited to 'src/bank-lib')
-rw-r--r--src/bank-lib/Makefile.am29
-rw-r--r--src/bank-lib/bank_api_admin.c4
-rw-r--r--src/bank-lib/fakebank.c4486
-rw-r--r--src/bank-lib/fakebank.h696
-rw-r--r--src/bank-lib/fakebank_api_check.c238
-rw-r--r--src/bank-lib/fakebank_bank.c224
-rw-r--r--src/bank-lib/fakebank_bank.h54
-rw-r--r--src/bank-lib/fakebank_bank_accounts_withdrawals.c101
-rw-r--r--src/bank-lib/fakebank_bank_accounts_withdrawals.h50
-rw-r--r--src/bank-lib/fakebank_bank_get_accounts.c80
-rw-r--r--src/bank-lib/fakebank_bank_get_accounts.h48
-rw-r--r--src/bank-lib/fakebank_bank_get_accounts_withdrawals.c111
-rw-r--r--src/bank-lib/fakebank_bank_get_accounts_withdrawals.h53
-rw-r--r--src/bank-lib/fakebank_bank_get_root.c58
-rw-r--r--src/bank-lib/fakebank_bank_get_root.h44
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals.c196
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals.h54
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.c97
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.h50
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.c139
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.h50
-rw-r--r--src/bank-lib/fakebank_bank_testing_register.c128
-rw-r--r--src/bank-lib/fakebank_bank_testing_register.h53
-rw-r--r--src/bank-lib/fakebank_common_lookup.c103
-rw-r--r--src/bank-lib/fakebank_common_lookup.h62
-rw-r--r--src/bank-lib/fakebank_common_lp.c344
-rw-r--r--src/bank-lib/fakebank_common_lp.h100
-rw-r--r--src/bank-lib/fakebank_common_make_admin_transfer.c117
-rw-r--r--src/bank-lib/fakebank_common_make_admin_transfer.h56
-rw-r--r--src/bank-lib/fakebank_common_parser.c138
-rw-r--r--src/bank-lib/fakebank_common_parser.h50
-rw-r--r--src/bank-lib/fakebank_common_transact.c261
-rw-r--r--src/bank-lib/fakebank_common_transact.h76
-rw-r--r--src/bank-lib/fakebank_stop.c192
-rw-r--r--src/bank-lib/fakebank_tbi.c126
-rw-r--r--src/bank-lib/fakebank_tbi.h54
-rw-r--r--src/bank-lib/fakebank_tbi_get_withdrawal_operation.c127
-rw-r--r--src/bank-lib/fakebank_tbi_get_withdrawal_operation.h51
-rw-r--r--src/bank-lib/fakebank_tbi_post_withdrawal_operation.c221
-rw-r--r--src/bank-lib/fakebank_tbi_post_withdrawal_operation.h53
-rw-r--r--src/bank-lib/fakebank_tbr.c74
-rw-r--r--src/bank-lib/fakebank_tbr.h58
-rw-r--r--src/bank-lib/fakebank_tbr_get_history.c305
-rw-r--r--src/bank-lib/fakebank_tbr_get_history.h52
-rw-r--r--src/bank-lib/fakebank_tbr_get_root.c50
-rw-r--r--src/bank-lib/fakebank_tbr_get_root.h45
-rw-r--r--src/bank-lib/fakebank_twg.c106
-rw-r--r--src/bank-lib/fakebank_twg.h56
-rw-r--r--src/bank-lib/fakebank_twg_admin_add_incoming.c160
-rw-r--r--src/bank-lib/fakebank_twg_admin_add_incoming.h52
-rw-r--r--src/bank-lib/fakebank_twg_get_root.c58
-rw-r--r--src/bank-lib/fakebank_twg_get_root.h46
-rw-r--r--src/bank-lib/fakebank_twg_history.c537
-rw-r--r--src/bank-lib/fakebank_twg_history.h67
-rw-r--r--src/bank-lib/fakebank_twg_transfer.c178
-rw-r--r--src/bank-lib/fakebank_twg_transfer.h55
56 files changed, 6672 insertions, 4401 deletions
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index ffd428a6f..783ed825c 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -58,7 +58,34 @@ libtalerfakebank_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalerfakebank_la_SOURCES = \
- fakebank.c
+ fakebank.c fakebank.h \
+ fakebank_api_check.c \
+ fakebank_common_lookup.c fakebank_common_lookup.h \
+ fakebank_common_lp.c fakebank_common_lp.h \
+ fakebank_common_make_admin_transfer.c fakebank_common_make_admin_transfer.h \
+ fakebank_common_parser.c fakebank_common_parser.h \
+ fakebank_common_transact.c fakebank_common_transact.h \
+ fakebank_stop.c \
+ fakebank_bank.c fakebank_bank.h \
+ fakebank_bank_accounts_withdrawals.c fakebank_bank_accounts_withdrawals.h \
+ fakebank_bank_get_accounts.c fakebank_bank_get_accounts.h \
+ fakebank_bank_get_accounts_withdrawals.c fakebank_bank_get_accounts_withdrawals.h \
+ fakebank_bank_get_root.c fakebank_bank_get_root.h \
+ fakebank_bank_post_accounts_withdrawals.c fakebank_bank_post_accounts_withdrawals.h \
+ fakebank_bank_post_accounts_withdrawals_abort.c fakebank_bank_post_accounts_withdrawals_abort.h \
+ fakebank_bank_post_accounts_withdrawals_confirm.c fakebank_bank_post_accounts_withdrawals_confirm.h \
+ fakebank_bank_testing_register.c fakebank_bank_testing_register.h \
+ fakebank_tbr.c fakebank_tbr.h \
+ fakebank_tbr_get_history.c fakebank_tbr_get_history.h \
+ fakebank_tbr_get_root.c fakebank_tbr_get_root.h \
+ fakebank_tbi.c fakebank_tbi.h \
+ fakebank_tbi_get_withdrawal_operation.c fakebank_tbi_get_withdrawal_operation.h \
+ fakebank_tbi_post_withdrawal_operation.c fakebank_tbi_post_withdrawal_operation.h \
+ fakebank_twg.c fakebank_twg.h \
+ fakebank_twg_admin_add_incoming.c fakebank_twg_admin_add_incoming.h \
+ fakebank_twg_get_root.c fakebank_twg_get_root.h \
+ fakebank_twg_history.c fakebank_twg_history.h \
+ fakebank_twg_transfer.c fakebank_twg_transfer.h
libtalerfakebank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/mhd/libtalermhd.la \
diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c
index 0b8e80e98..47d8a5c6d 100644
--- a/src/bank-lib/bank_api_admin.c
+++ b/src/bank-lib/bank_api_admin.c
@@ -16,7 +16,7 @@
*/
/**
* @file bank-lib/bank_api_admin.c
- * @brief Implementation of the /admin/ requests of the bank's HTTP API
+ * @brief Implementation of the /admin/add-incoming requests of the bank's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
@@ -27,7 +27,7 @@
/**
- * @brief An admin/add-incoming Handle
+ * @brief An /admin/add-incoming Handle
*/
struct TALER_BANK_AdminAddIncomingHandle
{
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index d0fbf7691..846695571 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2016-2022 Taler Systems SA
+ (C) 2016-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -21,8 +21,6 @@
* @brief library that fakes being a Taler bank for testcases
* @author Christian Grothoff <christian@grothoff.org>
*/
-// TODO: support adding WAD transfers
-
#include "platform.h"
#include <pthread.h>
#include <poll.h>
@@ -33,1668 +31,12 @@
#include "taler_bank_service.h"
#include "taler_mhd_lib.h"
#include <gnunet/gnunet_mhd_compat.h>
-
-/**
- * Maximum POST request size (for /admin/add-incoming)
- */
-#define REQUEST_BUFFER_MAX (4 * 1024)
-
-/**
- * How long are exchange base URLs allowed to be at most?
- * Set to a relatively low number as this does contribute
- * significantly to our RAM consumption.
- */
-#define MAX_URL_LEN 64
-
-/**
- * Per account information.
- */
-struct Account;
-
-
-/**
- * Types of long polling activities.
- */
-enum LongPollType
-{
- /**
- * Transfer TO the exchange.
- */
- LP_CREDIT,
-
- /**
- * Transfer FROM the exchange.
- */
- LP_DEBIT,
-
- /**
- * Withdraw operation completion/abort.
- */
- LP_WITHDRAW
-
-};
-
-/**
- * Client waiting for activity on this account.
- */
-struct LongPoller
-{
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *next;
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *prev;
-
- /**
- * Account this long poller is waiting on.
- */
- struct Account *account;
-
- /**
- * Withdraw operation we are waiting on,
- * only if @e type is #LP_WITHDRAW, otherwise NULL.
- */
- const struct WithdrawalOperation *wo;
-
- /**
- * Entry in the heap for this long poller.
- */
- struct GNUNET_CONTAINER_HeapNode *hn;
-
- /**
- * Client that is waiting for transactions.
- */
- struct MHD_Connection *conn;
-
- /**
- * When will this long poller time out?
- */
- struct GNUNET_TIME_Absolute timeout;
-
- /**
- * What does the @e connection wait for?
- */
- enum LongPollType type;
-
-};
-
-
-/**
- * Details about a transcation we (as the simulated bank) received.
- */
-struct Transaction;
-
-
-/**
- * Information we keep per withdraw operation.
- */
-struct WithdrawalOperation
-{
- /**
- * Unique (random) operation ID.
- */
- struct GNUNET_ShortHashCode wopid;
-
- /**
- * Debited account.
- */
- struct Account *debit_account;
-
- /**
- * Target exchange account, or NULL if unknown.
- */
- const struct Account *exchange_account;
-
- /**
- * RowID of the resulting transaction, if any. Otherwise 0.
- */
- uint64_t row_id;
-
- /**
- * Amount transferred.
- */
- struct TALER_Amount amount;
-
- /**
- * Public key of the reserve, wire transfer subject.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- /**
- * When was the transaction made? 0 if not yet.
- */
- struct GNUNET_TIME_Timestamp timestamp;
-
- /**
- * Was the withdrawal aborted?
- */
- bool aborted;
-
- /**
- * Did the bank confirm the withdrawal?
- */
- bool confirmation_done;
-
- /**
- * Is @e reserve_pub initialized?
- */
- bool selection_done;
-
-};
-
-
-/**
- * Per account information.
- */
-struct Account
-{
-
- /**
- * Inbound transactions for this account in a MDLL.
- */
- struct Transaction *in_head;
-
- /**
- * Inbound transactions for this account in a MDLL.
- */
- struct Transaction *in_tail;
-
- /**
- * Outbound transactions for this account in a MDLL.
- */
- struct Transaction *out_head;
-
- /**
- * Outbound transactions for this account in a MDLL.
- */
- struct Transaction *out_tail;
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *lp_head;
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *lp_tail;
-
- /**
- * Account name (string, not payto!)
- */
- char *account_name;
-
- /**
- * Receiver name for payto:// URIs.
- */
- char *receiver_name;
-
- /**
- * Payto URI for this account.
- */
- char *payto_uri;
-
- /**
- * Password set for the account (if any).
- */
- char *password;
-
- /**
- * Current account balance.
- */
- struct TALER_Amount balance;
-
- /**
- * true if the balance is negative.
- */
- bool is_negative;
-
-};
-
-
-/**
- * Details about a transcation we (as the simulated bank) received.
- */
-struct Transaction
-{
- /**
- * We store inbound transactions in a MDLL.
- */
- struct Transaction *next_in;
-
- /**
- * We store inbound transactions in a MDLL.
- */
- struct Transaction *prev_in;
-
- /**
- * We store outbound transactions in a MDLL.
- */
- struct Transaction *next_out;
-
- /**
- * We store outbound transactions in a MDLL.
- */
- struct Transaction *prev_out;
-
- /**
- * Amount to be transferred.
- */
- struct TALER_Amount amount;
-
- /**
- * Account to debit.
- */
- struct Account *debit_account;
-
- /**
- * Account to credit.
- */
- struct Account *credit_account;
-
- /**
- * Random unique identifier for the request.
- * Used to detect idempotent requests.
- */
- struct GNUNET_HashCode request_uid;
-
- /**
- * When did the transaction happen?
- */
- struct GNUNET_TIME_Timestamp date;
-
- /**
- * Number of this transaction.
- */
- uint64_t row_id;
-
- /**
- * What does the @e subject contain?
- */
- enum
- {
- /**
- * Transfer TO the exchange.
- */
- T_CREDIT,
-
- /**
- * Transfer FROM the exchange.
- */
- T_DEBIT,
-
- /**
- * Exchange-to-exchange WAD transfer.
- */
- T_WAD,
- } type;
-
- /**
- * Wire transfer subject.
- */
- union
- {
-
- /**
- * Used if @e type is T_DEBIT.
- */
- struct
- {
-
- /**
- * Subject of the transfer.
- */
- struct TALER_WireTransferIdentifierRawP wtid;
-
- /**
- * Base URL of the exchange.
- */
- char exchange_base_url[MAX_URL_LEN];
-
- } debit;
-
- /**
- * Used if @e type is T_CREDIT.
- */
- struct
- {
-
- /**
- * Reserve public key of the credit operation.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- } credit;
-
- /**
- * Used if @e type is T_WAD.
- */
- struct
- {
-
- /**
- * Subject of the transfer.
- */
- struct TALER_WadIdentifierP wad;
-
- /**
- * Base URL of the originating exchange.
- */
- char origin_base_url[MAX_URL_LEN];
-
- } wad;
-
- } subject;
-
- /**
- * Has this transaction not yet been subjected to
- * #TALER_FAKEBANK_check_credit() or #TALER_FAKEBANK_check_debit() and
- * should thus be counted in #TALER_FAKEBANK_check_empty()?
- */
- bool unchecked;
-};
-
-
-/**
- * Function called to clean up context of a connection.
- *
- * @param ctx context to clean up
- */
-typedef void
-(*ConnectionCleaner)(void *ctx);
-
-/**
- * Universal context we keep per connection.
- */
-struct ConnectionContext
-{
- /**
- * Function we call upon completion to clean up.
- */
- ConnectionCleaner ctx_cleaner;
-
- /**
- * Request-handler specific context.
- */
- void *ctx;
-};
-
-
-/**
- * This is the "base" structure for both the /history and the
- * /history-range API calls.
- */
-struct HistoryArgs
-{
-
- /**
- * Bank account number of the requesting client.
- */
- uint64_t account_number;
-
- /**
- * Index of the starting transaction, exclusive (!).
- */
- uint64_t start_idx;
-
- /**
- * Requested number of results and order
- * (positive: ascending, negative: descending)
- */
- int64_t delta;
-
- /**
- * Timeout for long polling.
- */
- struct GNUNET_TIME_Relative lp_timeout;
-
- /**
- * true if starting point was given.
- */
- bool have_start;
-
-};
-
-
-/**
- * Context we keep per history request.
- */
-struct HistoryContext
-{
- /**
- * When does this request time out.
- */
- struct GNUNET_TIME_Absolute timeout;
-
- /**
- * Client arguments for this request.
- */
- struct HistoryArgs ha;
-
- /**
- * Account the request is about.
- */
- struct Account *acc;
-
- /**
- * Payto URI of the account.
- */
- char *payto_uri;
-
- /**
- * JSON object we are building to return.
- */
- json_t *history;
-
-};
-
-
-/**
- * Function called to clean up a history context.
- *
- * @param cls a `struct HistoryContext *`
- */
-static void
-history_cleanup (void *cls)
-{
- struct HistoryContext *hc = cls;
-
- GNUNET_free (hc->payto_uri);
- json_decref (hc->history);
- GNUNET_free (hc);
-}
-
-
-/**
- * Context we keep per get withdrawal operation request.
- */
-struct WithdrawContext
-{
- /**
- * When does this request time out.
- */
- struct GNUNET_TIME_Absolute timeout;
-
- /**
- * The withdrawal operation this is about.
- */
- struct WithdrawalOperation *wo;
-
-};
-
-
-/**
- * Function called to clean up a withdraw context.
- *
- * @param cls a `struct WithdrawContext *`
- */
-static void
-withdraw_cleanup (void *cls)
-{
- struct WithdrawContext *wc = cls;
-
- GNUNET_free (wc);
-}
-
-
-/**
- * Handle for the fake bank.
- */
-struct TALER_FAKEBANK_Handle
-{
- /**
- * We store transactions in a revolving array.
- */
- struct Transaction **transactions;
-
- /**
- * HTTP server we run to pretend to be the "test" bank.
- */
- struct MHD_Daemon *mhd_bank;
-
- /**
- * Task running HTTP server for the "test" bank,
- * unless we are using a thread pool (then NULL).
- */
- struct GNUNET_SCHEDULER_Task *mhd_task;
-
- /**
- * Task for expiring long-polling connections,
- * unless we are using a thread pool (then NULL).
- */
- struct GNUNET_SCHEDULER_Task *lp_task;
-
- /**
- * Task for expiring long-polling connections, unless we are using the
- * GNUnet scheduler (then NULL).
- */
- pthread_t lp_thread;
-
- /**
- * MIN-heap of long pollers, sorted by timeout.
- */
- struct GNUNET_CONTAINER_Heap *lp_heap;
-
- /**
- * Hashmap of reserve public keys to
- * `struct Transaction` with that reserve public
- * key. Used to prevent public-key re-use.
- */
- struct GNUNET_CONTAINER_MultiPeerMap *rpubs;
-
- /**
- * Hashmap of short hashes (wopids) to
- * `struct WithdrawalOperation`.
- * Used to lookup withdrawal operations.
- */
- struct GNUNET_CONTAINER_MultiShortmap *wops;
-
- /**
- * (Base) URL to suggest for the exchange. Can
- * be NULL if there is no suggestion to be made.
- */
- char *exchange_url;
-
- /**
- * Lock for accessing @a rpubs map.
- */
- pthread_mutex_t rpubs_lock;
-
- /**
- * Hashmap of hashes of account names to `struct Account`.
- */
- struct GNUNET_CONTAINER_MultiHashMap *accounts;
-
- /**
- * Lock for accessing @a accounts hash map.
- */
- pthread_mutex_t accounts_lock;
-
- /**
- * Hashmap of hashes of transaction request_uids to `struct Transaction`.
- */
- struct GNUNET_CONTAINER_MultiHashMap *uuid_map;
-
- /**
- * Lock for accessing @a uuid_map.
- */
- pthread_mutex_t uuid_map_lock;
-
- /**
- * Lock for accessing the internals of
- * accounts and transaction array entries.
- */
- pthread_mutex_t big_lock;
-
- /**
- * How much money should be put into new accounts
- * on /register.
- */
- struct TALER_Amount signup_bonus;
-
- /**
- * Current transaction counter.
- */
- uint64_t serial_counter;
-
- /**
- * Number of transactions we keep in memory (at most).
- */
- uint64_t ram_limit;
-
- /**
- * Currency used by the fakebank.
- */
- char *currency;
-
- /**
- * Hostname of the fakebank.
- */
- char *hostname;
-
- /**
- * BaseURL of the fakebank.
- */
- char *my_baseurl;
-
- /**
- * Our port number.
- */
- uint16_t port;
-
-#ifdef __linux__
- /**
- * Event FD to signal @a lp_thread a change in
- * @a lp_heap.
- */
- int lp_event;
-#else
- /**
- * Pipe input to signal @a lp_thread a change in
- * @a lp_heap.
- */
- int lp_event_in;
-
- /**
- * Pipe output to signal @a lp_thread a change in
- * @a lp_heap.
- */
- int lp_event_out;
-#endif
-
- /**
- * Set to true once we are shutting down.
- */
- bool in_shutdown;
-
- /**
- * Should we run MHD immediately again?
- */
- bool mhd_again;
-
-#if EPOLL_SUPPORT
- /**
- * Boxed @e mhd_fd.
- */
- struct GNUNET_NETWORK_Handle *mhd_rfd;
-
- /**
- * File descriptor to use to wait for MHD.
- */
- int mhd_fd;
-#endif
-};
-
-
-/**
- * Task run whenever HTTP server operations are pending.
- *
- * @param cls the `struct TALER_FAKEBANK_Handle`
- */
-static void
-run_mhd (void *cls);
-
-
-/**
- * Find withdrawal operation @a wopid in @a h.
- *
- * @param h fakebank handle
- * @param wopid withdrawal operation ID as a string
- * @return NULL if operation was not found
- */
-static struct WithdrawalOperation *
-lookup_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
- const char *wopid)
-{
- struct GNUNET_ShortHashCode sh;
-
- if (NULL == h->wops)
- return NULL;
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (wopid,
- strlen (wopid),
- &sh,
- sizeof (sh)))
- {
- GNUNET_break_op (0);
- return NULL;
- }
- return GNUNET_CONTAINER_multishortmap_get (h->wops,
- &sh);
-}
-
-
-/**
- * Trigger the @a lp. Frees associated resources,
- * except the entry of @a lp in the timeout heap.
- * Must be called while the ``big lock`` is held.
- *
- * @param[in] lp long poller to trigger
- * @param[in,out] h fakebank handle
- */
-static void
-lp_trigger (struct LongPoller *lp,
- struct TALER_FAKEBANK_Handle *h)
-{
- struct Account *acc = lp->account;
-
- GNUNET_CONTAINER_DLL_remove (acc->lp_head,
- acc->lp_tail,
- lp);
- MHD_resume_connection (lp->conn);
- GNUNET_free (lp);
- h->mhd_again = true;
-#ifdef __linux__
- if (-1 == h->lp_event)
-#else
- if ( (-1 == h->lp_event_in) &&
- (-1 == h->lp_event_out) )
-#endif
- {
- if (NULL != h->mhd_task)
- GNUNET_SCHEDULER_cancel (h->mhd_task);
- h->mhd_task =
- GNUNET_SCHEDULER_add_now (&run_mhd,
- h);
- }
-}
-
-
-/**
- * Thread that is run to wake up connections that have hit their timeout. Runs
- * until in_shutdown is set to true. Must be send signals via lp_event on
- * shutdown and/or whenever the heap changes to an earlier timeout.
- *
- * @param cls a `struct TALER_FAKEBANK_Handle *`
- * @return NULL
- */
-static void *
-lp_expiration_thread (void *cls)
-{
- struct TALER_FAKEBANK_Handle *h = cls;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- while (! h->in_shutdown)
- {
- struct LongPoller *lp;
- int timeout_ms;
-
- lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
- while ( (NULL != lp) &&
- GNUNET_TIME_absolute_is_past (lp->timeout))
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
- lp_trigger (lp,
- h);
- lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
- }
- if (NULL != lp)
- {
- struct GNUNET_TIME_Relative rem;
- unsigned long long left_ms;
-
- rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
- left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
- if (left_ms > INT_MAX)
- timeout_ms = INT_MAX;
- else
- timeout_ms = (int) left_ms;
- }
- else
- {
- timeout_ms = -1; /* infinity */
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- {
- struct pollfd p = {
-#ifdef __linux__
- .fd = h->lp_event,
-#else
- .fd = h->lp_event_out,
-#endif
- .events = POLLIN
- };
- int ret;
-
- ret = poll (&p,
- 1,
- timeout_ms);
- if (-1 == ret)
- {
- if (EINTR != errno)
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "poll");
- }
- else if (1 == ret)
- {
- /* clear event */
- uint64_t ev;
- ssize_t iret;
-
-#ifdef __linux__
- iret = read (h->lp_event,
-#else
- iret = read (h->lp_event_out,
-#endif
- &ev,
- sizeof (ev));
- if (-1 == iret)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "read");
- }
- else
- {
- GNUNET_break (sizeof (uint64_t) == iret);
- }
- }
- }
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return NULL;
-}
-
-
-/**
- * Lookup account with @a name, and if it does not exist, create it.
- *
- * @param[in,out] h bank to lookup account at
- * @param name account name to resolve
- * @param receiver_name receiver name in payto:// URI,
- * NULL if the account must already exist
- * @return account handle, NULL if account does not yet exist
- */
-static struct Account *
-lookup_account (struct TALER_FAKEBANK_Handle *h,
- const char *name,
- const char *receiver_name)
-{
- struct GNUNET_HashCode hc;
- size_t slen;
- struct Account *account;
-
- memset (&hc,
- 0,
- sizeof (hc));
- slen = strlen (name);
- GNUNET_CRYPTO_hash (name,
- slen,
- &hc);
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->accounts_lock));
- account = GNUNET_CONTAINER_multihashmap_get (h->accounts,
- &hc);
- if (NULL == account)
- {
- if (NULL == receiver_name)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->accounts_lock));
- return NULL;
- }
- account = GNUNET_new (struct Account);
- account->account_name = GNUNET_strdup (name);
- account->receiver_name = GNUNET_strdup (receiver_name);
- GNUNET_asprintf (&account->payto_uri,
- "payto://x-taler-bank/%s/%s?receiver-name=%s",
- h->hostname,
- account->account_name,
- account->receiver_name);
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (h->currency,
- &account->balance));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (h->accounts,
- &hc,
- account,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->accounts_lock));
- return account;
-}
-
-
-/**
- * Generate log messages for failed check operation.
- *
- * @param h handle to output transaction log for
- */
-static void
-check_log (struct TALER_FAKEBANK_Handle *h)
-{
- for (uint64_t i = 0; i<h->ram_limit; i++)
- {
- struct Transaction *t = h->transactions[i];
-
- if (NULL == t)
- continue;
- if (! t->unchecked)
- continue;
- switch (t->type)
- {
- case T_DEBIT:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "%s -> %s (%s) %s (%s)\n",
- t->debit_account->account_name,
- t->credit_account->account_name,
- TALER_amount2s (&t->amount),
- t->subject.debit.exchange_base_url,
- "DEBIT");
- break;
- case T_CREDIT:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "%s -> %s (%s) %s (%s)\n",
- t->debit_account->account_name,
- t->credit_account->account_name,
- TALER_amount2s (&t->amount),
- TALER_B2S (&t->subject.credit.reserve_pub),
- "CREDIT");
- break;
- case T_WAD:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "%s -> %s (%s) %s[%s] (%s)\n",
- t->debit_account->account_name,
- t->credit_account->account_name,
- TALER_amount2s (&t->amount),
- t->subject.wad.origin_base_url,
- TALER_B2S (&t->subject.wad),
- "WAD");
- break;
- }
- }
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
- const struct TALER_Amount *want_amount,
- const char *want_debit,
- const char *want_credit,
- const char *exchange_base_url,
- struct TALER_WireTransferIdentifierRawP *wtid)
-{
- struct Account *debit_account;
- struct Account *credit_account;
-
- GNUNET_assert (0 ==
- strcasecmp (want_amount->currency,
- h->currency));
- debit_account = lookup_account (h,
- want_debit,
- NULL);
- credit_account = lookup_account (h,
- want_credit,
- NULL);
- if (NULL == debit_account)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted: %s->%s (%s) from exchange %s (DEBIT), but debit account does not even exist!\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- exchange_base_url);
- return GNUNET_SYSERR;
- }
- if (NULL == credit_account)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted: %s->%s (%s) from exchange %s (DEBIT), but credit account does not even exist!\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- exchange_base_url);
- return GNUNET_SYSERR;
- }
- for (struct Transaction *t = debit_account->out_tail;
- NULL != t;
- t = t->prev_out)
- {
- if ( (t->unchecked) &&
- (credit_account == t->credit_account) &&
- (T_DEBIT == t->type) &&
- (0 == TALER_amount_cmp (want_amount,
- &t->amount)) &&
- (0 == strcasecmp (exchange_base_url,
- t->subject.debit.exchange_base_url)) )
- {
- *wtid = t->subject.debit.wtid;
- t->unchecked = false;
- return GNUNET_OK;
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Did not find matching transaction! I have:\n");
- check_log (h);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted: %s->%s (%s) from exchange %s (DEBIT)\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- exchange_base_url);
- return GNUNET_SYSERR;
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
- const struct TALER_Amount *want_amount,
- const char *want_debit,
- const char *want_credit,
- const struct TALER_ReservePublicKeyP *reserve_pub)
-{
- struct Account *debit_account;
- struct Account *credit_account;
-
- GNUNET_assert (0 == strcasecmp (want_amount->currency,
- h->currency));
- debit_account = lookup_account (h,
- want_debit,
- NULL);
- credit_account = lookup_account (h,
- want_credit,
- NULL);
- if (NULL == debit_account)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but debit account is unknown.\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- TALER_B2S (reserve_pub));
- return GNUNET_SYSERR;
- }
- if (NULL == credit_account)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but credit account is unknown.\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- TALER_B2S (reserve_pub));
- return GNUNET_SYSERR;
- }
- for (struct Transaction *t = credit_account->in_tail;
- NULL != t;
- t = t->prev_in)
- {
- if ( (t->unchecked) &&
- (debit_account == t->debit_account) &&
- (T_CREDIT == t->type) &&
- (0 == TALER_amount_cmp (want_amount,
- &t->amount)) &&
- (0 == GNUNET_memcmp (reserve_pub,
- &t->subject.credit.reserve_pub)) )
- {
- t->unchecked = false;
- return GNUNET_OK;
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Did not find matching transaction!\nI have:\n");
- check_log (h);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted:\n%s -> %s (%s) with subject %s (CREDIT)\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- TALER_B2S (reserve_pub));
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Update @a account balance by @a amount.
- *
- * The @a big_lock must already be locked when calling
- * this function.
- *
- * @param[in,out] account account to update
- * @param amount balance change
- * @param debit true to subtract, false to add @a amount
- */
-static void
-update_balance (struct Account *account,
- const struct TALER_Amount *amount,
- bool debit)
-{
- if (debit == account->is_negative)
- {
- GNUNET_assert (0 <=
- TALER_amount_add (&account->balance,
- &account->balance,
- amount));
- return;
- }
- if (0 <= TALER_amount_cmp (&account->balance,
- amount))
- {
- GNUNET_assert (0 <=
- TALER_amount_subtract (&account->balance,
- &account->balance,
- amount));
- }
- else
- {
- GNUNET_assert (0 <=
- TALER_amount_subtract (&account->balance,
- amount,
- &account->balance));
- account->is_negative = ! account->is_negative;
- }
-}
-
-
-/**
- * Add transaction to the debit and credit accounts,
- * updating the balances as needed.
- *
- * The transaction @a t must already be locked
- * when calling this function!
- *
- * @param[in,out] h bank handle
- * @param[in,out] t transaction to add to account lists
- */
-static void
-post_transaction (struct TALER_FAKEBANK_Handle *h,
- struct Transaction *t)
-{
- struct Account *debit_acc = t->debit_account;
- struct Account *credit_acc = t->credit_account;
- uint64_t row_id;
- struct Transaction *old;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- row_id = ++h->serial_counter;
- old = h->transactions[row_id % h->ram_limit];
- h->transactions[row_id % h->ram_limit] = t;
- t->row_id = row_id;
- GNUNET_CONTAINER_MDLL_insert_tail (out,
- debit_acc->out_head,
- debit_acc->out_tail,
- t);
- update_balance (debit_acc,
- &t->amount,
- true);
- GNUNET_CONTAINER_MDLL_insert_tail (in,
- credit_acc->in_head,
- credit_acc->in_tail,
- t);
- update_balance (credit_acc,
- &t->amount,
- false);
- if (NULL != old)
- {
- struct Account *da;
- struct Account *ca;
-
- da = old->debit_account;
- ca = old->credit_account;
- /* slot was already in use, must clean out old
- entry first! */
- GNUNET_CONTAINER_MDLL_remove (out,
- da->out_head,
- da->out_tail,
- old);
- GNUNET_CONTAINER_MDLL_remove (in,
- ca->in_head,
- ca->in_tail,
- old);
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- if ( (NULL != old) &&
- (T_DEBIT == old->type) )
- {
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->uuid_map_lock));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
- &old->request_uid,
- old));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- }
- GNUNET_free (old);
-}
-
-
-/**
- * Trigger long pollers that might have been waiting
- * for @a t.
- *
- * @param h fakebank handle
- * @param t transaction to notify on
- */
-static void
-notify_transaction (struct TALER_FAKEBANK_Handle *h,
- struct Transaction *t)
-{
- struct Account *debit_acc = t->debit_account;
- struct Account *credit_acc = t->credit_account;
- struct LongPoller *nxt;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- for (struct LongPoller *lp = debit_acc->lp_head;
- NULL != lp;
- lp = nxt)
- {
- nxt = lp->next;
- if (LP_DEBIT == lp->type)
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_node (lp->hn));
- lp_trigger (lp,
- h);
- }
- }
- for (struct LongPoller *lp = credit_acc->lp_head;
- NULL != lp;
- lp = nxt)
- {
- nxt = lp->next;
- if (LP_CREDIT == lp->type)
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_node (lp->hn));
- lp_trigger (lp,
- h);
- }
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
-}
-
-
-/**
- * Tell the fakebank to create another wire transfer *from* an exchange.
- *
- * @param h fake bank handle
- * @param debit_account account to debit
- * @param credit_account account to credit
- * @param amount amount to transfer
- * @param subject wire transfer subject to use
- * @param exchange_base_url exchange URL
- * @param request_uid unique number to make the request unique, or NULL to create one
- * @param[out] ret_row_id pointer to store the row ID of this transaction
- * @param[out] timestamp set to the time of the transfer
- * @return #GNUNET_YES if the transfer was successful,
- * #GNUNET_SYSERR if the request_uid was reused for a different transfer
- */
-static enum GNUNET_GenericReturnValue
-make_transfer (
- struct TALER_FAKEBANK_Handle *h,
- const char *debit_account,
- const char *credit_account,
- const struct TALER_Amount *amount,
- const struct TALER_WireTransferIdentifierRawP *subject,
- const char *exchange_base_url,
- const struct GNUNET_HashCode *request_uid,
- uint64_t *ret_row_id,
- struct GNUNET_TIME_Timestamp *timestamp)
-{
- struct Transaction *t;
- struct Account *debit_acc;
- struct Account *credit_acc;
- size_t url_len;
-
- GNUNET_assert (0 == strcasecmp (amount->currency,
- h->currency));
- GNUNET_assert (NULL != debit_account);
- GNUNET_assert (NULL != credit_account);
- GNUNET_break (0 != strncasecmp ("payto://",
- debit_account,
- strlen ("payto://")));
- GNUNET_break (0 != strncasecmp ("payto://",
- credit_account,
- strlen ("payto://")));
- url_len = strlen (exchange_base_url);
- GNUNET_assert (url_len < MAX_URL_LEN);
- debit_acc = lookup_account (h,
- debit_account,
- debit_account);
- credit_acc = lookup_account (h,
- credit_account,
- credit_account);
- if (NULL != request_uid)
- {
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->uuid_map_lock));
- t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
- request_uid);
- if (NULL != t)
- {
- if ( (debit_acc != t->debit_account) ||
- (credit_acc != t->credit_account) ||
- (0 != TALER_amount_cmp (amount,
- &t->amount)) ||
- (T_DEBIT != t->type) ||
- (0 != GNUNET_memcmp (subject,
- &t->subject.debit.wtid)) )
- {
- /* Transaction exists, but with different details. */
- GNUNET_break (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- return GNUNET_SYSERR;
- }
- *ret_row_id = t->row_id;
- *timestamp = t->date;
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- return GNUNET_OK;
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- }
- t = GNUNET_new (struct Transaction);
- t->unchecked = true;
- t->debit_account = debit_acc;
- t->credit_account = credit_acc;
- t->amount = *amount;
- t->date = GNUNET_TIME_timestamp_get ();
- if (NULL != timestamp)
- *timestamp = t->date;
- t->type = T_DEBIT;
- GNUNET_memcpy (t->subject.debit.exchange_base_url,
- exchange_base_url,
- url_len);
- t->subject.debit.wtid = *subject;
- if (NULL == request_uid)
- GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
- &t->request_uid);
- else
- t->request_uid = *request_uid;
- post_transaction (h,
- t);
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->uuid_map_lock));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (
- h->uuid_map,
- &t->request_uid,
- t,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
- (unsigned long long) t->row_id,
- debit_account,
- credit_account,
- TALER_amount2s (amount),
- TALER_B2S (subject),
- exchange_base_url);
- *ret_row_id = t->row_id;
- notify_transaction (h,
- t);
- return GNUNET_OK;
-}
-
-
-/**
- * Tell the fakebank to create another wire transfer *to* an exchange.
- *
- * @param h fake bank handle
- * @param debit_account account to debit
- * @param credit_account account to credit
- * @param amount amount to transfer
- * @param reserve_pub reserve public key to use in subject
- * @param[out] row_id serial_id of the transfer
- * @param[out] timestamp when was the transfer made
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-make_admin_transfer (
- struct TALER_FAKEBANK_Handle *h,
- const char *debit_account,
- const char *credit_account,
- const struct TALER_Amount *amount,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- uint64_t *row_id,
- struct GNUNET_TIME_Timestamp *timestamp)
-{
- struct Transaction *t;
- const struct GNUNET_PeerIdentity *pid;
- struct Account *debit_acc;
- struct Account *credit_acc;
-
- GNUNET_static_assert (sizeof (*pid) ==
- sizeof (*reserve_pub));
- pid = (const struct GNUNET_PeerIdentity *) reserve_pub;
- GNUNET_assert (NULL != debit_account);
- GNUNET_assert (NULL != credit_account);
- GNUNET_assert (0 == strcasecmp (amount->currency,
- h->currency));
- GNUNET_break (0 != strncasecmp ("payto://",
- debit_account,
- strlen ("payto://")));
- GNUNET_break (0 != strncasecmp ("payto://",
- credit_account,
- strlen ("payto://")));
- debit_acc = lookup_account (h,
- debit_account,
- debit_account);
- credit_acc = lookup_account (h,
- credit_account,
- credit_account);
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->rpubs_lock));
- t = GNUNET_CONTAINER_multipeermap_get (h->rpubs,
- pid);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->rpubs_lock));
- if (NULL != t)
- {
- /* duplicate reserve public key not allowed */
- GNUNET_break_op (0);
- return GNUNET_NO;
- }
-
- t = GNUNET_new (struct Transaction);
- t->unchecked = true;
- t->debit_account = debit_acc;
- t->credit_account = credit_acc;
- t->amount = *amount;
- t->date = GNUNET_TIME_timestamp_get ();
- if (NULL != timestamp)
- *timestamp = t->date;
- t->type = T_CREDIT;
- t->subject.credit.reserve_pub = *reserve_pub;
- post_transaction (h,
- t);
- if (NULL != row_id)
- *row_id = t->row_id;
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->rpubs_lock));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multipeermap_put (
- h->rpubs,
- pid,
- t,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->rpubs_lock));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Making transfer from %s to %s over %s and subject %s at row %llu\n",
- debit_account,
- credit_account,
- TALER_amount2s (amount),
- TALER_B2S (reserve_pub),
- (unsigned long long) t->row_id);
- notify_transaction (h,
- t);
- return GNUNET_OK;
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
-{
- for (uint64_t i = 0; i<h->ram_limit; i++)
- {
- struct Transaction *t = h->transactions[i];
-
- if ( (NULL != t) &&
- (t->unchecked) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Expected empty transaction set, but I have:\n");
- check_log (h);
- return GNUNET_SYSERR;
- }
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Helper function to free memory when finished.
- *
- * @param cls NULL
- * @param key key of the account to free (ignored)
- * @param val a `struct Account` to free.
- */
-static enum GNUNET_GenericReturnValue
-free_account (void *cls,
- const struct GNUNET_HashCode *key,
- void *val)
-{
- struct Account *account = val;
-
- (void) cls;
- (void) key;
- GNUNET_assert (NULL == account->lp_head);
- GNUNET_free (account->account_name);
- GNUNET_free (account->receiver_name);
- GNUNET_free (account->payto_uri);
- GNUNET_free (account->password);
- GNUNET_free (account);
- return GNUNET_OK;
-}
-
-
-/**
- * Helper function to free memory when finished.
- *
- * @param cls NULL
- * @param key key of the operation to free (ignored)
- * @param val a `struct WithdrawalOperation *` to free.
- */
-static enum GNUNET_GenericReturnValue
-free_withdraw_op (void *cls,
- const struct GNUNET_ShortHashCode *key,
- void *val)
-{
- struct WithdrawalOperation *wo = val;
-
- (void) cls;
- (void) key;
- GNUNET_free (wo);
- return GNUNET_OK;
-}
-
-
-void
-TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
-{
- if (NULL != h->lp_task)
- {
- GNUNET_SCHEDULER_cancel (h->lp_task);
- h->lp_task = NULL;
- }
-#if EPOLL_SUPPORT
- if (NULL != h->mhd_rfd)
- {
- GNUNET_NETWORK_socket_free_memory_only_ (h->mhd_rfd);
- h->mhd_rfd = NULL;
- }
-#endif
-#ifdef __linux__
- if (-1 != h->lp_event)
-#else
- if (-1 != h->lp_event_in && -1 != h->lp_event_out)
-#endif
- {
- uint64_t val = 1;
- void *ret;
- struct LongPoller *lp;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- h->in_shutdown = true;
- while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
- lp_trigger (lp,
- h);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- GNUNET_break (sizeof (val) ==
-#ifdef __linux__
- write (h->lp_event,
-#else
- write (h->lp_event_in,
-#endif
- &val,
- sizeof (val)));
- GNUNET_break (0 ==
- pthread_join (h->lp_thread,
- &ret));
- GNUNET_break (NULL == ret);
-#ifdef __linux__
- GNUNET_break (0 == close (h->lp_event));
- h->lp_event = -1;
-#else
- GNUNET_break (0 == close (h->lp_event_in));
- GNUNET_break (0 == close (h->lp_event_out));
- h->lp_event_in = -1;
- h->lp_event_out = -1;
-#endif
- }
- else
- {
- struct LongPoller *lp;
-
- while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
- lp_trigger (lp,
- h);
- }
- if (NULL != h->mhd_bank)
- {
- MHD_stop_daemon (h->mhd_bank);
- h->mhd_bank = NULL;
- }
- if (NULL != h->mhd_task)
- {
- GNUNET_SCHEDULER_cancel (h->mhd_task);
- h->mhd_task = NULL;
- }
- if (NULL != h->accounts)
- {
- GNUNET_CONTAINER_multihashmap_iterate (h->accounts,
- &free_account,
- NULL);
- GNUNET_CONTAINER_multihashmap_destroy (h->accounts);
- }
- if (NULL != h->wops)
- {
- GNUNET_CONTAINER_multishortmap_iterate (h->wops,
- &free_withdraw_op,
- NULL);
- GNUNET_CONTAINER_multishortmap_destroy (h->wops);
- }
- GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map);
- GNUNET_CONTAINER_multipeermap_destroy (h->rpubs);
- GNUNET_CONTAINER_heap_destroy (h->lp_heap);
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->big_lock));
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->uuid_map_lock));
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->accounts_lock));
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->rpubs_lock));
- for (uint64_t i = 0; i<h->ram_limit; i++)
- GNUNET_free (h->transactions[i]);
- GNUNET_free (h->transactions);
- GNUNET_free (h->my_baseurl);
- GNUNET_free (h->currency);
- GNUNET_free (h->exchange_url);
- GNUNET_free (h->hostname);
- GNUNET_free (h);
-}
+#include "fakebank.h"
+#include "fakebank_bank.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_tbi.h"
+#include "fakebank_tbr.h"
+#include "fakebank_twg.h"
/**
@@ -1703,9 +45,9 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
* @a con_cls that might still need to be cleaned up. Call the
* respective function to free the memory.
*
- * @param cls client-defined closure
+ * @param cls a `struct TALER_FAKEBANK_Handle *`
* @param connection connection handle
- * @param con_cls value as set by the last call to
+ * @param con_cls a `struct ConnectionContext *`
* the #MHD_AccessHandlerCallback
* @param toe reason for request termination
* @see #MHD_OPTION_NOTIFY_COMPLETED
@@ -1717,9 +59,10 @@ handle_mhd_completion_callback (void *cls,
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
- /* struct TALER_FAKEBANK_Handle *h = cls; */
+ struct TALER_FAKEBANK_Handle *h = cls;
struct ConnectionContext *cc = *con_cls;
- (void) cls;
+
+ (void) h;
(void) connection;
(void) toe;
if (NULL == cc)
@@ -1730,2661 +73,6 @@ handle_mhd_completion_callback (void *cls,
/**
- * Handle incoming HTTP request for /admin/add/incoming.
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account account into which to deposit the funds (credit)
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request (a `struct ConnectionContext *`)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- uint64_t row_id;
- struct GNUNET_TIME_Timestamp timestamp;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
- *con_cls = cc;
- }
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- &cc->ctx,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
- {
- const char *debit_account;
- struct TALER_Amount amount;
- struct TALER_ReservePublicKeyP reserve_pub;
- char *debit;
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("reserve_pub",
- &reserve_pub),
- GNUNET_JSON_spec_string ("debit_account",
- &debit_account),
- TALER_JSON_spec_amount ("amount",
- h->currency,
- &amount),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- if (0 != strcasecmp (amount.currency,
- h->currency))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Currency `%s' does not match our configuration\n",
- amount.currency);
- json_decref (json);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_GENERIC_CURRENCY_MISMATCH,
- NULL);
- }
- debit = TALER_xtalerbank_account_from_payto (debit_account);
- if (NULL == debit)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
- debit_account);
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s\n",
- debit,
- account,
- TALER_B2S (&reserve_pub),
- TALER_amount2s (&amount));
- ret = make_admin_transfer (h,
- debit,
- account,
- &amount,
- &reserve_pub,
- &row_id,
- &timestamp);
- GNUNET_free (debit);
- if (GNUNET_OK != ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Reserve public key not unique\n");
- json_decref (json);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
- NULL);
- }
- }
- json_decref (json);
-
- /* Finally build response object */
- return TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_uint64 ("row_id",
- row_id),
- GNUNET_JSON_pack_timestamp ("timestamp",
- timestamp));
-}
-
-
-/**
- * Handle incoming HTTP request for /transfer.
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account account making the transfer
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request (a `struct ConnectionContext *`)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_transfer (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- uint64_t row_id;
- struct GNUNET_TIME_Timestamp ts;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
- *con_cls = cc;
- }
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- &cc->ctx,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
- {
- struct GNUNET_HashCode uuid;
- struct TALER_WireTransferIdentifierRawP wtid;
- const char *credit_account;
- char *credit;
- const char *base_url;
- struct TALER_Amount amount;
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("request_uid",
- &uuid),
- TALER_JSON_spec_amount ("amount",
- h->currency,
- &amount),
- GNUNET_JSON_spec_string ("exchange_base_url",
- &base_url),
- GNUNET_JSON_spec_fixed_auto ("wtid",
- &wtid),
- GNUNET_JSON_spec_string ("credit_account",
- &credit_account),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- {
- enum GNUNET_GenericReturnValue ret;
-
- credit = TALER_xtalerbank_account_from_payto (credit_account);
- if (NULL == credit)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
- credit_account);
- }
- ret = make_transfer (h,
- account,
- credit,
- &amount,
- &wtid,
- base_url,
- &uuid,
- &row_id,
- &ts);
- if (GNUNET_OK != ret)
- {
- MHD_RESULT res;
- char *uids;
-
- GNUNET_break (0);
- uids = GNUNET_STRINGS_data_to_string_alloc (&uuid,
- sizeof (uuid));
- json_decref (json);
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED,
- uids);
- GNUNET_free (uids);
- return res;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
- account,
- credit,
- TALER_B2S (&wtid),
- TALER_amount2s (&amount),
- base_url);
- GNUNET_free (credit);
- }
- }
- json_decref (json);
-
- /* Finally build response object */
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_uint64 ("row_id",
- row_id),
- GNUNET_JSON_pack_timestamp ("timestamp",
- ts));
-}
-
-
-/**
- * Handle incoming HTTP request for / (home page).
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @return MHD result code
- */
-static MHD_RESULT
-handle_home_page (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection)
-{
- MHD_RESULT ret;
- struct MHD_Response *resp;
-#define HELLOMSG "Hello, Fakebank!"
-
- (void) h;
- resp = MHD_create_response_from_buffer (
- strlen (HELLOMSG),
- HELLOMSG,
- MHD_RESPMEM_MUST_COPY);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- resp);
- MHD_destroy_response (resp);
- return ret;
-}
-
-
-/**
- * Parse URL history arguments, of _both_ APIs:
- * /history/incoming and /history/outgoing.
- *
- * @param h bank handle to work on
- * @param connection MHD connection.
- * @param[out] ha will contain the parsed values.
- * @return #GNUNET_OK only if the parsing succeeds,
- * #GNUNET_SYSERR if it failed,
- * #GNUNET_NO if it failed and an error was returned
- */
-static enum GNUNET_GenericReturnValue
-parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- struct HistoryArgs *ha)
-{
- const char *start;
- const char *delta;
- const char *long_poll_ms;
- unsigned long long lp_timeout;
- unsigned long long sval;
- long long d;
- char dummy;
-
- start = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "start");
- ha->have_start = (NULL != start);
- delta = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "delta");
- long_poll_ms = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "long_poll_ms");
- lp_timeout = 0;
- if ( (NULL == delta) ||
- (1 != sscanf (delta,
- "%lld%c",
- &d,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if ( (NULL != long_poll_ms) &&
- (1 != sscanf (long_poll_ms,
- "%llu%c",
- &lp_timeout,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "long_poll_ms"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if ( (NULL != start) &&
- (1 != sscanf (start,
- "%llu%c",
- &sval,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "start"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if (NULL == start)
- ha->start_idx = (d > 0) ? 0 : h->serial_counter;
- else
- ha->start_idx = (uint64_t) sval;
- ha->delta = (int64_t) d;
- if (0 == ha->delta)
- {
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- ha->lp_timeout
- = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- lp_timeout);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request for %lld records from %llu\n",
- (long long) ha->delta,
- (unsigned long long) ha->start_idx);
- return GNUNET_OK;
-}
-
-
-/**
- * Task run when a long poller is about to time out.
- * Only used in single-threaded mode.
- *
- * @param cls a `struct TALER_FAKEBANK_Handle *`
- */
-static void
-lp_timeout (void *cls)
-{
- struct TALER_FAKEBANK_Handle *h = cls;
- struct LongPoller *lp;
-
- h->lp_task = NULL;
- while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
- {
- if (GNUNET_TIME_absolute_is_future (lp->timeout))
- break;
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Timeout reached for long poller %p\n",
- lp->conn);
- lp_trigger (lp,
- h);
- }
- if (NULL == lp)
- return;
- h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
- &lp_timeout,
- h);
-}
-
-
-/**
- * Reschedule the timeout task of @a h for time @a t.
- *
- * @param h fakebank handle
- * @param t when will the next connection timeout expire
- */
-static void
-reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
- struct GNUNET_TIME_Absolute t)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Scheduling timeout task for %s\n",
- GNUNET_STRINGS_absolute_time_to_string (t));
-#ifdef __linux__
- if (-1 != h->lp_event)
-#else
- if (-1 != h->lp_event_in && -1 != h->lp_event_out)
-#endif
- {
- uint64_t num = 1;
-
- GNUNET_break (sizeof (num) ==
-#ifdef __linux__
- write (h->lp_event,
-#else
- write (h->lp_event_in,
-#endif
- &num,
- sizeof (num)));
- }
- else
- {
- if (NULL != h->lp_task)
- GNUNET_SCHEDULER_cancel (h->lp_task);
- h->lp_task = GNUNET_SCHEDULER_add_at (t,
- &lp_timeout,
- h);
- }
-}
-
-
-/**
- * Start long-polling for @a connection and @a acc
- * for transfers in @a dir. Must be called with the
- * "big lock" held.
- *
- * @param[in,out] h fakebank handle
- * @param[in,out] connection to suspend
- * @param[in,out] acc account affected
- * @param lp_timeout how long to suspend
- * @param dir direction of transfers to watch for
- * @param wo withdraw operation to watch, only
- * if @a dir is #LP_WITHDRAW
- */
-static void
-start_lp (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- struct Account *acc,
- struct GNUNET_TIME_Relative lp_timeout,
- enum LongPollType dir,
- const struct WithdrawalOperation *wo)
-{
- struct LongPoller *lp;
- bool toc;
-
- lp = GNUNET_new (struct LongPoller);
- lp->account = acc;
- lp->wo = wo;
- lp->conn = connection;
- lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
- lp->type = dir;
- lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
- lp,
- lp->timeout.abs_value_us);
- toc = (lp ==
- GNUNET_CONTAINER_heap_peek (h->lp_heap));
- GNUNET_CONTAINER_DLL_insert (acc->lp_head,
- acc->lp_tail,
- lp);
- MHD_suspend_connection (connection);
- if (toc)
- reschedule_lp_timeout (h,
- lp->timeout);
-
-}
-
-
-/**
- * Handle incoming HTTP request for /history/outgoing
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account which account the request is about
- * @param con_cls closure for request
- */
-static MHD_RESULT
-handle_debit_history (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- struct HistoryContext *hc;
- struct Transaction *pos;
- enum GNUNET_GenericReturnValue ret;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &history_cleanup;
- *con_cls = cc;
- hc = GNUNET_new (struct HistoryContext);
- cc->ctx = hc;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling /history/outgoing connection %p\n",
- connection);
- if (GNUNET_OK !=
- (ret = parse_history_common_args (h,
- connection,
- &hc->ha)))
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- hc->acc = lookup_account (h,
- account,
- NULL);
- if (NULL == hc->acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account);
- }
- GNUNET_asprintf (&hc->payto_uri,
- "payto://x-taler-bank/localhost/%s?receiver-name=%s",
- account,
- hc->acc->receiver_name);
- /* New invariant: */
- GNUNET_assert (0 == strcmp (hc->payto_uri,
- hc->acc->payto_uri));
- hc->history = json_array ();
- if (NULL == hc->history)
- {
- GNUNET_break (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_NO;
- }
- hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
- }
- else
- {
- hc = cc->ctx;
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- }
-
- if (! hc->ha.have_start)
- {
- pos = (0 > hc->ha.delta)
- ? hc->acc->out_tail
- : hc->acc->out_head;
- }
- else
- {
- struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
- bool overflow;
- uint64_t dir;
- bool skip = true;
-
- dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
- overflow = (t->row_id != hc->ha.start_idx);
- /* If account does not match, linear scan for
- first matching account. */
- while ( (! overflow) &&
- (NULL != t) &&
- (t->debit_account != hc->acc) )
- {
- skip = false;
- t = h->transactions[(t->row_id + dir) % h->ram_limit];
- if ( (NULL != t) &&
- (t->row_id == hc->ha.start_idx) )
- overflow = true; /* full circle, give up! */
- }
- if ( (NULL == t) ||
- overflow)
- {
- /* FIXME: these conditions are unclear to me. */
- if ( (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout)) &&
- (0 < hc->ha.delta))
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- if (overflow)
- {
- return TALER_MHD_reply_with_ec (
- connection,
- TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
- NULL);
- }
- goto finish;
- }
- if (h->in_shutdown)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- goto finish;
- }
- start_lp (h,
- connection,
- hc->acc,
- GNUNET_TIME_absolute_get_remaining (hc->timeout),
- LP_DEBIT,
- NULL);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
- }
- if (t->debit_account != hc->acc)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid start specified, transaction %llu not with account %s!\n",
- (unsigned long long) hc->ha.start_idx,
- account);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_NO;
- }
- if (skip)
- {
- /* range is exclusive, skip the matching entry */
- if (0 > hc->ha.delta)
- pos = t->prev_out;
- else
- pos = t->next_out;
- }
- else
- {
- pos = t;
- }
- }
- if (NULL != pos)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning %lld debit transactions starting (inclusive) from %llu\n",
- (long long) hc->ha.delta,
- (unsigned long long) pos->row_id);
- while ( (0 != hc->ha.delta) &&
- (NULL != pos) )
- {
- json_t *trans;
- char *credit_payto;
-
- if (T_DEBIT != pos->type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected CREDIT transaction #%llu for account `%s'\n",
- (unsigned long long) pos->row_id,
- account);
- if (0 > hc->ha.delta)
- pos = pos->prev_in;
- if (0 < hc->ha.delta)
- pos = pos->next_in;
- continue;
- }
- GNUNET_asprintf (&credit_payto,
- "payto://x-taler-bank/localhost/%s?receiver-name=%s",
- pos->credit_account->account_name,
- pos->credit_account->receiver_name);
-
- trans = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("row_id",
- pos->row_id),
- GNUNET_JSON_pack_timestamp ("date",
- pos->date),
- TALER_JSON_pack_amount ("amount",
- &pos->amount),
- GNUNET_JSON_pack_string ("credit_account",
- credit_payto),
- GNUNET_JSON_pack_string ("exchange_base_url",
- pos->subject.debit.exchange_base_url),
- GNUNET_JSON_pack_data_auto ("wtid",
- &pos->subject.debit.wtid));
- GNUNET_assert (NULL != trans);
- GNUNET_free (credit_payto);
- GNUNET_assert (0 ==
- json_array_append_new (hc->history,
- trans));
- if (hc->ha.delta > 0)
- hc->ha.delta--;
- else
- hc->ha.delta++;
- if (0 > hc->ha.delta)
- pos = pos->prev_out;
- if (0 < hc->ha.delta)
- pos = pos->next_out;
- }
- if ( (0 == json_array_size (hc->history)) &&
- (! h->in_shutdown) &&
- (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
- (0 < hc->ha.delta))
- {
- start_lp (h,
- connection,
- hc->acc,
- GNUNET_TIME_absolute_get_remaining (hc->timeout),
- LP_DEBIT,
- NULL);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
-finish:
- if (0 == json_array_size (hc->history))
- {
- GNUNET_break (h->in_shutdown ||
- (! GNUNET_TIME_absolute_is_future (hc->timeout)));
- return TALER_MHD_reply_static (connection,
- MHD_HTTP_NO_CONTENT,
- NULL,
- NULL,
- 0);
- }
- {
- json_t *h = hc->history;
-
- hc->history = NULL;
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string (
- "debit_account",
- hc->payto_uri),
- GNUNET_JSON_pack_array_steal (
- "outgoing_transactions",
- h));
- }
-}
-
-
-/**
- * Handle incoming HTTP request for /history/incoming
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account which account the request is about
- * @param con_cls closure for request (NULL or &special_ptr)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_credit_history (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- struct HistoryContext *hc;
- const struct Transaction *pos;
- enum GNUNET_GenericReturnValue ret;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &history_cleanup;
- *con_cls = cc;
- hc = GNUNET_new (struct HistoryContext);
- cc->ctx = hc;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling /history/incoming connection %p\n",
- connection);
- if (GNUNET_OK !=
- (ret = parse_history_common_args (h,
- connection,
- &hc->ha)))
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- hc->acc = lookup_account (h,
- account,
- NULL);
- if (NULL == hc->acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account);
- }
- /* FIXME: was simply: acc->payto_uri -- same!? */
- GNUNET_asprintf (&hc->payto_uri,
- "payto://x-taler-bank/%s/%s?receiver-name=%s",
- h->hostname,
- account,
- hc->acc->receiver_name);
- GNUNET_assert (0 == strcmp (hc->payto_uri,
- hc->acc->payto_uri));
- hc->history = json_array ();
- if (NULL == hc->history)
- {
- GNUNET_break (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_NO;
- }
- hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
- }
- else
- {
- hc = cc->ctx;
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- }
-
- if (! hc->ha.have_start)
- {
- pos = (0 > hc->ha.delta)
- ? hc->acc->in_tail
- : hc->acc->in_head;
- }
- else
- {
- struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
- bool overflow;
- uint64_t dir;
- bool skip = true;
-
- overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
- dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
- /* If account does not match, linear scan for
- first matching account. */
- while ( (! overflow) &&
- (NULL != t) &&
- (t->credit_account != hc->acc) )
- {
- skip = false;
- t = h->transactions[(t->row_id + dir) % h->ram_limit];
- if ( (NULL != t) &&
- (t->row_id == hc->ha.start_idx) )
- overflow = true; /* full circle, give up! */
- }
- if ( (NULL == t) ||
- overflow)
- {
- /* FIXME: these conditions are unclear to me. */
- if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
- (0 < hc->ha.delta))
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- if (overflow)
- return TALER_MHD_reply_with_ec (
- connection,
- TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
- NULL);
- goto finish;
- }
- if (h->in_shutdown)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- goto finish;
- }
- start_lp (h,
- connection,
- hc->acc,
- GNUNET_TIME_absolute_get_remaining (hc->timeout),
- LP_CREDIT,
- NULL);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
- }
- if (skip)
- {
- /* range from application is exclusive, skip the
- matching entry */
- if (0 > hc->ha.delta)
- pos = t->prev_in;
- else
- pos = t->next_in;
- }
- else
- {
- pos = t;
- }
- }
- if (NULL != pos)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning %lld credit transactions starting (inclusive) from %llu\n",
- (long long) hc->ha.delta,
- (unsigned long long) pos->row_id);
- while ( (0 != hc->ha.delta) &&
- (NULL != pos) )
- {
- json_t *trans;
-
- if (T_CREDIT != pos->type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected DEBIT transaction #%llu for account `%s'\n",
- (unsigned long long) pos->row_id,
- account);
- if (0 > hc->ha.delta)
- pos = pos->prev_in;
- if (0 < hc->ha.delta)
- pos = pos->next_in;
- continue;
- }
- trans = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("row_id",
- pos->row_id),
- GNUNET_JSON_pack_timestamp ("date",
- pos->date),
- TALER_JSON_pack_amount ("amount",
- &pos->amount),
- GNUNET_JSON_pack_string ("debit_account",
- pos->debit_account->payto_uri),
- GNUNET_JSON_pack_data_auto ("reserve_pub",
- &pos->subject.credit.reserve_pub));
- GNUNET_assert (NULL != trans);
- GNUNET_assert (0 ==
- json_array_append_new (hc->history,
- trans));
- if (hc->ha.delta > 0)
- hc->ha.delta--;
- else
- hc->ha.delta++;
- if (0 > hc->ha.delta)
- pos = pos->prev_in;
- if (0 < hc->ha.delta)
- pos = pos->next_in;
- }
- if ( (0 == json_array_size (hc->history)) &&
- (! h->in_shutdown) &&
- (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
- (0 < hc->ha.delta))
- {
- start_lp (h,
- connection,
- hc->acc,
- GNUNET_TIME_absolute_get_remaining (hc->timeout),
- LP_CREDIT,
- NULL);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
-finish:
- if (0 == json_array_size (hc->history))
- {
- GNUNET_break (h->in_shutdown ||
- (! GNUNET_TIME_absolute_is_future (hc->timeout)));
- return TALER_MHD_reply_static (connection,
- MHD_HTTP_NO_CONTENT,
- NULL,
- NULL,
- 0);
- }
- {
- json_t *h = hc->history;
-
- hc->history = NULL;
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string (
- "credit_account",
- hc->payto_uri),
- GNUNET_JSON_pack_array_steal (
- "incoming_transactions",
- h));
- }
-}
-
-
-/**
- * Handle incoming HTTP request.
- *
- * @param h our handle
- * @param connection the connection
- * @param url the requested url
- * @param method the method (POST, GET, ...)
- * @param account which account should process the request
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure
- * @return MHD result code
- */
-static MHD_RESULT
-serve (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *url,
- const char *method,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Fakebank, serving URL `%s' for account `%s'\n",
- url,
- account);
- if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET))
- {
- if ( (0 == strcmp (url,
- "/history/incoming")) &&
- (NULL != account) )
- return handle_credit_history (h,
- connection,
- account,
- con_cls);
- if ( (0 == strcmp (url,
- "/history/outgoing")) &&
- (NULL != account) )
- return handle_debit_history (h,
- connection,
- account,
- con_cls);
- if (0 == strcmp (url,
- "/"))
- return handle_home_page (h,
- connection);
- }
- else if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_POST))
- {
- if ( (0 == strcmp (url,
- "/admin/add-incoming")) &&
- (NULL != account) )
- return handle_admin_add_incoming (h,
- connection,
- account,
- upload_data,
- upload_data_size,
- con_cls);
- if ( (0 == strcmp (url,
- "/transfer")) &&
- (NULL != account) )
- return handle_transfer (h,
- connection,
- account,
- upload_data,
- upload_data_size,
- con_cls);
- }
- /* Unexpected URL path, just close the connection. */
- TALER_LOG_ERROR ("Breaking URL: %s %s\n",
- method,
- url);
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- url);
-}
-
-
-/**
- * Handle GET /withdrawal-operation/{wopid} request.
- *
- * @param h the handle
- * @param connection the connection
- * @param wopid the withdrawal operation identifier
- * @param lp how long is the long-polling timeout
- * @param con_cls closure for request
- * @return MHD result code
- */
-static MHD_RESULT
-get_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *wopid,
- struct GNUNET_TIME_Relative lp,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- struct WithdrawContext *wc;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &withdraw_cleanup;
- *con_cls = cc;
- wc = GNUNET_new (struct WithdrawContext);
- cc->ctx = wc;
- wc->wo = lookup_withdrawal_operation (h,
- wopid);
- if (NULL == wc->wo)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- wopid);
- }
- wc->timeout = GNUNET_TIME_relative_to_absolute (lp);
- }
- else
- {
- wc = cc->ctx;
- }
- if (GNUNET_TIME_absolute_is_past (wc->timeout) ||
- h->in_shutdown ||
- wc->wo->confirmation_done ||
- wc->wo->aborted)
- {
- json_t *wt;
-
- wt = json_array ();
- GNUNET_assert (NULL != wt);
- GNUNET_assert (0 ==
- json_array_append_new (wt,
- json_string ("x-taler-bank")));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_bool ("aborted",
- wc->wo->aborted),
- GNUNET_JSON_pack_bool ("selection_done",
- wc->wo->selection_done),
- GNUNET_JSON_pack_bool ("transfer_done",
- wc->wo->confirmation_done),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("suggested_exchange",
- h->exchange_url)),
- TALER_JSON_pack_amount ("amount",
- &wc->wo->amount),
- GNUNET_JSON_pack_array_steal ("wire_types",
- wt));
- }
-
- start_lp (h,
- connection,
- wc->wo->debit_account,
- GNUNET_TIME_absolute_get_remaining (wc->timeout),
- LP_WITHDRAW,
- wc->wo);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
-}
-
-
-/**
- * Handle POST /withdrawal-operation/ request.
- *
- * @param h our handle
- * @param connection the connection
- * @param wopid the withdrawal operation identifier
- * @param reserve_pub public key of the reserve
- * @param exchange_payto_uri payto://-URI of the exchange
- * @return MHD result code
- */
-static MHD_RESULT
-do_post_withdrawal (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *wopid,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const char *exchange_payto_uri)
-{
- struct WithdrawalOperation *wo;
- char *credit_name;
- struct Account *credit_account;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- wo = lookup_withdrawal_operation (h,
- wopid);
- if (NULL == wo)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- wopid);
- }
- if ( (wo->selection_done) &&
- (0 != GNUNET_memcmp (&wo->reserve_pub,
- reserve_pub)) )
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
- "reserve public key changed");
- }
- {
- /* check if reserve_pub is already in use */
- const struct GNUNET_PeerIdentity *pid;
-
- pid = (const struct GNUNET_PeerIdentity *) &wo->reserve_pub;
- if (GNUNET_CONTAINER_multipeermap_contains (h->rpubs,
- pid))
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
- NULL);
- }
- }
- credit_name = TALER_xtalerbank_account_from_payto (exchange_payto_uri);
- if (NULL == credit_name)
- {
- GNUNET_break_op (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
- NULL);
- }
- credit_account = lookup_account (h,
- credit_name,
- NULL);
- if (NULL == credit_account)
- {
- MHD_RESULT res;
-
- GNUNET_break_op (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- credit_name);
- GNUNET_free (credit_name);
- return res;
- }
- GNUNET_free (credit_name);
- if ( (NULL != wo->exchange_account) &&
- (credit_account != wo->exchange_account) )
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
- "exchange account changed");
- }
- wo->exchange_account = credit_account;
- wo->reserve_pub = *reserve_pub;
- wo->selection_done = true;
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_bool ("transfer_done",
- wo->confirmation_done));
-}
-
-
-/**
- * Handle POST /withdrawal-operation/ request.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param wopid the withdrawal operation identifier
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request
- * @return MHD result code
- */
-static MHD_RESULT
-post_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *wopid,
- const void *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- MHD_RESULT res;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
- *con_cls = cc;
- }
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- &cc->ctx,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
-
- {
- struct TALER_ReservePublicKeyP reserve_pub;
- const char *exchange_payto_url;
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("reserve_pub",
- &reserve_pub),
- GNUNET_JSON_spec_string ("selected_exchange",
- &exchange_payto_url),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- res = do_post_withdrawal (h,
- connection,
- wopid,
- &reserve_pub,
- exchange_payto_url);
- }
- json_decref (json);
- return res;
-}
-
-
-/**
- * Handle incoming HTTP request to the bank integration API.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param url the requested url
- * @param method the method (POST, GET, ...)
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request
- * @return MHD result code
- */
-static MHD_RESULT
-handle_bank_integration (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *url,
- const char *method,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_HEAD))
- method = MHD_HTTP_METHOD_GET;
- if ( (0 == strcmp (url,
- "/config")) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- {
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string ("version",
- "0:0:0"),
- GNUNET_JSON_pack_string ("currency",
- h->currency),
- GNUNET_JSON_pack_string ("name",
- "taler-bank-integration"));
- }
- if ( (0 == strncmp (url,
- "/withdrawal-operation/",
- strlen ("/withdrawal-operation/"))) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- {
- const char *wopid = &url[strlen ("/withdrawal-operation/")];
- const char *lp_s
- = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "long_poll_ms");
- struct GNUNET_TIME_Relative lp = GNUNET_TIME_UNIT_ZERO;
-
- if (NULL != lp_s)
- {
- unsigned long long d;
- char dummy;
-
- if (1 != sscanf (lp_s,
- "%llu%c",
- &d,
- &dummy))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "long_poll_ms");
- }
- lp = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- d);
- }
- return get_withdrawal_operation (h,
- connection,
- wopid,
- lp,
- con_cls);
-
- }
- if ( (0 == strncmp (url,
- "/withdrawal-operation/",
- strlen ("/withdrawal-operation/"))) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_POST)) )
- {
- const char *wopid = &url[strlen ("/withdrawal-operation/")];
- return post_withdrawal_operation (h,
- connection,
- wopid,
- upload_data,
- upload_data_size,
- con_cls);
- }
-
- TALER_LOG_ERROR ("Breaking URL: %s %s\n",
- method,
- url);
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- url);
-}
-
-
-/**
- * Handle GET /accounts/${account_name} request
- * to the Taler bank access API.
- *
- * @param h the handle
- * @param connection the connection
- * @param account_name name of the account
- * @return MHD result code
- */
-static MHD_RESULT
-get_account_access (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account_name)
-{
- struct Account *acc;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- acc = lookup_account (h,
- account_name,
- NULL);
- if (NULL == acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account_name);
- }
-
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string ("payto_uri",
- acc->payto_uri),
- GNUNET_JSON_pack_object_steal (
- "balance",
- GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("credit_debit_indicator",
- acc->is_negative
- ? "debit"
- : "credit"),
- TALER_JSON_pack_amount ("amount",
- &acc->balance))));
-}
-
-
-/**
- * Handle GET /accounts/${account_name}/withdrawals/{withdrawal_id} request
- * to the Taler bank access API.
- *
- * @param h the handle
- * @param connection the connection
- * @param account_name name of the account
- * @param withdrawal_id withdrawal ID to return status of
- * @return MHD result code
- */
-static MHD_RESULT
-get_account_withdrawals_access (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account_name,
- const char *withdrawal_id)
-{
- struct WithdrawalOperation *wo;
- struct Account *acc;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- wo = lookup_withdrawal_operation (h,
- withdrawal_id);
- if (NULL == wo)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- withdrawal_id);
- }
- acc = lookup_account (h,
- account_name,
- NULL);
- if (NULL == acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account_name);
- }
- if (wo->debit_account != acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- account_name);
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_bool ("aborted",
- wo->aborted),
- GNUNET_JSON_pack_bool ("selection_done",
- wo->selection_done),
- GNUNET_JSON_pack_bool ("transfer_done",
- wo->confirmation_done),
- GNUNET_JSON_pack_allow_null (
- GNUNET_JSON_pack_string ("selected_exchange_account",
- wo->exchange_account->payto_uri)),
- GNUNET_JSON_pack_allow_null (
- wo->selection_done
- ? GNUNET_JSON_pack_data_auto ("selected_reserve_pub",
- &wo->reserve_pub)
- : GNUNET_JSON_pack_string ("selected_reserve_pub",
- NULL)),
- TALER_JSON_pack_amount ("amount",
- &wo->amount));
-}
-
-
-/**
- * Handle POST /accounts/$account_name/withdrawals request.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param account_name name of the account
- * @param amount amont to withdraw
- * @return MHD result code
- */
-static MHD_RESULT
-do_post_account_withdrawals_access (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account_name,
- const struct TALER_Amount *amount)
-{
- struct Account *acc;
- struct WithdrawalOperation *wo;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- acc = lookup_account (h,
- account_name,
- NULL);
- if (NULL == acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account_name);
- }
- wo = GNUNET_new (struct WithdrawalOperation);
- wo->debit_account = acc;
- wo->amount = *amount;
- if (NULL == h->wops)
- {
- h->wops = GNUNET_CONTAINER_multishortmap_create (32,
- GNUNET_YES);
- }
- while (1)
- {
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
- &wo->wopid,
- sizeof (wo->wopid));
- if (GNUNET_OK ==
- GNUNET_CONTAINER_multishortmap_put (h->wops,
- &wo->wopid,
- wo,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
- break;
- }
- {
- char *wopids;
- char *uri;
- MHD_RESULT res;
-
- wopids = GNUNET_STRINGS_data_to_string_alloc (&wo->wopid,
- sizeof (wo->wopid));
- GNUNET_asprintf (&uri,
- "taler+http://withdraw/%s:%u/taler-bank-integration/%s",
- h->hostname,
- (unsigned int) h->port,
- wopids);
- GNUNET_free (wopids);
- res = TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string ("taler_withdraw_uri",
- uri),
- GNUNET_JSON_pack_data_auto ("withdrawal_id",
- &wo->wopid));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- GNUNET_free (uri);
- return res;
- }
-}
-
-
-/**
- * Handle POST /accounts/$account_name/withdrawals request.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param account_name name of the account
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request
- * @return MHD result code
- */
-static MHD_RESULT
-post_account_withdrawals_access (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account_name,
- const void *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- MHD_RESULT res;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
- *con_cls = cc;
- }
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- &cc->ctx,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
-
- {
- struct TALER_Amount amount;
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_amount ("amount",
- h->currency,
- &amount),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- res = do_post_account_withdrawals_access (h,
- connection,
- account_name,
- &amount);
- }
- json_decref (json);
- return res;
-}
-
-
-/**
- * Handle POST /testing/register request.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request
- * @return MHD result code
- */
-static MHD_RESULT
-post_testing_register (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const void *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- MHD_RESULT res;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
- *con_cls = cc;
- }
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- &cc->ctx,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
-
- {
- const char *username;
- const char *password;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_string ("username",
- &username),
- GNUNET_JSON_spec_string ("password",
- &password),
- GNUNET_JSON_spec_end ()
- };
- enum GNUNET_GenericReturnValue ret;
- struct Account *acc;
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- acc = lookup_account (h,
- username,
- NULL);
- if (NULL != acc)
- {
- if (0 != strcmp (password,
- acc->password))
- {
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_REGISTER_CONFLICT,
- "password");
- }
- }
- else
- {
- acc = lookup_account (h,
- username,
- username);
- GNUNET_assert (NULL != acc);
- acc->password = GNUNET_strdup (password);
- acc->balance = h->signup_bonus; /* magic money creation! */
- }
- res = TALER_MHD_reply_static (connection,
- MHD_HTTP_NO_CONTENT,
- NULL,
- NULL,
- 0);
- }
- json_decref (json);
- return res;
-}
-
-
-/**
- * Notify long pollers that a @a wo was updated.
- * Must be called with the "big_lock" still held.
- *
- * @param h fakebank handle
- * @param wo withdraw operation that finished
- */
-static void
-notify_withdrawal (struct TALER_FAKEBANK_Handle *h,
- const struct WithdrawalOperation *wo)
-{
- struct Account *debit_acc = wo->debit_account;
- struct LongPoller *nxt;
-
- for (struct LongPoller *lp = debit_acc->lp_head;
- NULL != lp;
- lp = nxt)
- {
- nxt = lp->next;
- if ( (LP_WITHDRAW == lp->type) &&
- (wo == lp->wo) )
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_node (lp->hn));
- lp_trigger (lp,
- h);
- }
- }
-}
-
-
-/**
- * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/abort request.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param account_name name of the debited account
- * @param withdrawal_id the withdrawal operation identifier
- * @return MHD result code
- */
-static MHD_RESULT
-access_withdrawals_abort (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account_name,
- const char *withdrawal_id)
-{
- struct WithdrawalOperation *wo;
- struct Account *acc;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- wo = lookup_withdrawal_operation (h,
- withdrawal_id);
- if (NULL == wo)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- withdrawal_id);
- }
- acc = lookup_account (h,
- account_name,
- NULL);
- if (NULL == acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account_name);
- }
- if (wo->debit_account != acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- account_name);
- }
- if (wo->confirmation_done)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_ABORT_CONFIRM_CONFLICT,
- account_name);
- }
- wo->aborted = true;
- notify_withdrawal (h,
- wo);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_json (connection,
- json_object (), /* FIXME: #7301 */
- MHD_HTTP_OK);
-}
-
-
-/**
- * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/confirm request.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param account_name name of the debited account
- * @param withdrawal_id the withdrawal operation identifier
- * @return MHD result code
- */
-static MHD_RESULT
-access_withdrawals_confirm (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account_name,
- const char *withdrawal_id)
-{
- struct WithdrawalOperation *wo;
- struct Account *acc;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- wo = lookup_withdrawal_operation (h,
- withdrawal_id);
- if (NULL == wo)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- withdrawal_id);
- }
- acc = lookup_account (h,
- account_name,
- NULL);
- if (NULL == acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account_name);
- }
- if (wo->debit_account != acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_TRANSACTION_NOT_FOUND,
- account_name);
- }
- if (NULL == wo->exchange_account)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_BANK_POST_WITHDRAWAL_OPERATION_REQUIRED,
- NULL);
- }
- if (wo->aborted)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
- account_name);
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- if (GNUNET_OK !=
- make_admin_transfer (h,
- wo->debit_account->account_name,
- wo->exchange_account->account_name,
- &wo->amount,
- &wo->reserve_pub,
- &wo->row_id,
- &wo->timestamp))
- {
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
- NULL);
- }
- /* Re-acquiring the lock and continuing to operate on 'wo'
- is currently (!) acceptable because we NEVER free 'wo'
- until shutdown. We may want to revise this if keeping
- all withdraw operations in RAM becomes an issue... */
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- wo->confirmation_done = true;
- notify_withdrawal (h,
- wo);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_json (connection,
- json_object (),
- MHD_HTTP_OK);
-}
-
-
-/**
- * Handle incoming HTTP request to the Taler bank access API.
- *
- * @param h our fakebank handle
- * @param connection the connection
- * @param url the requested url
- * @param method the method (POST, GET, ...)
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request
- * @return MHD result code
- */
-static MHD_RESULT
-handle_bank_access (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *url,
- const char *method,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_HEAD))
- method = MHD_HTTP_METHOD_GET;
- if ( (0 == strcmp (url,
- "/config")) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- {
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string ("version",
- "0:0:0"),
- GNUNET_JSON_pack_string ("currency",
- h->currency),
- GNUNET_JSON_pack_string ("name",
- "taler-bank-access"));
- }
- if ( (0 == strcmp (url,
- "/public-accounts")) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- {
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_array_steal ("public_accounts",
- json_array ()));
- }
- if ( (0 == strncmp (url,
- "/accounts/",
- strlen ("/accounts/"))) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_POST)) )
- {
- const char *acc_name = &url[strlen ("/accounts/")];
- const char *end_acc = strchr (acc_name,
- '/');
- char *acc;
- MHD_RESULT ret;
-
- if ( (NULL == end_acc) ||
- (0 != strncmp (end_acc,
- "/withdrawals",
- strlen ("/withdrawals"))) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- acc_name);
- }
- acc = GNUNET_strndup (acc_name,
- end_acc - acc_name);
- end_acc += strlen ("/withdrawals");
- if ('/' == *end_acc)
- {
- const char *wid = end_acc + 1;
- const char *opid = strchr (wid,
- '/');
- char *wi;
-
- if ( (NULL == opid) ||
- ( (0 != strcmp (opid,
- "/abort")) &&
- (0 != strcmp (opid,
- "/confirm")) ) )
- {
- GNUNET_break_op (0);
- GNUNET_free (acc);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- acc_name);
- }
- wi = GNUNET_strndup (wid,
- opid - wid);
- if (0 == strcmp (opid,
- "/abort"))
- {
- ret = access_withdrawals_abort (h,
- connection,
- acc,
- wi);
- GNUNET_free (wi);
- GNUNET_free (acc);
- return ret;
- }
- if (0 == strcmp (opid,
- "/confirm"))
- {
- ret = access_withdrawals_confirm (h,
- connection,
- acc,
- wi);
- GNUNET_free (wi);
- GNUNET_free (acc);
- return ret;
- }
- GNUNET_assert (0);
- }
- ret = post_account_withdrawals_access (h,
- connection,
- acc,
- upload_data,
- upload_data_size,
- con_cls);
- GNUNET_free (acc);
- return ret;
- }
-
- if ( (0 == strncmp (url,
- "/accounts/",
- strlen ("/accounts/"))) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET)) )
- {
- const char *acc_name = &url[strlen ("/accounts/")];
- const char *end_acc = strchr (acc_name,
- '/');
- const char *wid;
- char *acc;
- MHD_RESULT ret;
-
- if (NULL == end_acc)
- {
- ret = get_account_access (h,
- connection,
- acc_name);
- return ret;
- }
- if (0 != strncmp (end_acc,
- "/withdrawals/",
- strlen ("/withdrawals/")))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- acc_name);
- }
- acc = GNUNET_strndup (acc_name,
- end_acc - acc_name);
- wid = &end_acc[strlen ("/withdrawals/")];
- ret = get_account_withdrawals_access (h,
- connection,
- acc,
- wid);
- GNUNET_free (acc);
- return ret;
- }
- /* FIXME: implement transactions API: 1.12.2 */
-
- /* registration API */
- if ( (0 == strcmp (url,
- "/testing/register")) &&
- (0 == strcasecmp (method,
- MHD_HTTP_METHOD_POST)) )
- {
- return post_testing_register (h,
- connection,
- upload_data,
- upload_data_size,
- con_cls);
- }
-
- TALER_LOG_ERROR ("Breaking URL: %s %s\n",
- method,
- url);
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- url);
-}
-
-
-/**
- * Handle incoming HTTP request for /history/incoming
- * of the Anastasis API. This one can return transactions
- * created by debits from the exchange!
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account which account the request is about
- * @param con_cls closure for request (NULL or &special_ptr)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_anastasis_credit_history (
- struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- struct HistoryContext *hc;
- const struct Transaction *pos;
- enum GNUNET_GenericReturnValue ret;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &history_cleanup;
- *con_cls = cc;
- hc = GNUNET_new (struct HistoryContext);
- cc->ctx = hc;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling /history/incoming connection %p\n",
- connection);
- if (GNUNET_OK !=
- (ret = parse_history_common_args (h,
- connection,
- &hc->ha)))
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- hc->acc = lookup_account (h,
- account,
- NULL);
- if (NULL == hc->acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account);
- }
- /* FIXME: was simply: acc->payto_uri -- same!? */
- GNUNET_asprintf (&hc->payto_uri,
- "payto://x-taler-bank/localhost/%s?receiver-name=%s",
- account,
- hc->acc->receiver_name);
- GNUNET_assert (0 == strcmp (hc->payto_uri,
- hc->acc->payto_uri));
- hc->history = json_array ();
- if (NULL == hc->history)
- {
- GNUNET_break (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_NO;
- }
- hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
- }
- else
- {
- hc = cc->ctx;
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- }
-
- if (! hc->ha.have_start)
- {
- pos = (0 > hc->ha.delta)
- ? hc->acc->in_tail
- : hc->acc->in_head;
- }
- else
- {
- struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
- bool overflow;
- uint64_t dir;
- bool skip = true;
-
- overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
- dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
- /* If account does not match, linear scan for
- first matching account. */
- while ( (! overflow) &&
- (NULL != t) &&
- (t->credit_account != hc->acc) )
- {
- skip = false;
- t = h->transactions[(t->row_id + dir) % h->ram_limit];
- if ( (NULL != t) &&
- (t->row_id == hc->ha.start_idx) )
- overflow = true; /* full circle, give up! */
- }
- if ( (NULL == t) ||
- overflow)
- {
- /* FIXME: these conditions are unclear to me. */
- if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
- (0 < hc->ha.delta))
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- if (overflow)
- return TALER_MHD_reply_with_ec (
- connection,
- TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
- NULL);
- goto finish;
- }
- if (h->in_shutdown)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- goto finish;
- }
- start_lp (h,
- connection,
- hc->acc,
- GNUNET_TIME_absolute_get_remaining (hc->timeout),
- LP_CREDIT,
- NULL);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
- }
- if (skip)
- {
- /* range from application is exclusive, skip the
- matching entry */
- if (0 > hc->ha.delta)
- pos = t->prev_in;
- else
- pos = t->next_in;
- }
- else
- {
- pos = t;
- }
- }
- if (NULL != pos)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning %lld credit transactions starting (inclusive) from %llu\n",
- (long long) hc->ha.delta,
- (unsigned long long) pos->row_id);
- while ( (0 != hc->ha.delta) &&
- (NULL != pos) )
- {
- json_t *trans;
- char *subject;
-
- if (T_DEBIT != pos->type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected CREDIT transaction #%llu for account `%s'\n",
- (unsigned long long) pos->row_id,
- account);
- if (0 > hc->ha.delta)
- pos = pos->prev_in;
- if (0 < hc->ha.delta)
- pos = pos->next_in;
- continue;
- }
-
- {
- char *wtids;
-
- wtids = GNUNET_STRINGS_data_to_string_alloc (
- &pos->subject.debit.wtid,
- sizeof (pos->subject.debit.wtid));
- GNUNET_asprintf (&subject,
- "%s %s",
- wtids,
- pos->subject.debit.exchange_base_url);
- GNUNET_free (wtids);
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Found transaction over %s with subject %s\n",
- TALER_amount2s (&pos->amount),
- subject);
- trans = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_string ("type",
- "RESERVE"),
- GNUNET_JSON_pack_uint64 ("row_id",
- pos->row_id),
- GNUNET_JSON_pack_timestamp ("date",
- pos->date),
- TALER_JSON_pack_amount ("amount",
- &pos->amount),
- GNUNET_JSON_pack_string ("debit_account",
- pos->debit_account->payto_uri),
- GNUNET_JSON_pack_string ("subject",
- subject));
- GNUNET_free (subject);
- GNUNET_assert (NULL != trans);
- GNUNET_assert (0 ==
- json_array_append_new (hc->history,
- trans));
- if (hc->ha.delta > 0)
- hc->ha.delta--;
- else
- hc->ha.delta++;
- if (0 > hc->ha.delta)
- pos = pos->prev_in;
- if (0 < hc->ha.delta)
- pos = pos->next_in;
- }
- if ( (0 == json_array_size (hc->history)) &&
- (! h->in_shutdown) &&
- (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
- (0 < hc->ha.delta))
- {
- start_lp (h,
- connection,
- hc->acc,
- GNUNET_TIME_absolute_get_remaining (hc->timeout),
- LP_CREDIT,
- NULL);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
-finish:
- if (0 == json_array_size (hc->history))
- {
- GNUNET_break (h->in_shutdown ||
- (! GNUNET_TIME_absolute_is_future (hc->timeout)));
- return TALER_MHD_reply_static (connection,
- MHD_HTTP_NO_CONTENT,
- NULL,
- NULL,
- 0);
- }
- {
- json_t *h = hc->history;
-
- hc->history = NULL;
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_string (
- "credit_account",
- hc->payto_uri),
- GNUNET_JSON_pack_array_steal (
- "incoming_transactions",
- h));
- }
-}
-
-
-/**
- * Handle incoming HTTP request to Anastasis API.
- *
- * @param h our handle
- * @param connection the connection
- * @param url the requested url
- * @param method the method (POST, GET, ...)
- * @param account which account should process the request
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure
- * @return MHD result code
- */
-static MHD_RESULT
-handle_anastasis_api (
- struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *url,
- const char *method,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Fakebank - Anastasis API: serving URL `%s' for account `%s'\n",
- url,
- account);
- if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET))
- {
- if ( (0 == strcmp (url,
- "/history/incoming")) &&
- (NULL != account) )
- return handle_anastasis_credit_history (h,
- connection,
- account,
- con_cls);
- if (0 == strcmp (url,
- "/"))
- return handle_home_page (h,
- connection);
- }
- /* Unexpected URL path, just close the connection. */
- TALER_LOG_ERROR ("Breaking URL: %s %s\n",
- method,
- url);
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- url);
-}
-
-
-/**
* Handle incoming HTTP request.
*
* @param cls a `struct TALER_FAKEBANK_Handle`
@@ -4418,26 +106,26 @@ handle_mhd_request (void *cls,
strlen ("/taler-bank-integration/")))
{
url += strlen ("/taler-bank-integration");
- return handle_bank_integration (h,
- connection,
- url,
- method,
- upload_data,
- upload_data_size,
- con_cls);
+ return TALER_FAKEBANK_tbi_main_ (h,
+ connection,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
}
if (0 == strncmp (url,
"/taler-bank-access/",
strlen ("/taler-bank-access/")))
{
url += strlen ("/taler-bank-access");
- return handle_bank_access (h,
- connection,
- url,
- method,
- upload_data,
- upload_data_size,
- con_cls);
+ return TALER_FAKEBANK_bank_main_ (h,
+ connection,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
}
/* Next is duplication to be more libeufin-like: */
if (0 == strncmp (url,
@@ -4445,13 +133,13 @@ handle_mhd_request (void *cls,
strlen ("/access-api/")))
{
url += strlen ("/access-api");
- return handle_bank_access (h,
- connection,
- url,
- method,
- upload_data,
- upload_data_size,
- con_cls);
+ return TALER_FAKEBANK_bank_main_ (h,
+ connection,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
}
if (0 == strncmp (url,
"/anastasis-api/",
@@ -4465,14 +153,14 @@ handle_mhd_request (void *cls,
end - url - 1);
url = end;
}
- return handle_anastasis_api (h,
- connection,
- account,
- url,
- method,
- upload_data,
- upload_data_size,
- con_cls);
+ return TALER_FAKEBANK_tbr_main_ (h,
+ connection,
+ account,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
}
if (0 == strncmp (url,
@@ -4489,14 +177,14 @@ handle_mhd_request (void *cls,
end - url - 1);
url = end;
}
- ret = serve (h,
- connection,
- account,
- url,
- method,
- upload_data,
- upload_data_size,
- con_cls);
+ ret = TALER_FAKEBANK_twg_main_ (h,
+ connection,
+ account,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
GNUNET_free (account);
return ret;
}
@@ -4529,7 +217,7 @@ schedule_httpd (struct TALER_FAKEBANK_Handle *h)
h->mhd_task =
GNUNET_SCHEDULER_add_read_net (tv,
h->mhd_rfd,
- &run_mhd,
+ &TALER_FAKEBANK_run_mhd_,
h);
}
@@ -4603,7 +291,7 @@ schedule_httpd (struct TALER_FAKEBANK_Handle *h)
tv,
wrs,
wws,
- &run_mhd,
+ &TALER_FAKEBANK_run_mhd_,
h);
if (NULL != wrs)
GNUNET_NETWORK_fdset_destroy (wrs);
@@ -4620,8 +308,8 @@ schedule_httpd (struct TALER_FAKEBANK_Handle *h)
*
* @param cls the `struct TALER_FAKEBANK_Handle`
*/
-static void
-run_mhd (void *cls)
+void
+TALER_FAKEBANK_run_mhd_ (void *cls)
{
struct TALER_FAKEBANK_Handle *h = cls;
@@ -4770,22 +458,23 @@ TALER_FAKEBANK_start3 (const char *hostname,
(unsigned int) port);
if (0 == num_threads)
{
- h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
+ h->mhd_bank = MHD_start_daemon (
+ MHD_USE_DEBUG
#if EPOLL_SUPPORT
- | MHD_USE_EPOLL
+ | MHD_USE_EPOLL
#endif
- | MHD_USE_DUAL_STACK
- | MHD_ALLOW_SUSPEND_RESUME,
- port,
- NULL, NULL,
- &handle_mhd_request, h,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback, h,
- MHD_OPTION_LISTEN_BACKLOG_SIZE,
- (unsigned int) 1024,
- MHD_OPTION_CONNECTION_LIMIT,
- (unsigned int) 65536,
- MHD_OPTION_END);
+ | MHD_USE_DUAL_STACK
+ | MHD_ALLOW_SUSPEND_RESUME,
+ port,
+ NULL, NULL,
+ &handle_mhd_request, h,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, h,
+ MHD_OPTION_LISTEN_BACKLOG_SIZE,
+ (unsigned int) 1024,
+ MHD_OPTION_CONNECTION_LIMIT,
+ (unsigned int) 65536,
+ MHD_OPTION_END);
if (NULL == h->mhd_bank)
{
TALER_FAKEBANK_stop (h);
@@ -4828,7 +517,7 @@ TALER_FAKEBANK_start3 (const char *hostname,
if (0 !=
pthread_create (&h->lp_thread,
NULL,
- &lp_expiration_thread,
+ &TALER_FAKEBANK_lp_expiration_thread_,
h))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
@@ -4845,24 +534,25 @@ TALER_FAKEBANK_start3 (const char *hostname,
TALER_FAKEBANK_stop (h);
return NULL;
}
- h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
- | MHD_USE_AUTO_INTERNAL_THREAD
- | MHD_ALLOW_SUSPEND_RESUME
- | MHD_USE_TURBO
- | MHD_USE_TCP_FASTOPEN
- | MHD_USE_DUAL_STACK,
- port,
- NULL, NULL,
- &handle_mhd_request, h,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback, h,
- MHD_OPTION_LISTEN_BACKLOG_SIZE,
- (unsigned int) 1024,
- MHD_OPTION_CONNECTION_LIMIT,
- (unsigned int) 65536,
- MHD_OPTION_THREAD_POOL_SIZE,
- num_threads,
- MHD_OPTION_END);
+ h->mhd_bank = MHD_start_daemon (
+ MHD_USE_DEBUG
+ | MHD_USE_AUTO_INTERNAL_THREAD
+ | MHD_ALLOW_SUSPEND_RESUME
+ | MHD_USE_TURBO
+ | MHD_USE_TCP_FASTOPEN
+ | MHD_USE_DUAL_STACK,
+ port,
+ NULL, NULL,
+ &handle_mhd_request, h,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, h,
+ MHD_OPTION_LISTEN_BACKLOG_SIZE,
+ (unsigned int) 1024,
+ MHD_OPTION_CONNECTION_LIMIT,
+ (unsigned int) 65536,
+ MHD_OPTION_THREAD_POOL_SIZE,
+ num_threads,
+ MHD_OPTION_END);
if (NULL == h->mhd_bank)
{
GNUNET_break (0);
diff --git a/src/bank-lib/fakebank.h b/src/bank-lib/fakebank.h
new file mode 100644
index 000000000..a9285ec13
--- /dev/null
+++ b/src/bank-lib/fakebank.h
@@ -0,0 +1,696 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank.h
+ * @brief general state of the fakebank
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_H
+#define FAKEBANK_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+/**
+ * How long are exchange base URLs allowed to be at most?
+ * Set to a relatively low number as this does contribute
+ * significantly to our RAM consumption.
+ */
+#define MAX_URL_LEN 64
+
+
+/**
+ * Maximum POST request size.
+ */
+#define REQUEST_BUFFER_MAX (4 * 1024)
+
+
+/**
+ * Per account information.
+ */
+struct Account;
+
+
+/**
+ * Types of long polling activities.
+ */
+enum LongPollType
+{
+ /**
+ * Transfer TO the exchange.
+ */
+ LP_CREDIT,
+
+ /**
+ * Transfer FROM the exchange.
+ */
+ LP_DEBIT,
+
+ /**
+ * Withdraw operation completion/abort.
+ */
+ LP_WITHDRAW
+
+};
+
+/**
+ * Client waiting for activity on this account.
+ */
+struct LongPoller
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *prev;
+
+ /**
+ * Fakebank this long poller belongs with.
+ */
+ struct TALER_FAKEBANK_Handle *h;
+
+ /**
+ * Account this long poller is waiting on.
+ */
+ struct Account *account;
+
+ /**
+ * Withdraw operation we are waiting on,
+ * only if @e type is #LP_WITHDRAW, otherwise NULL.
+ */
+ const struct WithdrawalOperation *wo;
+
+ /**
+ * Entry in the heap for this long poller.
+ */
+ struct GNUNET_CONTAINER_HeapNode *hn;
+
+ /**
+ * Client that is waiting for transactions.
+ */
+ struct MHD_Connection *conn;
+
+ /**
+ * When will this long poller time out?
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * What does the @e connection wait for?
+ */
+ enum LongPollType type;
+
+};
+
+
+/**
+ * Details about a transcation we (as the simulated bank) received.
+ */
+struct Transaction;
+
+
+/**
+ * Information we keep per withdraw operation.
+ */
+struct WithdrawalOperation
+{
+ /**
+ * Unique (random) operation ID.
+ */
+ struct GNUNET_ShortHashCode wopid;
+
+ /**
+ * Debited account.
+ */
+ struct Account *debit_account;
+
+ /**
+ * Target exchange account, or NULL if unknown.
+ */
+ const struct Account *exchange_account;
+
+ /**
+ * RowID of the resulting transaction, if any. Otherwise 0.
+ */
+ uint64_t row_id;
+
+ /**
+ * Amount transferred.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Public key of the reserve, wire transfer subject.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * When was the transaction made? 0 if not yet.
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Was the withdrawal aborted?
+ */
+ bool aborted;
+
+ /**
+ * Did the bank confirm the withdrawal?
+ */
+ bool confirmation_done;
+
+ /**
+ * Is @e reserve_pub initialized?
+ */
+ bool selection_done;
+
+};
+
+
+/**
+ * Per account information.
+ */
+struct Account
+{
+
+ /**
+ * Inbound transactions for this account in a MDLL.
+ */
+ struct Transaction *in_head;
+
+ /**
+ * Inbound transactions for this account in a MDLL.
+ */
+ struct Transaction *in_tail;
+
+ /**
+ * Outbound transactions for this account in a MDLL.
+ */
+ struct Transaction *out_head;
+
+ /**
+ * Outbound transactions for this account in a MDLL.
+ */
+ struct Transaction *out_tail;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *lp_head;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *lp_tail;
+
+ /**
+ * Account name (string, not payto!)
+ */
+ char *account_name;
+
+ /**
+ * Receiver name for payto:// URIs.
+ */
+ char *receiver_name;
+
+ /**
+ * Payto URI for this account.
+ */
+ char *payto_uri;
+
+ /**
+ * Password set for the account (if any).
+ */
+ char *password;
+
+ /**
+ * Current account balance.
+ */
+ struct TALER_Amount balance;
+
+ /**
+ * true if the balance is negative.
+ */
+ bool is_negative;
+
+};
+
+
+/**
+ * Details about a transcation we (as the simulated bank) received.
+ */
+struct Transaction
+{
+ /**
+ * We store inbound transactions in a MDLL.
+ */
+ struct Transaction *next_in;
+
+ /**
+ * We store inbound transactions in a MDLL.
+ */
+ struct Transaction *prev_in;
+
+ /**
+ * We store outbound transactions in a MDLL.
+ */
+ struct Transaction *next_out;
+
+ /**
+ * We store outbound transactions in a MDLL.
+ */
+ struct Transaction *prev_out;
+
+ /**
+ * Amount to be transferred.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Account to debit.
+ */
+ struct Account *debit_account;
+
+ /**
+ * Account to credit.
+ */
+ struct Account *credit_account;
+
+ /**
+ * Random unique identifier for the request.
+ * Used to detect idempotent requests.
+ */
+ struct GNUNET_HashCode request_uid;
+
+ /**
+ * When did the transaction happen?
+ */
+ struct GNUNET_TIME_Timestamp date;
+
+ /**
+ * Number of this transaction.
+ */
+ uint64_t row_id;
+
+ /**
+ * What does the @e subject contain?
+ */
+ enum
+ {
+ /**
+ * Transfer TO the exchange.
+ */
+ T_CREDIT,
+
+ /**
+ * Transfer FROM the exchange.
+ */
+ T_DEBIT,
+
+ /**
+ * Exchange-to-exchange WAD transfer.
+ */
+ T_WAD,
+ } type;
+
+ /**
+ * Wire transfer subject.
+ */
+ union
+ {
+
+ /**
+ * Used if @e type is T_DEBIT.
+ */
+ struct
+ {
+
+ /**
+ * Subject of the transfer.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Base URL of the exchange.
+ */
+ char exchange_base_url[MAX_URL_LEN];
+
+ } debit;
+
+ /**
+ * Used if @e type is T_CREDIT.
+ */
+ struct
+ {
+
+ /**
+ * Reserve public key of the credit operation.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ } credit;
+
+ /**
+ * Used if @e type is T_WAD.
+ */
+ struct
+ {
+
+ /**
+ * Subject of the transfer.
+ */
+ struct TALER_WadIdentifierP wad;
+
+ /**
+ * Base URL of the originating exchange.
+ */
+ char origin_base_url[MAX_URL_LEN];
+
+ } wad;
+
+ } subject;
+
+ /**
+ * Has this transaction not yet been subjected to
+ * #TALER_FAKEBANK_check_credit() or #TALER_FAKEBANK_check_debit() and
+ * should thus be counted in #TALER_FAKEBANK_check_empty()?
+ */
+ bool unchecked;
+};
+
+
+/**
+ * Function called to clean up context of a connection.
+ *
+ * @param ctx context to clean up
+ */
+typedef void
+(*ConnectionCleaner)(void *ctx);
+
+/**
+ * Universal context we keep per connection.
+ */
+struct ConnectionContext
+{
+ /**
+ * Function we call upon completion to clean up.
+ */
+ ConnectionCleaner ctx_cleaner;
+
+ /**
+ * Request-handler specific context.
+ */
+ void *ctx;
+};
+
+
+/**
+ * This is the "base" structure for both the /history and the
+ * /history-range API calls.
+ */
+struct HistoryArgs
+{
+
+ /**
+ * Bank account number of the requesting client.
+ */
+ uint64_t account_number;
+
+ /**
+ * Index of the starting transaction, exclusive (!).
+ */
+ uint64_t start_idx;
+
+ /**
+ * Requested number of results and order
+ * (positive: ascending, negative: descending)
+ */
+ int64_t delta;
+
+ /**
+ * Timeout for long polling.
+ */
+ struct GNUNET_TIME_Relative lp_timeout;
+
+ /**
+ * true if starting point was given.
+ */
+ bool have_start;
+
+};
+
+
+/**
+ * Context we keep per history request.
+ */
+struct HistoryContext
+{
+ /**
+ * When does this request time out.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Client arguments for this request.
+ */
+ struct HistoryArgs ha;
+
+ /**
+ * Account the request is about.
+ */
+ struct Account *acc;
+
+ /**
+ * Payto URI of the account.
+ */
+ char *payto_uri;
+
+ /**
+ * JSON object we are building to return.
+ */
+ json_t *history;
+
+};
+
+
+/**
+ * Context we keep per get withdrawal operation request.
+ */
+struct WithdrawContext
+{
+ /**
+ * When does this request time out.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * The withdrawal operation this is about.
+ */
+ struct WithdrawalOperation *wo;
+
+};
+
+
+/**
+ * Handle for the fake bank.
+ */
+struct TALER_FAKEBANK_Handle
+{
+ /**
+ * We store transactions in a revolving array.
+ */
+ struct Transaction **transactions;
+
+ /**
+ * HTTP server we run to pretend to be the "test" bank.
+ */
+ struct MHD_Daemon *mhd_bank;
+
+ /**
+ * Task running HTTP server for the "test" bank,
+ * unless we are using a thread pool (then NULL).
+ */
+ struct GNUNET_SCHEDULER_Task *mhd_task;
+
+ /**
+ * Task for expiring long-polling connections,
+ * unless we are using a thread pool (then NULL).
+ */
+ struct GNUNET_SCHEDULER_Task *lp_task;
+
+ /**
+ * Task for expiring long-polling connections, unless we are using the
+ * GNUnet scheduler (then NULL).
+ */
+ pthread_t lp_thread;
+
+ /**
+ * MIN-heap of long pollers, sorted by timeout.
+ */
+ struct GNUNET_CONTAINER_Heap *lp_heap;
+
+ /**
+ * Hashmap of reserve public keys to
+ * `struct Transaction` with that reserve public
+ * key. Used to prevent public-key re-use.
+ */
+ struct GNUNET_CONTAINER_MultiPeerMap *rpubs;
+
+ /**
+ * Hashmap of short hashes (wopids) to
+ * `struct WithdrawalOperation`.
+ * Used to lookup withdrawal operations.
+ */
+ struct GNUNET_CONTAINER_MultiShortmap *wops;
+
+ /**
+ * (Base) URL to suggest for the exchange. Can
+ * be NULL if there is no suggestion to be made.
+ */
+ char *exchange_url;
+
+ /**
+ * Lock for accessing @a rpubs map.
+ */
+ pthread_mutex_t rpubs_lock;
+
+ /**
+ * Hashmap of hashes of account names to `struct Account`.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *accounts;
+
+ /**
+ * Lock for accessing @a accounts hash map.
+ */
+ pthread_mutex_t accounts_lock;
+
+ /**
+ * Hashmap of hashes of transaction request_uids to `struct Transaction`.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *uuid_map;
+
+ /**
+ * Lock for accessing @a uuid_map.
+ */
+ pthread_mutex_t uuid_map_lock;
+
+ /**
+ * Lock for accessing the internals of
+ * accounts and transaction array entries.
+ */
+ pthread_mutex_t big_lock;
+
+ /**
+ * How much money should be put into new accounts
+ * on /register.
+ */
+ struct TALER_Amount signup_bonus;
+
+ /**
+ * Current transaction counter.
+ */
+ uint64_t serial_counter;
+
+ /**
+ * Number of transactions we keep in memory (at most).
+ */
+ uint64_t ram_limit;
+
+ /**
+ * Currency used by the fakebank.
+ */
+ char *currency;
+
+ /**
+ * Hostname of the fakebank.
+ */
+ char *hostname;
+
+ /**
+ * BaseURL of the fakebank.
+ */
+ char *my_baseurl;
+
+ /**
+ * Our port number.
+ */
+ uint16_t port;
+
+#ifdef __linux__
+ /**
+ * Event FD to signal @a lp_thread a change in
+ * @a lp_heap.
+ */
+ int lp_event;
+#else
+ /**
+ * Pipe input to signal @a lp_thread a change in
+ * @a lp_heap.
+ */
+ int lp_event_in;
+
+ /**
+ * Pipe output to signal @a lp_thread a change in
+ * @a lp_heap.
+ */
+ int lp_event_out;
+#endif
+
+ /**
+ * Set to true once we are shutting down.
+ */
+ bool in_shutdown;
+
+ /**
+ * Should we run MHD immediately again?
+ */
+ bool mhd_again;
+
+#if EPOLL_SUPPORT
+ /**
+ * Boxed @e mhd_fd.
+ */
+ struct GNUNET_NETWORK_Handle *mhd_rfd;
+
+ /**
+ * File descriptor to use to wait for MHD.
+ */
+ int mhd_fd;
+#endif
+};
+
+
+/**
+ * Task run whenever HTTP server operations are pending.
+ *
+ * @param cls the `struct TALER_FAKEBANK_Handle`
+ */
+void
+TALER_FAKEBANK_run_mhd_ (void *cls);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_api_check.c b/src/bank-lib/fakebank_api_check.c
new file mode 100644
index 000000000..04656ebab
--- /dev/null
+++ b/src/bank-lib/fakebank_api_check.c
@@ -0,0 +1,238 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_api_check.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Generate log messages for failed check operation.
+ *
+ * @param h handle to output transaction log for
+ */
+static void
+check_log (struct TALER_FAKEBANK_Handle *h)
+{
+ for (uint64_t i = 0; i<h->ram_limit; i++)
+ {
+ struct Transaction *t = h->transactions[i];
+
+ if (NULL == t)
+ continue;
+ if (! t->unchecked)
+ continue;
+ switch (t->type)
+ {
+ case T_DEBIT:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%s -> %s (%s) %s (%s)\n",
+ t->debit_account->account_name,
+ t->credit_account->account_name,
+ TALER_amount2s (&t->amount),
+ t->subject.debit.exchange_base_url,
+ "DEBIT");
+ break;
+ case T_CREDIT:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%s -> %s (%s) %s (%s)\n",
+ t->debit_account->account_name,
+ t->credit_account->account_name,
+ TALER_amount2s (&t->amount),
+ TALER_B2S (&t->subject.credit.reserve_pub),
+ "CREDIT");
+ break;
+ case T_WAD:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%s -> %s (%s) %s[%s] (%s)\n",
+ t->debit_account->account_name,
+ t->credit_account->account_name,
+ TALER_amount2s (&t->amount),
+ t->subject.wad.origin_base_url,
+ TALER_B2S (&t->subject.wad),
+ "WAD");
+ break;
+ }
+ }
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
+ const struct TALER_Amount *want_amount,
+ const char *want_debit,
+ const char *want_credit,
+ const char *exchange_base_url,
+ struct TALER_WireTransferIdentifierRawP *wtid)
+{
+ struct Account *debit_account;
+ struct Account *credit_account;
+
+ GNUNET_assert (0 ==
+ strcasecmp (want_amount->currency,
+ h->currency));
+ debit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_debit,
+ NULL);
+ credit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_credit,
+ NULL);
+ if (NULL == debit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted: %s->%s (%s) from exchange %s (DEBIT), but debit account does not even exist!\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ exchange_base_url);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == credit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted: %s->%s (%s) from exchange %s (DEBIT), but credit account does not even exist!\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ exchange_base_url);
+ return GNUNET_SYSERR;
+ }
+ for (struct Transaction *t = debit_account->out_tail;
+ NULL != t;
+ t = t->prev_out)
+ {
+ if ( (t->unchecked) &&
+ (credit_account == t->credit_account) &&
+ (T_DEBIT == t->type) &&
+ (0 == TALER_amount_cmp (want_amount,
+ &t->amount)) &&
+ (0 == strcasecmp (exchange_base_url,
+ t->subject.debit.exchange_base_url)) )
+ {
+ *wtid = t->subject.debit.wtid;
+ t->unchecked = false;
+ return GNUNET_OK;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Did not find matching transaction! I have:\n");
+ check_log (h);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted: %s->%s (%s) from exchange %s (DEBIT)\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ exchange_base_url);
+ return GNUNET_SYSERR;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
+ const struct TALER_Amount *want_amount,
+ const char *want_debit,
+ const char *want_credit,
+ const struct TALER_ReservePublicKeyP *reserve_pub)
+{
+ struct Account *debit_account;
+ struct Account *credit_account;
+
+ GNUNET_assert (0 == strcasecmp (want_amount->currency,
+ h->currency));
+ debit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_debit,
+ NULL);
+ credit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_credit,
+ NULL);
+ if (NULL == debit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but debit account is unknown.\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ TALER_B2S (reserve_pub));
+ return GNUNET_SYSERR;
+ }
+ if (NULL == credit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but credit account is unknown.\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ TALER_B2S (reserve_pub));
+ return GNUNET_SYSERR;
+ }
+ for (struct Transaction *t = credit_account->in_tail;
+ NULL != t;
+ t = t->prev_in)
+ {
+ if ( (t->unchecked) &&
+ (debit_account == t->debit_account) &&
+ (T_CREDIT == t->type) &&
+ (0 == TALER_amount_cmp (want_amount,
+ &t->amount)) &&
+ (0 == GNUNET_memcmp (reserve_pub,
+ &t->subject.credit.reserve_pub)) )
+ {
+ t->unchecked = false;
+ return GNUNET_OK;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Did not find matching transaction!\nI have:\n");
+ check_log (h);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted:\n%s -> %s (%s) with subject %s (CREDIT)\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ TALER_B2S (reserve_pub));
+ return GNUNET_SYSERR;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
+{
+ for (uint64_t i = 0; i<h->ram_limit; i++)
+ {
+ struct Transaction *t = h->transactions[i];
+
+ if ( (NULL != t) &&
+ (t->unchecked) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected empty transaction set, but I have:\n");
+ check_log (h);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_bank.c b/src/bank-lib/fakebank_bank.c
new file mode 100644
index 000000000..ede148a41
--- /dev/null
+++ b/src/bank-lib/fakebank_bank.c
@@ -0,0 +1,224 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank.c
+ * @brief Main dispatcher for the Taler Bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank.h"
+#include "fakebank_bank_get_accounts.h"
+#include "fakebank_bank_get_accounts_withdrawals.h"
+#include "fakebank_bank_get_root.h"
+#include "fakebank_bank_post_accounts_withdrawals.h"
+#include "fakebank_bank_post_accounts_withdrawals_abort.h"
+#include "fakebank_bank_post_accounts_withdrawals_confirm.h"
+#include "fakebank_bank_testing_register.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_HEAD))
+ method = MHD_HTTP_METHOD_GET;
+ if ( (0 == strcmp (url,
+ "/config")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("version",
+ "0:0:0"),
+ GNUNET_JSON_pack_string ("currency",
+ h->currency),
+ GNUNET_JSON_pack_string ("name",
+ "taler-bank-access"));
+ }
+ if ( (0 == strcmp (url,
+ "/public-accounts")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_array_steal ("public_accounts",
+ json_array ()));
+ }
+ if ( (0 == strncmp (url,
+ "/accounts/",
+ strlen ("/accounts/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST)) )
+ {
+ const char *acc_name = &url[strlen ("/accounts/")];
+ const char *end_acc = strchr (acc_name,
+ '/');
+ char *acc;
+ MHD_RESULT ret;
+
+ if ( (NULL == end_acc) ||
+ (0 != strncmp (end_acc,
+ "/withdrawals",
+ strlen ("/withdrawals"))) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ acc_name);
+ }
+ acc = GNUNET_strndup (acc_name,
+ end_acc - acc_name);
+ end_acc += strlen ("/withdrawals");
+ if ('/' == *end_acc)
+ {
+ const char *wid = end_acc + 1;
+ const char *opid = strchr (wid,
+ '/');
+ char *wi;
+
+ if ( (NULL == opid) ||
+ ( (0 != strcmp (opid,
+ "/abort")) &&
+ (0 != strcmp (opid,
+ "/confirm")) ) )
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (acc);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ acc_name);
+ }
+ wi = GNUNET_strndup (wid,
+ opid - wid);
+ if (0 == strcmp (opid,
+ "/abort"))
+ {
+ ret = TALER_FAKEBANK_bank_withdrawals_abort_ (h,
+ connection,
+ acc,
+ wi);
+ GNUNET_free (wi);
+ GNUNET_free (acc);
+ return ret;
+ }
+ if (0 == strcmp (opid,
+ "/confirm"))
+ {
+ ret = TALER_FAKEBANK_bank_withdrawals_confirm_ (h,
+ connection,
+ acc,
+ wi);
+ GNUNET_free (wi);
+ GNUNET_free (acc);
+ return ret;
+ }
+ GNUNET_assert (0);
+ }
+ ret = TALER_FAKEBANK_bank_post_account_withdrawals_ (
+ h,
+ connection,
+ acc,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ GNUNET_free (acc);
+ return ret;
+ }
+
+ if ( (0 == strncmp (url,
+ "/accounts/",
+ strlen ("/accounts/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ const char *acc_name = &url[strlen ("/accounts/")];
+ const char *end_acc = strchr (acc_name,
+ '/');
+ const char *wid;
+ char *acc;
+ MHD_RESULT ret;
+
+ if (NULL == end_acc)
+ {
+ return TALER_FAKEBANK_bank_get_accounts_ (h,
+ connection,
+ acc_name);
+ }
+ if (0 != strncmp (end_acc,
+ "/withdrawals/",
+ strlen ("/withdrawals/")))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ acc_name);
+ }
+ acc = GNUNET_strndup (acc_name,
+ end_acc - acc_name);
+ wid = &end_acc[strlen ("/withdrawals/")];
+ ret = TALER_FAKEBANK_bank_get_accounts_withdrawals_ (h,
+ connection,
+ acc,
+ wid);
+ GNUNET_free (acc);
+ return ret;
+ }
+ /* FIXME: implement transactions API: 1.12.2 */
+
+ /* registration API */
+ if ( (0 == strcmp (url,
+ "/testing/register")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST)) )
+ {
+ return TALER_FAKEBANK_bank_testing_register_ (h,
+ connection,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_bank.h b/src/bank-lib/fakebank_bank.h
new file mode 100644
index 000000000..1c51f88f8
--- /dev/null
+++ b/src/bank-lib/fakebank_bank.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank.h
+ * @brief Main dispatcher for the Taler Bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_H
+#define FAKEBANK_BANK_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle incoming HTTP request to the Taler bank API.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_main_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_accounts_withdrawals.c b/src/bank-lib/fakebank_bank_accounts_withdrawals.c
new file mode 100644
index 000000000..ffcff0e20
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_accounts_withdrawals.c
@@ -0,0 +1,101 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_accounts_withdrawals.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_accounts_withdrawals.h"
+#include "fakebank_common_lookup.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+ struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+ if (wo->debit_account != acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ account_name);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_bool ("aborted",
+ wo->aborted),
+ GNUNET_JSON_pack_bool ("selection_done",
+ wo->selection_done),
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wo->confirmation_done),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("selected_exchange_account",
+ wo->exchange_account->payto_uri)),
+ GNUNET_JSON_pack_allow_null (
+ wo->selection_done
+ ? GNUNET_JSON_pack_data_auto ("selected_reserve_pub",
+ &wo->reserve_pub)
+ : GNUNET_JSON_pack_string ("selected_reserve_pub",
+ NULL)),
+ TALER_JSON_pack_amount ("amount",
+ &wo->amount));
+}
diff --git a/src/bank-lib/fakebank_bank_accounts_withdrawals.h b/src/bank-lib/fakebank_bank_accounts_withdrawals.h
new file mode 100644
index 000000000..2a598dee9
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_accounts_withdrawals.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_accounts_withdrawals.h
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_ACCOUNTS_WITHDRAWALS_H
+#define FAKEBANK_BANK_ACCOUNTS_WITHDRAWALS_H
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle GET /accounts/${account_name}/withdrawals/{withdrawal_id} request
+ * to the Taler bank access API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param withdrawal_id withdrawal ID to return status of
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_get_accounts.c b/src/bank-lib/fakebank_bank_get_accounts.c
new file mode 100644
index 000000000..e85387d2a
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_accounts.c
@@ -0,0 +1,80 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_accounts.c
+ * @brief implements the Taler Bank API "GET /accounts/" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_accounts.h"
+#include "fakebank_common_lookup.h"
+
+/**
+ * Handle GET /accounts/${account_name} request of the Taler bank API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_accounts_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name)
+{
+ struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("payto_uri",
+ acc->payto_uri),
+ GNUNET_JSON_pack_object_steal (
+ "balance",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("credit_debit_indicator",
+ acc->is_negative
+ ? "debit"
+ : "credit"),
+ TALER_JSON_pack_amount ("amount",
+ &acc->balance))));
+}
diff --git a/src/bank-lib/fakebank_bank_get_accounts.h b/src/bank-lib/fakebank_bank_get_accounts.h
new file mode 100644
index 000000000..7d3872133
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_accounts.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_accounts.h
+ * @brief implements the Taler Bank API "GET /accounts/" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_GET_ACCOUNTS_H
+#define FAKEBANK_BANK_GET_ACCOUNTS_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle GET /accounts/${account_name} request of the Taler bank API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_accounts_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_get_accounts_withdrawals.c b/src/bank-lib/fakebank_bank_get_accounts_withdrawals.c
new file mode 100644
index 000000000..176b6fb42
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_accounts_withdrawals.c
@@ -0,0 +1,111 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_accounts_withdrawals.c
+ * @brief implements the Taler Bank API "GET /accounts/ACC/withdrawals/WID" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_accounts_withdrawals.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Handle GET /accounts/${account_name}/withdrawals/{withdrawal_id} request
+ * to the Taler bank access API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param withdrawal_id withdrawal ID to return status of
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_accounts_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+ struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+ if (wo->debit_account != acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ account_name);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_bool ("aborted",
+ wo->aborted),
+ GNUNET_JSON_pack_bool ("selection_done",
+ wo->selection_done),
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wo->confirmation_done),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("selected_exchange_account",
+ wo->exchange_account->payto_uri)),
+ GNUNET_JSON_pack_allow_null (
+ wo->selection_done
+ ? GNUNET_JSON_pack_data_auto ("selected_reserve_pub",
+ &wo->reserve_pub)
+ : GNUNET_JSON_pack_string ("selected_reserve_pub",
+ NULL)),
+ TALER_JSON_pack_amount ("amount",
+ &wo->amount));
+}
diff --git a/src/bank-lib/fakebank_bank_get_accounts_withdrawals.h b/src/bank-lib/fakebank_bank_get_accounts_withdrawals.h
new file mode 100644
index 000000000..53dd873ec
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_accounts_withdrawals.h
@@ -0,0 +1,53 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_accounts_withdrawals.h
+ * @brief implements the Taler Bank API "GET /accounts/ACC/withdrawals/WID" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_GET_ACCOUNTS_WITHDRAWALS_H
+#define FAKEBANK_BANK_GET_ACCOUNTS_WITHDRAWALS_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_accounts_withdrawals.h"
+
+
+/**
+ * Handle GET /accounts/${account_name}/withdrawals/{withdrawal_id} request
+ * to the Taler bank access API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param withdrawal_id withdrawal ID to return status of
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_accounts_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_get_root.c b/src/bank-lib/fakebank_bank_get_root.c
new file mode 100644
index 000000000..8c34697b4
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_root.c
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_root.c
+ * @brief handle a GET "/" request for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_root.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_root_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection)
+{
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+#define HELLOMSG "Hello, Fakebank!"
+
+ (void) h;
+ resp = MHD_create_response_from_buffer (
+ strlen (HELLOMSG),
+ HELLOMSG,
+ MHD_RESPMEM_MUST_COPY);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
diff --git a/src/bank-lib/fakebank_bank_get_root.h b/src/bank-lib/fakebank_bank_get_root.h
new file mode 100644
index 000000000..2eedb94a1
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_root.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_root.c
+ * @brief handle a GET "/" request for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_GET_ROOT_H
+#define FAKEBANK_BANK_GET_ROOT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_root_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals.c b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.c
new file mode 100644
index 000000000..f05faf14d
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.c
@@ -0,0 +1,196 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals.c
+ * @brief implementation of the bank API's POST /accounts/AID/withdrawals endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_post_accounts_withdrawals.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Execute POST /accounts/$account_name/withdrawals request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param amount amont to withdraw
+ * @return MHD result code
+ */
+static MHD_RESULT
+do_post_account_withdrawals (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const struct TALER_Amount *amount)
+{
+ struct Account *acc;
+ struct WithdrawalOperation *wo;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+ wo = GNUNET_new (struct WithdrawalOperation);
+ wo->debit_account = acc;
+ wo->amount = *amount;
+ if (NULL == h->wops)
+ {
+ h->wops = GNUNET_CONTAINER_multishortmap_create (32,
+ GNUNET_YES);
+ }
+ while (1)
+ {
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &wo->wopid,
+ sizeof (wo->wopid));
+ if (GNUNET_OK ==
+ GNUNET_CONTAINER_multishortmap_put (h->wops,
+ &wo->wopid,
+ wo,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ break;
+ }
+ {
+ char *wopids;
+ char *uri;
+ MHD_RESULT res;
+
+ wopids = GNUNET_STRINGS_data_to_string_alloc (&wo->wopid,
+ sizeof (wo->wopid));
+ GNUNET_asprintf (&uri,
+ "taler+http://withdraw/%s:%u/taler-bank-integration/%s",
+ h->hostname,
+ (unsigned int) h->port,
+ wopids);
+ GNUNET_free (wopids);
+ res = TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("taler_withdraw_uri",
+ uri),
+ GNUNET_JSON_pack_data_auto ("withdrawal_id",
+ &wo->wopid));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ GNUNET_free (uri);
+ return res;
+ }
+}
+
+
+/**
+ * Handle POST /accounts/$account_name/withdrawals request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_post_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ MHD_RESULT res;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+
+ {
+ struct TALER_Amount amount;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount ("amount",
+ h->currency,
+ &amount),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ res = do_post_account_withdrawals (h,
+ connection,
+ account_name,
+ &amount);
+ }
+ json_decref (json);
+ return res;
+}
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals.h b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.h
new file mode 100644
index 000000000..1becf1efc
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals.c
+ * @brief implementation of the bank API's POST /accounts/AID/withdrawals endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_H
+#define FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle POST /accounts/$account_name/withdrawals request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_post_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.c b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.c
new file mode 100644
index 000000000..4a252f9b8
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.c
@@ -0,0 +1,97 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals_abort.c
+ * @brief implement bank API withdrawals /abort endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_post_accounts_withdrawals_abort.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_abort_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+ struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+ if (wo->debit_account != acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ account_name);
+ }
+ if (wo->confirmation_done)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_ABORT_CONFIRM_CONFLICT,
+ account_name);
+ }
+ wo->aborted = true;
+ TALER_FAKEBANK_notify_withdrawal_ (h,
+ wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_json (connection,
+ json_object (), /* FIXME: #7301 */
+ MHD_HTTP_OK);
+}
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.h b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.h
new file mode 100644
index 000000000..ba99e493e
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_abort.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals_abort.h
+ * @brief implement bank API withdrawals /abort endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_ABORT_H
+#define FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_ABORT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/abort request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the debited account
+ * @param withdrawal_id the withdrawal operation identifier
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_abort_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.c b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.c
new file mode 100644
index 000000000..232b4f92f
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.c
@@ -0,0 +1,139 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.c
+ * @brief implement bank API withdrawals /confirm endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_post_accounts_withdrawals_confirm.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_make_admin_transfer.h"
+
+
+/**
+ * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/confirm request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the debited account
+ * @param withdrawal_id the withdrawal operation identifier
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_confirm_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+ struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+ if (wo->debit_account != acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ account_name);
+ }
+ if (NULL == wo->exchange_account)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_BANK_POST_WITHDRAWAL_OPERATION_REQUIRED,
+ NULL);
+ }
+ if (wo->aborted)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
+ account_name);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (GNUNET_OK !=
+ TALER_FAKEBANK_make_admin_transfer_ (
+ h,
+ wo->debit_account->account_name,
+ wo->exchange_account->account_name,
+ &wo->amount,
+ &wo->reserve_pub,
+ &wo->row_id,
+ &wo->timestamp))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ NULL);
+ }
+ /* Re-acquiring the lock and continuing to operate on 'wo'
+ is currently (!) acceptable because we NEVER free 'wo'
+ until shutdown. We may want to revise this if keeping
+ all withdraw operations in RAM becomes an issue... */
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo->confirmation_done = true;
+ TALER_FAKEBANK_notify_withdrawal_ (h,
+ wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_json (connection,
+ json_object (),
+ MHD_HTTP_OK);
+}
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.h b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.h
new file mode 100644
index 000000000..2c301ca2c
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals_confirm.h
+ * @brief implement bank API withdrawals /confirm endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_CONFIRM_H
+#define FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_CONFIRM_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/confirm request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the debited account
+ * @param withdrawal_id the withdrawal operation identifier
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_confirm_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_testing_register.c b/src/bank-lib/fakebank_bank_testing_register.c
new file mode 100644
index 000000000..e5720f1a6
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_testing_register.c
@@ -0,0 +1,128 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_testing_register.c
+ * @brief implementation of /testing/register endpoint for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_testing_register.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_testing_register_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ MHD_RESULT res;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+
+ {
+ const char *username;
+ const char *password;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("username",
+ &username),
+ GNUNET_JSON_spec_string ("password",
+ &password),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue ret;
+ struct Account *acc;
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ username,
+ NULL);
+ if (NULL != acc)
+ {
+ if (0 != strcmp (password,
+ acc->password))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_REGISTER_CONFLICT,
+ "password");
+ }
+ }
+ else
+ {
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ username,
+ username);
+ GNUNET_assert (NULL != acc);
+ acc->password = GNUNET_strdup (password);
+ acc->balance = h->signup_bonus; /* magic money creation! */
+ }
+ res = TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ json_decref (json);
+ return res;
+}
diff --git a/src/bank-lib/fakebank_bank_testing_register.h b/src/bank-lib/fakebank_bank_testing_register.h
new file mode 100644
index 000000000..d8744ecc2
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_testing_register.h
@@ -0,0 +1,53 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_testing_register.h
+ * @brief implementation of /testing/register endpoint for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_TESTING_REGISTER_H
+#define FAKEBANK_BANK_TESTING_REGISTER_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Handle POST /testing/register request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_testing_register_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_lookup.c b/src/bank-lib/fakebank_common_lookup.c
new file mode 100644
index 000000000..b4f853871
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lookup.c
@@ -0,0 +1,103 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lookup.c
+ * @brief common helper functions related to lookups
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+
+struct WithdrawalOperation *
+TALER_FAKEBANK_lookup_withdrawal_operation_ (struct TALER_FAKEBANK_Handle *h,
+ const char *wopid)
+{
+ struct GNUNET_ShortHashCode sh;
+
+ if (NULL == h->wops)
+ return NULL;
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (wopid,
+ strlen (wopid),
+ &sh,
+ sizeof (sh)))
+ {
+ GNUNET_break_op (0);
+ return NULL;
+ }
+ return GNUNET_CONTAINER_multishortmap_get (h->wops,
+ &sh);
+}
+
+
+struct Account *
+TALER_FAKEBANK_lookup_account_ (struct TALER_FAKEBANK_Handle *h,
+ const char *name,
+ const char *receiver_name)
+{
+ struct GNUNET_HashCode hc;
+ size_t slen;
+ struct Account *account;
+
+ memset (&hc,
+ 0,
+ sizeof (hc));
+ slen = strlen (name);
+ GNUNET_CRYPTO_hash (name,
+ slen,
+ &hc);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->accounts_lock));
+ account = GNUNET_CONTAINER_multihashmap_get (h->accounts,
+ &hc);
+ if (NULL == account)
+ {
+ if (NULL == receiver_name)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->accounts_lock));
+ return NULL;
+ }
+ account = GNUNET_new (struct Account);
+ account->account_name = GNUNET_strdup (name);
+ account->receiver_name = GNUNET_strdup (receiver_name);
+ GNUNET_asprintf (&account->payto_uri,
+ "payto://x-taler-bank/%s/%s?receiver-name=%s",
+ h->hostname,
+ account->account_name,
+ account->receiver_name);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (h->currency,
+ &account->balance));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (h->accounts,
+ &hc,
+ account,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->accounts_lock));
+ return account;
+}
diff --git a/src/bank-lib/fakebank_common_lookup.h b/src/bank-lib/fakebank_common_lookup.h
new file mode 100644
index 000000000..b93447743
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lookup.h
@@ -0,0 +1,62 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lookup.h
+ * @brief common helper functions related to lookups
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+
+#ifndef FAKEBANK_COMMON_LOOKUP_H
+#define FAKEBANK_COMMON_LOOKUP_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Lookup account with @a name, and if it does not exist, create it.
+ *
+ * @param[in,out] h bank to lookup account at
+ * @param name account name to resolve
+ * @param receiver_name receiver name in payto:// URI,
+ * NULL if the account must already exist
+ * @return account handle, NULL if account does not yet exist
+ */
+struct Account *
+TALER_FAKEBANK_lookup_account_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *name,
+ const char *receiver_name);
+
+
+/**
+ * Find withdrawal operation @a wopid in @a h.
+ *
+ * @param h fakebank handle
+ * @param wopid withdrawal operation ID as a string
+ * @return NULL if operation was not found
+ */
+struct WithdrawalOperation *
+TALER_FAKEBANK_lookup_withdrawal_operation_ (struct TALER_FAKEBANK_Handle *h,
+ const char *wopid);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_lp.c b/src/bank-lib/fakebank_common_lp.c
new file mode 100644
index 000000000..22a9e3ab4
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lp.c
@@ -0,0 +1,344 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lp.c
+ * @brief long-polling support for fakebank
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include <poll.h>
+#ifdef __linux__
+#include <sys/eventfd.h>
+#endif
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+void
+TALER_FAKEBANK_lp_trigger_ (struct LongPoller *lp)
+{
+ struct TALER_FAKEBANK_Handle *h = lp->h;
+ struct Account *acc = lp->account;
+
+ GNUNET_CONTAINER_DLL_remove (acc->lp_head,
+ acc->lp_tail,
+ lp);
+ MHD_resume_connection (lp->conn);
+ GNUNET_free (lp);
+ h->mhd_again = true;
+#ifdef __linux__
+ if (-1 == h->lp_event)
+#else
+ if ( (-1 == h->lp_event_in) &&
+ (-1 == h->lp_event_out) )
+#endif
+ {
+ if (NULL != h->mhd_task)
+ GNUNET_SCHEDULER_cancel (h->mhd_task);
+ h->mhd_task =
+ GNUNET_SCHEDULER_add_now (&TALER_FAKEBANK_run_mhd_,
+ h);
+ }
+}
+
+
+void *
+TALER_FAKEBANK_lp_expiration_thread_ (void *cls)
+{
+ struct TALER_FAKEBANK_Handle *h = cls;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ while (! h->in_shutdown)
+ {
+ struct LongPoller *lp;
+ int timeout_ms;
+
+ lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
+ while ( (NULL != lp) &&
+ GNUNET_TIME_absolute_is_past (lp->timeout))
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
+ }
+ if (NULL != lp)
+ {
+ struct GNUNET_TIME_Relative rem;
+ unsigned long long left_ms;
+
+ rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
+ left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
+ if (left_ms > INT_MAX)
+ timeout_ms = INT_MAX;
+ else
+ timeout_ms = (int) left_ms;
+ }
+ else
+ {
+ timeout_ms = -1; /* infinity */
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ {
+ struct pollfd p = {
+#ifdef __linux__
+ .fd = h->lp_event,
+#else
+ .fd = h->lp_event_out,
+#endif
+ .events = POLLIN
+ };
+ int ret;
+
+ ret = poll (&p,
+ 1,
+ timeout_ms);
+ if (-1 == ret)
+ {
+ if (EINTR != errno)
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "poll");
+ }
+ else if (1 == ret)
+ {
+ /* clear event */
+ uint64_t ev;
+ ssize_t iret;
+
+#ifdef __linux__
+ iret = read (h->lp_event,
+ &ev,
+ sizeof (ev));
+#else
+ iret = read (h->lp_event_out,
+ &ev,
+ sizeof (ev));
+#endif
+ if (-1 == iret)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "read");
+ }
+ else
+ {
+ GNUNET_break (sizeof (uint64_t) == iret);
+ }
+ }
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return NULL;
+}
+
+
+/**
+ * Trigger long pollers that might have been waiting
+ * for @a t.
+ *
+ * @param h fakebank handle
+ * @param t transaction to notify on
+ */
+void
+TALER_FAKEBANK_notify_transaction_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t)
+{
+ struct Account *debit_acc = t->debit_account;
+ struct Account *credit_acc = t->credit_account;
+ struct LongPoller *nxt;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ for (struct LongPoller *lp = debit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if (LP_DEBIT == lp->type)
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ }
+ for (struct LongPoller *lp = credit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if (LP_CREDIT == lp->type)
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+}
+
+
+/**
+ * Notify long pollers that a @a wo was updated.
+ * Must be called with the "big_lock" still held.
+ *
+ * @param h fakebank handle
+ * @param wo withdraw operation that finished
+ */
+void
+TALER_FAKEBANK_notify_withdrawal_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const struct WithdrawalOperation *wo)
+{
+ struct Account *debit_acc = wo->debit_account;
+ struct LongPoller *nxt;
+
+ for (struct LongPoller *lp = debit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if ( (LP_WITHDRAW == lp->type) &&
+ (wo == lp->wo) )
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ }
+}
+
+
+/**
+ * Task run when a long poller is about to time out.
+ * Only used in single-threaded mode.
+ *
+ * @param cls a `struct TALER_FAKEBANK_Handle *`
+ */
+static void
+lp_timeout (void *cls)
+{
+ struct TALER_FAKEBANK_Handle *h = cls;
+ struct LongPoller *lp;
+
+ h->lp_task = NULL;
+ while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
+ {
+ if (GNUNET_TIME_absolute_is_future (lp->timeout))
+ break;
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Timeout reached for long poller %p\n",
+ lp->conn);
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ if (NULL == lp)
+ return;
+ h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
+ &lp_timeout,
+ h);
+}
+
+
+/**
+ * Reschedule the timeout task of @a h for time @a t.
+ *
+ * @param h fakebank handle
+ * @param t when will the next connection timeout expire
+ */
+static void
+reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
+ struct GNUNET_TIME_Absolute t)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Scheduling timeout task for %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (t));
+#ifdef __linux__
+ if (-1 != h->lp_event)
+#else
+ if (-1 != h->lp_event_in && -1 != h->lp_event_out)
+#endif
+ {
+ uint64_t num = 1;
+
+ GNUNET_break (sizeof (num) ==
+#ifdef __linux__
+ write (h->lp_event,
+ &num,
+ sizeof (num)));
+#else
+ write (h->lp_event_in,
+ &num,
+ sizeof (num)));
+#endif
+ }
+ else
+ {
+ if (NULL != h->lp_task)
+ GNUNET_SCHEDULER_cancel (h->lp_task);
+ h->lp_task = GNUNET_SCHEDULER_add_at (t,
+ &lp_timeout,
+ h);
+ }
+}
+
+
+void
+TALER_FAKEBANK_start_lp_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct Account *acc,
+ struct GNUNET_TIME_Relative lp_timeout,
+ enum LongPollType dir,
+ const struct WithdrawalOperation *wo)
+{
+ struct LongPoller *lp;
+ bool toc;
+
+ lp = GNUNET_new (struct LongPoller);
+ lp->account = acc;
+ lp->h = h;
+ lp->wo = wo;
+ lp->conn = connection;
+ lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
+ lp->type = dir;
+ lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
+ lp,
+ lp->timeout.abs_value_us);
+ toc = (lp ==
+ GNUNET_CONTAINER_heap_peek (h->lp_heap));
+ GNUNET_CONTAINER_DLL_insert (acc->lp_head,
+ acc->lp_tail,
+ lp);
+ MHD_suspend_connection (connection);
+ if (toc)
+ reschedule_lp_timeout (h,
+ lp->timeout);
+
+}
diff --git a/src/bank-lib/fakebank_common_lp.h b/src/bank-lib/fakebank_common_lp.h
new file mode 100644
index 000000000..37094e12b
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lp.h
@@ -0,0 +1,100 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lp.h
+ * @brief long-polling support for fakebank
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_LP_H
+#define FAKEBANK_COMMON_LP_H
+#include "taler_fakebank_lib.h"
+
+
+/**
+ * Trigger the @a lp. Frees associated resources, except the entry of @a lp in
+ * the timeout heap. Must be called while the ``big lock`` is held.
+ *
+ * @param[in] lp long poller to trigger
+ */
+void
+TALER_FAKEBANK_lp_trigger_ (struct LongPoller *lp);
+
+
+/**
+ * Trigger long pollers that might have been waiting
+ * for @a t.
+ *
+ * @param h fakebank handle
+ * @param t transaction to notify on
+ */
+void
+TALER_FAKEBANK_notify_transaction_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t);
+
+
+/**
+ * Notify long pollers that a @a wo was updated.
+ * Must be called with the "big_lock" still held.
+ *
+ * @param h fakebank handle
+ * @param wo withdraw operation that finished
+ */
+void
+TALER_FAKEBANK_notify_withdrawal_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const struct WithdrawalOperation *wo);
+
+
+/**
+ * Start long-polling for @a connection and @a acc
+ * for transfers in @a dir. Must be called with the
+ * "big lock" held.
+ *
+ * @param[in,out] h fakebank handle
+ * @param[in,out] connection to suspend
+ * @param[in,out] acc account affected
+ * @param lp_timeout how long to suspend
+ * @param dir direction of transfers to watch for
+ * @param wo withdraw operation to watch, only
+ * if @a dir is #LP_WITHDRAW
+ */
+void
+TALER_FAKEBANK_start_lp_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct Account *acc,
+ struct GNUNET_TIME_Relative lp_timeout,
+ enum LongPollType dir,
+ const struct WithdrawalOperation *wo);
+
+
+/**
+ * Main routine of a thread that is run to wake up connections that have hit
+ * their timeout. Runs until in_shutdown is set to true. Must be send signals
+ * via lp_event on shutdown and/or whenever the heap changes to an earlier
+ * timeout.
+ *
+ * @param cls a `struct TALER_FAKEBANK_Handle *`
+ * @return NULL
+ */
+void *
+TALER_FAKEBANK_lp_expiration_thread_ (void *cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_make_admin_transfer.c b/src/bank-lib/fakebank_common_make_admin_transfer.c
new file mode 100644
index 000000000..4a11d412c
--- /dev/null
+++ b/src/bank-lib/fakebank_common_make_admin_transfer.c
@@ -0,0 +1,117 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_make_admin_transfer.c
+ * @brief routine to create transfers to the exchange
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_transact.h"
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_admin_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ uint64_t *row_id,
+ struct GNUNET_TIME_Timestamp *timestamp)
+{
+ struct Transaction *t;
+ const struct GNUNET_PeerIdentity *pid;
+ struct Account *debit_acc;
+ struct Account *credit_acc;
+
+ GNUNET_static_assert (sizeof (*pid) ==
+ sizeof (*reserve_pub));
+ pid = (const struct GNUNET_PeerIdentity *) reserve_pub;
+ GNUNET_assert (NULL != debit_account);
+ GNUNET_assert (NULL != credit_account);
+ GNUNET_assert (0 == strcasecmp (amount->currency,
+ h->currency));
+ GNUNET_break (0 != strncasecmp ("payto://",
+ debit_account,
+ strlen ("payto://")));
+ GNUNET_break (0 != strncasecmp ("payto://",
+ credit_account,
+ strlen ("payto://")));
+ debit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ debit_account,
+ debit_account);
+ credit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ credit_account,
+ credit_account);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->rpubs_lock));
+ t = GNUNET_CONTAINER_multipeermap_get (h->rpubs,
+ pid);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->rpubs_lock));
+ if (NULL != t)
+ {
+ /* duplicate reserve public key not allowed */
+ GNUNET_break_op (0);
+ return GNUNET_NO;
+ }
+
+ t = GNUNET_new (struct Transaction);
+ t->unchecked = true;
+ t->debit_account = debit_acc;
+ t->credit_account = credit_acc;
+ t->amount = *amount;
+ t->date = GNUNET_TIME_timestamp_get ();
+ if (NULL != timestamp)
+ *timestamp = t->date;
+ t->type = T_CREDIT;
+ t->subject.credit.reserve_pub = *reserve_pub;
+ TALER_FAKEBANK_transact_ (h,
+ t);
+ if (NULL != row_id)
+ *row_id = t->row_id;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->rpubs_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multipeermap_put (
+ h->rpubs,
+ pid,
+ t,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->rpubs_lock));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Making transfer from %s to %s over %s and subject %s at row %llu\n",
+ debit_account,
+ credit_account,
+ TALER_amount2s (amount),
+ TALER_B2S (reserve_pub),
+ (unsigned long long) t->row_id);
+ TALER_FAKEBANK_notify_transaction_ (h,
+ t);
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_common_make_admin_transfer.h b/src/bank-lib/fakebank_common_make_admin_transfer.h
new file mode 100644
index 000000000..841cfb481
--- /dev/null
+++ b/src/bank-lib/fakebank_common_make_admin_transfer.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_make_admin_transfer.h
+ * @brief routine to create transfers to the exchange
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_MAKE_ADMIN_TRANSFER_H
+#define FAKEBANK_COMMON_MAKE_ADMIN_TRANSFER_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Tell the fakebank to create another wire transfer *to* an exchange.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param reserve_pub reserve public key to use in subject
+ * @param[out] row_id serial_id of the transfer
+ * @param[out] timestamp when was the transfer made
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_admin_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ uint64_t *row_id,
+ struct GNUNET_TIME_Timestamp *timestamp);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_parser.c b/src/bank-lib/fakebank_common_parser.c
new file mode 100644
index 000000000..98b8d6097
--- /dev/null
+++ b/src/bank-lib/fakebank_common_parser.c
@@ -0,0 +1,138 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_parser.c
+ * @brief functions to help parse REST requests
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_common_parse_history_args (
+ const struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct HistoryArgs *ha)
+{
+ const char *start;
+ const char *delta;
+ const char *long_poll_ms;
+ unsigned long long lp_timeout;
+ unsigned long long sval;
+ long long d;
+ char dummy;
+
+ start = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "start");
+ ha->have_start = (NULL != start);
+ delta = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "delta");
+ long_poll_ms = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "long_poll_ms");
+ lp_timeout = 0;
+ if ( (NULL == delta) ||
+ (1 != sscanf (delta,
+ "%lld%c",
+ &d,
+ &dummy)) )
+ {
+ /* Fail if one of the above failed. */
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "delta"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if ( (NULL != long_poll_ms) &&
+ (1 != sscanf (long_poll_ms,
+ "%llu%c",
+ &lp_timeout,
+ &dummy)) )
+ {
+ /* Fail if one of the above failed. */
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "long_poll_ms"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if ( (NULL != start) &&
+ (1 != sscanf (start,
+ "%llu%c",
+ &sval,
+ &dummy)) )
+ {
+ /* Fail if one of the above failed. */
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "start"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if (NULL == start)
+ ha->start_idx = (d > 0) ? 0 : h->serial_counter;
+ else
+ ha->start_idx = (uint64_t) sval;
+ ha->delta = (int64_t) d;
+ if (0 == ha->delta)
+ {
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "delta"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ ha->lp_timeout
+ = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ lp_timeout);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Request for %lld records from %llu\n",
+ (long long) ha->delta,
+ (unsigned long long) ha->start_idx);
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_common_parser.h b/src/bank-lib/fakebank_common_parser.h
new file mode 100644
index 000000000..8e0d14649
--- /dev/null
+++ b/src/bank-lib/fakebank_common_parser.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_parser.h
+ * @brief functions to help parse REST requests
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_PARSER_H
+#define FAKEBANK_COMMON_PARSER_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Parse URL history arguments, of _both_ APIs:
+ * /history/incoming and /history/outgoing.
+ *
+ * @param h bank handle to work on
+ * @param connection MHD connection.
+ * @param[out] ha will contain the parsed values.
+ * @return #GNUNET_OK only if the parsing succeeds,
+ * #GNUNET_SYSERR if it failed,
+ * #GNUNET_NO if it failed and an error was returned
+ */
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_common_parse_history_args (
+ const struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct HistoryArgs *ha);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_transact.c b/src/bank-lib/fakebank_common_transact.c
new file mode 100644
index 000000000..a099ef966
--- /dev/null
+++ b/src/bank-lib/fakebank_common_transact.c
@@ -0,0 +1,261 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_transact.c
+ * @brief actual transaction logic for FAKEBANK
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_transact.h"
+
+
+/**
+ * Update @a account balance by @a amount.
+ *
+ * The @a big_lock must already be locked when calling
+ * this function.
+ *
+ * @param[in,out] account account to update
+ * @param amount balance change
+ * @param debit true to subtract, false to add @a amount
+ */
+static void
+update_balance (struct Account *account,
+ const struct TALER_Amount *amount,
+ bool debit)
+{
+ if (debit == account->is_negative)
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_add (&account->balance,
+ &account->balance,
+ amount));
+ return;
+ }
+ if (0 <= TALER_amount_cmp (&account->balance,
+ amount))
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_subtract (&account->balance,
+ &account->balance,
+ amount));
+ }
+ else
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_subtract (&account->balance,
+ amount,
+ &account->balance));
+ account->is_negative = ! account->is_negative;
+ }
+}
+
+
+/**
+ * Add transaction to the debit and credit accounts,
+ * updating the balances as needed.
+ *
+ * The transaction @a t must already be locked
+ * when calling this function!
+ *
+ * @param[in,out] h bank handle
+ * @param[in,out] t transaction to add to account lists
+ */
+void
+TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t)
+{
+ struct Account *debit_acc = t->debit_account;
+ struct Account *credit_acc = t->credit_account;
+ uint64_t row_id;
+ struct Transaction *old;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ row_id = ++h->serial_counter;
+ old = h->transactions[row_id % h->ram_limit];
+ h->transactions[row_id % h->ram_limit] = t;
+ t->row_id = row_id;
+ GNUNET_CONTAINER_MDLL_insert_tail (out,
+ debit_acc->out_head,
+ debit_acc->out_tail,
+ t);
+ update_balance (debit_acc,
+ &t->amount,
+ true);
+ GNUNET_CONTAINER_MDLL_insert_tail (in,
+ credit_acc->in_head,
+ credit_acc->in_tail,
+ t);
+ update_balance (credit_acc,
+ &t->amount,
+ false);
+ if (NULL != old)
+ {
+ struct Account *da;
+ struct Account *ca;
+
+ da = old->debit_account;
+ ca = old->credit_account;
+ /* slot was already in use, must clean out old
+ entry first! */
+ GNUNET_CONTAINER_MDLL_remove (out,
+ da->out_head,
+ da->out_tail,
+ old);
+ GNUNET_CONTAINER_MDLL_remove (in,
+ ca->in_head,
+ ca->in_tail,
+ old);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if ( (NULL != old) &&
+ (T_DEBIT == old->type) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
+ &old->request_uid,
+ old));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ }
+ GNUNET_free (old);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *subject,
+ const char *exchange_base_url,
+ const struct GNUNET_HashCode *request_uid,
+ uint64_t *ret_row_id,
+ struct GNUNET_TIME_Timestamp *timestamp)
+{
+ struct Transaction *t;
+ struct Account *debit_acc;
+ struct Account *credit_acc;
+ size_t url_len;
+
+ GNUNET_assert (0 == strcasecmp (amount->currency,
+ h->currency));
+ GNUNET_assert (NULL != debit_account);
+ GNUNET_assert (NULL != credit_account);
+ GNUNET_break (0 != strncasecmp ("payto://",
+ debit_account,
+ strlen ("payto://")));
+ GNUNET_break (0 != strncasecmp ("payto://",
+ credit_account,
+ strlen ("payto://")));
+ url_len = strlen (exchange_base_url);
+ GNUNET_assert (url_len < MAX_URL_LEN);
+ debit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ debit_account,
+ debit_account);
+ credit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ credit_account,
+ credit_account);
+ if (NULL != request_uid)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
+ request_uid);
+ if (NULL != t)
+ {
+ if ( (debit_acc != t->debit_account) ||
+ (credit_acc != t->credit_account) ||
+ (0 != TALER_amount_cmp (amount,
+ &t->amount)) ||
+ (T_DEBIT != t->type) ||
+ (0 != GNUNET_memcmp (subject,
+ &t->subject.debit.wtid)) )
+ {
+ /* Transaction exists, but with different details. */
+ GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ return GNUNET_SYSERR;
+ }
+ *ret_row_id = t->row_id;
+ *timestamp = t->date;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ return GNUNET_OK;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ }
+ t = GNUNET_new (struct Transaction);
+ t->unchecked = true;
+ t->debit_account = debit_acc;
+ t->credit_account = credit_acc;
+ t->amount = *amount;
+ t->date = GNUNET_TIME_timestamp_get ();
+ if (NULL != timestamp)
+ *timestamp = t->date;
+ t->type = T_DEBIT;
+ GNUNET_memcpy (t->subject.debit.exchange_base_url,
+ exchange_base_url,
+ url_len);
+ t->subject.debit.wtid = *subject;
+ if (NULL == request_uid)
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
+ &t->request_uid);
+ else
+ t->request_uid = *request_uid;
+ TALER_FAKEBANK_transact_ (h,
+ t);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (
+ h->uuid_map,
+ &t->request_uid,
+ t,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
+ (unsigned long long) t->row_id,
+ debit_account,
+ credit_account,
+ TALER_amount2s (amount),
+ TALER_B2S (subject),
+ exchange_base_url);
+ *ret_row_id = t->row_id;
+ TALER_FAKEBANK_notify_transaction_ (h,
+ t);
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_common_transact.h b/src/bank-lib/fakebank_common_transact.h
new file mode 100644
index 000000000..0914785e9
--- /dev/null
+++ b/src/bank-lib/fakebank_common_transact.h
@@ -0,0 +1,76 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_transact.h
+ * @brief actual transaction logic for FAKEBANK
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_TRANSACT_H
+#define FAKEBANK_COMMON_TRANSACT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Add transaction to the debit and credit accounts,
+ * updating the balances as needed.
+ *
+ * The transaction @a t must already be locked
+ * when calling this function!
+ *
+ * @param[in,out] h bank handle
+ * @param[in,out] t transaction to add to account lists
+ */
+void
+TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t);
+
+
+/**
+ * Tell the fakebank to create another wire transfer *from* an exchange.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param subject wire transfer subject to use
+ * @param exchange_base_url exchange URL
+ * @param request_uid unique number to make the request unique, or NULL to create one
+ * @param[out] ret_row_id pointer to store the row ID of this transaction
+ * @param[out] timestamp set to the time of the transfer
+ * @return #GNUNET_YES if the transfer was successful,
+ * #GNUNET_SYSERR if the request_uid was reused for a different transfer
+ */
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *subject,
+ const char *exchange_base_url,
+ const struct GNUNET_HashCode *request_uid,
+ uint64_t *ret_row_id,
+ struct GNUNET_TIME_Timestamp *timestamp);
+
+#endif
diff --git a/src/bank-lib/fakebank_stop.c b/src/bank-lib/fakebank_stop.c
new file mode 100644
index 000000000..e31d47523
--- /dev/null
+++ b/src/bank-lib/fakebank_stop.c
@@ -0,0 +1,192 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_stop.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include <poll.h>
+#ifdef __linux__
+#include <sys/eventfd.h>
+#endif
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lp.h"
+
+
+/**
+ * Helper function to free memory when finished.
+ *
+ * @param cls NULL
+ * @param key key of the account to free (ignored)
+ * @param val a `struct Account` to free.
+ */
+static enum GNUNET_GenericReturnValue
+free_account (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *val)
+{
+ struct Account *account = val;
+
+ (void) cls;
+ (void) key;
+ GNUNET_assert (NULL == account->lp_head);
+ GNUNET_free (account->account_name);
+ GNUNET_free (account->receiver_name);
+ GNUNET_free (account->payto_uri);
+ GNUNET_free (account->password);
+ GNUNET_free (account);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Helper function to free memory when finished.
+ *
+ * @param cls NULL
+ * @param key key of the operation to free (ignored)
+ * @param val a `struct WithdrawalOperation *` to free.
+ */
+static enum GNUNET_GenericReturnValue
+free_withdraw_op (void *cls,
+ const struct GNUNET_ShortHashCode *key,
+ void *val)
+{
+ struct WithdrawalOperation *wo = val;
+
+ (void) cls;
+ (void) key;
+ GNUNET_free (wo);
+ return GNUNET_OK;
+}
+
+
+void
+TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
+{
+ if (NULL != h->lp_task)
+ {
+ GNUNET_SCHEDULER_cancel (h->lp_task);
+ h->lp_task = NULL;
+ }
+#if EPOLL_SUPPORT
+ if (NULL != h->mhd_rfd)
+ {
+ GNUNET_NETWORK_socket_free_memory_only_ (h->mhd_rfd);
+ h->mhd_rfd = NULL;
+ }
+#endif
+#ifdef __linux__
+ if (-1 != h->lp_event)
+#else
+ if (-1 != h->lp_event_in && -1 != h->lp_event_out)
+#endif
+ {
+ uint64_t val = 1;
+ void *ret;
+ struct LongPoller *lp;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ h->in_shutdown = true;
+ while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+#ifdef __linux__
+ GNUNET_break (sizeof (val) ==
+ write (h->lp_event,
+ &val,
+ sizeof (val)));
+#else
+ GNUNET_break (sizeof (val) ==
+ write (h->lp_event_in,
+ &val,
+ sizeof (val)));
+#endif
+ GNUNET_break (0 ==
+ pthread_join (h->lp_thread,
+ &ret));
+ GNUNET_break (NULL == ret);
+#ifdef __linux__
+ GNUNET_break (0 == close (h->lp_event));
+ h->lp_event = -1;
+#else
+ GNUNET_break (0 == close (h->lp_event_in));
+ GNUNET_break (0 == close (h->lp_event_out));
+ h->lp_event_in = -1;
+ h->lp_event_out = -1;
+#endif
+ }
+ else
+ {
+ struct LongPoller *lp;
+
+ while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ if (NULL != h->mhd_bank)
+ {
+ MHD_stop_daemon (h->mhd_bank);
+ h->mhd_bank = NULL;
+ }
+ if (NULL != h->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (h->mhd_task);
+ h->mhd_task = NULL;
+ }
+ if (NULL != h->accounts)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (h->accounts,
+ &free_account,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (h->accounts);
+ }
+ if (NULL != h->wops)
+ {
+ GNUNET_CONTAINER_multishortmap_iterate (h->wops,
+ &free_withdraw_op,
+ NULL);
+ GNUNET_CONTAINER_multishortmap_destroy (h->wops);
+ }
+ GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map);
+ GNUNET_CONTAINER_multipeermap_destroy (h->rpubs);
+ GNUNET_CONTAINER_heap_destroy (h->lp_heap);
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->big_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->uuid_map_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->accounts_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->rpubs_lock));
+ for (uint64_t i = 0; i<h->ram_limit; i++)
+ GNUNET_free (h->transactions[i]);
+ GNUNET_free (h->transactions);
+ GNUNET_free (h->my_baseurl);
+ GNUNET_free (h->currency);
+ GNUNET_free (h->exchange_url);
+ GNUNET_free (h->hostname);
+ GNUNET_free (h);
+}
diff --git a/src/bank-lib/fakebank_tbi.c b/src/bank-lib/fakebank_tbi.c
new file mode 100644
index 000000000..27b930d9f
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi.c
@@ -0,0 +1,126 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi.c
+ * @brief main entry point to the Taler Bank Integration (TBI) API implementation
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_tbi.h"
+#include "fakebank_tbi_get_withdrawal_operation.h"
+#include "fakebank_tbi_post_withdrawal_operation.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbi_main_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_HEAD))
+ method = MHD_HTTP_METHOD_GET;
+ if ( (0 == strcmp (url,
+ "/config")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("version",
+ "0:0:0"),
+ GNUNET_JSON_pack_string ("currency",
+ h->currency),
+ GNUNET_JSON_pack_string ("name",
+ "taler-bank-integration"));
+ }
+ if ( (0 == strncmp (url,
+ "/withdrawal-operation/",
+ strlen ("/withdrawal-operation/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ const char *wopid = &url[strlen ("/withdrawal-operation/")];
+ const char *lp_s
+ = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "long_poll_ms");
+ struct GNUNET_TIME_Relative lp = GNUNET_TIME_UNIT_ZERO;
+
+ if (NULL != lp_s)
+ {
+ unsigned long long d;
+ char dummy;
+
+ if (1 != sscanf (lp_s,
+ "%llu%c",
+ &d,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "long_poll_ms");
+ }
+ lp = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ d);
+ }
+ return TALER_FAKEBANK_tbi_get_withdrawal_operation_ (h,
+ connection,
+ wopid,
+ lp,
+ con_cls);
+
+ }
+ if ( (0 == strncmp (url,
+ "/withdrawal-operation/",
+ strlen ("/withdrawal-operation/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST)) )
+ {
+ const char *wopid = &url[strlen ("/withdrawal-operation/")];
+
+ return TALER_FAKEBANK_tbi_post_withdrawal (h,
+ connection,
+ wopid,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
+
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_tbi.h b/src/bank-lib/fakebank_tbi.h
new file mode 100644
index 000000000..ef9f35fa2
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi.c
+ * @brief main entry point to the Taler Bank Integration (TBI) API implementation
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+#ifndef FAKEBANK_TBI_H
+#define FAKEBANK_TBI_H
+
+/**
+ * Handle incoming HTTP request to the bank integration API.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbi_main_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbi_get_withdrawal_operation.c b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.c
new file mode 100644
index 000000000..fba8c5de6
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.c
@@ -0,0 +1,127 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_get_withdrawal_operation.c
+ * @brief Implementation of the GET /withdrawal-operation/ request of the Taler Bank Integration API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_tbi_get_withdrawal_operation.h"
+
+/**
+ * Function called to clean up a withdraw context.
+ *
+ * @param cls a `struct WithdrawContext *`
+ */
+static void
+withdraw_cleanup (void *cls)
+{
+ struct WithdrawContext *wc = cls;
+
+ GNUNET_free (wc);
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbi_get_withdrawal_operation_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ struct GNUNET_TIME_Relative lp,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct WithdrawContext *wc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &withdraw_cleanup;
+ *con_cls = cc;
+ wc = GNUNET_new (struct WithdrawContext);
+ cc->ctx = wc;
+ wc->wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ wopid);
+ if (NULL == wc->wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ wopid);
+ }
+ wc->timeout = GNUNET_TIME_relative_to_absolute (lp);
+ }
+ else
+ {
+ wc = cc->ctx;
+ }
+ if (GNUNET_TIME_absolute_is_past (wc->timeout) ||
+ h->in_shutdown ||
+ wc->wo->confirmation_done ||
+ wc->wo->aborted)
+ {
+ json_t *wt;
+
+ wt = json_array ();
+ GNUNET_assert (NULL != wt);
+ GNUNET_assert (0 ==
+ json_array_append_new (wt,
+ json_string ("x-taler-bank")));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_bool ("aborted",
+ wc->wo->aborted),
+ GNUNET_JSON_pack_bool ("selection_done",
+ wc->wo->selection_done),
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wc->wo->confirmation_done),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("suggested_exchange",
+ h->exchange_url)),
+ TALER_JSON_pack_amount ("amount",
+ &wc->wo->amount),
+ GNUNET_JSON_pack_array_steal ("wire_types",
+ wt));
+ }
+
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ wc->wo->debit_account,
+ GNUNET_TIME_absolute_get_remaining (wc->timeout),
+ LP_WITHDRAW,
+ wc->wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+}
diff --git a/src/bank-lib/fakebank_tbi_get_withdrawal_operation.h b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.h
new file mode 100644
index 000000000..b42e5a768
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.h
@@ -0,0 +1,51 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_get_withdrawal_operation.h
+ * @brief Implementation of the GET /withdrawal-operation/ request of the Taler Bank Integration API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBI_GET_WITHDRAWAL_OPERATION_H
+#define FAKEBANK_TBI_GET_WITHDRAWAL_OPERATION_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle GET /withdrawal-operation/{wopid} request.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param wopid the withdrawal operation identifier
+ * @param lp how long is the long-polling timeout
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbi_get_withdrawal_operation_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ struct GNUNET_TIME_Relative lp,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbi_post_withdrawal_operation.c b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.c
new file mode 100644
index 000000000..3dbbb3c91
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.c
@@ -0,0 +1,221 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_post_withdrawal_operation.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_tbi_post_withdrawal_operation.h"
+
+
+/**
+ * Execute POST /withdrawal-operation/ request.
+ *
+ * @param h our handle
+ * @param connection the connection
+ * @param wopid the withdrawal operation identifier
+ * @param reserve_pub public key of the reserve
+ * @param exchange_payto_uri payto://-URI of the exchange
+ * @return MHD result code
+ */
+static MHD_RESULT
+do_post_withdrawal (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const char *exchange_payto_uri)
+{
+ struct WithdrawalOperation *wo;
+ char *credit_name;
+ struct Account *credit_account;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ wopid);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ wopid);
+ }
+ if ( (wo->selection_done) &&
+ (0 != GNUNET_memcmp (&wo->reserve_pub,
+ reserve_pub)) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
+ "reserve public key changed");
+ }
+ {
+ /* check if reserve_pub is already in use */
+ const struct GNUNET_PeerIdentity *pid;
+
+ pid = (const struct GNUNET_PeerIdentity *) &wo->reserve_pub;
+ if (GNUNET_CONTAINER_multipeermap_contains (h->rpubs,
+ pid))
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ NULL);
+ }
+ }
+ credit_name = TALER_xtalerbank_account_from_payto (exchange_payto_uri);
+ if (NULL == credit_name)
+ {
+ GNUNET_break_op (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
+ NULL);
+ }
+ credit_account = TALER_FAKEBANK_lookup_account_ (h,
+ credit_name,
+ NULL);
+ if (NULL == credit_account)
+ {
+ MHD_RESULT res;
+
+ GNUNET_break_op (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ res = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ credit_name);
+ GNUNET_free (credit_name);
+ return res;
+ }
+ GNUNET_free (credit_name);
+ if ( (NULL != wo->exchange_account) &&
+ (credit_account != wo->exchange_account) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
+ "exchange account changed");
+ }
+ wo->exchange_account = credit_account;
+ wo->reserve_pub = *reserve_pub;
+ wo->selection_done = true;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wo->confirmation_done));
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbi_post_withdrawal (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ MHD_RESULT res;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+
+ {
+ struct TALER_ReservePublicKeyP reserve_pub;
+ const char *exchange_payto_url;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &reserve_pub),
+ GNUNET_JSON_spec_string ("selected_exchange",
+ &exchange_payto_url),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ res = do_post_withdrawal (h,
+ connection,
+ wopid,
+ &reserve_pub,
+ exchange_payto_url);
+ }
+ json_decref (json);
+ return res;
+}
diff --git a/src/bank-lib/fakebank_tbi_post_withdrawal_operation.h b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.h
new file mode 100644
index 000000000..8873bb5f6
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.h
@@ -0,0 +1,53 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_post_withdrawal_operation.c
+ * @brief Implementation of the Taler Bank Integration API for POAT /withdrawal-operation/ requests
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBI_POST_WITHDRAWAL_OPERATION_H
+#define FAKEBANK_TBI_POST_WITHDRAWAL_OPERATION_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle POST /withdrawal-operation/ request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param wopid the withdrawal operation identifier
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbi_post_withdrawal (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbr.c b/src/bank-lib/fakebank_tbr.c
new file mode 100644
index 000000000..de96394e0
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr.c
@@ -0,0 +1,74 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr.c
+ * @brief main entry point for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_tbr_get_history.h"
+#include "fakebank_tbr_get_root.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbr_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Fakebank - Anastasis API: serving URL `%s' for account `%s'\n",
+ url,
+ account);
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET))
+ {
+ if ( (0 == strcmp (url,
+ "/history/incoming")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_tbr_get_history_incoming (h,
+ connection,
+ account,
+ con_cls);
+ if (0 == strcmp (url,
+ "/"))
+ return TALER_FAKEBANK_tbr_get_root (h,
+ connection);
+ }
+ /* Unexpected URL path, just close the connection. */
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_tbr.h b/src/bank-lib/fakebank_tbr.h
new file mode 100644
index 000000000..fb9bdfefc
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr.h
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr.h
+ * @brief main entry point for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+#ifndef FAKEBANK_TBR_H
+#define FAKEBANK_TBR_H
+
+/**
+ * Handle incoming HTTP request to the Taler Bank Revenue API.
+ *
+ * @param h our handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param account which account should process the request
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbr_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbr_get_history.c b/src/bank-lib/fakebank_tbr_get_history.c
new file mode 100644
index 000000000..3a7bfa450
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_history.c
@@ -0,0 +1,305 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_history.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_parser.h"
+#include "fakebank_tbr_get_history.h"
+
+
+/**
+ * Function called to clean up a history context.
+ *
+ * @param cls a `struct HistoryContext *`
+ */
+static void
+history_cleanup (void *cls)
+{
+ struct HistoryContext *hc = cls;
+
+ GNUNET_free (hc->payto_uri);
+ json_decref (hc->history);
+ GNUNET_free (hc);
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_history_incoming (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct HistoryContext *hc;
+ const struct Transaction *pos;
+ enum GNUNET_GenericReturnValue ret;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &history_cleanup;
+ *con_cls = cc;
+ hc = GNUNET_new (struct HistoryContext);
+ cc->ctx = hc;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling /history/incoming connection %p\n",
+ connection);
+ if (GNUNET_OK !=
+ (ret = TALER_FAKEBANK_common_parse_history_args (h,
+ connection,
+ &hc->ha)))
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ hc->acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == hc->acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ /* FIXME: was simply: acc->payto_uri -- same!? */
+ GNUNET_asprintf (&hc->payto_uri,
+ "payto://x-taler-bank/localhost/%s?receiver-name=%s",
+ account,
+ hc->acc->receiver_name);
+ GNUNET_assert (0 == strcmp (hc->payto_uri,
+ hc->acc->payto_uri));
+ hc->history = json_array ();
+ if (NULL == hc->history)
+ {
+ GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_NO;
+ }
+ hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
+ }
+ else
+ {
+ hc = cc->ctx;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+
+ if (! hc->ha.have_start)
+ {
+ pos = (0 > hc->ha.delta)
+ ? hc->acc->in_tail
+ : hc->acc->in_head;
+ }
+ else
+ {
+ struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
+ bool overflow;
+ uint64_t dir;
+ bool skip = true;
+
+ overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
+ dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
+ /* If account does not match, linear scan for
+ first matching account. */
+ while ( (! overflow) &&
+ (NULL != t) &&
+ (t->credit_account != hc->acc) )
+ {
+ skip = false;
+ t = h->transactions[(t->row_id + dir) % h->ram_limit];
+ if ( (NULL != t) &&
+ (t->row_id == hc->ha.start_idx) )
+ overflow = true; /* full circle, give up! */
+ }
+ if ( (NULL == t) ||
+ overflow)
+ {
+ /* FIXME: these conditions are unclear to me. */
+ if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
+ (0 < hc->ha.delta))
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (overflow)
+ return TALER_MHD_reply_with_ec (
+ connection,
+ TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
+ NULL);
+ goto finish;
+ }
+ if (h->in_shutdown)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ goto finish;
+ }
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (
+ hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ if (skip)
+ {
+ /* range from application is exclusive, skip the
+ matching entry */
+ if (0 > hc->ha.delta)
+ pos = t->prev_in;
+ else
+ pos = t->next_in;
+ }
+ else
+ {
+ pos = t;
+ }
+ }
+ if (NULL != pos)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning %lld credit transactions starting (inclusive) from %llu\n",
+ (long long) hc->ha.delta,
+ (unsigned long long) pos->row_id);
+ while ( (0 != hc->ha.delta) &&
+ (NULL != pos) )
+ {
+ json_t *trans;
+ char *subject;
+
+ if (T_DEBIT != pos->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected CREDIT transaction #%llu for account `%s'\n",
+ (unsigned long long) pos->row_id,
+ account);
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ continue;
+ }
+
+ {
+ char *wtids;
+
+ wtids = GNUNET_STRINGS_data_to_string_alloc (
+ &pos->subject.debit.wtid,
+ sizeof (pos->subject.debit.wtid));
+ GNUNET_asprintf (&subject,
+ "%s %s",
+ wtids,
+ pos->subject.debit.exchange_base_url);
+ GNUNET_free (wtids);
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Found transaction over %s with subject %s\n",
+ TALER_amount2s (&pos->amount),
+ subject);
+ trans = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "RESERVE"),
+ GNUNET_JSON_pack_uint64 ("row_id",
+ pos->row_id),
+ GNUNET_JSON_pack_timestamp ("date",
+ pos->date),
+ TALER_JSON_pack_amount ("amount",
+ &pos->amount),
+ GNUNET_JSON_pack_string ("debit_account",
+ pos->debit_account->payto_uri),
+ GNUNET_JSON_pack_string ("subject",
+ subject));
+ GNUNET_free (subject);
+ GNUNET_assert (NULL != trans);
+ GNUNET_assert (0 ==
+ json_array_append_new (hc->history,
+ trans));
+ if (hc->ha.delta > 0)
+ hc->ha.delta--;
+ else
+ hc->ha.delta++;
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ }
+ if ( (0 == json_array_size (hc->history)) &&
+ (! h->in_shutdown) &&
+ (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+finish:
+ if (0 == json_array_size (hc->history))
+ {
+ GNUNET_break (h->in_shutdown ||
+ (! GNUNET_TIME_absolute_is_future (hc->timeout)));
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ {
+ json_t *h = hc->history;
+
+ hc->history = NULL;
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string (
+ "credit_account",
+ hc->payto_uri),
+ GNUNET_JSON_pack_array_steal (
+ "incoming_transactions",
+ h));
+ }
+}
diff --git a/src/bank-lib/fakebank_tbr_get_history.h b/src/bank-lib/fakebank_tbr_get_history.h
new file mode 100644
index 000000000..99170ab7b
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_history.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_history.h
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBR_GET_HISTORY_H
+#define FAKEBANK_TBR_GET_HISTORY_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for /history/incoming
+ * of the Anastasis API. This one can return transactions
+ * created by debits from the exchange!
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account which account the request is about
+ * @param con_cls closure for request (NULL or &special_ptr)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_history_incoming (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbr_get_root.c b/src/bank-lib/fakebank_tbr_get_root.c
new file mode 100644
index 000000000..6e518d661
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_root.c
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_root.c
+ * @brief return the main "/" page for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_root (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection)
+{
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+#define HELLOMSG "Hello, Fakebank (Bank Revenue API here)!"
+
+ (void) h;
+ resp = MHD_create_response_from_buffer (
+ strlen (HELLOMSG),
+ HELLOMSG,
+ MHD_RESPMEM_MUST_COPY);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
diff --git a/src/bank-lib/fakebank_tbr_get_root.h b/src/bank-lib/fakebank_tbr_get_root.h
new file mode 100644
index 000000000..eda8060bf
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_root.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_root.h
+ * @brief return the main "/" page for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBR_GET_ROOT_H
+#define FAKEBANK_TBR_GET_ROOT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_root (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg.c b/src/bank-lib/fakebank_twg.c
new file mode 100644
index 000000000..f362dd4a8
--- /dev/null
+++ b/src/bank-lib/fakebank_twg.c
@@ -0,0 +1,106 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg.c
+ * @brief main entry point for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_twg.h"
+#include "fakebank_twg_admin_add_incoming.h"
+#include "fakebank_twg_get_root.h"
+#include "fakebank_twg_history.h"
+#include "fakebank_twg_transfer.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_twg_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Fakebank, serving URL `%s' for account `%s'\n",
+ url,
+ account);
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET))
+ {
+ if ( (0 == strcmp (url,
+ "/history/incoming")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_twg_get_credit_history_ (h,
+ connection,
+ account,
+ con_cls);
+ if ( (0 == strcmp (url,
+ "/history/outgoing")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_twg_get_debit_history_ (h,
+ connection,
+ account,
+ con_cls);
+ if (0 == strcmp (url,
+ "/"))
+ return TALER_FAKEBANK_twg_get_root_ (h,
+ connection);
+ }
+ else if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST))
+ {
+ if ( (0 == strcmp (url,
+ "/admin/add-incoming")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_twg_admin_add_incoming_ (h,
+ connection,
+ account,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ if ( (0 == strcmp (url,
+ "/transfer")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_handle_transfer_ (h,
+ connection,
+ account,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
+ /* Unexpected URL path, just close the connection. */
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_twg.h b/src/bank-lib/fakebank_twg.h
new file mode 100644
index 000000000..de808a21f
--- /dev/null
+++ b/src/bank-lib/fakebank_twg.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg.h
+ * @brief main entry point for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_H
+#define FAKEBANK_TWG_H
+
+#include "taler_fakebank_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+/**
+ * Handle incoming HTTP request to the Taler Wire Gateway
+ * API.
+ *
+ * @param h our handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param account which account should process the request
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_admin_add_incoming.c b/src/bank-lib/fakebank_twg_admin_add_incoming.c
new file mode 100644
index 000000000..2db4f1fe5
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_admin_add_incoming.c
@@ -0,0 +1,160 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_admin_add_incoming.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_make_admin_transfer.h"
+#include "fakebank_twg_admin_add_incoming.h"
+
+MHD_RESULT
+TALER_FAKEBANK_twg_admin_add_incoming_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ uint64_t row_id;
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+ {
+ const char *debit_account;
+ struct TALER_Amount amount;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ char *debit;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &reserve_pub),
+ GNUNET_JSON_spec_string ("debit_account",
+ &debit_account),
+ TALER_JSON_spec_amount ("amount",
+ h->currency,
+ &amount),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ if (0 != strcasecmp (amount.currency,
+ h->currency))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Currency `%s' does not match our configuration\n",
+ amount.currency);
+ json_decref (json);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_GENERIC_CURRENCY_MISMATCH,
+ NULL);
+ }
+ debit = TALER_xtalerbank_account_from_payto (debit_account);
+ if (NULL == debit)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
+ debit_account);
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s\n",
+ debit,
+ account,
+ TALER_B2S (&reserve_pub),
+ TALER_amount2s (&amount));
+ ret = TALER_FAKEBANK_make_admin_transfer_ (h,
+ debit,
+ account,
+ &amount,
+ &reserve_pub,
+ &row_id,
+ &timestamp);
+ GNUNET_free (debit);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Reserve public key not unique\n");
+ json_decref (json);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ NULL);
+ }
+ }
+ json_decref (json);
+
+ /* Finally build response object */
+ return TALER_MHD_REPLY_JSON_PACK (connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_uint64 ("row_id",
+ row_id),
+ GNUNET_JSON_pack_timestamp ("timestamp",
+ timestamp));
+}
diff --git a/src/bank-lib/fakebank_twg_admin_add_incoming.h b/src/bank-lib/fakebank_twg_admin_add_incoming.h
new file mode 100644
index 000000000..220a10fc8
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_admin_add_incoming.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_admin_add_incoming.h
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_ADMIN_ADD_INCOMING_H
+#define FAKEBANK_TWG_ADMIN_ADD_INCOMING_H
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle incoming HTTP request for /admin/add/incoming.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account account into which to deposit the funds (credit)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct ConnectionContext *`)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_admin_add_incoming_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_get_root.c b/src/bank-lib/fakebank_twg_get_root.c
new file mode 100644
index 000000000..09589890e
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_get_root.c
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_get_root.c
+ * @brief return the "/" page for the taler wire gateway
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_root_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection)
+{
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+#define HELLOMSG "Hello, Fakebank (Taler Wire Gateway)!"
+
+ (void) h;
+ resp = MHD_create_response_from_buffer (
+ strlen (HELLOMSG),
+ HELLOMSG,
+ MHD_RESPMEM_MUST_COPY);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
diff --git a/src/bank-lib/fakebank_twg_get_root.h b/src/bank-lib/fakebank_twg_get_root.h
new file mode 100644
index 000000000..8bbcf4192
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_get_root.h
@@ -0,0 +1,46 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_get_root.h
+ * @brief return the "/" page for the taler wire gateway
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_GET_ROOT_H
+#define FAKEBANK_TWG_GET_ROOT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_root_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_history.c b/src/bank-lib/fakebank_twg_history.c
new file mode 100644
index 000000000..062285078
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_history.c
@@ -0,0 +1,537 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_history.c
+ * @brief routines to return account histories for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_parser.h"
+
+/**
+ * Function called to clean up a history context.
+ *
+ * @param cls a `struct HistoryContext *`
+ */
+static void
+history_cleanup (void *cls)
+{
+ struct HistoryContext *hc = cls;
+
+ GNUNET_free (hc->payto_uri);
+ json_decref (hc->history);
+ GNUNET_free (hc);
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_twg_get_debit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct HistoryContext *hc;
+ struct Transaction *pos;
+ enum GNUNET_GenericReturnValue ret;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &history_cleanup;
+ *con_cls = cc;
+ hc = GNUNET_new (struct HistoryContext);
+ cc->ctx = hc;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling /history/outgoing connection %p\n",
+ connection);
+ if (GNUNET_OK !=
+ (ret = TALER_FAKEBANK_common_parse_history_args (h,
+ connection,
+ &hc->ha)))
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ hc->acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == hc->acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ GNUNET_asprintf (&hc->payto_uri,
+ "payto://x-taler-bank/localhost/%s?receiver-name=%s",
+ account,
+ hc->acc->receiver_name);
+ /* New invariant: */
+ GNUNET_assert (0 == strcmp (hc->payto_uri,
+ hc->acc->payto_uri));
+ hc->history = json_array ();
+ if (NULL == hc->history)
+ {
+ GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_NO;
+ }
+ hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
+ }
+ else
+ {
+ hc = cc->ctx;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+
+ if (! hc->ha.have_start)
+ {
+ pos = (0 > hc->ha.delta)
+ ? hc->acc->out_tail
+ : hc->acc->out_head;
+ }
+ else
+ {
+ struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
+ bool overflow;
+ uint64_t dir;
+ bool skip = true;
+
+ dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
+ overflow = (t->row_id != hc->ha.start_idx);
+ /* If account does not match, linear scan for
+ first matching account. */
+ while ( (! overflow) &&
+ (NULL != t) &&
+ (t->debit_account != hc->acc) )
+ {
+ skip = false;
+ t = h->transactions[(t->row_id + dir) % h->ram_limit];
+ if ( (NULL != t) &&
+ (t->row_id == hc->ha.start_idx) )
+ overflow = true; /* full circle, give up! */
+ }
+ if ( (NULL == t) ||
+ overflow)
+ {
+ /* FIXME: these conditions are unclear to me. */
+ if ( (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (overflow)
+ {
+ return TALER_MHD_reply_with_ec (
+ connection,
+ TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
+ NULL);
+ }
+ goto finish;
+ }
+ if (h->in_shutdown)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ goto finish;
+ }
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (
+ hc->timeout),
+ LP_DEBIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ if (t->debit_account != hc->acc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid start specified, transaction %llu not with account %s!\n",
+ (unsigned long long) hc->ha.start_idx,
+ account);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_NO;
+ }
+ if (skip)
+ {
+ /* range is exclusive, skip the matching entry */
+ if (0 > hc->ha.delta)
+ pos = t->prev_out;
+ else
+ pos = t->next_out;
+ }
+ else
+ {
+ pos = t;
+ }
+ }
+ if (NULL != pos)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning %lld debit transactions starting (inclusive) from %llu\n",
+ (long long) hc->ha.delta,
+ (unsigned long long) pos->row_id);
+ while ( (0 != hc->ha.delta) &&
+ (NULL != pos) )
+ {
+ json_t *trans;
+ char *credit_payto;
+
+ if (T_DEBIT != pos->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected CREDIT transaction #%llu for account `%s'\n",
+ (unsigned long long) pos->row_id,
+ account);
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ continue;
+ }
+ GNUNET_asprintf (&credit_payto,
+ "payto://x-taler-bank/localhost/%s?receiver-name=%s",
+ pos->credit_account->account_name,
+ pos->credit_account->receiver_name);
+
+ trans = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("row_id",
+ pos->row_id),
+ GNUNET_JSON_pack_timestamp ("date",
+ pos->date),
+ TALER_JSON_pack_amount ("amount",
+ &pos->amount),
+ GNUNET_JSON_pack_string ("credit_account",
+ credit_payto),
+ GNUNET_JSON_pack_string ("exchange_base_url",
+ pos->subject.debit.exchange_base_url),
+ GNUNET_JSON_pack_data_auto ("wtid",
+ &pos->subject.debit.wtid));
+ GNUNET_assert (NULL != trans);
+ GNUNET_free (credit_payto);
+ GNUNET_assert (0 ==
+ json_array_append_new (hc->history,
+ trans));
+ if (hc->ha.delta > 0)
+ hc->ha.delta--;
+ else
+ hc->ha.delta++;
+ if (0 > hc->ha.delta)
+ pos = pos->prev_out;
+ if (0 < hc->ha.delta)
+ pos = pos->next_out;
+ }
+ if ( (0 == json_array_size (hc->history)) &&
+ (! h->in_shutdown) &&
+ (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (hc->timeout),
+ LP_DEBIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+finish:
+ if (0 == json_array_size (hc->history))
+ {
+ GNUNET_break (h->in_shutdown ||
+ (! GNUNET_TIME_absolute_is_future (hc->timeout)));
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ {
+ json_t *h = hc->history;
+
+ hc->history = NULL;
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string (
+ "debit_account",
+ hc->payto_uri),
+ GNUNET_JSON_pack_array_steal (
+ "outgoing_transactions",
+ h));
+ }
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_twg_get_credit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct HistoryContext *hc;
+ const struct Transaction *pos;
+ enum GNUNET_GenericReturnValue ret;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &history_cleanup;
+ *con_cls = cc;
+ hc = GNUNET_new (struct HistoryContext);
+ cc->ctx = hc;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling /history/incoming connection %p\n",
+ connection);
+ if (GNUNET_OK !=
+ (ret = TALER_FAKEBANK_common_parse_history_args (h,
+ connection,
+ &hc->ha)))
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ hc->acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == hc->acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ /* FIXME: was simply: acc->payto_uri -- same!? */
+ GNUNET_asprintf (&hc->payto_uri,
+ "payto://x-taler-bank/%s/%s?receiver-name=%s",
+ h->hostname,
+ account,
+ hc->acc->receiver_name);
+ GNUNET_assert (0 == strcmp (hc->payto_uri,
+ hc->acc->payto_uri));
+ hc->history = json_array ();
+ if (NULL == hc->history)
+ {
+ GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_NO;
+ }
+ hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
+ }
+ else
+ {
+ hc = cc->ctx;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+
+ if (! hc->ha.have_start)
+ {
+ pos = (0 > hc->ha.delta)
+ ? hc->acc->in_tail
+ : hc->acc->in_head;
+ }
+ else
+ {
+ struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
+ bool overflow;
+ uint64_t dir;
+ bool skip = true;
+
+ overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
+ dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
+ /* If account does not match, linear scan for
+ first matching account. */
+ while ( (! overflow) &&
+ (NULL != t) &&
+ (t->credit_account != hc->acc) )
+ {
+ skip = false;
+ t = h->transactions[(t->row_id + dir) % h->ram_limit];
+ if ( (NULL != t) &&
+ (t->row_id == hc->ha.start_idx) )
+ overflow = true; /* full circle, give up! */
+ }
+ if ( (NULL == t) ||
+ overflow)
+ {
+ /* FIXME: these conditions are unclear to me. */
+ if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
+ (0 < hc->ha.delta))
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (overflow)
+ return TALER_MHD_reply_with_ec (
+ connection,
+ TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
+ NULL);
+ goto finish;
+ }
+ if (h->in_shutdown)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ goto finish;
+ }
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (
+ hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ if (skip)
+ {
+ /* range from application is exclusive, skip the
+ matching entry */
+ if (0 > hc->ha.delta)
+ pos = t->prev_in;
+ else
+ pos = t->next_in;
+ }
+ else
+ {
+ pos = t;
+ }
+ }
+ if (NULL != pos)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning %lld credit transactions starting (inclusive) from %llu\n",
+ (long long) hc->ha.delta,
+ (unsigned long long) pos->row_id);
+ while ( (0 != hc->ha.delta) &&
+ (NULL != pos) )
+ {
+ json_t *trans;
+
+ if (T_CREDIT != pos->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected DEBIT transaction #%llu for account `%s'\n",
+ (unsigned long long) pos->row_id,
+ account);
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ continue;
+ }
+ trans = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("row_id",
+ pos->row_id),
+ GNUNET_JSON_pack_timestamp ("date",
+ pos->date),
+ TALER_JSON_pack_amount ("amount",
+ &pos->amount),
+ GNUNET_JSON_pack_string ("debit_account",
+ pos->debit_account->payto_uri),
+ GNUNET_JSON_pack_data_auto ("reserve_pub",
+ &pos->subject.credit.reserve_pub));
+ GNUNET_assert (NULL != trans);
+ GNUNET_assert (0 ==
+ json_array_append_new (hc->history,
+ trans));
+ if (hc->ha.delta > 0)
+ hc->ha.delta--;
+ else
+ hc->ha.delta++;
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ }
+ if ( (0 == json_array_size (hc->history)) &&
+ (! h->in_shutdown) &&
+ (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+finish:
+ if (0 == json_array_size (hc->history))
+ {
+ GNUNET_break (h->in_shutdown ||
+ (! GNUNET_TIME_absolute_is_future (hc->timeout)));
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ {
+ json_t *h = hc->history;
+
+ hc->history = NULL;
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string (
+ "credit_account",
+ hc->payto_uri),
+ GNUNET_JSON_pack_array_steal (
+ "incoming_transactions",
+ h));
+ }
+}
diff --git a/src/bank-lib/fakebank_twg_history.h b/src/bank-lib/fakebank_twg_history.h
new file mode 100644
index 000000000..c49678aef
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_history.h
@@ -0,0 +1,67 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_history.c
+ * @brief routines to return account histories for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_HISTORY_H
+#define FAKEBANK_TWG_HISTORY_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for /history/outgoing
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account which account the request is about
+ * @param con_cls closure for request
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_debit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls);
+
+
+/**
+ * Handle incoming HTTP request for /history/incoming
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account which account the request is about
+ * @param con_cls closure for request (NULL or &special_ptr)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_credit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_transfer.c b/src/bank-lib/fakebank_twg_transfer.c
new file mode 100644
index 000000000..fef314a52
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_transfer.c
@@ -0,0 +1,178 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_transfer.c
+ * @brief implementation of the Taler Wire Gateway "/transfer" endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_transact.h"
+#include "fakebank_twg_transfer.h"
+
+
+/**
+ * Handle incoming HTTP request for /transfer.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account account making the transfer
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct ConnectionContext *`)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_handle_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ uint64_t row_id;
+ struct GNUNET_TIME_Timestamp ts;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+ {
+ struct GNUNET_HashCode uuid;
+ struct TALER_WireTransferIdentifierRawP wtid;
+ const char *credit_account;
+ char *credit;
+ const char *base_url;
+ struct TALER_Amount amount;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("request_uid",
+ &uuid),
+ TALER_JSON_spec_amount ("amount",
+ h->currency,
+ &amount),
+ GNUNET_JSON_spec_string ("exchange_base_url",
+ &base_url),
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &wtid),
+ GNUNET_JSON_spec_string ("credit_account",
+ &credit_account),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ {
+ enum GNUNET_GenericReturnValue ret;
+
+ credit = TALER_xtalerbank_account_from_payto (credit_account);
+ if (NULL == credit)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
+ credit_account);
+ }
+ ret = TALER_FAKEBANK_make_transfer_ (h,
+ account,
+ credit,
+ &amount,
+ &wtid,
+ base_url,
+ &uuid,
+ &row_id,
+ &ts);
+ if (GNUNET_OK != ret)
+ {
+ MHD_RESULT res;
+ char *uids;
+
+ GNUNET_break (0);
+ uids = GNUNET_STRINGS_data_to_string_alloc (&uuid,
+ sizeof (uuid));
+ json_decref (json);
+ res = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED,
+ uids);
+ GNUNET_free (uids);
+ return res;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
+ account,
+ credit,
+ TALER_B2S (&wtid),
+ TALER_amount2s (&amount),
+ base_url);
+ GNUNET_free (credit);
+ }
+ }
+ json_decref (json);
+
+ /* Finally build response object */
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_uint64 ("row_id",
+ row_id),
+ GNUNET_JSON_pack_timestamp ("timestamp",
+ ts));
+}
diff --git a/src/bank-lib/fakebank_twg_transfer.h b/src/bank-lib/fakebank_twg_transfer.h
new file mode 100644
index 000000000..2019565bf
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_transfer.h
@@ -0,0 +1,55 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 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 Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_transfer.h
+ * @brief implementation of the Taler Wire Gateway "/transfer" endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_TRANSFER_H
+#define FAKEBANK_TWG_TRANSFER_H
+
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for /transfer.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account account making the transfer
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct ConnectionContext *`)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_handle_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif