From de3e26303e0069614d4a5aa425e4fa5ddb088b8b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 1 Apr 2016 16:15:35 +0200 Subject: [PATCH] implementing #4356, tests still failing, but main logic should now be updated --- src/exchange-lib/exchange_api_wire.c | 317 +++--------------- src/exchange-lib/test_exchange_api.c | 96 +++--- src/exchange/taler-exchange-httpd.c | 16 +- .../taler-exchange-httpd_validation.c | 38 ++- .../taler-exchange-httpd_validation.h | 9 +- src/exchange/taler-exchange-httpd_wire.c | 190 +---------- src/exchange/taler-exchange-httpd_wire.h | 37 +- src/include/taler_exchange_service.h | 46 +-- src/include/taler_signatures.h | 26 -- src/include/taler_wire_plugin.h | 15 + src/wire/plugin_wire_sepa.c | 60 +++- src/wire/plugin_wire_template.c | 20 ++ src/wire/plugin_wire_test.c | 53 +++ 13 files changed, 277 insertions(+), 646 deletions(-) diff --git a/src/exchange-lib/exchange_api_wire.c b/src/exchange-lib/exchange_api_wire.c index 27ae1dce7..0e93a9d20 100644 --- a/src/exchange-lib/exchange_api_wire.c +++ b/src/exchange-lib/exchange_api_wire.c @@ -1,6 +1,6 @@ /* 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 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;imethods = methods; - request_wire_method (wh); - return; + else + { + /* some supported methods were invalid, release 'keep', preserve + full 'json' for application-level error handling. */ + json_decref (keep); + } } break; case MHD_HTTP_BAD_REQUEST: @@ -440,19 +217,9 @@ handle_wire_finished (void *cls, response_code = 0; 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, - 0, - NULL, - NULL); + response_code, + json); if (NULL != json) json_decref (json); TALER_EXCHANGE_wire_cancel (wh); @@ -481,8 +248,8 @@ handle_wire_finished (void *cls, */ struct TALER_EXCHANGE_WireHandle * TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange, - TALER_EXCHANGE_WireResultCallback wire_cb, - void *wire_cb_cls) + TALER_EXCHANGE_WireResultCallback wire_cb, + void *wire_cb_cls) { struct TALER_EXCHANGE_WireHandle *wh; struct TALER_EXCHANGE_Context *ctx; diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 2a7f670cd..6eae08f4d 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -1,6 +1,6 @@ /* 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 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 * 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 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) - * @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 - * format details as provided by /wire/METHOD/, or NULL if the - * 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). + * format details as provided by /wire. */ static void wire_cb (void *cls, unsigned int http_status, - const char *method, json_t *obj) { struct InterpreterState *is = cls; 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) { 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, - cmd->label, - method); + cmd->label); json_dumpf (obj, stderr, 0); fail (is); return; } - if (0 == http_status) + switch (http_status) { - /* end of iteration, move to next command */ - is->ip++; - is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, - is); - return; + case MHD_HTTP_OK: + { + json_t *method; + + 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 - with a "positive" result; so we switch to an - expected value of 0 for the 2nd iteration */ - cmd->expected_response_code = 0; + is->ip++; + is->task = GNUNET_SCHEDULER_add_now (&interpreter_run, + is); } @@ -1899,9 +1879,9 @@ interpreter_run (void *cls, /* finally, use private key from withdraw sign command */ cmd->details.refresh_link.rlh = TALER_EXCHANGE_refresh_link (exchange, - &ref->details.reserve_withdraw.coin_priv, - &link_cb, - is); + &ref->details.reserve_withdraw.coin_priv, + &link_cb, + is); if (NULL == cmd->details.refresh_link.rlh) { GNUNET_break (0); @@ -1912,8 +1892,8 @@ interpreter_run (void *cls, return; case OC_WIRE: cmd->details.wire.wh = TALER_EXCHANGE_wire (exchange, - &wire_cb, - is); + &wire_cb, + is); trigger_context_task (); return; case OC_WIRE_DEPOSITS: @@ -1926,9 +1906,9 @@ interpreter_run (void *cls, } cmd->details.wire_deposits.wdh = TALER_EXCHANGE_wire_deposits (exchange, - &cmd->details.wire_deposits.wtid, - &wire_deposits_cb, - is); + &cmd->details.wire_deposits.wtid, + &wire_deposits_cb, + is); trigger_context_task (); return; case OC_DEPOSIT_WTID: @@ -2337,14 +2317,14 @@ run (void *cls, #if WIRE_TEST { .oc = OC_WIRE, .label = "wire-test", - /* /wire/test replies with a 200 OK */ + /* expecting 'test' method in response */ .expected_response_code = MHD_HTTP_OK, .details.wire.format = "test" }, #endif #if WIRE_SEPA { .oc = OC_WIRE, .label = "wire-sepa", - /* /wire/sepa replies with a 200 redirect */ + /* expecting 'sepa' method in response */ .expected_response_code = MHD_HTTP_OK, .details.wire.format = "sepa" }, #endif diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 877876dbb..2ef58b0f7 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1,6 +1,6 @@ /* 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 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, &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 */ { "/reserve/status", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, diff --git a/src/exchange/taler-exchange-httpd_validation.c b/src/exchange/taler-exchange-httpd_validation.c index b7e8a7f80..0b72960c5 100644 --- a/src/exchange/taler-exchange-httpd_validation.c +++ b/src/exchange/taler-exchange-httpd_validation.c @@ -200,33 +200,39 @@ TMH_VALIDATION_test_method (const char *type) /** - * Obtain supported validation methods as a JSON array, - * and as a hash. + * Obtain JSON of the supported wire methods for a given + * 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 */ json_t * -TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h) +TMH_VALIDATION_get_wire_methods (const char *prefix) { json_t *methods; - struct GNUNET_HashContext *hc; - const char *wf; + json_t *method; struct Plugin *p; + struct TALER_WIRE_Plugin *plugin; + char *account_name; - methods = json_array (); - hc = GNUNET_CRYPTO_hash_context_start (); + methods = json_object (); for (p=wire_head;NULL != p;p = p->next) { - wf = p->type; - json_array_append_new (methods, - json_string (wf)); - GNUNET_CRYPTO_hash_context_read (hc, - wf, - strlen (wf) + 1); + plugin = p->plugin; + GNUNET_asprintf (&account_name, + "%s-%s\n", + prefix, + p->type); + 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; } diff --git a/src/exchange/taler-exchange-httpd_validation.h b/src/exchange/taler-exchange-httpd_validation.h index f41e2ee5f..a5403edd4 100644 --- a/src/exchange/taler-exchange-httpd_validation.h +++ b/src/exchange/taler-exchange-httpd_validation.h @@ -63,14 +63,15 @@ TMH_VALIDATION_test_method (const char *type); /** - * Obtain supported validation methods as a JSON array, - * and as a hash. + * Obtain JSON of the supported wire methods for a given + * 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 */ json_t * -TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h); +TMH_VALIDATION_get_wire_methods (const char *prefix); #endif diff --git a/src/exchange/taler-exchange-httpd_wire.c b/src/exchange/taler-exchange-httpd_wire.c index 1b3d3b541..975788b35 100644 --- a/src/exchange/taler-exchange-httpd_wire.c +++ b/src/exchange/taler-exchange-httpd_wire.c @@ -43,191 +43,15 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - struct TALER_ExchangeWireSupportMethodsPS wsm; - struct TALER_ExchangePublicKeyP pub; - struct TALER_ExchangeSignatureP sig; - json_t *methods; + static json_t *wire_methods; - wsm.purpose.size = htonl (sizeof (wsm)); - wsm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_WIRE_TYPES); - methods = TMH_VALIDATION_get_methods (&wsm.h_wire_types); - TMH_KS_sign (&wsm.purpose, - &pub, - &sig); - 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))); + if (NULL == wire_methods) + wire_methods = TMH_VALIDATION_get_wire_methods ("wire-incoming"); + + return TMH_RESPONSE_reply_json (connection, + wire_methods, + MHD_HTTP_OK); } -/** - * 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 */ diff --git a/src/exchange/taler-exchange-httpd_wire.h b/src/exchange/taler-exchange-httpd_wire.h index dc6dcc0f4..cf04f16ff 100644 --- a/src/exchange/taler-exchange-httpd_wire.h +++ b/src/exchange/taler-exchange-httpd_wire.h @@ -1,6 +1,6 @@ /* 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 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); -/** - * 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 diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index e4aa89a59..4a1592cf2 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -396,53 +396,35 @@ struct TALER_EXCHANGE_WireHandle; * Callbacks of this type are used to serve the result of submitting a * wire format inquiry request to a 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). + * If the request fails to generate a valid response from the + * exchange, @a http_status will also be zero. * * @param cls closure * @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) - * @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 - * format details as provided by /wire/METHOD/, or NULL if the - * 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). + * format details as provided by /wire, or NULL if the + * reply was not in JSON format. */ typedef void (*TALER_EXCHANGE_WireResultCallback) (void *cls, unsigned int http_status, - const char *method, json_t *obj); /** - * Obtain information about a exchange's wire instructions. - * A exchange may provide wire instructions for creating - * a reserve. The wire instructions also indicate - * which wire formats merchants may use with the exchange. - * This API is typically used by a wallet for wiring - * funds, and possibly by a merchant to determine - * supported wire formats. + * Obtain information about a exchange's wire instructions. A + * exchange may provide wire instructions for creating a reserve. The + * wire instructions also indicate which wire formats merchants may + * use with the exchange. This API is typically used by a wallet for + * wiring funds, and possibly by a merchant to determine supported + * wire formats. * * Note that while we return the (main) response verbatim to the * caller for further processing, we do already verify that 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, - * we return an HTTP status code of zero to @a cb. + * response are all valid). If the exchange's reply is not + * 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 wire_cb the callback to call when a reply for this request is available @@ -451,8 +433,8 @@ typedef void */ struct TALER_EXCHANGE_WireHandle * TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange, - TALER_EXCHANGE_WireResultCallback wire_cb, - void *wire_cb_cls); + TALER_EXCHANGE_WireResultCallback wire_cb, + void *wire_cb_cls); /** diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index d958f16ba..23bdaa578 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -101,11 +101,6 @@ */ #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. */ @@ -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 * the wire transfer identifier associated with a deposit. diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h index c80aa2e49..e166558a7 100644 --- a/src/include/taler_wire_plugin.h +++ b/src/include/taler_wire_plugin.h @@ -98,6 +98,21 @@ struct TALER_WIRE_Plugin 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 * diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c index 904afa9bf..995436725 100644 --- a/src/wire/plugin_wire_sepa.c +++ b/src/wire/plugin_wire_sepa.c @@ -380,7 +380,7 @@ verify_wire_sepa_signature_ok (const json_t *json, 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"); 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. * @@ -604,6 +661,7 @@ libtaler_plugin_wire_sepa_init (void *cls) plugin = GNUNET_new (struct TALER_WIRE_Plugin); plugin->cls = sc; plugin->amount_round = &sepa_amount_round; + plugin->get_wire_details = &sepa_get_wire_details; plugin->wire_validate = &sepa_wire_validate; plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer; plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel; diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c index 91b380c38..fd6fbfbe9 100644 --- a/src/wire/plugin_wire_template.c +++ b/src/wire/plugin_wire_template.c @@ -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 * @@ -216,6 +235,7 @@ libtaler_plugin_wire_template_init (void *cls) plugin = GNUNET_new (struct TALER_WIRE_Plugin); plugin->cls = tc; plugin->amount_round = &template_amount_round; + plugin->get_wire_details = &template_get_wire_details; plugin->wire_validate = &template_wire_validate; plugin->prepare_wire_transfer = &template_prepare_wire_transfer; plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel; diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c index 3ef76aa6b..a03f8127d 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire/plugin_wire_test.c @@ -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. * 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->cls = tc; plugin->amount_round = &test_amount_round; + plugin->get_wire_details = &test_get_wire_details; plugin->wire_validate = &test_wire_validate; plugin->prepare_wire_transfer = &test_prepare_wire_transfer; plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel;