summaryrefslogtreecommitdiff
path: root/src/bank-lib/fakebank.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bank-lib/fakebank.c')
-rw-r--r--src/bank-lib/fakebank.c4141
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,
- &timestamp);
- GNUNET_free (debit);
- if (GNUNET_OK != ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Reserve public key not unique\n");
- json_decref (json);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
- NULL);
- }
- }
- json_decref (json);
-
- /* Finally build response object */
- return TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_uint64 ("row_id",
- row_id),
- GNUNET_JSON_pack_timestamp ("timestamp",
- timestamp));
-}
-
-
-/**
- * Handle incoming HTTP request for /transfer.
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account account making the transfer
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request (a `struct ConnectionContext *`)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_transfer (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- uint64_t row_id;
- struct GNUNET_TIME_Timestamp ts;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
- *con_cls = cc;
- }
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- &cc->ctx,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
- {
- struct GNUNET_HashCode uuid;
- struct TALER_WireTransferIdentifierRawP wtid;
- const char *credit_account;
- char *credit;
- const char *base_url;
- struct TALER_Amount amount;
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("request_uid",
- &uuid),
- TALER_JSON_spec_amount ("amount",
- h->currency,
- &amount),
- GNUNET_JSON_spec_string ("exchange_base_url",
- &base_url),
- GNUNET_JSON_spec_fixed_auto ("wtid",
- &wtid),
- GNUNET_JSON_spec_string ("credit_account",
- &credit_account),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- {
- enum GNUNET_GenericReturnValue ret;
-
- credit = TALER_xtalerbank_account_from_payto (credit_account);
- if (NULL == credit)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
- credit_account);
- }
- ret = make_transfer (h,
- account,
- credit,
- &amount,
- &wtid,
- base_url,
- &uuid,
- &row_id,
- &ts);
- if (GNUNET_OK != ret)
- {
- MHD_RESULT res;
- char *uids;
-
- GNUNET_break (0);
- uids = GNUNET_STRINGS_data_to_string_alloc (&uuid,
- sizeof (uuid));
- json_decref (json);
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED,
- uids);
- GNUNET_free (uids);
- return res;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
- account,
- credit,
- TALER_B2S (&wtid),
- TALER_amount2s (&amount),
- base_url);
- GNUNET_free (credit);
- }
- }
- json_decref (json);
-
- /* Finally build response object */
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_uint64 ("row_id",
- row_id),
- GNUNET_JSON_pack_timestamp ("timestamp",
- ts));
-}
-
-
-/**
- * Handle incoming HTTP request for / (home page).
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @return MHD result code
- */
-static MHD_RESULT
-handle_home_page (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection)
-{
- MHD_RESULT ret;
- struct MHD_Response *resp;
-#define HELLOMSG "Hello, Fakebank!"
-
- (void) h;
- resp = MHD_create_response_from_buffer (
- strlen (HELLOMSG),
- HELLOMSG,
- MHD_RESPMEM_MUST_COPY);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- resp);
- MHD_destroy_response (resp);
- return ret;
-}
-
-
-/**
- * Parse URL history arguments, of _both_ APIs:
- * /history/incoming and /history/outgoing.
- *
- * @param h bank handle to work on
- * @param connection MHD connection.
- * @param[out] ha will contain the parsed values.
- * @return #GNUNET_OK only if the parsing succeeds,
- * #GNUNET_SYSERR if it failed,
- * #GNUNET_NO if it failed and an error was returned
- */
-static enum GNUNET_GenericReturnValue
-parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- struct HistoryArgs *ha)
-{
- const char *start;
- const char *delta;
- const char *long_poll_ms;
- unsigned long long lp_timeout;
- unsigned long long sval;
- long long d;
- char dummy;
-
- start = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "start");
- ha->have_start = (NULL != start);
- delta = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "delta");
- long_poll_ms = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "long_poll_ms");
- lp_timeout = 0;
- if ( (NULL == delta) ||
- (1 != sscanf (delta,
- "%lld%c",
- &d,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if ( (NULL != long_poll_ms) &&
- (1 != sscanf (long_poll_ms,
- "%llu%c",
- &lp_timeout,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "long_poll_ms"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if ( (NULL != start) &&
- (1 != sscanf (start,
- "%llu%c",
- &sval,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "start"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if (NULL == start)
- ha->start_idx = (d > 0) ? 0 : h->serial_counter;
- else
- ha->start_idx = (uint64_t) sval;
- ha->delta = (int64_t) d;
- if (0 == ha->delta)
- {
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- ha->lp_timeout
- = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- lp_timeout);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request for %lld records from %llu\n",
- (long long) ha->delta,
- (unsigned long long) ha->start_idx);
- return GNUNET_OK;
-}
-
-
-/**
- * Task run when a long poller is about to time out.
- * Only used in single-threaded mode.
- *
- * @param cls a `struct TALER_FAKEBANK_Handle *`
- */
-static void
-lp_timeout (void *cls)
-{
- struct TALER_FAKEBANK_Handle *h = cls;
- struct LongPoller *lp;
-
- h->lp_task = NULL;
- while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
- {
- if (GNUNET_TIME_absolute_is_future (lp->timeout))
- break;
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Timeout reached for long poller %p\n",
- lp->conn);
- lp_trigger (lp,
- h);
- }
- if (NULL == lp)
- return;
- h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
- &lp_timeout,
- h);
-}
-
-
-/**
- * Reschedule the timeout task of @a h for time @a t.
- *
- * @param h fakebank handle
- * @param t when will the next connection timeout expire
- */
-static void
-reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
- struct GNUNET_TIME_Absolute t)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Scheduling timeout task for %s\n",
- GNUNET_STRINGS_absolute_time_to_string (t));
-#ifdef __linux__
- if (-1 != h->lp_event)
-#else
- if (-1 != h->lp_event_in && -1 != h->lp_event_out)
-#endif
- {
- uint64_t num = 1;
-
- GNUNET_break (sizeof (num) ==
-#ifdef __linux__
- write (h->lp_event,
-#else
- write (h->lp_event_in,
-#endif
- &num,
- sizeof (num)));
- }
- else
- {
- if (NULL != h->lp_task)
- GNUNET_SCHEDULER_cancel (h->lp_task);
- h->lp_task = GNUNET_SCHEDULER_add_at (t,
- &lp_timeout,
- h);
- }
-}
-
-
-/**
- * Start long-polling for @a connection and @a acc
- * for transfers in @a dir. Must be called with the
- * "big lock" held.
- *
- * @param[in,out] h fakebank handle
- * @param[in,out] connection to suspend
- * @param[in,out] acc account affected
- * @param lp_timeout how long to suspend
- * @param dir direction of transfers to watch for
- * @param wo withdraw operation to watch, only
- * if @a dir is #LP_WITHDRAW
- */
-static void
-start_lp (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- struct Account *acc,
- struct GNUNET_TIME_Relative lp_timeout,
- enum LongPollType dir,
- const struct WithdrawalOperation *wo)
-{
- struct LongPoller *lp;
- bool toc;
-
- lp = GNUNET_new (struct LongPoller);
- lp->account = acc;
- lp->wo = wo;
- lp->conn = connection;
- lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
- lp->type = dir;
- lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
- lp,
- lp->timeout.abs_value_us);
- toc = (lp ==
- GNUNET_CONTAINER_heap_peek (h->lp_heap));
- GNUNET_CONTAINER_DLL_insert (acc->lp_head,
- acc->lp_tail,
- lp);
- MHD_suspend_connection (connection);
- if (toc)
- reschedule_lp_timeout (h,
- lp->timeout);
-
-}
-
-
-/**
- * Handle incoming HTTP request for /history/outgoing
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account which account the request is about
- * @param con_cls closure for request
- */
-static MHD_RESULT
-handle_debit_history (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- void **con_cls)
-{
- struct ConnectionContext *cc = *con_cls;
- struct HistoryContext *hc;
- struct Transaction *pos;
- enum GNUNET_GenericReturnValue ret;
-
- if (NULL == cc)
- {
- cc = GNUNET_new (struct ConnectionContext);
- cc->ctx_cleaner = &history_cleanup;
- *con_cls = cc;
- hc = GNUNET_new (struct HistoryContext);
- cc->ctx = hc;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling /history/outgoing connection %p\n",
- connection);
- if (GNUNET_OK !=
- (ret = parse_history_common_args (h,
- connection,
- &hc->ha)))
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- hc->acc = lookup_account (h,
- account,
- NULL);
- if (NULL == hc->acc)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_BANK_UNKNOWN_ACCOUNT,
- account);
- }
- GNUNET_asprintf (&hc->payto_uri,
- "payto://x-taler-bank/localhost/%s?receiver-name=%s",
- account,
- hc->acc->receiver_name);
- /* New invariant: */
- GNUNET_assert (0 == strcmp (hc->payto_uri,
- hc->acc->payto_uri));
- hc->history = json_array ();
- if (NULL == hc->history)
- {
- GNUNET_break (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_NO;
- }
- hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
- }
- else
- {
- hc = cc->ctx;
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- }
-
- if (! hc->ha.have_start)
- {
- pos = (0 > hc->ha.delta)
- ? hc->acc->out_tail
- : hc->acc->out_head;
- }
- else
- {
- struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
- bool overflow;
- uint64_t dir;
- bool skip = true;
-
- dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
- overflow = (t->row_id != hc->ha.start_idx);
- /* If account does not match, linear scan for
- first matching account. */
- while ( (! overflow) &&
- (NULL != t) &&
- (t->debit_account != hc->acc) )
- {
- skip = false;
- t = h->transactions[(t->row_id + dir) % h->ram_limit];
- if ( (NULL != t) &&
- (t->row_id == hc->ha.start_idx) )
- overflow = true; /* full circle, give up! */
- }
- if ( (NULL == t) ||
- overflow)
- {
- /* FIXME: these conditions are unclear to me. */
- if ( (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout)) &&
- (0 < hc->ha.delta))
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- if (overflow)
- {
- return TALER_MHD_reply_with_ec (
- connection,
- TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
- NULL);
- }
- goto finish;
- }
- if (h->in_shutdown)
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- goto finish;
- }
- start_lp (h,
- connection,
- hc->acc,
- GNUNET_TIME_absolute_get_remaining (hc->timeout),
- LP_DEBIT,
- NULL);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_YES;
- }
- if (t->debit_account != hc->acc)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid start specified, transaction %llu not with account %s!\n",
- (unsigned long long) hc->ha.start_idx,
- account);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return MHD_NO;
- }
- if (skip)
- {
- /* range is exclusive, skip the matching entry */
- if (0 > hc->ha.delta)
- pos = t->prev_out;
- else
- pos = t->next_out;
- }
- else
- {
- pos = t;
- }
- }
- if (NULL != pos)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning %lld debit transactions starting (inclusive) from %llu\n",
- (long long) hc->ha.delta,
- (unsigned long long) pos->row_id);
- while ( (0 != hc->ha.delta) &&
- (NULL != pos) )
- {
- json_t *trans;
- char *credit_payto;
-
- if (T_DEBIT != pos->type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected CREDIT transaction #%llu for account `%s'\n",
- (unsigned long long) pos->row_id,
- account);
- if (0 > hc->ha.delta)
- pos = pos->prev_in;
- if (0 < hc->ha.delta)
- pos = pos->next_in;
- continue;
- }
- GNUNET_asprintf (&credit_payto,
- "payto://x-taler-bank/localhost/%s?receiver-name=%s",
- pos->credit_account->account_name,
- pos->credit_account->receiver_name);
-
- trans = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("row_id",
- pos->row_id),
- GNUNET_JSON_pack_timestamp ("date",
- pos->date),
- TALER_JSON_pack_amount ("amount",
- &pos->amount),
- GNUNET_JSON_pack_string ("credit_account",
- credit_payto),
- GNUNET_JSON_pack_string ("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);