diff options
Diffstat (limited to 'src/bank-lib/fakebank.c')
-rw-r--r-- | src/bank-lib/fakebank.c | 4141 |
1 files changed, 61 insertions, 4080 deletions
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c index c916ad70c..3a004dc80 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-2024 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,10 @@ #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" /** @@ -1703,9 +43,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 +57,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,2335 +71,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, - ×tamp); - 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 ("debit_account", - hc->payto_uri), // FIXME #7275: inefficient to return this here always! - 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_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/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; - - 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 ("credit_account", - hc->payto_uri), // FIXME #7275: inefficient to repeat this always here! - 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_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 ("paytoUri", /* FIXME: #7300 */ - 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 ("publicAccounts", /* FIXME: #7300 */ - 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. * * @param cls a `struct TALER_FAKEBANK_Handle` @@ -4082,61 +94,28 @@ handle_mhd_request (void *cls, void **con_cls) { struct TALER_FAKEBANK_Handle *h = cls; - char *account = NULL; - char *end; - MHD_RESULT ret; (void) version; if (0 == strncmp (url, - "/taler-bank-integration/", - strlen ("/taler-bank-integration/"))) + "/taler-integration/", + strlen ("/taler-integration/"))) { - url += strlen ("/taler-bank-integration"); - return handle_bank_integration (h, + url += strlen ("/taler-integration"); + return TALER_FAKEBANK_tbi_main_ (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, - "/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); - } - if (0 == strncmp (url, - "/taler-wire-gateway/", - strlen ("/taler-wire-gateway/"))) - url += strlen ("/taler-wire-gateway"); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Handling request for `%s'\n", - url); - if ( (strlen (url) > 1) && - (NULL != (end = strchr (url + 1, '/'))) ) - { - account = GNUNET_strndup (url + 1, - end - url - 1); - url = end; - } - ret = serve (h, - connection, - account, - url, - method, - upload_data, - upload_data_size, - con_cls); - GNUNET_free (account); - return ret; } @@ -4167,7 +146,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); } @@ -4241,7 +220,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); @@ -4258,8 +237,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; @@ -4408,22 +387,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); @@ -4466,7 +446,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, @@ -4483,24 +463,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); |