summaryrefslogtreecommitdiff
path: root/src/bank-lib
diff options
context:
space:
mode:
authorMarcello Stanisci <stanisci.m@gmail.com>2019-04-08 02:09:23 +0200
committerMarcello Stanisci <stanisci.m@gmail.com>2019-04-08 02:37:46 +0200
commite13a8902f07a7848d09204cd6f1b9736fe3cf885 (patch)
treeb455d3061e069814b3591497c8d9ecfe2ad446c2 /src/bank-lib
parent9fc380f7c8ebf8b5623718de376c72e6677b93a6 (diff)
downloadexchange-e13a8902f07a7848d09204cd6f1b9736fe3cf885.tar.gz
exchange-e13a8902f07a7848d09204cd6f1b9736fe3cf885.tar.bz2
exchange-e13a8902f07a7848d09204cd6f1b9736fe3cf885.zip
Put /history[-range] logic in a dedicate file.
Diffstat (limited to 'src/bank-lib')
-rw-r--r--src/bank-lib/Makefile.am1
-rw-r--r--src/bank-lib/fakebank.c954
-rw-r--r--src/bank-lib/fakebank.h322
-rw-r--r--src/bank-lib/fakebank_history.c427
4 files changed, 767 insertions, 937 deletions
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index e6e2e878c..ddc4cf4b3 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -57,6 +57,7 @@ libtalerfakebank_la_LDFLAGS = \
-no-undefined
libtalerfakebank_la_SOURCES = \
+ fakebank_history.c \
fakebank.c
libtalerfakebank_la_LIBADD = \
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index f3b58fd26..37aae0423 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -25,6 +25,7 @@
#include "platform.h"
#include "taler_fakebank_lib.h"
#include "taler_bank_service.h"
+#include "fakebank.h"
/**
* Maximum POST request size (for /admin/add/incoming)
@@ -33,169 +34,6 @@
/**
- * Parse URL arguments of a /history[-range] HTTP request.
- *
- * @param connection MHD connection object.
- * @param ha @a HistoryArgs structure.
- */
-#define PARSE_HISTORY_ARGS(connection, ha) \
- parse_history_args_ (connection, ha, __FUNCTION__)
-
-/**
- * Details about a transcation we (as the simulated bank) received.
- */
-struct Transaction
-{
- /**
- * We store transactions in a DLL.
- */
- struct Transaction *next;
-
- /**
- * We store transactions in a DLL.
- */
- struct Transaction *prev;
-
- /**
- * Amount to be transferred.
- */
- struct TALER_Amount amount;
-
- /**
- * Account to debit.
- */
- uint64_t debit_account;
-
- /**
- * Account to credit.
- */
- uint64_t credit_account;
-
- /**
- * Subject of the transfer.
- */
- char *subject;
-
- /**
- * Base URL of the exchange.
- */
- char *exchange_base_url;
-
- /**
- * When did the transaction happen?
- */
- struct GNUNET_TIME_Absolute date;
-
- /**
- * Number of this transaction.
- */
- uint64_t row_id;
-
- /**
- * Flag set if the transfer was rejected.
- */
- int rejected;
-
- /**
- * Has this transaction been subjected to #TALER_FAKEBANK_check()
- * and should thus no longer be counted in
- * #TALER_FAKEBANK_check_empty()?
- */
- int checked;
-};
-
-
-/**
- * Needed to implement ascending/descending ordering
- * of /history results.
- */
-struct HistoryElement
-{
-
- /**
- * History JSON element.
- */
- json_t *element;
-
- /**
- * Previous element.
- */
- struct HistoryElement *prev;
-
- /**
- * Next element.
- */
- struct HistoryElement *next;
-};
-
-
-/**
- * Values to implement the "/history-range" range.
- */
-struct HistoryRangeDates
-{
- /**
- * Oldest row in the results.
- */
- struct GNUNET_TIME_Absolute start;
-
- /**
- * Youngest row in the results.
- */
- struct GNUNET_TIME_Absolute end;
-};
-
-/**
- * Values to implement the "/history" range.
- */
-struct HistoryRangeIds
-{
-
- /**
- * (Exclusive) row ID for the result set.
- */
- unsigned long long start;
-
- /**
- * How many transactions we want in the result set. If
- * negative/positive, @a start will be strictly younger/older
- * of any element in the result set.
- */
- long long count;
-};
-
-
-/**
- * This is the "base" structure for both the /history and the
- * /history-range API calls.
- */
-struct HistoryArgs
-{
-
- /**
- * Direction asked by the client: CREDIT / DEBIT / BOTH / CANCEL.
- */
- enum TALER_BANK_Direction direction;
-
- /**
- * Bank account number of the requesting client.
- */
- unsigned long long account_number;
-
- /**
- * Ordering of the results.
- */
- unsigned int ascending;
-
- /**
- * Overloaded type that indicates the "range" to be returned
- * in the results; this can be either a date range, or a
- * starting row id + the count.
- */
- void *range;
-};
-
-/**
* Handle for the fake bank.
*/
struct TALER_FAKEBANK_Handle
@@ -746,442 +584,6 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
return ret;
}
-/***********************************
- * Serving "/history" starts here. *
- ***********************************/
-
-/**
- * Type for a function that decides whether or not
- * the history-building loop should iterate once again.
- * Typically called from inside the 'while' condition.
- *
- * @param ha history argument.
- * @param pos current position.
- * @return GNUNET_YES if the iteration shuold go on.
- */
-typedef int (*CheckAdvance)
- (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/**
- * Type for a function that steps over the next element
- * in the list of all transactions, after the current @a pos
- * _got_ included in the result.
- */
-typedef struct Transaction * (*Step)
- (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-/*
- * Type for a function that steps over the next element
- * in the list of all transactions, after the current @a pos
- * did _not_ get included in the result.
- */
-typedef struct Transaction * (*Skip)
- (const struct HistoryArgs *ha,
- const struct Transaction *pos);
-
-
-
-
-/**
- * Decides whether the history builder will advance or not
- * to the next element.
- *
- * @param ha history args
- * @return GNUNET_YES/NO to advance/not-advance.
- */
-static int
-handle_history_advance (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- const struct HistoryRangeIds *hri = ha->range;
-
- return (NULL != pos) && (0 != hri->count);
-}
-
-
-/**
- * Iterates on the "next" element to be processed. To
- * be used when the current element does not get inserted in
- * the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-static struct Transaction *
-handle_history_skip (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- const struct HistoryRangeIds *hri = ha->range;
-
- if (hri->count > 0)
- return pos->next;
- if (hri->count < 0)
- return pos->prev;
- return NULL;
-}
-
-
-/**
- * Iterates on the "next" element to be processed. To
- * be used when the current element _gets_ inserted in the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-static struct Transaction *
-handle_history_step (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- struct HistoryRangeIds *hri = ha->range;
-
- if (hri->count > 0)
- {
- hri->count--;
- return pos->next;
- }
- if (hri->count < 0)
- {
- hri->count++;
- return pos->prev;
- }
- return NULL;
-}
-
-
-/**
- * Decides whether the history builder will advance or not
- * to the next element.
- *
- * @param ha history args
- * @return GNUNET_YES/NO to advance/not-advance.
- */
-static int
-handle_history_range_advance (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- const struct HistoryRangeDates *hrd = ha->range;
-
- if ( (NULL != pos) &&
- (pos->date.abs_value_us <= hrd->end.abs_value_us) )
- return GNUNET_YES;
-
- return GNUNET_NO;
-}
-
-
-/**
- * Iterates towards the "next" element to be processed. To
- * be used when the current element does not get inserted in
- * the result.
- *
- * @param ha history arguments.
- * @param pos current element being processed.
- * @return the next element to be processed.
- */
-static struct Transaction *
-handle_history_range_skip (const struct HistoryArgs *ha,
- const struct Transaction *pos)
-{
- /* Transactions
- * are stored from "head"/older to "tail"/younger. */
- return pos->next;
-}
-
-/**
- * Iterates on the "next" element to be processed. To
- * be used when the current element _gets_ inserted in the result.
- * Same implementation of the "skip" counterpart, as /history-range
- * does not have the notion of count/delta.
- */
-Step handle_history_range_step = handle_history_range_skip;
-
-/**
- * Actual history response builder.
- *
- * @param pos first (included) element in the result set.
- * @param ha history arguments.
- * @param caller_name which function is building the history.
- * @return MHD_YES / MHD_NO, after having enqueued the response
- * object into MHD.
- */
-static int
-build_history_response (struct MHD_Connection *connection,
- struct Transaction *pos,
- struct HistoryArgs *ha,
- Skip skip,
- Step step,
- CheckAdvance advance)
-{
-
- struct HistoryElement *history_results_head = NULL;
- struct HistoryElement *history_results_tail = NULL;
- struct HistoryElement *history_element = NULL;
- json_t *history;
- json_t *jresponse;
- int ret;
-
- while (advance (ha,
- pos))
- {
- json_t *trans;
- char *subject;
- const char *sign;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Found transaction over %s from %llu to %llu\n",
- TALER_amount2s (&pos->amount),
- (unsigned long long) pos->debit_account,
- (unsigned long long) pos->credit_account);
-
- if ( (! ( ( (ha->account_number == pos->debit_account) &&
- (0 != (ha->direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
- ( (ha->account_number == pos->credit_account) &&
- (0 != (ha->direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) ||
- ( (0 == (ha->direction & TALER_BANK_DIRECTION_CANCEL)) &&
- (GNUNET_YES == pos->rejected) ) )
- {
- pos = skip (ha,
- pos);
- continue;
- }
-
- GNUNET_asprintf (&subject,
- "%s %s",
- pos->subject,
- pos->exchange_base_url);
- sign =
- (ha->account_number == pos->debit_account)
- ? (pos->rejected ? "cancel-" : "-")
- : (pos->rejected ? "cancel+" : "+");
- trans = json_pack
- ("{s:I, s:o, s:o, s:s, s:I, s:s}",
- "row_id", (json_int_t) pos->row_id,
- "date", GNUNET_JSON_from_time_abs (pos->date),
- "amount", TALER_JSON_from_amount (&pos->amount),
- "sign", sign,
- "counterpart", (json_int_t)
- ( (ha->account_number == pos->debit_account)
- ? pos->credit_account
- : pos->debit_account),
- "wt_subject", subject);
- GNUNET_assert (NULL != trans);
- GNUNET_free (subject);
-
- history_element = GNUNET_new (struct HistoryElement);
- history_element->element = trans;
-
-
- /* XXX: the ordering feature is missing. */
-
- GNUNET_CONTAINER_DLL_insert_tail (history_results_head,
- history_results_tail,
- history_element);
- pos = step (ha, pos);
- }
-
- history = json_array ();
- if (NULL != history_results_head)
- history_element = history_results_head;
-
- while (NULL != history_element)
- {
- json_array_append_new (history,
- history_element->element);
- history_element = history_element->next;
- if (NULL != history_element)
- GNUNET_free_non_null (history_element->prev);
- }
- GNUNET_free_non_null (history_results_tail);
-
- if (0 == json_array_size (history))
- {
- struct MHD_Response *resp;
-
- json_decref (history);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning empty transaction history\n");
- resp = MHD_create_response_from_buffer
- (0,
- "",
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NO_CONTENT,
- resp);
- MHD_destroy_response (resp);
- return ret;
- }
-
- jresponse = json_pack ("{s:o}",
- "data",
- history);
- if (NULL == jresponse)
- {
- GNUNET_break (0);
- return MHD_NO;
- }
-
- /* Finally build response object */
- {
- struct MHD_Response *resp;
- void *json_str;
- size_t json_len;
-
- json_str = json_dumps (jresponse,
- JSON_INDENT(2));
- json_decref (jresponse);
- if (NULL == json_str)
- {
- GNUNET_break (0);
- return MHD_NO;
- }
- json_len = strlen (json_str);
- resp = MHD_create_response_from_buffer (json_len,
- json_str,
- MHD_RESPMEM_MUST_FREE);
- if (NULL == resp)
- {
- GNUNET_break (0);
- free (json_str);
- return MHD_NO;
- }
- (void) MHD_add_response_header (resp,
- MHD_HTTP_HEADER_CONTENT_TYPE,
- "application/json");
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- resp);
- MHD_destroy_response (resp);
- }
- return ret;
-}
-
-
-
-/**
- * Parse URL history arguments, of _both_ APIs:
- * /history and /history-range.
- *
- * @param connection MHD connection.
- * @param function_name name of the caller.
- * @param ha[out] will contain the parsed values.
- * @return GNUNET_OK only if the parsing succeedes.
- */
-static int
-parse_history_common_args (struct MHD_Connection *connection,
- struct HistoryArgs *ha)
-{
- /**
- * @variable
- * Just check if given and == "basic", no need to keep around.
- */
- const char *auth;
-
- /**
- * All those will go into the structure, after parsing.
- */
- const char *direction;
- const char *cancelled;
- const char *ordering;
- const char *account_number;
-
-
- auth = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "auth");
- direction = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "direction");
- cancelled = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "cancelled");
- ordering = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "ordering");
- account_number = MHD_lookup_connection_value
- (connection,
- MHD_GET_ARGUMENT_KIND,
- "account_number");
-
- /* Fail if one of the above failed. */
- if ( (NULL == direction) ||
- (NULL == cancelled) ||
- ( (0 != strcasecmp (cancelled,
- "OMIT")) &&
- (0 != strcasecmp (cancelled,
- "SHOW")) ) ||
- ( (0 != strcasecmp (direction,
- "BOTH")) &&
- (0 != strcasecmp (direction,
- "CREDIT")) &&
- (0 != strcasecmp (direction,
- "DEBIT")) ) ||
- (1 != sscanf (account_number,
- "%llu",
- &ha->account_number)) ||
- ( (NULL == auth) || (0 != strcasecmp (auth,
- "basic")) ) )
- {
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break (0);
- return GNUNET_NO;
- }
-
- if (0 == strcasecmp (direction,
- "CREDIT"))
- {
- ha->direction = TALER_BANK_DIRECTION_CREDIT;
- }
- else if (0 == strcasecmp (direction,
- "DEBIT"))
- {
- ha->direction = TALER_BANK_DIRECTION_DEBIT;
- }
- else if (0 == strcasecmp (direction,
- "BOTH"))
- {
- ha->direction = TALER_BANK_DIRECTION_BOTH;
- }
-
- /* Direction is invalid. */
- else
- {
- GNUNET_break (0);
- return GNUNET_NO;
- }
-
- if (0 == strcasecmp (cancelled,
- "OMIT"))
- {
- /* nothing */
- } else if (0 == strcasecmp (cancelled,
- "SHOW"))
- {
- ha->direction |= TALER_BANK_DIRECTION_CANCEL;
- }
-
- /* Cancel-showing policy is invalid. */
- else
- {
- GNUNET_break (0);
- return GNUNET_NO;
- }
-
- if ((NULL != ordering)
- && 0 == strcmp ("ascending",
- ordering))
- ha->ascending = GNUNET_YES;
- else
- ha->ascending = GNUNET_NO;
-
- return GNUNET_OK;
-}
-
/**
* Handle incoming HTTP request for /history
*
@@ -1201,8 +603,8 @@ handle_history_new (struct TALER_FAKEBANK_Handle *h,
const char *delta;
struct Transaction *pos;
- if (GNUNET_OK != parse_history_common_args (connection,
- &ha))
+ if (GNUNET_OK != TFH_parse_history_common_args (connection,
+ &ha))
{
GNUNET_break (0);
return MHD_NO;
@@ -1259,12 +661,12 @@ handle_history_new (struct TALER_FAKEBANK_Handle *h,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"/history, start row (0 == no transactions exist): %llu\n",
NULL != pos ? pos->row_id : 0);
- return build_history_response (connection,
- pos,
- &ha,
- &handle_history_skip,
- &handle_history_step,
- &handle_history_advance);
+ return TFH_build_history_response (connection,
+ pos,
+ &ha,
+ &TFH_handle_history_skip,
+ &TFH_handle_history_step,
+ &TFH_handle_history_advance);
}
/**
@@ -1288,7 +690,7 @@ handle_history_range (struct TALER_FAKEBANK_Handle *h,
long long unsigned int end_stamp;
struct Transaction *pos;
- if (GNUNET_OK != parse_history_common_args (connection,
+ if (GNUNET_OK != TFH_parse_history_common_args (connection,
&ha))
{
GNUNET_break (0);
@@ -1309,7 +711,7 @@ handle_history_range (struct TALER_FAKEBANK_Handle *h,
&end_stamp)) )
{
GNUNET_break (0);
- return GNUNET_NO;
+ return MHD_NO;
}
hrd.start.abs_value_us = start_stamp * 1000LL * 1000LL;
@@ -1325,336 +727,14 @@ handle_history_range (struct TALER_FAKEBANK_Handle *h,
if (hrd.start.abs_value_us <= pos->date.abs_value_us)
break;
}
- return build_history_response (connection,
- pos,
- &ha,
- &handle_history_range_skip,
- handle_history_range_step,
- &handle_history_range_advance);
-}
-
-/**
- * Handle incoming HTTP request for /history
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param con_cls place to store state, not used
- * @return MHD result code
- */
-static int
-handle_history (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- void **con_cls)
-{
- const char *auth;
- const char *delta;
- const char *start;
- const char *dir;
- const char *acc;
- const char *cancelled;
- const char *ordering;
- unsigned long long account_number;
- unsigned long long start_number;
- long long count;
- enum TALER_BANK_Direction direction;
- struct Transaction *pos;
- json_t *history;
- json_t *jresponse;
- int ret;
- int ascending;
- struct HistoryElement *history_results_head = NULL;
- struct HistoryElement *history_results_tail = NULL;
- struct HistoryElement *history_element = NULL;
-
- auth = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "auth");
- delta = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "delta");
- dir = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "direction");
- cancelled = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "cancelled");
- start = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "start");
- ordering = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "ordering");
- acc = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "account_number");
- if ( (NULL == auth) ||
- (0 != strcasecmp (auth,
- "basic")) ||
- (NULL == acc) ||
- (NULL == delta) )
- {
- /* Invalid request,
- given that this is fakebank we impolitely just
- kill the connection instead of returning a nice error. */
- GNUNET_break (0);
- return MHD_NO;
- }
- start_number = 0;
- if ( (1 != sscanf (delta,
- "%lld",
- &count)) ||
- (1 != sscanf (acc,
- "%llu",
- &account_number)) ||
- ( (NULL != start) &&
- (1 != sscanf (start,
- "%llu",
- &start_number)) ) ||
- (NULL == dir) ||
- (NULL == cancelled) ||
- ( (0 != strcasecmp (cancelled,
- "OMIT")) &&
- (0 != strcasecmp (cancelled,
- "SHOW")) ) ||
- ( (0 != strcasecmp (dir,
- "BOTH")) &&
- (0 != strcasecmp (dir,
- "CREDIT")) &&
- (0 != strcasecmp (dir,
- "DEBIT")) ) )
- {
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break (0);
- return MHD_NO;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Client asked for up to %lld results of type %s for account %llu starting at %llu\n",
- count,
- dir,
- (unsigned long long) account_number,
- start_number);
- if (0 == strcasecmp (dir,
- "CREDIT"))
- {
- direction = TALER_BANK_DIRECTION_CREDIT;
- }
- else if (0 == strcasecmp (dir,
- "DEBIT"))
- {
- direction = TALER_BANK_DIRECTION_DEBIT;
- }
- else if (0 == strcasecmp (dir,
- "BOTH"))
- {
- direction = TALER_BANK_DIRECTION_BOTH;
- }
- else
- {
- GNUNET_assert (0);
- return MHD_NO;
- }
- if (0 == strcasecmp (cancelled,
- "OMIT"))
- {
- /* nothing */
- } else if (0 == strcasecmp (cancelled,
- "SHOW"))
- {
- direction |= TALER_BANK_DIRECTION_CANCEL;
- }
- else
- {
- GNUNET_assert (0);
- return MHD_NO;
- }
-
- if (NULL == start)
- pos = 0 > count ? h->transactions_tail : h->transactions_head;
-
- else if (NULL != h->transactions_head)
- {
- for (pos = h->transactions_head;
- NULL != pos;
- pos = pos->next)
- if (pos->row_id == start_number)
- break;
- if (NULL == pos)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid range specified, transaction %llu not known!\n",
- (unsigned long long) start_number);
- return MHD_NO;
- }
- /* range is exclusive, skip the matching entry */
- if (count > 0)
- pos = pos->next;
- if (count < 0)
- pos = pos->prev;
- }
- else
- {
- /* list is empty */
- pos = NULL;
- }
-
- history = json_array ();
- if ((NULL != ordering)
- && 0 == strcmp ("ascending",
- ordering))
- ascending = GNUNET_YES;
- else
- ascending = GNUNET_NO;
-
- while ( (NULL != pos) &&
- (0 != count) )
- {
- json_t *trans;
- char *subject;
- const char *sign;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Found transaction over %s from %llu to %llu\n",
- TALER_amount2s (&pos->amount),
- (unsigned long long) pos->debit_account,
- (unsigned long long) pos->credit_account);
-
- if ( (! ( ( (account_number == pos->debit_account) &&
- (0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
- ( (account_number == pos->credit_account) &&
- (0 != (direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) ||
- ( (0 == (direction & TALER_BANK_DIRECTION_CANCEL)) &&
- (GNUNET_YES == pos->rejected) ) )
- {
- if (count > 0)
- pos = pos->next;
- if (count < 0)
- pos = pos->prev;
- continue;
- }
-
- GNUNET_asprintf (&subject,
- "%s %s",
- pos->subject,
- pos->exchange_base_url);
- sign =
- (account_number == pos->debit_account)
- ? (pos->rejected ? "cancel-" : "-")
- : (pos->rejected ? "cancel+" : "+");
- trans = json_pack ("{s:I, s:o, s:o, s:s, s:I, s:s}",
- "row_id", (json_int_t) pos->row_id,
- "date", GNUNET_JSON_from_time_abs (pos->date),
- "amount", TALER_JSON_from_amount (&pos->amount),
- "sign", sign,
- "counterpart", (json_int_t) ( (account_number == pos->debit_account)
- ? pos->credit_account
- : pos->debit_account),
- "wt_subject", subject);
- GNUNET_assert (NULL != trans);
- GNUNET_free (subject);
-
- history_element = GNUNET_new (struct HistoryElement);
- history_element->element = trans;
-
- if (((0 < count) && (GNUNET_YES == ascending))
- || ((0 > count) && (GNUNET_NO == ascending)))
- GNUNET_CONTAINER_DLL_insert_tail (history_results_head,
- history_results_tail,
- history_element);
- else
- GNUNET_CONTAINER_DLL_insert (history_results_head,
- history_results_tail,
- history_element);
- if (count > 0)
- {
- pos = pos->next;
- count--;
- }
- if (count < 0)
- {
- pos = pos->prev;
- count++;
- }
- }
-
- if (NULL != history_results_head)
- history_element = history_results_head;
- while (NULL != history_element)
- {
- json_array_append_new (history,
- history_element->element);
- history_element = history_element->next;
- if (NULL != history_element)
- GNUNET_free_non_null (history_element->prev);
- }
- GNUNET_free_non_null (history_results_tail);
-
- if (0 == json_array_size (history))
- {
- struct MHD_Response *resp;
-
- json_decref (history);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning empty transaction history\n");
- resp = MHD_create_response_from_buffer (0,
- "",
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NO_CONTENT,
- resp);
- MHD_destroy_response (resp);
- return ret;
- }
-
- jresponse = json_pack ("{s:o}",
- "data",
- history);
- if (NULL == jresponse)
- {
- GNUNET_break (0);
- return MHD_NO;
- }
-
- /* Finally build response object */
- {
- struct MHD_Response *resp;
- void *json_str;
- size_t json_len;
-
- json_str = json_dumps (jresponse,
- JSON_INDENT(2));
- json_decref (jresponse);
- if (NULL == json_str)
- {
- GNUNET_break (0);
- return MHD_NO;
- }
- json_len = strlen (json_str);
- resp = MHD_create_response_from_buffer (json_len,
- json_str,
- MHD_RESPMEM_MUST_FREE);
- if (NULL == resp)
- {
- GNUNET_break (0);
- free (json_str);
- return MHD_NO;
- }
- (void) MHD_add_response_header (resp,
- MHD_HTTP_HEADER_CONTENT_TYPE,
- "application/json");
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- resp);
- MHD_destroy_response (resp);
- }
- return ret;
+ return TFH_build_history_response (connection,
+ pos,
+ &ha,
+ &TFH_handle_history_range_skip,
+ TFH_handle_history_range_step,
+ &TFH_handle_history_range_advance);
}
-/***********************************
- * End of /history implementation. *
- ***********************************/
-
/**
* Handle incoming HTTP request.
*
diff --git a/src/bank-lib/fakebank.h b/src/bank-lib/fakebank.h
new file mode 100644
index 000000000..4e81b3539
--- /dev/null
+++ b/src/bank-lib/fakebank.h
@@ -0,0 +1,322 @@
+/*
+ This file is part of TALER
+ (C) 2016, 2017, 2018 Inria and GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file bank-lib/fakebank.h
+ * @brief definitions for the "/history[-range]" layer.
+ * @author Marcello Stanisci <stanisci.m@gmail.com>
+ */
+
+#ifndef FAKEBANK_H
+#define FAKEBANK_H
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_bank_service.h"
+
+/**
+ * Details about a transcation we (as the simulated bank) received.
+ */
+struct Transaction
+{
+ /**
+ * We store transactions in a DLL.
+ */
+ struct Transaction *next;
+
+ /**
+ * We store transactions in a DLL.
+ */
+ struct Transaction *prev;
+
+ /**
+ * Amount to be transferred.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Account to debit.
+ */
+ uint64_t debit_account;
+
+ /**
+ * Account to credit.
+ */
+ uint64_t credit_account;
+
+ /**
+ * Subject of the transfer.
+ */
+ char *subject;
+
+ /**
+ * Base URL of the exchange.
+ */
+ char *exchange_base_url;
+
+ /**
+ * When did the transaction happen?
+ */
+ struct GNUNET_TIME_Absolute date;
+
+ /**
+ * Number of this transaction.
+ */
+ uint64_t row_id;
+
+ /**
+ * Flag set if the transfer was rejected.
+ */
+ int rejected;
+
+ /**
+ * Has this transaction been subjected to #TALER_FAKEBANK_check()
+ * and should thus no longer be counted in
+ * #TALER_FAKEBANK_check_empty()?
+ */
+ int checked;
+};
+
+
+/******************************************
+ * Definitions for "/history" start here. *
+ ******************************************/
+
+/**
+ * Needed to implement ascending/descending ordering
+ * of /history results.
+ */
+struct HistoryElement
+{
+
+ /**
+ * History JSON element.
+ */
+ json_t *element;
+
+ /**
+ * Previous element.
+ */
+ struct HistoryElement *prev;
+
+ /**
+ * Next element.
+ */
+ struct HistoryElement *next;
+};
+
+
+/**
+ * Values to implement the "/history-range" range.
+ */
+struct HistoryRangeDates
+{
+ /**
+ * Oldest row in the results.
+ */
+ struct GNUNET_TIME_Absolute start;
+
+ /**
+ * Youngest row in the results.
+ */
+ struct GNUNET_TIME_Absolute end;
+};
+
+/**
+ * Values to implement the "/history" range.
+ */
+struct HistoryRangeIds
+{
+
+ /**
+ * (Exclusive) row ID for the result set.
+ */
+ unsigned long long start;
+
+ /**
+ * How many transactions we want in the result set. If
+ * negative/positive, @a start will be strictly younger/older
+ * of any element in the result set.
+ */
+ long long count;
+};
+
+
+/**
+ * This is the "base" structure for both the /history and the
+ * /history-range API calls.
+ */
+struct HistoryArgs
+{
+
+ /**
+ * Direction asked by the client: CREDIT / DEBIT / BOTH / CANCEL.
+ */
+ enum TALER_BANK_Direction direction;
+
+ /**
+ * Bank account number of the requesting client.
+ */
+ unsigned long long account_number;
+
+ /**
+ * Ordering of the results.
+ */
+ unsigned int ascending;
+
+ /**
+ * Overloaded type that indicates the "range" to be returned
+ * in the results; this can be either a date range, or a
+ * starting row id + the count.
+ */
+ void *range;
+};
+
+
+
+/**
+ * Type for a function that decides whether or not
+ * the history-building loop should iterate once again.
+ * Typically called from inside the 'while' condition.
+ *
+ * @param ha history argument.
+ * @param pos current position.
+ * @return GNUNET_YES if the iteration shuold go on.
+ */
+typedef int (*CheckAdvance)
+ (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/**
+ * Type for a function that steps over the next element
+ * in the list of all transactions, after the current @a pos
+ * _got_ included in the result.
+ */
+typedef struct Transaction * (*Step)
+ (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/*
+ * Type for a function that steps over the next element
+ * in the list of all transactions, after the current @a pos
+ * did _not_ get included in the result.
+ */
+typedef struct Transaction * (*Skip)
+ (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/**
+ * Actual history response builder.
+ *
+ * @param pos first (included) element in the result set.
+ * @param ha history arguments.
+ * @param caller_name which function is building the history.
+ * @return MHD_YES / MHD_NO, after having enqueued the response
+ * object into MHD.
+ */
+int
+TFH_build_history_response (struct MHD_Connection *connection,
+ struct Transaction *pos,
+ struct HistoryArgs *ha,
+ Skip skip,
+ Step step,
+ CheckAdvance advance);
+
+
+/**
+ * Parse URL history arguments, of _both_ APIs:
+ * /history and /history-range.
+ *
+ * @param connection MHD connection.
+ * @param function_name name of the caller.
+ * @param ha[out] will contain the parsed values.
+ * @return GNUNET_OK only if the parsing succeedes.
+ */
+int
+TFH_parse_history_common_args (struct MHD_Connection *connection,
+ struct HistoryArgs *ha);
+
+
+/**
+ * Decides whether the history builder will advance or not
+ * to the next element.
+ *
+ * @param ha history args
+ * @return GNUNET_YES/NO to advance/not-advance.
+ */
+int
+TFH_handle_history_advance (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/**
+ * Iterates on the "next" element to be processed. To
+ * be used when the current element does not get inserted in
+ * the result.
+ *
+ * @param ha history arguments.
+ * @param pos current element being processed.
+ * @return the next element to be processed.
+ */
+struct Transaction *
+TFH_handle_history_skip (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/**
+ * Iterates on the "next" element to be processed. To
+ * be used when the current element _gets_ inserted in the result.
+ *
+ * @param ha history arguments.
+ * @param pos current element being processed.
+ * @return the next element to be processed.
+ */
+struct Transaction *
+TFH_handle_history_step (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/**
+ * Decides whether the history builder will advance or not
+ * to the next element.
+ *
+ * @param ha history args
+ * @return GNUNET_YES/NO to advance/not-advance.
+ */
+int
+TFH_handle_history_range_advance (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/**
+ * Iterates towards the "next" element to be processed. To
+ * be used when the current element does not get inserted in
+ * the result.
+ *
+ * @param ha history arguments.
+ * @param pos current element being processed.
+ * @return the next element to be processed.
+ */
+struct Transaction *
+TFH_handle_history_range_skip (const struct HistoryArgs *ha,
+ const struct Transaction *pos);
+
+/**
+ * Iterates on the "next" element to be processed. To
+ * be used when the current element _gets_ inserted in the result.
+ * Same implementation of the "skip" counterpart, as /history-range
+ * does not have the notion of count/delta.
+ */
+Step TFH_handle_history_range_step;
+#endif
diff --git a/src/bank-lib/fakebank_history.c b/src/bank-lib/fakebank_history.c
new file mode 100644
index 000000000..17960a4e5
--- /dev/null
+++ b/src/bank-lib/fakebank_history.c
@@ -0,0 +1,427 @@
+/*
+ This file is part of TALER
+ (C) 2016, 2017, 2018 Inria and GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file bank-lib/fakebank_history.c
+ * @brief definitions for the "/history[-range]" layer.
+ * @author Marcello Stanisci <stanisci.m@gmail.com>
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_json_lib.h"
+#include "fakebank.h"
+
+/**
+ * Decides whether the history builder will advance or not
+ * to the next element.
+ *
+ * @param ha history args
+ * @return GNUNET_YES/NO to advance/not-advance.
+ */
+int
+TFH_handle_history_advance (const struct HistoryArgs *ha,
+ const struct Transaction *pos)
+{
+ const struct HistoryRangeIds *hri = ha->range;
+
+ return (NULL != pos) && (0 != hri->count);
+}
+
+
+/**
+ * Iterates on the "next" element to be processed. To
+ * be used when the current element does not get inserted in
+ * the result.
+ *
+ * @param ha history arguments.
+ * @param pos current element being processed.
+ * @return the next element to be processed.
+ */
+struct Transaction *
+TFH_handle_history_skip (const struct HistoryArgs *ha,
+ const struct Transaction *pos)
+{
+ const struct HistoryRangeIds *hri = ha->range;
+
+ if (hri->count > 0)
+ return pos->next;
+ if (hri->count < 0)
+ return pos->prev;
+ return NULL;
+}
+
+
+/**
+ * Iterates on the "next" element to be processed. To
+ * be used when the current element _gets_ inserted in the result.
+ *
+ * @param ha history arguments.
+ * @param pos current element being processed.
+ * @return the next element to be processed.
+ */
+struct Transaction *
+TFH_handle_history_step (const struct HistoryArgs *ha,
+ const struct Transaction *pos)
+{
+ struct HistoryRangeIds *hri = ha->range;
+
+ if (hri->count > 0)
+ {
+ hri->count--;
+ return pos->next;
+ }
+ if (hri->count < 0)
+ {
+ hri->count++;
+ return pos->prev;
+ }
+ return NULL;
+}
+
+
+/**
+ * Decides whether the history builder will advance or not
+ * to the next element.
+ *
+ * @param ha history args
+ * @return GNUNET_YES/NO to advance/not-advance.
+ */
+int
+TFH_handle_history_range_advance (const struct HistoryArgs *ha,
+ const struct Transaction *pos)
+{
+ const struct HistoryRangeDates *hrd = ha->range;
+
+ if ( (NULL != pos) &&
+ (pos->date.abs_value_us <= hrd->end.abs_value_us) )
+ return GNUNET_YES;
+
+ return GNUNET_NO;
+}
+
+
+/**
+ * Iterates towards the "next" element to be processed. To
+ * be used when the current element does not get inserted in
+ * the result.
+ *
+ * @param ha history arguments.
+ * @param pos current element being processed.
+ * @return the next element to be processed.
+ */
+struct Transaction *
+TFH_handle_history_range_skip (const struct HistoryArgs *ha,
+ const struct Transaction *pos)
+{
+ /* Transactions
+ * are stored from "head"/older to "tail"/younger. */
+ return pos->next;
+}
+
+/**
+ * Iterates on the "next" element to be processed. To
+ * be used when the current element _gets_ inserted in the result.
+ * Same implementation of the "skip" counterpart, as /history-range
+ * does not have the notion of count/delta.
+ */
+Step TFH_handle_history_range_step = &TFH_handle_history_range_skip;
+
+/**
+ * Actual history response builder.
+ *
+ * @param pos first (included) element in the result set.
+ * @param ha history arguments.
+ * @param caller_name which function is building the history.
+ * @return MHD_YES / MHD_NO, after having enqueued the response
+ * object into MHD.
+ */
+int
+TFH_build_history_response (struct MHD_Connection *connection,
+ struct Transaction *pos,
+ struct HistoryArgs *ha,
+ Skip skip,
+ Step step,
+ CheckAdvance advance)
+{
+
+ struct HistoryElement *history_results_head = NULL;
+ struct HistoryElement *history_results_tail = NULL;
+ struct HistoryElement *history_element = NULL;
+ json_t *history;
+ json_t *jresponse;
+ int ret;
+
+ while (advance (ha,
+ pos))
+ {
+ json_t *trans;
+ char *subject;
+ const char *sign;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Found transaction over %s from %llu to %llu\n",
+ TALER_amount2s (&pos->amount),
+ (unsigned long long) pos->debit_account,
+ (unsigned long long) pos->credit_account);
+
+ if ( (! ( ( (ha->account_number == pos->debit_account) &&
+ (0 != (ha->direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
+ ( (ha->account_number == pos->credit_account) &&
+ (0 != (ha->direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) ||
+ ( (0 == (ha->direction & TALER_BANK_DIRECTION_CANCEL)) &&
+ (GNUNET_YES == pos->rejected) ) )
+ {
+ pos = skip (ha,
+ pos);
+ continue;
+ }
+
+ GNUNET_asprintf (&subject,
+ "%s %s",
+ pos->subject,
+ pos->exchange_base_url);
+ sign =
+ (ha->account_number == pos->debit_account)
+ ? (pos->rejected ? "cancel-" : "-")
+ : (pos->rejected ? "cancel+" : "+");
+ trans = json_pack
+ ("{s:I, s:o, s:o, s:s, s:I, s:s}",
+ "row_id", (json_int_t) pos->row_id,
+ "date", GNUNET_JSON_from_time_abs (pos->date),
+ "amount", TALER_JSON_from_amount (&pos->amount),
+ "sign", sign,
+ "counterpart", (json_int_t)
+ ( (ha->account_number == pos->debit_account)
+ ? pos->credit_account
+ : pos->debit_account),
+ "wt_subject", subject);
+ GNUNET_assert (NULL != trans);
+ GNUNET_free (subject);
+
+ history_element = GNUNET_new (struct HistoryElement);
+ history_element->element = trans;
+
+
+ /* XXX: the ordering feature is missing. */
+
+ GNUNET_CONTAINER_DLL_insert_tail (history_results_head,
+ history_results_tail,
+ history_element);
+ pos = step (ha, pos);
+ }
+
+ history = json_array ();
+ if (NULL != history_results_head)
+ history_element = history_results_head;
+
+ while (NULL != history_element)
+ {
+ json_array_append_new (history,
+ history_element->element);
+ history_element = history_element->next;
+ if (NULL != history_element)
+ GNUNET_free_non_null (history_element->prev);
+ }
+ GNUNET_free_non_null (history_results_tail);
+
+ if (0 == json_array_size (history))
+ {
+ struct MHD_Response *resp;
+
+ json_decref (history);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning empty transaction history\n");
+ resp = MHD_create_response_from_buffer
+ (0,
+ "",
+ MHD_RESPMEM_PERSISTENT);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NO_CONTENT,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+ }
+
+ jresponse = json_pack ("{s:o}",
+ "data",
+ history);
+ if (NULL == jresponse)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+
+ /* Finally build response object */
+ {
+ struct MHD_Response *resp;
+ void *json_str;
+ size_t json_len;
+
+ json_str = json_dumps (jresponse,
+ JSON_INDENT(2));
+ json_decref (jresponse);
+ if (NULL == json_str)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ json_len = strlen (json_str);
+ resp = MHD_create_response_from_buffer (json_len,
+ json_str,
+ MHD_RESPMEM_MUST_FREE);
+ if (NULL == resp)
+ {
+ GNUNET_break (0);
+ free (json_str);
+ return MHD_NO;
+ }
+ (void) MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ "application/json");
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ }
+ return ret;
+}
+
+/**
+ * Parse URL history arguments, of _both_ APIs:
+ * /history and /history-range.
+ *
+ * @param connection MHD connection.
+ * @param function_name name of the caller.
+ * @param ha[out] will contain the parsed values.
+ * @return GNUNET_OK only if the parsing succeedes.
+ */
+int
+TFH_parse_history_common_args (struct MHD_Connection *connection,
+ struct HistoryArgs *ha)
+{
+ /**
+ * @variable
+ * Just check if given and == "basic", no need to keep around.
+ */
+ const char *auth;
+
+ /**
+ * All those will go into the structure, after parsing.
+ */
+ const char *direction;
+ const char *cancelled;
+ const char *ordering;
+ const char *account_number;
+
+
+ auth = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "auth");
+ direction = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "direction");
+ cancelled = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "cancelled");
+ ordering = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "ordering");
+ account_number = MHD_lookup_connection_value
+ (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "account_number");
+
+ /* Fail if one of the above failed. */
+ if ( (NULL == direction) ||
+ (NULL == cancelled) ||
+ ( (0 != strcasecmp (cancelled,
+ "OMIT")) &&
+ (0 != strcasecmp (cancelled,
+ "SHOW")) ) ||
+ ( (0 != strcasecmp (direction,
+ "BOTH")) &&
+ (0 != strcasecmp (direction,
+ "CREDIT")) &&
+ (0 != strcasecmp (direction,
+ "DEBIT")) ) ||
+ (1 != sscanf (account_number,
+ "%llu",
+ &ha->account_number)) ||
+ ( (NULL == auth) || (0 != strcasecmp (auth,
+ "basic")) ) )
+ {
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+
+ if (0 == strcasecmp (direction,
+ "CREDIT"))
+ {
+ ha->direction = TALER_BANK_DIRECTION_CREDIT;
+ }
+ else if (0 == strcasecmp (direction,
+ "DEBIT"))
+ {
+ ha->direction = TALER_BANK_DIRECTION_DEBIT;
+ }
+ else if (0 == strcasecmp (direction,
+ "BOTH"))
+ {
+ ha->direction = TALER_BANK_DIRECTION_BOTH;
+ }
+
+ /* Direction is invalid. */
+ else
+ {
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+
+ if (0 == strcasecmp (cancelled,
+ "OMIT"))
+ {
+ /* nothing */
+ } else if (0 == strcasecmp (cancelled,
+ "SHOW"))
+ {
+ ha->direction |= TALER_BANK_DIRECTION_CANCEL;
+ }
+
+ /* Cancel-showing policy is invalid. */
+ else
+ {
+ GNUNET_break (0);
+ return GNUNET_NO;
+ }
+
+ if ((NULL != ordering)
+ && 0 == strcmp ("ascending",
+ ordering))
+ ha->ascending = GNUNET_YES;
+ else
+ ha->ascending = GNUNET_NO;
+
+ return GNUNET_OK;
+}
+
+