This commit is contained in:
Christian Grothoff 2017-12-06 19:24:00 +01:00
parent 5540747ca2
commit 042616899f
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
19 changed files with 1073 additions and 112 deletions

View File

@ -830,6 +830,7 @@ check_exchange_wire_out ()
* transactions). * transactions).
* *
* @param cls closure * @param cls closure
* @param ec error code in case something went wrong
* @param dir direction of the transfer * @param dir direction of the transfer
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off * @param row_off_size number of bytes in @a row_off
@ -838,6 +839,7 @@ check_exchange_wire_out ()
*/ */
static int static int
history_debit_cb (void *cls, history_debit_cb (void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
const void *row_off, const void *row_off,
size_t row_off_size, size_t row_off_size,
@ -848,6 +850,13 @@ history_debit_cb (void *cls,
if (TALER_BANK_DIRECTION_NONE == dir) if (TALER_BANK_DIRECTION_NONE == dir)
{ {
if (TALER_EC_NONE != ec)
{
/* FIXME: log properly to audit report! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Error fetching history: %u!\n",
(unsigned int) ec);
}
/* end of iteration, now check wire_out to see /* end of iteration, now check wire_out to see
if it matches #out_map */ if it matches #out_map */
hh = NULL; hh = NULL;
@ -1069,6 +1078,7 @@ conclude_credit_history ()
* transactions). * transactions).
* *
* @param cls closure * @param cls closure
* @param ec error code in case something went wrong
* @param dir direction of the transfer * @param dir direction of the transfer
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off * @param row_off_size number of bytes in @a row_off
@ -1077,6 +1087,7 @@ conclude_credit_history ()
*/ */
static int static int
history_credit_cb (void *cls, history_credit_cb (void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
const void *row_off, const void *row_off,
size_t row_off_size, size_t row_off_size,
@ -1087,6 +1098,13 @@ history_credit_cb (void *cls,
if (TALER_BANK_DIRECTION_NONE == dir) if (TALER_BANK_DIRECTION_NONE == dir)
{ {
if (TALER_EC_NONE != ec)
{
/* FIXME: log properly to audit report! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Error fetching history: %u!\n",
(unsigned int) ec);
}
/* end of operation */ /* end of operation */
hh = NULL; hh = NULL;
conclude_credit_history (); conclude_credit_history ();

View File

@ -17,7 +17,8 @@ libtalerbank_la_LDFLAGS = \
libtalerbank_la_SOURCES = \ libtalerbank_la_SOURCES = \
bank_api_admin.c \ bank_api_admin.c \
bank_api_common.c bank_api_common.h \ bank_api_common.c bank_api_common.h \
bank_api_history.c bank_api_history.c \
bank_api_reject.c
libtalerbank_la_LIBADD = \ libtalerbank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2015, 2016, 2017 GNUnet e.V. Copyright (C) 2015, 2016, 2017 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the 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 terms of the GNU General Public License as published by the Free Software
@ -79,11 +79,13 @@ handle_admin_add_incoming_finished (void *cls,
{ {
struct TALER_BANK_AdminAddIncomingHandle *aai = cls; struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
uint64_t serial_id = UINT64_MAX; uint64_t serial_id = UINT64_MAX;
enum TALER_ErrorCode ec;
aai->job = NULL; aai->job = NULL;
switch (response_code) switch (response_code)
{ {
case 0: case 0:
ec = TALER_EC_INVALID_RESPONSE;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
@ -100,29 +102,36 @@ handle_admin_add_incoming_finished (void *cls,
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
response_code = 0; response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break; break;
} }
ec = TALER_EC_NONE;
} }
break; break;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy /* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */ (or API version conflict); just pass JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_FORBIDDEN: case MHD_HTTP_FORBIDDEN:
/* Access denied */ /* Access denied */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_UNAUTHORIZED: case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says one of the signatures is /* Nothing really to verify, bank says one of the signatures is
invalid; as we checked them, this should never happen, we invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */ should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never /* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */ happen, we should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API /* Server had an internal issue; we should retry, but this API
leaves this to the application */ leaves this to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
@ -130,11 +139,13 @@ handle_admin_add_incoming_finished (void *cls,
"Unexpected response code %u\n", "Unexpected response code %u\n",
(unsigned int) response_code); (unsigned int) response_code);
GNUNET_break (0); GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (json);
response_code = 0; response_code = 0;
break; break;
} }
aai->cb (aai->cb_cls, aai->cb (aai->cb_cls,
response_code, response_code,
ec,
serial_id, serial_id,
json); json);
TALER_BANK_admin_add_incoming_cancel (aai); TALER_BANK_admin_add_incoming_cancel (aai);
@ -151,7 +162,7 @@ handle_admin_add_incoming_finished (void *cls,
* @param bank_base_url URL of the bank (used to execute this request) * @param bank_base_url URL of the bank (used to execute this request)
* @param auth authentication data to send to the bank * @param auth authentication data to send to the bank
* @param exchange_base_url base URL of the exchange (for tracking) * @param exchange_base_url base URL of the exchange (for tracking)
* @param wtid wire transfer identifier for the transfer * @param subject wire transfer subject for the transfer
* @param amount amount that was deposited * @param amount amount that was deposited
* @param debit_account_no account number to withdraw from (53 bits at most) * @param debit_account_no account number to withdraw from (53 bits at most)
* @param credit_account_no account number to deposit into (53 bits at most) * @param credit_account_no account number to deposit into (53 bits at most)
@ -166,7 +177,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url, const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth, const struct TALER_BANK_AuthenticationData *auth,
const char *exchange_base_url, const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid, const char *subject,
const struct TALER_Amount *amount, const struct TALER_Amount *amount,
uint64_t debit_account_no, uint64_t debit_account_no,
uint64_t credit_account_no, uint64_t credit_account_no,
@ -182,10 +193,10 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
GNUNET_break (0); GNUNET_break (0);
return NULL; return NULL;
} }
admin_obj = json_pack ("{s:{s:s}, s:s, s:o, s:o, s:I, s:I}", admin_obj = json_pack ("{s:{s:s}, s:s, s:s, s:o, s:I, s:I}",
"auth", "type", "basic", "auth", "type", "basic",
"exchange_url", exchange_base_url, "exchange_url", exchange_base_url,
"wtid", GNUNET_JSON_from_data_auto (wtid), "subject", subject,
"amount", TALER_JSON_from_amount (amount), "amount", TALER_JSON_from_amount (amount),
"debit_account", (json_int_t) debit_account_no, "debit_account", (json_int_t) debit_account_no,
"credit_account", (json_int_t) credit_account_no); "credit_account", (json_int_t) credit_account_no);

View File

@ -111,5 +111,33 @@ TALER_BANK_path_to_url_ (const char *u,
} }
/**
* Parse error code given in @a json.
*
* @param json the json to parse
* @return error code, or #TALER_EC_INVALID if not found
*/
enum TALER_ErrorCode
TALER_BANK_parse_ec_ (const json_t *json)
{
uint32_t ec;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint32 ("ec",
&ec),
GNUNET_JSON_spec_end()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return TALER_EC_INVALID;
}
return (enum TALER_ErrorCode) ec;
}
/* end of bank_api_common.c */ /* end of bank_api_common.c */

View File

@ -51,4 +51,14 @@ TALER_BANK_path_to_url_ (const char *u,
const char *path); const char *path);
/**
* Parse error code given in @a json.
*
* @param json the json to parse
* @return error code, or #TALER_EC_INVALID if not found
*/
enum TALER_ErrorCode
TALER_BANK_parse_ec_ (const json_t *json);
#endif #endif

View File

@ -118,22 +118,37 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (0 == strcasecmp (sign,
"+"))
direction = TALER_BANK_DIRECTION_CREDIT;
else if (0 == strcasecmp (sign,
"-"))
direction = TALER_BANK_DIRECTION_DEBIT;
else if (0 == strcasecmp (sign,
"cancel+"))
direction = TALER_BANK_DIRECTION_CREDIT | TALER_BANK_DIRECTION_CANCEL;
else if (0 == strcasecmp (sign,
"cancel-"))
direction = TALER_BANK_DIRECTION_DEBIT | TALER_BANK_DIRECTION_CANCEL;
else
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (hist_spec);
return GNUNET_SYSERR;
}
td.account_details = json_pack ("{s:s, s:s, s:I}", td.account_details = json_pack ("{s:s, s:s, s:I}",
"type", "test", "type", "test",
"bank_uri", hh->bank_base_url, "bank_uri", hh->bank_base_url,
"account_number", (json_int_t) other_account); "account_number", (json_int_t) other_account);
direction = (0 == strcasecmp (sign,
"+"))
? TALER_BANK_DIRECTION_CREDIT
: TALER_BANK_DIRECTION_DEBIT;
hh->hcb (hh->hcb_cls, hh->hcb (hh->hcb_cls,
MHD_HTTP_OK, MHD_HTTP_OK,
TALER_EC_NONE,
direction, direction,
serial_id, serial_id,
&td, &td,
transaction); transaction);
GNUNET_JSON_parse_free (hist_spec);
json_decref (td.account_details); json_decref (td.account_details);
GNUNET_JSON_parse_free (hist_spec);
} }
return GNUNET_OK; return GNUNET_OK;
} }
@ -153,6 +168,7 @@ handle_history_finished (void *cls,
const json_t *json) const json_t *json)
{ {
struct TALER_BANK_HistoryHandle *hh = cls; struct TALER_BANK_HistoryHandle *hh = cls;
enum TALER_ErrorCode ec;
hh->job = NULL; hh->job = NULL;
switch (response_code) switch (response_code)
@ -166,31 +182,38 @@ handle_history_finished (void *cls,
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
response_code = 0; response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break; break;
} }
response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */ response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
break; break;
case MHD_HTTP_NO_CONTENT: case MHD_HTTP_NO_CONTENT:
ec = TALER_EC_NONE;
break; break;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy /* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */ (or API version conflict); just pass JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_FORBIDDEN: case MHD_HTTP_FORBIDDEN:
/* Access denied */ /* Access denied */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_UNAUTHORIZED: case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says one of the signatures is /* Nothing really to verify, bank says one of the signatures is
invalid; as we checked them, this should never happen, we invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */ should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never /* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */ happen, we should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API /* Server had an internal issue; we should retry, but this API
leaves this to the application */ leaves this to the application */
ec = TALER_BANK_parse_ec_ (json);
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
@ -198,11 +221,13 @@ handle_history_finished (void *cls,
"Unexpected response code %u\n", "Unexpected response code %u\n",
(unsigned int) response_code); (unsigned int) response_code);
GNUNET_break (0); GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (json);
response_code = 0; response_code = 0;
break; break;
} }
hh->hcb (hh->hcb_cls, hh->hcb (hh->hcb_cls,
response_code, response_code,
ec,
TALER_BANK_DIRECTION_NONE, TALER_BANK_DIRECTION_NONE,
0LLU, 0LLU,
NULL, NULL,
@ -243,6 +268,8 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
struct TALER_BANK_HistoryHandle *hh; struct TALER_BANK_HistoryHandle *hh;
CURL *eh; CURL *eh;
char *url; char *url;
const char *dir;
const char *can;
if (0 == num_results) if (0 == num_results)
{ {
@ -254,36 +281,42 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
GNUNET_break (0); GNUNET_break (0);
return NULL; return NULL;
} }
dir = NULL;
if (TALER_BANK_DIRECTION_BOTH == (TALER_BANK_DIRECTION_BOTH & direction))
dir = "both";
else if (TALER_BANK_DIRECTION_CREDIT == (TALER_BANK_DIRECTION_CREDIT & direction))
dir = "credit";
else if (TALER_BANK_DIRECTION_DEBIT == (TALER_BANK_DIRECTION_BOTH & direction))
dir = "debit";
if (NULL == dir)
{
GNUNET_break (0);
return NULL;
}
if (TALER_BANK_DIRECTION_CANCEL == (TALER_BANK_DIRECTION_CANCEL & direction))
can = "show";
else
can = "omit";
if (UINT64_MAX == start_row) if (UINT64_MAX == start_row)
{ {
if (TALER_BANK_DIRECTION_BOTH == direction) GNUNET_asprintf (&url,
GNUNET_asprintf (&url, "/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s",
"/history?auth=basic&account_number=%llu&delta=%lld", (unsigned long long) account_number,
(unsigned long long) account_number, (long long) num_results,
(long long) num_results); dir,
else can);
GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s",
(unsigned long long) account_number,
(long long) num_results,
(TALER_BANK_DIRECTION_CREDIT == direction) ? "credit" : "debit");
} }
else else
{ {
if (TALER_BANK_DIRECTION_BOTH == direction) GNUNET_asprintf (&url,
GNUNET_asprintf (&url, "/history?auth=basic&account_number=%llu&delta=%lld&start=%llu&direction=%s&cancelled=%s",
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu", (unsigned long long) account_number,
(unsigned long long) account_number, (long long) num_results,
(long long) num_results, (unsigned long long) start_row,
(unsigned long long) start_row); dir,
else can);
GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu&direction=%s",
(unsigned long long) account_number,
(long long) num_results,
(unsigned long long) start_row,
(TALER_BANK_DIRECTION_CREDIT == direction) ? "credit" : "debit");
} }
hh = GNUNET_new (struct TALER_BANK_HistoryHandle); hh = GNUNET_new (struct TALER_BANK_HistoryHandle);

View File

@ -0,0 +1,245 @@
/*
This file is part of TALER
Copyright (C) 2015, 2016, 2017 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file bank-lib/bank_api_reject.c
* @brief Implementation of the /reject request of the bank's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include "bank_api_common.h"
#include <microhttpd.h> /* just for HTTP status codes */
#include "taler_signatures.h"
/**
* @brief A /reject Handle
*/
struct TALER_BANK_RejectHandle
{
/**
* The url for this request.
*/
char *request_url;
/**
* JSON encoding of the request to POST.
*/
char *json_enc;
/**
* Handle for the request.
*/
struct GNUNET_CURL_Job *job;
/**
* HTTP authentication-related headers for the request.
*/
struct curl_slist *authh;
/**
* Function to call with the result.
*/
TALER_BANK_RejectResultCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
};
/**
* Function called when we're done processing the
* HTTP /reject request.
*
* @param cls the `struct TALER_BANK_RejectHandle`
* @param response_code HTTP response code, 0 on error
* @param json parsed JSON result, NULL on error
*/
static void
handle_reject_finished (void *cls,
long response_code,
const json_t *json)
{
struct TALER_BANK_RejectHandle *rh = cls;
enum TALER_ErrorCode ec;
rh->job = NULL;
switch (response_code)
{
case 0:
ec = TALER_EC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break;
case MHD_HTTP_NO_CONTENT:
ec = TALER_EC_NONE;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break;
case MHD_HTTP_FORBIDDEN:
/* Access denied */
ec = TALER_BANK_parse_ec_ (json);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (json);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
ec = TALER_BANK_parse_ec_ (json);
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (json);
response_code = 0;
break;
}
rh->cb (rh->cb_cls,
response_code,
ec);
TALER_BANK_reject_cancel (rh);
}
/**
* Request rejection of a wire transfer, marking it as cancelled and voiding
* its effects.
*
* @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this request)
* @param auth authentication data to use
* @param account_number which account number should we query
* @param rowid transfer to reject
* @param rcb the callback to call with the operation result
* @param rcb_cls closure for @a rcb
* @return NULL
* if the inputs are invalid.
* In this case, the callback is not called.
*/
struct TALER_BANK_RejectHandle *
TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth,
uint64_t account_number,
uint64_t rowid,
TALER_BANK_RejectResultCallback rcb,
void *rcb_cls)
{
struct TALER_BANK_RejectHandle *rh;
json_t *reject_obj;
CURL *eh;
reject_obj = json_pack ("{s:{s:s}, s:I, s:I}",
"auth", "type", "basic",
"row_id", (json_int_t) rowid,
"credit_account", (json_int_t) account_number);
if (NULL == reject_obj)
{
GNUNET_break (0);
return NULL;
}
rh = GNUNET_new (struct TALER_BANK_RejectHandle);
rh->cb = rcb;
rh->cb_cls = rcb_cls;
rh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
"/reject");
rh->authh = TALER_BANK_make_auth_header_ (auth);
/* Append content type header here, can't do it in GNUNET_CURL_job_add
as that would override the CURLOPT_HTTPHEADER instead of appending. */
{
struct curl_slist *ext;
ext = curl_slist_append (rh->authh,
"Content-Type: application/json");
if (NULL == ext)
GNUNET_break (0);
else
rh->authh = ext;
}
eh = curl_easy_init ();
GNUNET_assert (NULL != (rh->json_enc =
json_dumps (reject_obj,
JSON_COMPACT)));
json_decref (reject_obj);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_HTTPHEADER,
rh->authh));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
rh->request_url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDS,
rh->json_enc));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDSIZE,
strlen (rh->json_enc)));
rh->job = GNUNET_CURL_job_add (ctx,
eh,
GNUNET_NO,
&handle_reject_finished,
rh);
return rh;
}
/**
* Cancel an reject request. This function cannot be used on a request
* handle if the response was is already served for it.
*
* @param rh the reject request handle
*/
void
TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh)
{
if (NULL != rh->job)
{
GNUNET_CURL_job_cancel (rh->job);
rh->job = NULL;
}
curl_slist_free_all (rh->authh);
GNUNET_free (rh->request_url);
GNUNET_free (rh->json_enc);
GNUNET_free (rh);
}
/* end of bank_api_reject.c */

View File

@ -81,6 +81,11 @@ struct Transaction
* Number of this transaction. * Number of this transaction.
*/ */
uint64_t serial_id; uint64_t serial_id;
/**
* Flag set if the transfer was rejected.
*/
int rejected;
}; };
@ -219,6 +224,31 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
} }
/**
* Reject incoming wire transfer to account @a credit_account
* as identified by @a rowid.
*
* @param h fake bank handle
* @param rowid identifies transfer to reject
* @param credit_account account number of owner of credited account
* @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not found
*/
int
TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
uint64_t rowid,
uint64_t credit_account)
{
for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
if ( (t->serial_id == rowid) &&
(t->credit_account == credit_account) )
{
t->rejected = GNUNET_YES;
return GNUNET_YES;
}
return GNUNET_NO;
}
/** /**
* Check that no wire transfers were ordered (or at least none * Check that no wire transfers were ordered (or at least none
* that have not been taken care of via #TALER_FAKEBANK_check()). * that have not been taken care of via #TALER_FAKEBANK_check()).
@ -287,6 +317,62 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
} }
/**
* Create and queue a bank error message with the HTTP response
* code @a response_code on connection @a connection.
*
* @param connection where to queue the reply
* @param response_code http status code to use
* @param ec taler error code to use
* @param message human readable error message
* @return MHD status code
*/
static int
create_bank_error (struct MHD_Connection *connection,
unsigned int response_code,
enum TALER_ErrorCode ec,
const char *message)
{
json_t *json;
struct MHD_Response *resp;
void *json_str;
size_t json_len;
int ret;
json = json_pack ("{s:s, s:I}",
"error",
message,
"ec",
(json_int_t) ec);
json_str = json_dumps (json,
JSON_INDENT(2));
json_decref (json);
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,
response_code,
resp);
MHD_destroy_response (resp);
return ret;
}
/** /**
* Function called whenever MHD is done with a request. If the * Function called whenever MHD is done with a request. If the
* request was a POST, we may have stored a `struct Buffer *` in the * request was a POST, we may have stored a `struct Buffer *` in the
@ -359,13 +445,13 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
break; break;
} }
{ {
const char *wtid; const char *subject;
uint64_t debit_account; uint64_t debit_account;
uint64_t credit_account; uint64_t credit_account;
const char *base_url; const char *base_url;
struct TALER_Amount amount; struct TALER_Amount amount;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("wtid", &wtid), GNUNET_JSON_spec_string ("subject", &subject),
GNUNET_JSON_spec_uint64 ("debit_account", &debit_account), GNUNET_JSON_spec_uint64 ("debit_account", &debit_account),
GNUNET_JSON_spec_uint64 ("credit_account", &credit_account), GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
TALER_JSON_spec_amount ("amount", &amount), TALER_JSON_spec_amount ("amount", &amount),
@ -385,7 +471,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
debit_account, debit_account,
credit_account, credit_account,
&amount, &amount,
wtid, subject,
base_url); base_url);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Receiving incoming wire transfer: %llu->%llu from %s\n", "Receiving incoming wire transfer: %llu->%llu from %s\n",
@ -433,6 +519,94 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
} }
/**
* Handle incoming HTTP request for /reject.
*
* @param h the 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 (a `struct Buffer *`)
* @return MHD result code
*/
static int
handle_reject (struct TALER_FAKEBANK_Handle *h,
struct MHD_Connection *connection,
const char *upload_data,
size_t *upload_data_size,
void **con_cls)
{
enum GNUNET_JSON_PostResult pr;
json_t *json;
struct MHD_Response *resp;
int ret;
int found;
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
con_cls,
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;
}
{
uint64_t serial_id;
uint64_t credit_account;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("row_id", &serial_id),
GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
NULL, NULL))
{
GNUNET_break (0);
json_decref (json);
return MHD_NO;
}
found = TALER_FAKEBANK_reject_transfer (h,
serial_id,
credit_account);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Rejected wire transfer #%llu (to %llu)\n",
(unsigned long long) serial_id,
(unsigned long long) credit_account);
}
json_decref (json);
if (GNUNET_OK != found)
return create_bank_error (connection,
MHD_HTTP_NOT_FOUND,
TALER_EC_BANK_REJECT_NOT_FOUND,
"transaction unknown");
/* finally build regular response */
resp = MHD_create_response_from_buffer (0,
NULL,
MHD_RESPMEM_PERSISTENT);
ret = MHD_queue_response (connection,
MHD_HTTP_NO_CONTENT,
resp);
MHD_destroy_response (resp);
return ret;
}
/** /**
* Handle incoming HTTP request for /history * Handle incoming HTTP request for /history
* *
@ -451,6 +625,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
const char *start; const char *start;
const char *dir; const char *dir;
const char *acc; const char *acc;
const char *cancelled;
unsigned long long account_number; unsigned long long account_number;
unsigned long long start_number; unsigned long long start_number;
long long count; long long count;
@ -469,6 +644,9 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
dir = MHD_lookup_connection_value (connection, dir = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND, MHD_GET_ARGUMENT_KIND,
"direction"); "direction");
cancelled = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"cancelled");
start = MHD_lookup_connection_value (connection, start = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND, MHD_GET_ARGUMENT_KIND,
"start"); "start");
@ -496,7 +674,14 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
(1 != sscanf (start, (1 != sscanf (start,
"%llu", "%llu",
&start_number)) ) || &start_number)) ) ||
( (NULL != dir) && (NULL == dir) ||
(NULL == cancelled) ||
( (0 != strcasecmp (cancelled,
"OMIT")) &&
(0 != strcasecmp (cancelled,
"SHOW")) ) ||
( (0 != strcasecmp (dir,
"BOTH")) &&
(0 != strcasecmp (dir, (0 != strcasecmp (dir,
"CREDIT")) && "CREDIT")) &&
(0 != strcasecmp (dir, (0 != strcasecmp (dir,
@ -513,13 +698,40 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
dir, dir,
(unsigned long long) account_number, (unsigned long long) account_number,
start); start);
if (NULL == dir) if (0 == strcasecmp (dir,
direction = TALER_BANK_DIRECTION_BOTH; "CREDIT"))
else if (0 == strcasecmp (dir, {
"CREDIT"))
direction = TALER_BANK_DIRECTION_CREDIT; direction = TALER_BANK_DIRECTION_CREDIT;
else }
else if (0 == strcasecmp (dir,
"DEBIT"))
{
direction = TALER_BANK_DIRECTION_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) if (NULL == start)
{ {
if (count > 0) if (count > 0)
@ -557,6 +769,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
{ {
json_t *trans; json_t *trans;
char *subject; char *subject;
const char *sign;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Found transaction over %s from %llu to %llu\n", "Found transaction over %s from %llu to %llu\n",
@ -564,10 +777,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
(unsigned long long) pos->debit_account, (unsigned long long) pos->debit_account,
(unsigned long long) pos->credit_account); (unsigned long long) pos->credit_account);
if (! ( ( (account_number == pos->debit_account) && if ( (! ( ( (account_number == pos->debit_account) &&
(0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) || (0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
( (account_number == pos->credit_account) && ( (account_number == pos->credit_account) &&
(0 != (direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) (0 != (direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) ||
( (0 == (direction & TALER_BANK_DIRECTION_CANCEL)) &&
(GNUNET_YES == pos->rejected) ) )
{ {
if (count > 0) if (count > 0)
pos = pos->next; pos = pos->next;
@ -580,11 +795,15 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
"%s %s", "%s %s",
pos->subject, pos->subject,
pos->exchange_base_url); 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}", trans = json_pack ("{s:I, s:o, s:o, s:s, s:I, s:s}",
"row_id", (json_int_t) pos->serial_id, "row_id", (json_int_t) pos->serial_id,
"date", GNUNET_JSON_from_time_abs (pos->date), "date", GNUNET_JSON_from_time_abs (pos->date),
"amount", TALER_JSON_from_amount (&pos->amount), "amount", TALER_JSON_from_amount (&pos->amount),
"sign", (account_number == pos->debit_account) ? "-" : "+", "sign", sign,
"counterpart", (json_int_t) ( (account_number == pos->debit_account) "counterpart", (json_int_t) ( (account_number == pos->debit_account)
? pos->credit_account ? pos->credit_account
: pos->debit_account), : pos->debit_account),
@ -698,6 +917,15 @@ handle_mhd_request (void *cls,
upload_data, upload_data,
upload_data_size, upload_data_size,
con_cls); con_cls);
if ( (0 == strcasecmp (url,
"/reject")) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
return handle_reject (h,
connection,
upload_data,
upload_data_size,
con_cls);
if ( (0 == strcasecmp (url, if ( (0 == strcasecmp (url,
"/history")) && "/history")) &&
(0 == strcasecmp (method, (0 == strcasecmp (method,

View File

@ -50,6 +50,7 @@ run (void *cls)
{ .oc = TBI_OC_ADMIN_ADD_INCOMING, { .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "deposit-1", .label = "deposit-1",
.details.admin_add_incoming.exchange_base_url = "https://exchange.net/", /* bogus */ .details.admin_add_incoming.exchange_base_url = "https://exchange.net/", /* bogus */
.details.admin_add_incoming.subject = "subject 1",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK, .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 1, .details.admin_add_incoming.credit_account_no = 1,
.details.admin_add_incoming.debit_account_no = 2, /* Ignored */ .details.admin_add_incoming.debit_account_no = 2, /* Ignored */
@ -58,6 +59,7 @@ run (void *cls)
{ .oc = TBI_OC_ADMIN_ADD_INCOMING, { .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "deposit-2", .label = "deposit-2",
.details.admin_add_incoming.exchange_base_url = "https://exchange.net/", /* bogus */ .details.admin_add_incoming.exchange_base_url = "https://exchange.net/", /* bogus */
.details.admin_add_incoming.subject = "subject 2",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK, .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 1, .details.admin_add_incoming.credit_account_no = 1,
.details.admin_add_incoming.debit_account_no = 2, /* Ignored */ .details.admin_add_incoming.debit_account_no = 2, /* Ignored */
@ -89,6 +91,27 @@ run (void *cls)
.details.history.direction = TALER_BANK_DIRECTION_DEBIT, .details.history.direction = TALER_BANK_DIRECTION_DEBIT,
.details.history.start_row_ref = "deposit-1", .details.history.start_row_ref = "deposit-1",
.details.history.num_results = 5 }, .details.history.num_results = 5 },
{ .oc = TBI_OC_REJECT,
.label = "reject-1",
.details.reject.cmd_ref = "deposit-1" },
{ .oc = TBI_OC_HISTORY,
.label = "history-r1",
.details.history.account_number = 2,
.details.history.direction = TALER_BANK_DIRECTION_CREDIT,
.details.history.start_row_ref = NULL,
.details.history.num_results = 5 },
{ .oc = TBI_OC_HISTORY,
.label = "history-r2",
.details.history.account_number = 2,
.details.history.direction = TALER_BANK_DIRECTION_DEBIT,
.details.history.start_row_ref = NULL,
.details.history.num_results = 5 },
{ .oc = TBI_OC_HISTORY,
.label = "history-r3",
.details.history.account_number = 2,
.details.history.direction = TALER_BANK_DIRECTION_BOTH | TALER_BANK_DIRECTION_CANCEL,
.details.history.start_row_ref = NULL,
.details.history.num_results = 5 },
{ .oc = TBI_OC_END } { .oc = TBI_OC_END }
}; };

View File

@ -48,6 +48,7 @@ run (void *cls)
/* Add EUR:5.01 to account 1 */ /* Add EUR:5.01 to account 1 */
{ .oc = TBI_OC_ADMIN_ADD_INCOMING, { .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "debit-1", .label = "debit-1",
.details.admin_add_incoming.subject = "subject 1",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK, .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 1, .details.admin_add_incoming.credit_account_no = 1,
.details.admin_add_incoming.debit_account_no = 2, .details.admin_add_incoming.debit_account_no = 2,
@ -68,6 +69,7 @@ run (void *cls)
.details.history.num_results = 5 }, .details.history.num_results = 5 },
{ .oc = TBI_OC_ADMIN_ADD_INCOMING, { .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "debit-2", .label = "debit-2",
.details.admin_add_incoming.subject = "subject 2",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK, .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 3, .details.admin_add_incoming.credit_account_no = 3,
.details.admin_add_incoming.debit_account_no = 2, .details.admin_add_incoming.debit_account_no = 2,
@ -75,6 +77,7 @@ run (void *cls)
.details.admin_add_incoming.amount = "KUDOS:3.21" }, .details.admin_add_incoming.amount = "KUDOS:3.21" },
{ .oc = TBI_OC_ADMIN_ADD_INCOMING, { .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "credit-2", .label = "credit-2",
.details.admin_add_incoming.subject = "credit 2",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK, .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 2, .details.admin_add_incoming.credit_account_no = 2,
.details.admin_add_incoming.debit_account_no = 3, .details.admin_add_incoming.debit_account_no = 3,
@ -105,6 +108,40 @@ run (void *cls)
/* check transfer list is now empty */ /* check transfer list is now empty */
{ .oc = TBI_OC_EXPECT_TRANSFERS_EMPTY, { .oc = TBI_OC_EXPECT_TRANSFERS_EMPTY,
.label = "expect-empty" }, .label = "expect-empty" },
/* Add EUR:5.01 to account 1 */
{ .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "credit-for-reject-1",
.details.admin_add_incoming.subject = "subject 3",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 1,
.details.admin_add_incoming.debit_account_no = 2,
.details.admin_add_incoming.exchange_base_url = "https://exchange.net/",
.details.admin_add_incoming.amount = "KUDOS:5.01" },
{ .oc = TBI_OC_REJECT,
.label = "reject-1",
.details.reject.cmd_ref = "credit-for-reject-1" },
{ .oc = TBI_OC_HISTORY,
.label = "history-r1",
.details.history.account_number = 1,
.details.history.direction = TALER_BANK_DIRECTION_BOTH,
/* range is exclusive, and everything up to and including "credit-2"
was already killed via TBI_OC_EXPECT_TRANSFER and thus won't show
in the history. So to see the rejected transfer, we need to start
looking after "credit-2" */
.details.history.start_row_ref = NULL,
.details.history.num_results = 5 },
{ .oc = TBI_OC_HISTORY,
.label = "history-r1c",
.details.history.account_number = 1,
.details.history.direction = TALER_BANK_DIRECTION_BOTH | TALER_BANK_DIRECTION_CANCEL,
.details.history.start_row_ref = NULL,
.details.history.num_results = 5 },
{ .oc = TBI_OC_EXPECT_TRANSFER,
.label = "expect-credit-reject-1",
.details.expect_transfer.cmd_ref = "credit-for-reject-1" },
/* check transfer list is now empty */
{ .oc = TBI_OC_EXPECT_TRANSFERS_EMPTY,
.label = "expect-empty-2" },
{ .oc = TBI_OC_END } { .oc = TBI_OC_END }
}; };

View File

@ -110,7 +110,6 @@ static const struct TBI_Command *
find_command (const struct InterpreterState *is, find_command (const struct InterpreterState *is,
const char *label) const char *label)
{ {
unsigned int i;
const struct TBI_Command *cmd; const struct TBI_Command *cmd;
if (NULL == label) if (NULL == label)
@ -119,7 +118,7 @@ find_command (const struct InterpreterState *is,
"Attempt to lookup command for empty label\n"); "Attempt to lookup command for empty label\n");
return NULL; return NULL;
} }
for (i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++) for (unsigned int i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
if ( (NULL != cmd->label) && if ( (NULL != cmd->label) &&
(0 == strcmp (cmd->label, (0 == strcmp (cmd->label,
label)) ) label)) )
@ -131,6 +130,63 @@ find_command (const struct InterpreterState *is,
} }
/**
* Test if the /admin/add/incoming transaction at offset @a off
* has been /rejected.
*
* @param is interpreter state (where we are right now)
* @param off offset of the command to test for rejection
* @return #GNUNET_YES if the command at @a off was cancelled
*/
static int
test_cancelled (struct InterpreterState *is,
unsigned int off)
{
const struct TBI_Command *cmd = &is->commands[off];
for (unsigned int i=0;i<is->ip;i++)
{
const struct TBI_Command *c = &is->commands[i];
if (TBI_OC_REJECT != c->oc)
continue;
if (0 == strcmp (c->details.reject.cmd_ref,
cmd->label))
return GNUNET_YES;
}
return GNUNET_NO;
}
/**
* Test if the /admin/add/incoming transaction at offset @a off
* has been #TBI_OC_EXPECT_TRANSFER treated, and thus been
* forgotten by the fakebank.
*
* @param is interpreter state (where we are right now)
* @param off offset of the command to test for rejection
* @return #GNUNET_YES if the command at @a off was cancelled
*/
static int
test_deleted_by_expected (struct InterpreterState *is,
unsigned int off)
{
const struct TBI_Command *cmd = &is->commands[off];
for (unsigned int i=0;i<is->ip;i++)
{
const struct TBI_Command *c = &is->commands[i];
if (TBI_OC_EXPECT_TRANSFER != c->oc)
continue;
if (0 == strcmp (c->details.expect_transfer.cmd_ref,
cmd->label))
return GNUNET_YES;
}
return GNUNET_NO;
}
/** /**
* Item in the transaction history, as reconstructed from the * Item in the transaction history, as reconstructed from the
* command history. * command history.
@ -214,6 +270,7 @@ build_history (struct InterpreterState *is,
for (unsigned int off = start;off != end + inc; off += inc) for (unsigned int off = start;off != end + inc; off += inc)
{ {
const struct TBI_Command *pos = &is->commands[off]; const struct TBI_Command *pos = &is->commands[off];
int cancelled;
if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc) if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
continue; continue;
@ -229,6 +286,15 @@ build_history (struct InterpreterState *is,
continue; /* skip until we find the marker */ continue; /* skip until we find the marker */
if (total >= cmd->details.history.num_results * inc) if (total >= cmd->details.history.num_results * inc)
break; /* hit limit specified by command */ break; /* hit limit specified by command */
if (GNUNET_YES ==
test_deleted_by_expected (is,
off))
continue;
cancelled = test_cancelled (is,
off);
if ( (GNUNET_YES == cancelled) &&
(0 == (cmd->details.history.direction & TALER_BANK_DIRECTION_CANCEL)) )
continue;
if ( ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) && if ( ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) &&
(cmd->details.history.account_number == (cmd->details.history.account_number ==
pos->details.admin_add_incoming.credit_account_no)) || pos->details.admin_add_incoming.credit_account_no)) ||
@ -253,6 +319,7 @@ build_history (struct InterpreterState *is,
for (unsigned int off = start;off != end + inc; off += inc) for (unsigned int off = start;off != end + inc; off += inc)
{ {
const struct TBI_Command *pos = &is->commands[off]; const struct TBI_Command *pos = &is->commands[off];
int cancelled;
if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc) if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
continue; continue;
@ -268,6 +335,10 @@ build_history (struct InterpreterState *is,
continue; /* skip until we find the marker */ continue; /* skip until we find the marker */
if (total >= cmd->details.history.num_results * inc) if (total >= cmd->details.history.num_results * inc)
break; /* hit limit specified by command */ break; /* hit limit specified by command */
if (GNUNET_YES ==
test_deleted_by_expected (is,
off))
continue;
if ( ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) && if ( ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) &&
(cmd->details.history.account_number == (cmd->details.history.account_number ==
@ -280,11 +351,19 @@ build_history (struct InterpreterState *is,
continue; continue;
} }
cancelled = test_cancelled (is,
off);
if ( (GNUNET_YES == cancelled) &&
(0 == (cmd->details.history.direction & TALER_BANK_DIRECTION_CANCEL)) )
continue;
if ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) && if ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) &&
(cmd->details.history.account_number == (cmd->details.history.account_number ==
pos->details.admin_add_incoming.credit_account_no)) pos->details.admin_add_incoming.credit_account_no))
{ {
h[total].direction = TALER_BANK_DIRECTION_CREDIT; h[total].direction = TALER_BANK_DIRECTION_CREDIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}", = json_pack ("{s:s, s:s, s:I}",
"type", "type",
@ -300,6 +379,8 @@ build_history (struct InterpreterState *is,
pos->details.admin_add_incoming.debit_account_no)) pos->details.admin_add_incoming.debit_account_no))
{ {
h[total].direction = TALER_BANK_DIRECTION_DEBIT; h[total].direction = TALER_BANK_DIRECTION_DEBIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}", = json_pack ("{s:s, s:s, s:I}",
"type", "type",
@ -323,17 +404,10 @@ build_history (struct InterpreterState *is,
/* h[total].execution_date; // unknown here */ /* h[total].execution_date; // unknown here */
h[total].serial_id h[total].serial_id
= pos->details.admin_add_incoming.serial_id; = pos->details.admin_add_incoming.serial_id;
{ GNUNET_asprintf (&h[total].details.wire_transfer_subject,
char *ws; "%s %s",
pos->details.admin_add_incoming.subject,
ws = GNUNET_STRINGS_data_to_string_alloc (&pos->details.admin_add_incoming.wtid, pos->details.admin_add_incoming.exchange_base_url);
sizeof (struct TALER_WireTransferIdentifierRawP));
GNUNET_asprintf (&h[total].details.wire_transfer_subject,
"%s %s",
ws,
pos->details.admin_add_incoming.exchange_base_url);
GNUNET_free (ws);
}
total++; total++;
} }
} }
@ -488,18 +562,34 @@ static void
interpreter_run (void *cls); interpreter_run (void *cls);
/**
* Run the next command.
*
* @param is interpreter to progress
*/
static void
next (struct InterpreterState *is)
{
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
/** /**
* Function called upon completion of our /admin/add/incoming request. * Function called upon completion of our /admin/add/incoming request.
* *
* @param cls closure with the interpreter state * @param cls closure with the interpreter state
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol) * 0 if the bank's reply is bogus (fails to follow the protocol)
* @param ec taler status code
* @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON * @param json detailed response from the HTTPD, or NULL if reply was not in JSON
*/ */
static void static void
add_incoming_cb (void *cls, add_incoming_cb (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id, uint64_t serial_id,
const json_t *json) const json_t *json)
{ {
@ -522,9 +612,7 @@ add_incoming_cb (void *cls,
fail (is); fail (is);
return; return;
} }
is->ip++; next (is);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
} }
@ -538,6 +626,7 @@ add_incoming_cb (void *cls,
* #MHD_HTTP_NO_CONTENT if there are no more results; on success the * #MHD_HTTP_NO_CONTENT if there are no more results; on success the
* last callback is always of this status (even if `abs(num_results)` were * last callback is always of this status (even if `abs(num_results)` were
* already returned). * already returned).
* @param ec taler status code
* @param dir direction of the transfer * @param dir direction of the transfer
* @param serial_id monotonically increasing counter corresponding to the transaction * @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer * @param details details about the wire transfer
@ -546,6 +635,7 @@ add_incoming_cb (void *cls,
static void static void
history_cb (void *cls, history_cb (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
uint64_t serial_id, uint64_t serial_id,
const struct TALER_BANK_TransferDetails *details, const struct TALER_BANK_TransferDetails *details,
@ -580,9 +670,7 @@ history_cb (void *cls,
fail (is); fail (is);
return; return;
} }
is->ip++; next (is);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
return; return;
} }
if (GNUNET_OK != if (GNUNET_OK !=
@ -612,6 +700,38 @@ history_cb (void *cls,
} }
/**
* Callbacks of this type are used to serve the result of asking
* the bank to reject an incoming wire transfer.
*
* @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for successful status request;
* #MHD_HTTP_NOT_FOUND if the rowid is unknown;
* 0 if the bank's reply is bogus (fails to follow the protocol),
* @param ec detailed error code
*/
static void
reject_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec)
{
struct InterpreterState *is = cls;
struct TBI_Command *cmd = &is->commands[is->ip];
cmd->details.reject.rh = NULL;
if (MHD_HTTP_NO_CONTENT != http_status)
{
GNUNET_break (0);
fprintf (stderr,
"Unexpected response code %u:\n",
http_status);
fail (is);
return;
}
next (is);
}
/** /**
* Run the main interpreter loop that performs bank operations. * Run the main interpreter loop that performs bank operations.
* *
@ -658,15 +778,13 @@ interpreter_run (void *cls)
fail (is); fail (is);
return; return;
} }
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, GNUNET_break (NULL != cmd->details.admin_add_incoming.subject);
&cmd->details.admin_add_incoming.wtid,
sizeof (cmd->details.admin_add_incoming.wtid));
cmd->details.admin_add_incoming.aih cmd->details.admin_add_incoming.aih
= TALER_BANK_admin_add_incoming (is->ctx, = TALER_BANK_admin_add_incoming (is->ctx,
"http://localhost:8080", "http://localhost:8080",
&auth, &auth,
cmd->details.admin_add_incoming.exchange_base_url, cmd->details.admin_add_incoming.exchange_base_url,
&cmd->details.admin_add_incoming.wtid, cmd->details.admin_add_incoming.subject,
&amount, &amount,
cmd->details.admin_add_incoming.debit_account_no, cmd->details.admin_add_incoming.debit_account_no,
cmd->details.admin_add_incoming.credit_account_no, cmd->details.admin_add_incoming.credit_account_no,
@ -722,7 +840,6 @@ interpreter_run (void *cls)
&amount)); &amount));
{ {
char *subject; char *subject;
char *expect;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_FAKEBANK_check (is->fakebank, TALER_FAKEBANK_check (is->fakebank,
@ -736,22 +853,17 @@ interpreter_run (void *cls)
fail (is); fail (is);
return; return;
} }
expect = GNUNET_STRINGS_data_to_string_alloc (&ref->details.admin_add_incoming.wtid, if (0 != strcmp (ref->details.admin_add_incoming.subject,
sizeof (ref->details.admin_add_incoming.wtid)); subject))
if (0 != strcmp (subject, expect))
{ {
GNUNET_free (expect);
GNUNET_free (subject); GNUNET_free (subject);
GNUNET_break (0); GNUNET_break (0);
fail (is); fail (is);
return; return;
} }
GNUNET_free (subject); GNUNET_free (subject);
GNUNET_free (expect);
} }
is->ip++; next (is);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
return; return;
case TBI_OC_EXPECT_TRANSFERS_EMPTY: case TBI_OC_EXPECT_TRANSFERS_EMPTY:
if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank)) if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank))
@ -760,9 +872,27 @@ interpreter_run (void *cls)
fail (is); fail (is);
return; return;
} }
is->ip++; next (is);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, return;
is); case TBI_OC_REJECT:
ref = find_command (is,
cmd->details.reject.cmd_ref);
GNUNET_assert (NULL != ref);
GNUNET_assert (TBI_OC_ADMIN_ADD_INCOMING == ref->oc);
cmd->details.reject.rh
= TALER_BANK_reject (is->ctx,
"http://localhost:8080",
&auth,
ref->details.admin_add_incoming.credit_account_no,
ref->details.admin_add_incoming.serial_id,
&reject_cb,
is);
if (NULL == cmd->details.reject.rh)
{
GNUNET_break (0);
fail (is);
return;
}
return; return;
default: default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -802,7 +932,6 @@ do_shutdown (void *cls)
{ {
struct InterpreterState *is = cls; struct InterpreterState *is = cls;
struct TBI_Command *cmd; struct TBI_Command *cmd;
unsigned int i;
if (NULL != is->timeout_task) if (NULL != is->timeout_task)
{ {
@ -810,7 +939,7 @@ do_shutdown (void *cls)
is->timeout_task = NULL; is->timeout_task = NULL;
} }
for (i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++) for (unsigned int i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
{ {
switch (cmd->oc) switch (cmd->oc)
{ {
@ -843,6 +972,17 @@ do_shutdown (void *cls)
break; break;
case TBI_OC_EXPECT_TRANSFERS_EMPTY: case TBI_OC_EXPECT_TRANSFERS_EMPTY:
break; break;
case TBI_OC_REJECT:
if (NULL != cmd->details.reject.rh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_BANK_reject_cancel (cmd->details.reject.rh);
cmd->details.reject.rh = NULL;
}
break;
default: default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n", "Unknown instruction %d at %u (%s)\n",

View File

@ -57,7 +57,12 @@ enum TBI_OpCode
/** /**
* Expect that we have exhaustively gone over all transfers at fakebank. * Expect that we have exhaustively gone over all transfers at fakebank.
*/ */
TBI_OC_EXPECT_TRANSFERS_EMPTY TBI_OC_EXPECT_TRANSFERS_EMPTY,
/**
* Reject incoming transfer.
*/
TBI_OC_REJECT
}; };
@ -110,10 +115,9 @@ struct TBI_Command
const char *exchange_base_url; const char *exchange_base_url;
/** /**
* Wire transfer identifier to use. Initialized to * Wire transfer subject to use.
* a random value.
*/ */
struct TALER_WireTransferIdentifierRawP wtid; const char *subject;
/** /**
* Which response code do we expect for this command? * Which response code do we expect for this command?
@ -186,6 +190,23 @@ struct TBI_Command
} expect_transfer; } expect_transfer;
/**
* Execute /reject operation.
*/
struct {
/**
* Reference to the matching transfer that is now to be rejected.
*/
const char *cmd_ref;
/**
* Set to the API's handle during the operation.
*/
struct TALER_BANK_RejectHandle *rh;
} reject;
} details; } details;
}; };

View File

@ -282,6 +282,7 @@ reject_cb (void *cls,
* the bank for the transaction history. * the bank for the transaction history.
* *
* @param cls closure with the `struct TALER_EXCHANGEDB_Session *` * @param cls closure with the `struct TALER_EXCHANGEDB_Session *`
* @param ec taler error code
* @param dir direction of the transfer * @param dir direction of the transfer
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off * @param row_off_size number of bytes in @a row_off
@ -290,6 +291,7 @@ reject_cb (void *cls,
*/ */
static int static int
history_cb (void *cls, history_cb (void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
const void *row_off, const void *row_off,
size_t row_off_size, size_t row_off_size,
@ -303,6 +305,12 @@ history_cb (void *cls,
{ {
hh = NULL; hh = NULL;
if (TALER_EC_NONE != ec)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Error fetching history: %u!\n",
(unsigned int) ec);
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"End of list. Committing progress!\n"); "End of list. Committing progress!\n");
qs = db_plugin->commit (db_plugin->cls, qs = db_plugin->commit (db_plugin->cls,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2015, 2016, 2017 GNUnet e.V. & Inria Copyright (C) 2015, 2016, 2017 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -25,6 +25,7 @@
#include <jansson.h> #include <jansson.h>
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_util.h" #include "taler_util.h"
#include "taler_error_codes.h"
/** /**
@ -98,12 +99,14 @@ struct TALER_BANK_AdminAddIncomingHandle;
* @param cls closure * @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol) * 0 if the bank's reply is bogus (fails to follow the protocol)
* @param ec detailed error code
* @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON * @param json detailed response from the HTTPD, or NULL if reply was not in JSON
*/ */
typedef void typedef void
(*TALER_BANK_AdminAddIncomingResultCallback) (void *cls, (*TALER_BANK_AdminAddIncomingResultCallback) (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id, uint64_t serial_id,
const json_t *json); const json_t *json);
@ -118,7 +121,7 @@ typedef void
* @param bank_base_url URL of the bank (used to execute this request) * @param bank_base_url URL of the bank (used to execute this request)
* @param auth authentication data to use * @param auth authentication data to use
* @param exchange_base_url base URL of the exchange (for tracking) * @param exchange_base_url base URL of the exchange (for tracking)
* @param wtid wire transfer identifier for the transfer * @param subject wire transfer subject for the transfer
* @param amount amount that was deposited * @param amount amount that was deposited
* @param debit_account_no account number to withdraw from (53 bits at most) * @param debit_account_no account number to withdraw from (53 bits at most)
* @param credit_account_no account number to deposit into (53 bits at most) * @param credit_account_no account number to deposit into (53 bits at most)
@ -133,7 +136,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url, const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth, const struct TALER_BANK_AuthenticationData *auth,
const char *exchange_base_url, const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid, const char *subject,
const struct TALER_Amount *amount, const struct TALER_Amount *amount,
uint64_t debit_account_no, uint64_t debit_account_no,
uint64_t credit_account_no, uint64_t credit_account_no,
@ -174,7 +177,15 @@ enum TALER_BANK_Direction {
/** /**
* Return both types of transactions. * Return both types of transactions.
*/ */
TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT | TALER_BANK_DIRECTION_DEBIT) TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT | TALER_BANK_DIRECTION_DEBIT),
/**
* Bit mask that is applied to view transactions that have been
* cancelled. The bit is set for cancelled transactions that are
* returned from /history, and must also be set in order for
* cancelled transactions to show up in the /history.
*/
TALER_BANK_DIRECTION_CANCEL = 4
}; };
@ -222,6 +233,7 @@ struct TALER_BANK_TransferDetails
* #MHD_HTTP_NO_CONTENT if there are no more results; on success the * #MHD_HTTP_NO_CONTENT if there are no more results; on success the
* last callback is always of this status (even if `abs(num_results)` were * last callback is always of this status (even if `abs(num_results)` were
* already returned). * already returned).
* @param ec detailed error code
* @param dir direction of the transfer * @param dir direction of the transfer
* @param serial_id monotonically increasing counter corresponding to the transaction * @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer * @param details details about the wire transfer
@ -230,6 +242,7 @@ struct TALER_BANK_TransferDetails
typedef void typedef void
(*TALER_BANK_HistoryResultCallback) (void *cls, (*TALER_BANK_HistoryResultCallback) (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
uint64_t serial_id, uint64_t serial_id,
const struct TALER_BANK_TransferDetails *details, const struct TALER_BANK_TransferDetails *details,
@ -277,5 +290,61 @@ void
TALER_BANK_history_cancel (struct TALER_BANK_HistoryHandle *hh); TALER_BANK_history_cancel (struct TALER_BANK_HistoryHandle *hh);
/**
* Handle for #TALER_BANK_reject() operation.
*/
struct TALER_BANK_RejectHandle;
/**
* Callbacks of this type are used to serve the result of asking
* the bank to reject an incoming wire transfer.
*
* @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for successful status request;
* #MHD_HTTP_NOT_FOUND if the rowid is unknown;
* 0 if the bank's reply is bogus (fails to follow the protocol),
* @param ec detailed error code
*/
typedef void
(*TALER_BANK_RejectResultCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec);
/**
* Request rejection of a wire transfer, marking it as cancelled and voiding
* its effects.
*
* @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this request)
* @param auth authentication data to use
* @param account_number which account number should we query
* @param rowid transfer to reject
* @param rcb the callback to call with the operation result
* @param rcb_cls closure for @a rcb
* @return NULL
* if the inputs are invalid.
* In this case, the callback is not called.
*/
struct TALER_BANK_RejectHandle *
TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth,
uint64_t account_number,
uint64_t rowid,
TALER_BANK_RejectResultCallback rcb,
void *rcb_cls);
/**
* Cancel an reject request. This function cannot be used on a request
* handle if the response was is already served for it.
*
* @param rh the reject request handle
*/
void
TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh);
#endif /* _TALER_BANK_SERVICE_H */ #endif /* _TALER_BANK_SERVICE_H */

View File

@ -1488,6 +1488,34 @@ enum TALER_ErrorCode
*/ */
TALER_EC_TEST_RSA_SIGN_ERROR = 4005, TALER_EC_TEST_RSA_SIGN_ERROR = 4005,
/* *************** Taler BANK/FAKEBANK error codes *************** */
/**
* Authentication failed for the /admin/add/incoming request.
* Returned with a status code of MHD_HTTP_FORBIDDEN.
*/
TALER_EC_BANK_TRANSFER_NOT_AUHTORIZED = 4101,
/**
* Authentication failed for the /history request.
* Returned with a status code of MHD_HTTP_FORBIDDEN.
*/
TALER_EC_BANK_HISTORY_NOT_AUHTORIZED = 4151,
/**
* The bank could not find the wire transfer that was supposed to
* be rejected.
* Returned with a status code of MHD_HTTP_NOT_FOUND.
*/
TALER_EC_BANK_REJECT_NOT_FOUND = 4250,
/**
* Authentication failed for the /reject request.
* Returned with a status code of MHD_HTTP_FORBIDDEN.
*/
TALER_EC_BANK_REJECT_NOT_AUHTORIZED = 4251,
/** /**
* End of error code range. * End of error code range.

View File

@ -88,6 +88,8 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
* to the transfer identifier and remove the transaction from the * to the transfer identifier and remove the transaction from the
* list. If the transaction was not recorded, return #GNUNET_SYSERR. * list. If the transaction was not recorded, return #GNUNET_SYSERR.
* *
* Rejected transfers do NOT show with "check".
*
* @param h bank instance * @param h bank instance
* @param want_amount transfer amount desired * @param want_amount transfer amount desired
* @param want_debit account that should have been debited * @param want_debit account that should have been debited
@ -106,6 +108,21 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
char **subject); char **subject);
/**
* Reject incoming wire transfer to account @a credit_account
* as identified by @a rowid.
*
* @param h fake bank handle
* @param rowid identifies transfer to reject
* @param credit_account account number of owner of credited account
* @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not found
*/
int
TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
uint64_t rowid,
uint64_t credit_account);
/** /**
* Stop running the fake bank. * Stop running the fake bank.
* *

View File

@ -83,6 +83,7 @@ struct TALER_WIRE_TransferDetails
* the bank for the transaction history. * the bank for the transaction history.
* *
* @param cls closure * @param cls closure
* @param ec taler error code
* @param dir direction of the transfer * @param dir direction of the transfer
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off * @param row_off_size number of bytes in @a row_off
@ -91,6 +92,7 @@ struct TALER_WIRE_TransferDetails
*/ */
typedef int typedef int
(*TALER_WIRE_HistoryResultCallback) (void *cls, (*TALER_WIRE_HistoryResultCallback) (void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
const void *row_off, const void *row_off,
size_t row_off_size, size_t row_off_size,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2016 GNUnet e.V. & Inria Copyright (C) 2017 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the 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 terms of the GNU General Public License as published by the Free Software
@ -553,12 +553,14 @@ test_prepare_wire_transfer (void *cls,
* @param cls closure with the `struct TALER_WIRE_ExecuteHandle` * @param cls closure with the `struct TALER_WIRE_ExecuteHandle`
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol) * 0 if the bank's reply is bogus (fails to follow the protocol)
* @param ec error code from the bank
* @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error
* @param json detailed response from the HTTPD, or NULL if reply was not JSON * @param json detailed response from the HTTPD, or NULL if reply was not JSON
*/ */
static void static void
execute_cb (void *cls, execute_cb (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id, uint64_t serial_id,
const json_t *json) const json_t *json)
{ {
@ -578,13 +580,15 @@ execute_cb (void *cls,
} }
if (NULL != emsg) if (NULL != emsg)
GNUNET_asprintf (&s, GNUNET_asprintf (&s,
"%u (%s)", "%u/%u (%s)",
http_status, http_status,
(unsigned int) ec,
emsg); emsg);
else else
GNUNET_asprintf (&s, GNUNET_asprintf (&s,
"%u", "%u/%u",
http_status); http_status,
(unsigned int) ec);
eh->cc (eh->cc_cls, eh->cc (eh->cc_cls,
(MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR, (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
serial_id, serial_id,
@ -676,6 +680,7 @@ test_execute_wire_transfer (void *cls,
char *emsg; char *emsg;
const char *json_s; const char *json_s;
const char *exchange_base_url; const char *exchange_base_url;
char *wire_s;
if (NULL == tc->ctx) if (NULL == tc->ctx)
{ {
@ -728,16 +733,19 @@ test_execute_wire_transfer (void *cls,
eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle); eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
eh->cc = cc; eh->cc = cc;
eh->cc_cls = cc_cls; eh->cc_cls = cc_cls;
wire_s = GNUNET_STRINGS_data_to_string_alloc (&bf.wtid,
sizeof (bf.wtid));
eh->aaih = TALER_BANK_admin_add_incoming (tc->ctx, eh->aaih = TALER_BANK_admin_add_incoming (tc->ctx,
tc->bank_uri, tc->bank_uri,
&tc->auth, &tc->auth,
exchange_base_url, exchange_base_url,
&bf.wtid, wire_s,
&amount, &amount,
(uint64_t) tc->exchange_account_no, (uint64_t) tc->exchange_account_no,
(uint64_t) account_no, (uint64_t) account_no,
&execute_cb, &execute_cb,
eh); eh);
GNUNET_free (wire_s);
json_decref (wire); json_decref (wire);
if (NULL == eh->aaih) if (NULL == eh->aaih)
{ {
@ -803,6 +811,7 @@ struct TALER_WIRE_HistoryHandle
* #MHD_HTTP_NO_CONTENT if there are no more results; on success the * #MHD_HTTP_NO_CONTENT if there are no more results; on success the
* last callback is always of this status (even if `abs(num_results)` were * last callback is always of this status (even if `abs(num_results)` were
* already returned). * already returned).
* @param ec taler error code
* @param dir direction of the transfer * @param dir direction of the transfer
* @param serial_id monotonically increasing counter corresponding to the transaction * @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer * @param details details about the wire transfer
@ -811,6 +820,7 @@ struct TALER_WIRE_HistoryHandle
static void static void
bhist_cb (void *cls, bhist_cb (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
uint64_t serial_id, uint64_t serial_id,
const struct TALER_BANK_TransferDetails *details, const struct TALER_BANK_TransferDetails *details,
@ -852,12 +862,17 @@ bhist_cb (void *cls,
sizeof (wd.wtid)); sizeof (wd.wtid));
wd.wtid_s = details->wire_transfer_subject; wd.wtid_s = details->wire_transfer_subject;
} }
else
{
wd.wtid_s = NULL;
}
GNUNET_free (subject); GNUNET_free (subject);
wd.account_details = details->account_details; wd.account_details = details->account_details;
if ( (NULL != whh->hres_cb) && if ( (NULL != whh->hres_cb) &&
(GNUNET_OK != (GNUNET_OK !=
whh->hres_cb (whh->hres_cb_cls, whh->hres_cb (whh->hres_cb_cls,
TALER_EC_NONE,
dir, dir,
&bserial_id, &bserial_id,
sizeof (bserial_id), sizeof (bserial_id),
@ -868,6 +883,7 @@ bhist_cb (void *cls,
case MHD_HTTP_NO_CONTENT: case MHD_HTTP_NO_CONTENT:
if (NULL != whh->hres_cb) if (NULL != whh->hres_cb)
(void) whh->hres_cb (whh->hres_cb_cls, (void) whh->hres_cb (whh->hres_cb_cls,
ec,
TALER_BANK_DIRECTION_NONE, TALER_BANK_DIRECTION_NONE,
NULL, NULL,
0, 0,
@ -880,6 +896,7 @@ bhist_cb (void *cls,
GNUNET_break (0); GNUNET_break (0);
if (NULL != whh->hres_cb) if (NULL != whh->hres_cb)
(void) whh->hres_cb (whh->hres_cb_cls, (void) whh->hres_cb (whh->hres_cb_cls,
ec,
TALER_BANK_DIRECTION_NONE, TALER_BANK_DIRECTION_NONE,
NULL, NULL,
0, 0,
@ -1004,26 +1021,32 @@ struct TALER_WIRE_RejectHandle
void *rej_cb_cls; void *rej_cb_cls;
/** /**
* Handle to task for timeout of operation. * Handle for the reject operation.
*/ */
struct GNUNET_SCHEDULER_Task *timeout_task; struct TALER_BANK_RejectHandle *brh;
}; };
/** /**
* Rejection operation failed with timeout, notify callback * Callbacks of this type are used to serve the result of asking
* and clean up. * the bank to reject an incoming wire transfer.
* *
* @param cls closure with `struct TALER_WIRE_RejectHandle` * @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for successful status request;
* #MHD_HTTP_NOT_FOUND if the rowid is unknown;
* 0 if the bank's reply is bogus (fails to follow the protocol),
* @param ec detailed error code
*/ */
static void static void
timeout_reject (void *cls) reject_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec)
{ {
struct TALER_WIRE_RejectHandle *rh = cls; struct TALER_WIRE_RejectHandle *rh = cls;
rh->timeout_task = NULL; rh->brh = NULL;
rh->rej_cb (rh->rej_cb_cls, rh->rej_cb (rh->rej_cb_cls,
TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */); ec);
GNUNET_free (rh); GNUNET_free (rh);
} }
@ -1052,14 +1075,30 @@ test_reject_transfer (void *cls,
TALER_WIRE_RejectTransferCallback rej_cb, TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls) void *rej_cb_cls)
{ {
struct TestClosure *tc = cls;
const uint64_t *rowid_b64 = start_off;
struct TALER_WIRE_RejectHandle *rh; struct TALER_WIRE_RejectHandle *rh;
GNUNET_break (0); /* not implemented, just a stub! */ if (sizeof (uint64_t) != start_off_len)
{
GNUNET_break (0);
return NULL;
}
rh = GNUNET_new (struct TALER_WIRE_RejectHandle); rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
rh->rej_cb = rej_cb; rh->rej_cb = rej_cb;
rh->rej_cb_cls = rej_cb_cls; rh->rej_cb_cls = rej_cb_cls;
rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject, rh->brh = TALER_BANK_reject (tc->ctx,
rh); tc->bank_uri,
&tc->auth,
(uint64_t) tc->exchange_account_no,
GNUNET_ntohll (*rowid_b64),
&reject_cb,
rh);
if (NULL == rh->brh)
{
GNUNET_free (rh);
return NULL;
}
return rh; return rh;
} }
@ -1082,7 +1121,8 @@ test_reject_transfer_cancel (void *cls,
{ {
void *ret = rh->rej_cb_cls; void *ret = rh->rej_cb_cls;
GNUNET_SCHEDULER_cancel (rh->timeout_task); if (NULL != rh->brh)
TALER_BANK_reject_cancel (rh->brh);
GNUNET_free (rh); GNUNET_free (rh);
return ret; return ret;
} }

View File

@ -159,6 +159,7 @@ timeout_cb (void *cls)
* the bank for the transaction history. * the bank for the transaction history.
* *
* @param cls closure * @param cls closure
* @param ec taler status code
* @param dir direction of the transfer * @param dir direction of the transfer
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off * @param row_off_size number of bytes in @a row_off
@ -167,6 +168,7 @@ timeout_cb (void *cls)
*/ */
static int static int
history_result_cb (void *cls, history_result_cb (void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, enum TALER_BANK_Direction dir,
const void *row_off, const void *row_off,
size_t row_off_size, size_t row_off_size,