implementing #4356, tests still failing, but main logic should now be updated

This commit is contained in:
Christian Grothoff 2016-04-01 16:15:35 +02:00
parent 92907bee45
commit de3e26303e
13 changed files with 277 additions and 646 deletions

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V. Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
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
@ -128,257 +128,6 @@ verify_wire_method_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh,
} }
/**
* Perform the next /wire/method request or signal
* the end of the iteration.
*
* @param wh the wire handle
* @return a handle for this request
*/
static void
request_wire_method (struct TALER_EXCHANGE_WireHandle *wh);
/**
* Function called when we're done processing the
* HTTP /wire/METHOD request.
*
* @param cls the `struct TALER_EXCHANGE_WireHandle`
* @param eh the curl request handle
*/
static void
handle_wire_method_finished (void *cls,
CURL *eh)
{
struct TALER_EXCHANGE_WireHandle *wh = cls;
long response_code;
json_t *json;
wh->job = NULL;
json = MAC_download_get_result (&wh->db,
eh,
&response_code);
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
const char *method;
method = json_string_value (json_array_get (wh->methods,
wh->methods_off - 1));
if (GNUNET_OK !=
verify_wire_method_signature_ok (wh,
method,
json))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
break;
}
case MHD_HTTP_FOUND:
/* /wire/test returns a 302 redirect, we should just give
this information back to the callback below */
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
response_code);
GNUNET_break (0);
response_code = 0;
break;
}
if (0 == response_code)
{
/* signal end of iteration */
wh->cb (wh->cb_cls,
0,
NULL,
NULL);
json_decref (json);
TALER_EXCHANGE_wire_cancel (wh);
return;
}
/* pass on successful reply */
wh->cb (wh->cb_cls,
response_code,
json_string_value (json_array_get (wh->methods,
wh->methods_off-1)),
json);
json_decref (json);
/* trigger request for the next /wire/method */
request_wire_method (wh);
}
/**
* Perform the next /wire/method request or signal
* the end of the iteration.
*
* @param wh the wire handle
* @return a handle for this request
*/
static void
request_wire_method (struct TALER_EXCHANGE_WireHandle *wh)
{
struct TALER_EXCHANGE_Context *ctx;
CURL *eh;
char *path;
if (json_array_size (wh->methods) <= wh->methods_off)
{
/* we are done, signal end of iteration */
wh->cb (wh->cb_cls,
0,
NULL,
NULL);
TALER_EXCHANGE_wire_cancel (wh);
return;
}
GNUNET_free_non_null (wh->db.buf);
wh->db.buf = NULL;
wh->db.buf_size = 0;
wh->db.eno = 0;
GNUNET_free_non_null (wh->url);
GNUNET_asprintf (&path,
"/wire/%s",
json_string_value (json_array_get (wh->methods,
wh->methods_off++)));
wh->url = MAH_path_to_url (wh->exchange,
path);
GNUNET_free (path);
eh = curl_easy_init ();
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
wh->url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEFUNCTION,
&MAC_download_cb));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEDATA,
&wh->db));
/* The default is 'disabled', but let's be sure */
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_FOLLOWLOCATION,
(long) 0));
ctx = MAH_handle_to_context (wh->exchange);
wh->job = MAC_job_add (ctx,
eh,
GNUNET_YES,
&handle_wire_method_finished,
wh);
TALER_EXCHANGE_perform (ctx);
}
/**
* Verify that the signature on the "200 OK" response
* for /wire from the exchange is valid.
*
* @param wh wire handle
* @param json json reply with the signature
* @return NULL if @a json is invalid, otherwise the
* "methods" array (with an RC of 1)
*/
static json_t *
verify_wire_signature_ok (const struct TALER_EXCHANGE_WireHandle *wh,
json_t *json)
{
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeWireSupportMethodsPS mp;
json_t *methods;
const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_HashContext *hc;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig", &exchange_sig),
GNUNET_JSON_spec_fixed_auto ("pub", &exchange_pub),
GNUNET_JSON_spec_json ("methods", &methods),
GNUNET_JSON_spec_end()
};
unsigned int i;
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return NULL;
}
if (! json_is_array (methods))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return NULL;
}
key_state = TALER_EXCHANGE_get_keys (wh->exchange);
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_state,
&exchange_pub))
{
GNUNET_break_op (0);
return NULL;
}
hc = GNUNET_CRYPTO_hash_context_start ();
for (i=0;i<json_array_size (methods);i++)
{
const json_t *element = json_array_get (methods, i);
const char *method;
if (! json_is_string (element))
{
GNUNET_CRYPTO_hash_context_abort (hc);
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return NULL;
}
method = json_string_value (element);
GNUNET_CRYPTO_hash_context_read (hc,
method,
strlen (method) + 1);
}
mp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_WIRE_TYPES);
mp.purpose.size = htonl (sizeof (struct TALER_ExchangeWireSupportMethodsPS));
GNUNET_CRYPTO_hash_context_finish (hc,
&mp.h_wire_types);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_WIRE_TYPES,
&mp.purpose,
&exchange_sig.eddsa_signature,
&exchange_pub.eddsa_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return NULL;
}
return methods;
}
/** /**
* Function called when we're done processing the * Function called when we're done processing the
* HTTP /wire request. * HTTP /wire request.
@ -404,19 +153,47 @@ handle_wire_finished (void *cls,
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
json_t *methods; const char *key;
json_t *method;
json_t *keep;
int ret;
if (NULL == /* We 'keep' methods that we support and that are well-formed;
(methods = verify_wire_signature_ok (wh, we fail (by setting response_code=0) if any method that we do
json))) support fails to verify. */
keep = json_object ();
json_object_foreach (json, key, method) {
ret = verify_wire_method_signature_ok (wh,
key,
method);
if (GNUNET_SYSERR == ret)
{
/* bogus reply */
GNUNET_break_op (0);
response_code = 0;
}
/* GNUNET_NO: not understood by us, simply skip! */
if (GNUNET_OK == ret)
{
/* supported and valid, keep! */
json_object_set (keep,
key,
method);
}
}
if (0 != response_code)
{ {
GNUNET_break_op (0); /* all supported methods were valid, use 'keep' for 'json' */
response_code = 0; json_decref (json);
json = keep;
break; break;
} }
wh->methods = methods; else
request_wire_method (wh); {
return; /* some supported methods were invalid, release 'keep', preserve
full 'json' for application-level error handling. */
json_decref (keep);
}
} }
break; break;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
@ -440,19 +217,9 @@ handle_wire_finished (void *cls,
response_code = 0; response_code = 0;
break; break;
} }
if (0 != response_code)
{
/* pass on successful reply */
wh->cb (wh->cb_cls,
response_code,
NULL,
json);
}
/* signal end of iteration */
wh->cb (wh->cb_cls, wh->cb (wh->cb_cls,
0, response_code,
NULL, json);
NULL);
if (NULL != json) if (NULL != json)
json_decref (json); json_decref (json);
TALER_EXCHANGE_wire_cancel (wh); TALER_EXCHANGE_wire_cancel (wh);
@ -481,8 +248,8 @@ handle_wire_finished (void *cls,
*/ */
struct TALER_EXCHANGE_WireHandle * struct TALER_EXCHANGE_WireHandle *
TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange, TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange,
TALER_EXCHANGE_WireResultCallback wire_cb, TALER_EXCHANGE_WireResultCallback wire_cb,
void *wire_cb_cls) void *wire_cb_cls)
{ {
struct TALER_EXCHANGE_WireHandle *wh; struct TALER_EXCHANGE_WireHandle *wh;
struct TALER_EXCHANGE_Context *ctx; struct TALER_EXCHANGE_Context *ctx;

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015, 2016 GNUnet e.V. Copyright (C) 2014, 2015, 2016 GNUnet e.V. and Inria
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
@ -1213,76 +1213,56 @@ find_pk (const struct TALER_EXCHANGE_Keys *keys,
* Callbacks called with the result(s) of a * Callbacks called with the result(s) of a
* wire format inquiry request to the exchange. * wire format inquiry request to the exchange.
* *
* The callback is invoked multiple times, once for each supported @a
* method. Finally, it is invoked one more time with cls/0/NULL/NULL
* to indicate the end of the iteration. If any request fails to
* generate a valid response from the exchange, @a http_status will also
* be zero and the iteration will also end. Thus, the iteration
* always ends with a final call with an @a http_status of 0. If the
* @a http_status is already 0 on the first call, then the response to
* the /wire request was invalid. Later, clients can tell the
* difference between @a http_status of 0 indicating a failed
* /wire/method request and a regular end of the iteration by @a
* method being non-NULL. If the exchange simply correctly asserts that
* it does not support any methods, @a method will be NULL but the @a
* http_status will be #MHD_HTTP_OK for the first call (followed by a
* cls/0/NULL/NULL call to signal the end of the iteration).
*
* @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 request; * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request;
* 0 if the exchange's reply is bogus (fails to follow the protocol) * 0 if the exchange's reply is bogus (fails to follow the protocol)
* @param method wire format method supported, i.e. "test" or "sepa", or NULL
* if already the /wire request failed.
* @param obj the received JSON reply, if successful this should be the wire * @param obj the received JSON reply, if successful this should be the wire
* format details as provided by /wire/METHOD/, or NULL if the * format details as provided by /wire.
* reply was not in JSON format (in this case, the client might
* want to do an HTTP request to /wire/METHOD/ with a browser to
* provide more information to the user about the @a method).
*/ */
static void static void
wire_cb (void *cls, wire_cb (void *cls,
unsigned int http_status, unsigned int http_status,
const char *method,
json_t *obj) json_t *obj)
{ {
struct InterpreterState *is = cls; struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip]; struct Command *cmd = &is->commands[is->ip];
if (0 == http_status)
{
/* 0 always signals the end of the iteration */
cmd->details.wire.wh = NULL;
}
else if ( (NULL != method) &&
(0 != strcasecmp (method,
cmd->details.wire.format)) )
{
/* not the method we care about, skip */
return;
}
if (cmd->expected_response_code != http_status) if (cmd->expected_response_code != http_status)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s/%s\n", "Unexpected response code %u to command %s\n",
http_status, http_status,
cmd->label, cmd->label);
method);
json_dumpf (obj, stderr, 0); json_dumpf (obj, stderr, 0);
fail (is); fail (is);
return; return;
} }
if (0 == http_status) switch (http_status)
{ {
/* end of iteration, move to next command */ case MHD_HTTP_OK:
is->ip++; {
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, json_t *method;
is);
return; method = json_object_get (obj,
cmd->details.wire.format);
if (NULL == method)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Expected method `%s' not included in response to command %s\n",
cmd->details.wire.format,
cmd->label);
json_dumpf (obj, stderr, 0);
fail (is);
return;
}
}
break;
default:
break;
} }
/* For now, we only support to be called only once is->ip++;
with a "positive" result; so we switch to an is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
expected value of 0 for the 2nd iteration */ is);
cmd->expected_response_code = 0;
} }
@ -1899,9 +1879,9 @@ interpreter_run (void *cls,
/* finally, use private key from withdraw sign command */ /* finally, use private key from withdraw sign command */
cmd->details.refresh_link.rlh cmd->details.refresh_link.rlh
= TALER_EXCHANGE_refresh_link (exchange, = TALER_EXCHANGE_refresh_link (exchange,
&ref->details.reserve_withdraw.coin_priv, &ref->details.reserve_withdraw.coin_priv,
&link_cb, &link_cb,
is); is);
if (NULL == cmd->details.refresh_link.rlh) if (NULL == cmd->details.refresh_link.rlh)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1912,8 +1892,8 @@ interpreter_run (void *cls,
return; return;
case OC_WIRE: case OC_WIRE:
cmd->details.wire.wh = TALER_EXCHANGE_wire (exchange, cmd->details.wire.wh = TALER_EXCHANGE_wire (exchange,
&wire_cb, &wire_cb,
is); is);
trigger_context_task (); trigger_context_task ();
return; return;
case OC_WIRE_DEPOSITS: case OC_WIRE_DEPOSITS:
@ -1926,9 +1906,9 @@ interpreter_run (void *cls,
} }
cmd->details.wire_deposits.wdh cmd->details.wire_deposits.wdh
= TALER_EXCHANGE_wire_deposits (exchange, = TALER_EXCHANGE_wire_deposits (exchange,
&cmd->details.wire_deposits.wtid, &cmd->details.wire_deposits.wtid,
&wire_deposits_cb, &wire_deposits_cb,
is); is);
trigger_context_task (); trigger_context_task ();
return; return;
case OC_DEPOSIT_WTID: case OC_DEPOSIT_WTID:
@ -2337,14 +2317,14 @@ run (void *cls,
#if WIRE_TEST #if WIRE_TEST
{ .oc = OC_WIRE, { .oc = OC_WIRE,
.label = "wire-test", .label = "wire-test",
/* /wire/test replies with a 200 OK */ /* expecting 'test' method in response */
.expected_response_code = MHD_HTTP_OK, .expected_response_code = MHD_HTTP_OK,
.details.wire.format = "test" }, .details.wire.format = "test" },
#endif #endif
#if WIRE_SEPA #if WIRE_SEPA
{ .oc = OC_WIRE, { .oc = OC_WIRE,
.label = "wire-sepa", .label = "wire-sepa",
/* /wire/sepa replies with a 200 redirect */ /* expecting 'sepa' method in response */
.expected_response_code = MHD_HTTP_OK, .expected_response_code = MHD_HTTP_OK,
.details.wire.format = "sepa" }, .details.wire.format = "sepa" },
#endif #endif

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V. Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
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
@ -177,20 +177,6 @@ handle_mhd_request (void *cls,
"Only GET is allowed", 0, "Only GET is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
{ "/wire/test", MHD_HTTP_METHOD_GET, "application/json",
NULL, 0,
&TMH_WIRE_handler_wire_test, MHD_HTTP_FOUND },
{ "/wire/test", NULL, "text/plain",
"Only GET is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
{ "/wire/sepa", MHD_HTTP_METHOD_GET, "application/json",
NULL, 0,
&TMH_WIRE_handler_wire_sepa, MHD_HTTP_OK },
{ "/wire/sepa", NULL, "text/plain",
"Only GET is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
/* Withdrawing coins / interaction with reserves */ /* Withdrawing coins / interaction with reserves */
{ "/reserve/status", MHD_HTTP_METHOD_GET, "application/json", { "/reserve/status", MHD_HTTP_METHOD_GET, "application/json",
NULL, 0, NULL, 0,

View File

@ -200,33 +200,39 @@ TMH_VALIDATION_test_method (const char *type)
/** /**
* Obtain supported validation methods as a JSON array, * Obtain JSON of the supported wire methods for a given
* and as a hash. * account name prefix.
* *
* @param[out] h set to the hash of the JSON methods * @param prefix prefix for the account, the suffix will
* be determined by the name of the plugin
* @return JSON array with the supported validation methods * @return JSON array with the supported validation methods
*/ */
json_t * json_t *
TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h) TMH_VALIDATION_get_wire_methods (const char *prefix)
{ {
json_t *methods; json_t *methods;
struct GNUNET_HashContext *hc; json_t *method;
const char *wf;
struct Plugin *p; struct Plugin *p;
struct TALER_WIRE_Plugin *plugin;
char *account_name;
methods = json_array (); methods = json_object ();
hc = GNUNET_CRYPTO_hash_context_start ();
for (p=wire_head;NULL != p;p = p->next) for (p=wire_head;NULL != p;p = p->next)
{ {
wf = p->type; plugin = p->plugin;
json_array_append_new (methods, GNUNET_asprintf (&account_name,
json_string (wf)); "%s-%s\n",
GNUNET_CRYPTO_hash_context_read (hc, prefix,
wf, p->type);
strlen (wf) + 1); method = plugin->get_wire_details (plugin->cls,
cfg,
account_name);
if (NULL != method)
json_object_set_new (methods,
p->type,
method);
GNUNET_free (account_name);
} }
GNUNET_CRYPTO_hash_context_finish (hc,
h);
return methods; return methods;
} }

View File

@ -63,14 +63,15 @@ TMH_VALIDATION_test_method (const char *type);
/** /**
* Obtain supported validation methods as a JSON array, * Obtain JSON of the supported wire methods for a given
* and as a hash. * account name prefix.
* *
* @param[out] h set to the hash of the JSON methods * @param prefix prefix for the account, the suffix will
* be determined by the name of the plugin
* @return JSON array with the supported validation methods * @return JSON array with the supported validation methods
*/ */
json_t * json_t *
TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h); TMH_VALIDATION_get_wire_methods (const char *prefix);
#endif #endif

View File

@ -43,191 +43,15 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh,
const char *upload_data, const char *upload_data,
size_t *upload_data_size) size_t *upload_data_size)
{ {
struct TALER_ExchangeWireSupportMethodsPS wsm; static json_t *wire_methods;
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;
json_t *methods;
wsm.purpose.size = htonl (sizeof (wsm)); if (NULL == wire_methods)
wsm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_WIRE_TYPES); wire_methods = TMH_VALIDATION_get_wire_methods ("wire-incoming");
methods = TMH_VALIDATION_get_methods (&wsm.h_wire_types);
TMH_KS_sign (&wsm.purpose, return TMH_RESPONSE_reply_json (connection,
&pub, wire_methods,
&sig); MHD_HTTP_OK);
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:o, s:o}",
"methods", methods,
"sig", GNUNET_JSON_from_data (&sig,
sizeof (sig)),
"pub", GNUNET_JSON_from_data (&pub,
sizeof (pub)));
} }
/**
* Handle a "/wire/test" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
int
TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size)
{
struct MHD_Response *response;
int ret;
char *bank_uri;
unsigned long long account_number;
response = MHD_create_response_from_buffer (0, NULL,
MHD_RESPMEM_PERSISTENT);
if (NULL == response)
{
GNUNET_break (0);
return MHD_NO;
}
TMH_RESPONSE_add_global_headers (response);
if (GNUNET_NO == TMH_VALIDATION_test_method ("test"))
{
/* Return 501: not implemented */
ret = MHD_queue_response (connection,
MHD_HTTP_NOT_IMPLEMENTED,
response);
MHD_destroy_response (response);
return ret;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"wire-test",
"BANK_URI",
&bank_uri))
{
/* oopsie, configuration error */
MHD_destroy_response (response);
return TMH_RESPONSE_reply_internal_error (connection,
"BANK_URI not configured");
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
"wire-test",
"BANK_ACCOUNT_NO_INCOMING",
&account_number))
{
/* oopsie, configuration error */
MHD_destroy_response (response);
GNUNET_free (bank_uri);
return TMH_RESPONSE_reply_internal_error (connection,
"BANK_ACCOUNT_NO_INCOMING not configured");
}
ret = TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:s, s:I, s:s}",
"type", "test",
"account_number", (json_int_t) account_number,
"bank_uri", bank_uri);
GNUNET_free (bank_uri);
return ret;
}
/**
* Handle a "/wire/sepa" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
int
TMH_WIRE_handler_wire_sepa (struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size)
{
struct MHD_Response *response;
int ret;
char *sepa_wire_file;
int fd;
struct stat sbuf;
if (GNUNET_NO == TMH_VALIDATION_test_method ("sepa"))
{
/* Return 501: not implemented */
response = MHD_create_response_from_buffer (0, NULL,
MHD_RESPMEM_PERSISTENT);
if (NULL == response)
{
GNUNET_break (0);
return MHD_NO;
}
TMH_RESPONSE_add_global_headers (response);
ret = MHD_queue_response (connection,
MHD_HTTP_NOT_IMPLEMENTED,
response);
MHD_destroy_response (response);
return ret;
}
/* Fetch reply */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (cfg,
"wire-sepa",
"SEPA_RESPONSE_FILE",
&sepa_wire_file))
{
return TMH_RESPONSE_reply_internal_error (connection,
"SEPA_RESPONSE_FILE not configured");
}
fd = open (sepa_wire_file,
O_RDONLY);
if (-1 == fd)
{
GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
"open",
sepa_wire_file);
GNUNET_free (sepa_wire_file);
return TMH_RESPONSE_reply_internal_error (connection,
"Failed to open SEPA_RESPONSE_FILE");
}
if (0 != fstat (fd, &sbuf))
{
GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
"fstat",
sepa_wire_file);
(void) close (fd);
GNUNET_free (sepa_wire_file);
return TMH_RESPONSE_reply_internal_error (connection,
"Failed to open SEPA_RESPONSE_FILE");
}
response = MHD_create_response_from_fd ((size_t) sbuf.st_size,
fd);
GNUNET_free (sepa_wire_file);
if (NULL == response)
{
(void) close (fd);
GNUNET_break (0);
return MHD_NO;
}
TMH_RESPONSE_add_global_headers (response);
if (NULL != rh->mime_type)
(void) MHD_add_response_header (response,
MHD_HTTP_HEADER_CONTENT_TYPE,
rh->mime_type);
ret = MHD_queue_response (connection,
rh->response_code,
response);
MHD_destroy_response (response);
return ret;
}
/* end of taler-exchange-httpd_wire.c */ /* end of taler-exchange-httpd_wire.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014 GNUnet e.V. Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
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
@ -44,39 +44,4 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh,
size_t *upload_data_size); size_t *upload_data_size);
/**
* Handle a "/wire/test" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
int
TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size);
/**
* Handle a "/wire/sepa" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param[in,out] connection_cls the connection's closure (can be updated)
* @param upload_data upload data
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
* @return MHD result code
*/
int
TMH_WIRE_handler_wire_sepa (struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size);
#endif #endif

View File

@ -396,53 +396,35 @@ struct TALER_EXCHANGE_WireHandle;
* Callbacks of this type are used to serve the result of submitting a * Callbacks of this type are used to serve the result of submitting a
* wire format inquiry request to a exchange. * wire format inquiry request to a exchange.
* *
* The callback is invoked multiple times, once for each supported @a * If the request fails to generate a valid response from the
* method. Finally, it is invoked one more time with cls/0/NULL/NULL * exchange, @a http_status will also be zero.
* to indicate the end of the iteration. If any request fails to
* generate a valid response from the exchange, @a http_status will also
* be zero and the iteration will also end. Thus, the iteration
* always ends with a final call with an @a http_status of 0. If the
* @a http_status is already 0 on the first call, then the response to
* the /wire request was invalid. Later, clients can tell the
* difference between @a http_status of 0 indicating a failed
* /wire/method request and a regular end of the iteration by @a
* method being non-NULL. If the exchange simply correctly asserts that
* it does not support any methods, @a method will be NULL but the @a
* http_status will be #MHD_HTTP_OK for the first call (followed by a
* cls/0/NULL/NULL call to signal the end of the iteration).
* *
* @param cls closure * @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request; * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful request;
* 0 if the exchange's reply is bogus (fails to follow the protocol) * 0 if the exchange's reply is bogus (fails to follow the protocol)
* @param method wire format method supported, i.e. "test" or "sepa", or NULL
* if already the /wire request failed.
* @param obj the received JSON reply, if successful this should be the wire * @param obj the received JSON reply, if successful this should be the wire
* format details as provided by /wire/METHOD/, or NULL if the * format details as provided by /wire, or NULL if the
* reply was not in JSON format (in this case, the client might * reply was not in JSON format.
* want to do an HTTP request to /wire/METHOD/ with a browser to
* provide more information to the user about the @a method).
*/ */
typedef void typedef void
(*TALER_EXCHANGE_WireResultCallback) (void *cls, (*TALER_EXCHANGE_WireResultCallback) (void *cls,
unsigned int http_status, unsigned int http_status,
const char *method,
json_t *obj); json_t *obj);
/** /**
* Obtain information about a exchange's wire instructions. * Obtain information about a exchange's wire instructions. A
* A exchange may provide wire instructions for creating * exchange may provide wire instructions for creating a reserve. The
* a reserve. The wire instructions also indicate * wire instructions also indicate which wire formats merchants may
* which wire formats merchants may use with the exchange. * use with the exchange. This API is typically used by a wallet for
* This API is typically used by a wallet for wiring * wiring funds, and possibly by a merchant to determine supported
* funds, and possibly by a merchant to determine * wire formats.
* supported wire formats.
* *
* Note that while we return the (main) response verbatim to the * Note that while we return the (main) response verbatim to the
* caller for further processing, we do already verify that the * caller for further processing, we do already verify that the
* response is well-formed (i.e. that signatures included in the * response is well-formed (i.e. that signatures included in the
* response are all valid). If the exchange's reply is not well-formed, * response are all valid). If the exchange's reply is not
* we return an HTTP status code of zero to @a cb. * well-formed, we return an HTTP status code of zero to @a cb.
* *
* @param exchange the exchange handle; the exchange must be ready to operate * @param exchange the exchange handle; the exchange must be ready to operate
* @param wire_cb the callback to call when a reply for this request is available * @param wire_cb the callback to call when a reply for this request is available
@ -451,8 +433,8 @@ typedef void
*/ */
struct TALER_EXCHANGE_WireHandle * struct TALER_EXCHANGE_WireHandle *
TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange, TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange,
TALER_EXCHANGE_WireResultCallback wire_cb, TALER_EXCHANGE_WireResultCallback wire_cb,
void *wire_cb_cls); void *wire_cb_cls);
/** /**

View File

@ -101,11 +101,6 @@
*/ */
#define TALER_SIGNATURE_EXCHANGE_KEY_SET 1035 #define TALER_SIGNATURE_EXCHANGE_KEY_SET 1035
/**
* Signature where the Exchange confirms the /wire response.
*/
#define TALER_SIGNATURE_EXCHANGE_WIRE_TYPES 1036
/** /**
* Signature where the Exchange confirms the /deposit/wtid response. * Signature where the Exchange confirms the /deposit/wtid response.
*/ */
@ -784,27 +779,6 @@ struct TALER_MasterWireSepaDetailsPS
}; };
/**
* @brief Information signed by a exchange's online signing key affirming
* the wire formats supported by the exchange.
*/
struct TALER_ExchangeWireSupportMethodsPS
{
/**
* Purpose is #TALER_SIGNATURE_EXCHANGE_WIRE_TYPES.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Hash over the various wire formats supported by this exchange
* (all as 0-terminated strings).
*/
struct GNUNET_HashCode h_wire_types GNUNET_PACKED;
};
/** /**
* @brief Format used to generate the signature on a request to obtain * @brief Format used to generate the signature on a request to obtain
* the wire transfer identifier associated with a deposit. * the wire transfer identifier associated with a deposit.

View File

@ -98,6 +98,21 @@ struct TALER_WIRE_Plugin
struct TALER_Amount *amount); struct TALER_Amount *amount);
/**
* Obtain wire transfer details in the plugin-specific format
* from the configuration.
*
* @param cls closure
* @param cfg configuration with details about wire accounts
* @param account_name which section in the configuration should we parse
* @return NULL if @a cfg fails to have valid wire details for @a account_name
*/
json_t *
(*get_wire_details)(void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *account_name);
/** /**
* Check if the given wire format JSON object is correctly formatted * Check if the given wire format JSON object is correctly formatted
* *

View File

@ -380,7 +380,7 @@ verify_wire_sepa_signature_ok (const json_t *json,
if (NULL == master_pub) if (NULL == master_pub)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Skipping signature check as master public key not given\n"); "Skipping signature check as master public key not given\n");
return GNUNET_OK; return GNUNET_OK;
} }
@ -491,6 +491,63 @@ sepa_wire_validate (void *cls,
} }
/**
* Obtain wire transfer details in the plugin-specific format
* from the configuration.
*
* @param cls closure
* @param cfg configuration with details about wire accounts
* @param account_name which section in the configuration should we parse
* @return NULL if @a cfg fails to have valid wire details for @a account_name
*/
static json_t *
sepa_get_wire_details (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *account_name)
{
char *sepa_wire_file;
json_error_t err;
json_t *ret;
/* Fetch reply */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_filename (cfg,
account_name,
"SEPA_RESPONSE_FILE",
&sepa_wire_file))
{
return NULL;
}
ret = json_load_file (sepa_wire_file,
JSON_REJECT_DUPLICATES,
&err);
if (NULL == ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse JSON in %s: %s (%s:%u)\n",
sepa_wire_file,
err.text,
err.source,
err.line);
GNUNET_free (sepa_wire_file);
return NULL;
}
if (GNUNET_YES != sepa_wire_validate (cls,
ret,
NULL))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to validate SEPA data in %s\n",
sepa_wire_file);
GNUNET_free (sepa_wire_file);
json_decref (ret);
return NULL;
}
GNUNET_free (sepa_wire_file);
return ret;
}
/** /**
* Prepare for exeuction of a wire transfer. * Prepare for exeuction of a wire transfer.
* *
@ -604,6 +661,7 @@ libtaler_plugin_wire_sepa_init (void *cls)
plugin = GNUNET_new (struct TALER_WIRE_Plugin); plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = sc; plugin->cls = sc;
plugin->amount_round = &sepa_amount_round; plugin->amount_round = &sepa_amount_round;
plugin->get_wire_details = &sepa_get_wire_details;
plugin->wire_validate = &sepa_wire_validate; plugin->wire_validate = &sepa_wire_validate;
plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer; plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel; plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel;

View File

@ -73,6 +73,25 @@ template_amount_round (void *cls,
} }
/**
* Obtain wire transfer details in the plugin-specific format
* from the configuration.
*
* @param cls closure
* @param cfg configuration with details about wire accounts
* @param account_name which section in the configuration should we parse
* @return NULL if @a cfg fails to have valid wire details for @a account_name
*/
static json_t *
template_get_wire_details (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *account_name)
{
GNUNET_break (0);
return NULL;
}
/** /**
* Check if the given wire format JSON object is correctly formatted * Check if the given wire format JSON object is correctly formatted
* *
@ -216,6 +235,7 @@ libtaler_plugin_wire_template_init (void *cls)
plugin = GNUNET_new (struct TALER_WIRE_Plugin); plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc; plugin->cls = tc;
plugin->amount_round = &template_amount_round; plugin->amount_round = &template_amount_round;
plugin->get_wire_details = &template_get_wire_details;
plugin->wire_validate = &template_wire_validate; plugin->wire_validate = &template_wire_validate;
plugin->prepare_wire_transfer = &template_prepare_wire_transfer; plugin->prepare_wire_transfer = &template_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel; plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel;

View File

@ -232,6 +232,58 @@ test_amount_round (void *cls,
} }
/**
* Obtain wire transfer details in the plugin-specific format
* from the configuration.
*
* @param cls closure
* @param cfg configuration with details about wire accounts
* @param account_name which section in the configuration should we parse
* @return NULL if @a cfg fails to have valid wire details for @a account_name
*/
static json_t *
test_get_wire_details (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *account_name)
{
json_t *ret;
char *bank_uri;
unsigned long long account_number;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
account_name,
"BANK_URI",
&bank_uri))
{
/* oopsie, configuration error */
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
account_name,
"BANK_URI");
return NULL;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
account_name,
"BANK_ACCOUNT_NUMBER",
&account_number))
{
/* oopsie, configuration error */
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
account_name,
"BANK_URI");
GNUNET_free (bank_uri);
return NULL;
}
ret = json_pack ("{s:s, s:I, s:s}",
"type", "test",
"account_number", (json_int_t) account_number,
"bank_uri", bank_uri);
GNUNET_free (bank_uri);
return ret;
}
/** /**
* Check if the given wire format JSON object is correctly formatted. * Check if the given wire format JSON object is correctly formatted.
* Right now, the only thing we require is a field * Right now, the only thing we require is a field
@ -628,6 +680,7 @@ libtaler_plugin_wire_test_init (void *cls)
plugin = GNUNET_new (struct TALER_WIRE_Plugin); plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc; plugin->cls = tc;
plugin->amount_round = &test_amount_round; plugin->amount_round = &test_amount_round;
plugin->get_wire_details = &test_get_wire_details;
plugin->wire_validate = &test_wire_validate; plugin->wire_validate = &test_wire_validate;
plugin->prepare_wire_transfer = &test_prepare_wire_transfer; plugin->prepare_wire_transfer = &test_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel; plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel;