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).
*
* @param cls closure
* @param ec error code in case something went wrong
* @param dir direction of the transfer
* @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off
@ -838,6 +839,7 @@ check_exchange_wire_out ()
*/
static int
history_debit_cb (void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
const void *row_off,
size_t row_off_size,
@ -848,6 +850,13 @@ history_debit_cb (void *cls,
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
if it matches #out_map */
hh = NULL;
@ -1069,6 +1078,7 @@ conclude_credit_history ()
* transactions).
*
* @param cls closure
* @param ec error code in case something went wrong
* @param dir direction of the transfer
* @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off
@ -1077,6 +1087,7 @@ conclude_credit_history ()
*/
static int
history_credit_cb (void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
const void *row_off,
size_t row_off_size,
@ -1087,6 +1098,13 @@ history_credit_cb (void *cls,
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 */
hh = NULL;
conclude_credit_history ();

View File

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

View File

@ -1,6 +1,6 @@
/*
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
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;
uint64_t serial_id = UINT64_MAX;
enum TALER_ErrorCode ec;
aai->job = NULL;
switch (response_code)
{
case 0:
ec = TALER_EC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
@ -100,29 +102,36 @@ handle_admin_add_incoming_finished (void *cls,
{
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break;
}
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 */
@ -130,11 +139,13 @@ handle_admin_add_incoming_finished (void *cls,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (json);
response_code = 0;
break;
}
aai->cb (aai->cb_cls,
response_code,
ec,
serial_id,
json);
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 auth authentication data to send to the bank
* @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 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)
@ -166,7 +177,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth,
const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
const char *subject,
const struct TALER_Amount *amount,
uint64_t debit_account_no,
uint64_t credit_account_no,
@ -182,10 +193,10 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
GNUNET_break (0);
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",
"exchange_url", exchange_base_url,
"wtid", GNUNET_JSON_from_data_auto (wtid),
"subject", subject,
"amount", TALER_JSON_from_amount (amount),
"debit_account", (json_int_t) debit_account_no,
"credit_account", (json_int_t) credit_account_no);

View File

@ -80,7 +80,7 @@ TALER_BANK_make_auth_header_ (const struct TALER_BANK_AuthenticationData *auth)
authh = append (authh,
"X-Taler-Bank-Password",
auth->details.basic.password);
return authh;
return authh;
}
return NULL;
}
@ -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 */

View File

@ -51,4 +51,14 @@ TALER_BANK_path_to_url_ (const char *u,
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

View File

@ -118,22 +118,37 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
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}",
"type", "test",
"bank_uri", hh->bank_base_url,
"account_number", (json_int_t) other_account);
direction = (0 == strcasecmp (sign,
"+"))
? TALER_BANK_DIRECTION_CREDIT
: TALER_BANK_DIRECTION_DEBIT;
hh->hcb (hh->hcb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
direction,
serial_id,
&td,
transaction);
GNUNET_JSON_parse_free (hist_spec);
json_decref (td.account_details);
GNUNET_JSON_parse_free (hist_spec);
}
return GNUNET_OK;
}
@ -153,6 +168,7 @@ handle_history_finished (void *cls,
const json_t *json)
{
struct TALER_BANK_HistoryHandle *hh = cls;
enum TALER_ErrorCode ec;
hh->job = NULL;
switch (response_code)
@ -166,31 +182,38 @@ handle_history_finished (void *cls,
{
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break;
}
response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
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 */
@ -198,11 +221,13 @@ handle_history_finished (void *cls,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (json);
response_code = 0;
break;
}
hh->hcb (hh->hcb_cls,
response_code,
ec,
TALER_BANK_DIRECTION_NONE,
0LLU,
NULL,
@ -243,6 +268,8 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
struct TALER_BANK_HistoryHandle *hh;
CURL *eh;
char *url;
const char *dir;
const char *can;
if (0 == num_results)
{
@ -254,36 +281,42 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
GNUNET_break (0);
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 (TALER_BANK_DIRECTION_BOTH == direction)
GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld",
(unsigned long long) account_number,
(long long) num_results);
else
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");
GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s",
(unsigned long long) account_number,
(long long) num_results,
dir,
can);
}
else
{
if (TALER_BANK_DIRECTION_BOTH == direction)
GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu",
(unsigned long long) account_number,
(long long) num_results,
(unsigned long long) start_row);
else
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");
GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu&direction=%s&cancelled=%s",
(unsigned long long) account_number,
(long long) num_results,
(unsigned long long) start_row,
dir,
can);
}
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.
*/
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
* 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
* 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;
}
{
const char *wtid;
const char *subject;
uint64_t debit_account;
uint64_t credit_account;
const char *base_url;
struct TALER_Amount amount;
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 ("credit_account", &credit_account),
TALER_JSON_spec_amount ("amount", &amount),
@ -385,7 +471,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
debit_account,
credit_account,
&amount,
wtid,
subject,
base_url);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"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
*
@ -451,6 +625,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
const char *start;
const char *dir;
const char *acc;
const char *cancelled;
unsigned long long account_number;
unsigned long long start_number;
long long count;
@ -469,6 +644,9 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
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");
@ -496,7 +674,14 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
(1 != sscanf (start,
"%llu",
&start_number)) ) ||
( (NULL != dir) &&
(NULL == dir) ||
(NULL == cancelled) ||
( (0 != strcasecmp (cancelled,
"OMIT")) &&
(0 != strcasecmp (cancelled,
"SHOW")) ) ||
( (0 != strcasecmp (dir,
"BOTH")) &&
(0 != strcasecmp (dir,
"CREDIT")) &&
(0 != strcasecmp (dir,
@ -513,13 +698,40 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
dir,
(unsigned long long) account_number,
start);
if (NULL == dir)
direction = TALER_BANK_DIRECTION_BOTH;
else if (0 == strcasecmp (dir,
"CREDIT"))
if (0 == strcasecmp (dir,
"CREDIT"))
{
direction = TALER_BANK_DIRECTION_CREDIT;
else
}
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)
{
if (count > 0)
@ -557,6 +769,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
{
json_t *trans;
char *subject;
const char *sign;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"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->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) ) ) ) )
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;
@ -580,11 +795,15 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
"%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->serial_id,
"date", GNUNET_JSON_from_time_abs (pos->date),
"amount", TALER_JSON_from_amount (&pos->amount),
"sign", (account_number == pos->debit_account) ? "-" : "+",
"sign", sign,
"counterpart", (json_int_t) ( (account_number == pos->debit_account)
? pos->credit_account
: pos->debit_account),
@ -698,6 +917,15 @@ handle_mhd_request (void *cls,
upload_data,
upload_data_size,
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,
"/history")) &&
(0 == strcasecmp (method,

View File

@ -50,6 +50,7 @@ run (void *cls)
{ .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "deposit-1",
.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.credit_account_no = 1,
.details.admin_add_incoming.debit_account_no = 2, /* Ignored */
@ -58,6 +59,7 @@ run (void *cls)
{ .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "deposit-2",
.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.credit_account_no = 1,
.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.start_row_ref = "deposit-1",
.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 }
};

View File

@ -48,6 +48,7 @@ run (void *cls)
/* Add EUR:5.01 to account 1 */
{ .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "debit-1",
.details.admin_add_incoming.subject = "subject 1",
.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,
@ -68,6 +69,7 @@ run (void *cls)
.details.history.num_results = 5 },
{ .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "debit-2",
.details.admin_add_incoming.subject = "subject 2",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 3,
.details.admin_add_incoming.debit_account_no = 2,
@ -75,6 +77,7 @@ run (void *cls)
.details.admin_add_incoming.amount = "KUDOS:3.21" },
{ .oc = TBI_OC_ADMIN_ADD_INCOMING,
.label = "credit-2",
.details.admin_add_incoming.subject = "credit 2",
.details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.credit_account_no = 2,
.details.admin_add_incoming.debit_account_no = 3,
@ -105,6 +108,40 @@ run (void *cls)
/* check transfer list is now empty */
{ .oc = TBI_OC_EXPECT_TRANSFERS_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 }
};

View File

@ -110,7 +110,6 @@ static const struct TBI_Command *
find_command (const struct InterpreterState *is,
const char *label)
{
unsigned int i;
const struct TBI_Command *cmd;
if (NULL == label)
@ -119,7 +118,7 @@ find_command (const struct InterpreterState *is,
"Attempt to lookup command for empty label\n");
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) &&
(0 == strcmp (cmd->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
* command history.
@ -214,6 +270,7 @@ build_history (struct InterpreterState *is,
for (unsigned int off = start;off != end + inc; off += inc)
{
const struct TBI_Command *pos = &is->commands[off];
int cancelled;
if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
continue;
@ -229,6 +286,15 @@ build_history (struct InterpreterState *is,
continue; /* skip until we find the marker */
if (total >= cmd->details.history.num_results * inc)
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)) &&
(cmd->details.history.account_number ==
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)
{
const struct TBI_Command *pos = &is->commands[off];
int cancelled;
if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
continue;
@ -268,6 +335,10 @@ build_history (struct InterpreterState *is,
continue; /* skip until we find the marker */
if (total >= cmd->details.history.num_results * inc)
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)) &&
(cmd->details.history.account_number ==
@ -280,11 +351,19 @@ build_history (struct InterpreterState *is,
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)) &&
(cmd->details.history.account_number ==
pos->details.admin_add_incoming.credit_account_no))
{
h[total].direction = TALER_BANK_DIRECTION_CREDIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}",
"type",
@ -300,6 +379,8 @@ build_history (struct InterpreterState *is,
pos->details.admin_add_incoming.debit_account_no))
{
h[total].direction = TALER_BANK_DIRECTION_DEBIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
h[total].details.account_details
= json_pack ("{s:s, s:s, s:I}",
"type",
@ -323,17 +404,10 @@ build_history (struct InterpreterState *is,
/* h[total].execution_date; // unknown here */
h[total].serial_id
= pos->details.admin_add_incoming.serial_id;
{
char *ws;
ws = GNUNET_STRINGS_data_to_string_alloc (&pos->details.admin_add_incoming.wtid,
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);
}
GNUNET_asprintf (&h[total].details.wire_transfer_subject,
"%s %s",
pos->details.admin_add_incoming.subject,
pos->details.admin_add_incoming.exchange_base_url);
total++;
}
}
@ -488,18 +562,34 @@ static void
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.
*
* @param cls closure with the interpreter state
* @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)
* @param ec taler status code
* @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
*/
static void
add_incoming_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id,
const json_t *json)
{
@ -522,9 +612,7 @@ add_incoming_cb (void *cls,
fail (is);
return;
}
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
next (is);
}
@ -538,6 +626,7 @@ add_incoming_cb (void *cls,
* #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
* already returned).
* @param ec taler status code
* @param dir direction of the transfer
* @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer
@ -546,6 +635,7 @@ add_incoming_cb (void *cls,
static void
history_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
uint64_t serial_id,
const struct TALER_BANK_TransferDetails *details,
@ -580,9 +670,7 @@ history_cb (void *cls,
fail (is);
return;
}
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
next (is);
return;
}
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.
*
@ -658,15 +778,13 @@ interpreter_run (void *cls)
fail (is);
return;
}
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&cmd->details.admin_add_incoming.wtid,
sizeof (cmd->details.admin_add_incoming.wtid));
GNUNET_break (NULL != cmd->details.admin_add_incoming.subject);
cmd->details.admin_add_incoming.aih
= TALER_BANK_admin_add_incoming (is->ctx,
"http://localhost:8080",
&auth,
cmd->details.admin_add_incoming.exchange_base_url,
&cmd->details.admin_add_incoming.wtid,
cmd->details.admin_add_incoming.subject,
&amount,
cmd->details.admin_add_incoming.debit_account_no,
cmd->details.admin_add_incoming.credit_account_no,
@ -722,7 +840,6 @@ interpreter_run (void *cls)
&amount));
{
char *subject;
char *expect;
if (GNUNET_OK !=
TALER_FAKEBANK_check (is->fakebank,
@ -736,22 +853,17 @@ interpreter_run (void *cls)
fail (is);
return;
}
expect = GNUNET_STRINGS_data_to_string_alloc (&ref->details.admin_add_incoming.wtid,
sizeof (ref->details.admin_add_incoming.wtid));
if (0 != strcmp (subject, expect))
if (0 != strcmp (ref->details.admin_add_incoming.subject,
subject))
{
GNUNET_free (expect);
GNUNET_free (subject);
GNUNET_break (0);
fail (is);
return;
}
GNUNET_free (subject);
GNUNET_free (expect);
}
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
next (is);
return;
case TBI_OC_EXPECT_TRANSFERS_EMPTY:
if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank))
@ -760,9 +872,27 @@ interpreter_run (void *cls)
fail (is);
return;
}
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
next (is);
return;
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;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -802,7 +932,6 @@ do_shutdown (void *cls)
{
struct InterpreterState *is = cls;
struct TBI_Command *cmd;
unsigned int i;
if (NULL != is->timeout_task)
{
@ -810,7 +939,7 @@ do_shutdown (void *cls)
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)
{
@ -843,6 +972,17 @@ do_shutdown (void *cls)
break;
case TBI_OC_EXPECT_TRANSFERS_EMPTY:
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:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"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.
*/
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;
/**
* Wire transfer identifier to use. Initialized to
* a random value.
* Wire transfer subject to use.
*/
struct TALER_WireTransferIdentifierRawP wtid;
const char *subject;
/**
* Which response code do we expect for this command?
@ -186,6 +190,23 @@ struct TBI_Command
} 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;
};

View File

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

View File

@ -1,6 +1,6 @@
/*
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
terms of the GNU Affero General Public License as published by the Free Software
@ -25,6 +25,7 @@
#include <jansson.h>
#include <gnunet/gnunet_curl_lib.h>
#include "taler_util.h"
#include "taler_error_codes.h"
/**
@ -98,12 +99,14 @@ struct TALER_BANK_AdminAddIncomingHandle;
* @param cls closure
* @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)
* @param ec detailed error code
* @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
*/
typedef void
(*TALER_BANK_AdminAddIncomingResultCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id,
const json_t *json);
@ -118,7 +121,7 @@ typedef void
* @param bank_base_url URL of the bank (used to execute this request)
* @param auth authentication data to use
* @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 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)
@ -133,7 +136,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth,
const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
const char *subject,
const struct TALER_Amount *amount,
uint64_t debit_account_no,
uint64_t credit_account_no,
@ -174,7 +177,15 @@ enum TALER_BANK_Direction {
/**
* 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
* last callback is always of this status (even if `abs(num_results)` were
* already returned).
* @param ec detailed error code
* @param dir direction of the transfer
* @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer
@ -230,6 +242,7 @@ struct TALER_BANK_TransferDetails
typedef void
(*TALER_BANK_HistoryResultCallback) (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
uint64_t serial_id,
const struct TALER_BANK_TransferDetails *details,
@ -277,5 +290,61 @@ void
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 */

View File

@ -1488,6 +1488,34 @@ enum TALER_ErrorCode
*/
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.

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
* list. If the transaction was not recorded, return #GNUNET_SYSERR.
*
* Rejected transfers do NOT show with "check".
*
* @param h bank instance
* @param want_amount transfer amount desired
* @param want_debit account that should have been debited
@ -106,6 +108,21 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
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.
*

View File

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

View File

@ -1,6 +1,6 @@
/*
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
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 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)
* @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 json detailed response from the HTTPD, or NULL if reply was not JSON
*/
static void
execute_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id,
const json_t *json)
{
@ -578,13 +580,15 @@ execute_cb (void *cls,
}
if (NULL != emsg)
GNUNET_asprintf (&s,
"%u (%s)",
"%u/%u (%s)",
http_status,
(unsigned int) ec,
emsg);
else
GNUNET_asprintf (&s,
"%u",
http_status);
"%u/%u",
http_status,
(unsigned int) ec);
eh->cc (eh->cc_cls,
(MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
serial_id,
@ -676,6 +680,7 @@ test_execute_wire_transfer (void *cls,
char *emsg;
const char *json_s;
const char *exchange_base_url;
char *wire_s;
if (NULL == tc->ctx)
{
@ -728,16 +733,19 @@ test_execute_wire_transfer (void *cls,
eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
eh->cc = cc;
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,
tc->bank_uri,
&tc->auth,
exchange_base_url,
&bf.wtid,
wire_s,
&amount,
(uint64_t) tc->exchange_account_no,
(uint64_t) account_no,
&execute_cb,
eh);
GNUNET_free (wire_s);
json_decref (wire);
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
* last callback is always of this status (even if `abs(num_results)` were
* already returned).
* @param ec taler error code
* @param dir direction of the transfer
* @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer
@ -811,6 +820,7 @@ struct TALER_WIRE_HistoryHandle
static void
bhist_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
uint64_t serial_id,
const struct TALER_BANK_TransferDetails *details,
@ -852,12 +862,17 @@ bhist_cb (void *cls,
sizeof (wd.wtid));
wd.wtid_s = details->wire_transfer_subject;
}
else
{
wd.wtid_s = NULL;
}
GNUNET_free (subject);
wd.account_details = details->account_details;
if ( (NULL != whh->hres_cb) &&
(GNUNET_OK !=
whh->hres_cb (whh->hres_cb_cls,
TALER_EC_NONE,
dir,
&bserial_id,
sizeof (bserial_id),
@ -868,6 +883,7 @@ bhist_cb (void *cls,
case MHD_HTTP_NO_CONTENT:
if (NULL != whh->hres_cb)
(void) whh->hres_cb (whh->hres_cb_cls,
ec,
TALER_BANK_DIRECTION_NONE,
NULL,
0,
@ -880,6 +896,7 @@ bhist_cb (void *cls,
GNUNET_break (0);
if (NULL != whh->hres_cb)
(void) whh->hres_cb (whh->hres_cb_cls,
ec,
TALER_BANK_DIRECTION_NONE,
NULL,
0,
@ -1004,26 +1021,32 @@ struct TALER_WIRE_RejectHandle
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
* and clean up.
* Callbacks of this type are used to serve the result of asking
* 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
timeout_reject (void *cls)
reject_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec)
{
struct TALER_WIRE_RejectHandle *rh = cls;
rh->timeout_task = NULL;
rh->brh = NULL;
rh->rej_cb (rh->rej_cb_cls,
TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
ec);
GNUNET_free (rh);
}
@ -1052,14 +1075,30 @@ test_reject_transfer (void *cls,
TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls)
{
struct TestClosure *tc = cls;
const uint64_t *rowid_b64 = start_off;
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->rej_cb = rej_cb;
rh->rej_cb_cls = rej_cb_cls;
rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
rh);
rh->brh = TALER_BANK_reject (tc->ctx,
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;
}
@ -1082,7 +1121,8 @@ test_reject_transfer_cancel (void *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);
return ret;
}

View File

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