diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/Makefile.am | 3 | ||||
-rw-r--r-- | src/backend/merchant.conf | 3 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 26 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 6 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_contract.c | 25 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_exchanges.c | 125 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_mhd.c | 8 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_parsing.c | 55 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_pay.c | 115 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_responses.c | 36 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_responses.h | 21 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_track.c | 70 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_track.h (renamed from src/backend/taler-merchant-httpd_util.h) | 14 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_util.c | 105 |
14 files changed, 363 insertions, 249 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 7436eaa2..1e2f1d1b 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -21,7 +21,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \ taler-merchant-httpd_contract.c taler-merchant-httpd_contract.h \ taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \ - taler-merchant-httpd_util.c taler-merchant-httpd_util.h + taler-merchant-httpd_track.c taler-merchant-httpd_track.h + taler_merchant_httpd_LDADD = \ $(top_srcdir)/src/backenddb/libtalermerchantdb.la \ diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf index eee0dfef..e925c6de 100644 --- a/src/backend/merchant.conf +++ b/src/backend/merchant.conf @@ -20,10 +20,11 @@ KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv DB = postgres # Which wireformat does this merchant use? (test/sepa/etc.) -# WIREFORMAT = "test" +WIREFORMAT = test # Determines which wire plugin will be used. We currently only # support one wire plugin at a time! +WIRE_TRANSFER_DELAY = 3 week # Configuration for postgres database. [merchantdb-postgres] diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index c02d5610..73541dd6 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -37,7 +37,7 @@ #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_contract.h" #include "taler-merchant-httpd_pay.h" -#include "taler-merchant-httpd_util.h" +#include "taler-merchant-httpd_track.h" /** @@ -80,7 +80,7 @@ static char *keyfile; * This value tells the exchange by which date this merchant would like * to receive the funds for a deposited payment */ -struct GNUNET_TIME_Relative edate_delay; +struct GNUNET_TIME_Relative wire_transfer_delay; /** * Which currency is supported by this merchant? @@ -174,12 +174,6 @@ url_handler (void *cls, { "/", MHD_HTTP_METHOD_GET, "text/plain", "Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.\n", 0, &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - { "/hash-contract", MHD_HTTP_METHOD_POST, "application/json", - NULL, 0, - &MH_handler_hash_contract, MHD_HTTP_OK }, - { "/hash-contract", NULL, "text/plain", - "Only POST is allowed", 0, - &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, { "/contract", MHD_HTTP_METHOD_POST, "application/json", NULL, 0, &MH_handler_contract, MHD_HTTP_OK }, @@ -193,6 +187,12 @@ url_handler (void *cls, { "/pay", NULL, "text/plain", "Only POST is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + { "/track/deposit", MHD_HTTP_METHOD_GET, "application/json", + NULL, 0, + &MH_handler_track_deposit, MHD_HTTP_OK}, + { "/track/deposit", NULL, "text/plain", + "Only GET is allowed", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_OK}, {NULL, NULL, NULL, NULL, 0, 0 } }; @@ -483,6 +483,10 @@ run (void *cls, result = GNUNET_SYSERR; GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-merchant-httpd", + "INFO", + NULL)); if (GNUNET_SYSERR == TMH_EXCHANGES_init (config)) { @@ -511,12 +515,12 @@ run (void *cls, if (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_time (config, "merchant", - "EDATE", - &edate_delay)) + "WIRE_TRANSFER_DELAY", + &wire_transfer_delay)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "merchant", - "EDATE"); + "WIRE_TRANSFER_DELAY"); GNUNET_SCHEDULER_shutdown (); return; } diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h index 3594c01f..2ca3c20b 100644 --- a/src/backend/taler-merchant-httpd.h +++ b/src/backend/taler-merchant-httpd.h @@ -179,10 +179,10 @@ extern struct TALER_MERCHANTDB_Plugin *db; /** * If the frontend does NOT specify an execution date, how long should * we tell the exchange to wait to aggregate transactions before - * executing? This delay is added to the current time when we - * generate the advisory execution time for the exchange. + * executing the wire transfer? This delay is added to the current + * time when we generate the advisory execution time for the exchange. */ -extern struct GNUNET_TIME_Relative edate_delay; +extern struct GNUNET_TIME_Relative wire_transfer_delay; /** * Kick MHD to run now, to be called after MHD_resume_connection(). diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index af18ce11..0278a7f7 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -57,7 +57,7 @@ check_products (json_t *products) struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("description", &description), /* FIXME: there are other fields in the product specification - that rre currently not labeled as optional. Maybe check + that are currently not labeled as optional. Maybe check those as well, or make them truly optional. */ GNUNET_JSON_spec_end() }; @@ -187,29 +187,6 @@ MH_handler_contract (struct TMH_RequestHandler *rh, "products in contract request malformed"); } - /* Check if this transaction ID erroneously corresponds to a - contract that already paid, in which case we should refuse - to sign it again (frontend buggy, it should use a fresh - transaction ID each time)! */ - if (GNUNET_OK == - db->check_payment (db->cls, - transaction_id)) - { - struct MHD_Response *resp; - int ret; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transaction %llu already paid in the past, refusing to sign!\n", - (unsigned long long) transaction_id); - resp = MHD_create_response_from_buffer (strlen ("Duplicate transaction ID!"), - "Duplicate transaction ID!", - MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, - MHD_HTTP_FORBIDDEN, - resp); - MHD_destroy_response (resp); - return ret; - } /* add fields to the contract that the backend should provide */ json_object_set (jcontract, diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c index 0501784a..4593c3d2 100644 --- a/src/backend/taler-merchant-httpd_exchanges.c +++ b/src/backend/taler-merchant-httpd_exchanges.c @@ -24,10 +24,26 @@ #include "taler-merchant-httpd_exchanges.h" + +/** + * Delay after which we'll re-fetch key information from the exchange. + */ +#define RELOAD_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2) + /** - * How often do we retry fetching /keys? + * Threshold after which exponential backoff should not increase. */ -#define KEYS_RETRY_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) +#define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + + +/** + * Perform our exponential back-off calculation, starting at 1 ms + * and then going by a factor of 2 up unto a maximum of RETRY_BACKOFF_THRESHOLD. + * + * @param r current backoff time, initially zero + */ +#define RETRY_BACKOFF(r) GNUNET_TIME_relative_min (RETRY_BACKOFF_THRESHOLD, \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MILLISECONDS, (r)), 2)); /** @@ -68,7 +84,8 @@ struct TMH_EXCHANGES_FindOperation struct Exchange *my_exchange; /** - * Task scheduled to asynchrnously return the result. + * Task scheduled to asynchronously return the result to + * the find continuation. */ struct GNUNET_SCHEDULER_Task *at; @@ -118,9 +135,9 @@ struct Exchange struct TALER_MasterPublicKeyP master_pub; /** - * At what time should we try to fetch /keys again? + * How long should we wait between the next retry? */ - struct GNUNET_TIME_Absolute retry_time; + struct GNUNET_TIME_Relative retry_delay; /** * Task where we retry fetching /keys from the exchange. @@ -128,8 +145,9 @@ struct Exchange struct GNUNET_SCHEDULER_Task *retry_task; /** - * Flag which indicates whether some HTTP transfer between - * this merchant and the exchange is still ongoing + * #GNUNET_YES to indicate that there is an ongoing + * transfer we are waiting for, + * #GNUNET_NO to indicate that key data is up-to-date. */ int pending; @@ -201,12 +219,16 @@ retry_exchange (void *cls) { struct Exchange *exchange = cls; + /* might be a scheduled reload and not our first attempt */ exchange->retry_task = NULL; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting to exchange exchange %s in retry_exchange\n", exchange->uri); - - exchange->pending = GNUNET_SYSERR; /* failed hard */ + if (NULL != exchange->conn) + { + TALER_EXCHANGE_disconnect (exchange->conn); + exchange->conn = NULL; + } exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx, exchange->uri, &keys_mgmt_cb, @@ -236,39 +258,41 @@ keys_mgmt_cb (void *cls, { struct Exchange *exchange = cls; struct TMH_EXCHANGES_FindOperation *fo; + struct GNUNET_TIME_Absolute expire; + struct GNUNET_TIME_Relative delay; - if (NULL != keys) - { - exchange->pending = GNUNET_NO; - } - else + if (NULL == keys) { + exchange->pending = GNUNET_YES; + exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to fetch /keys from `%s'\n", - exchange->uri); - TALER_EXCHANGE_disconnect (exchange->conn); - exchange->conn = NULL; - exchange->retry_time = GNUNET_TIME_relative_to_absolute (KEYS_RETRY_FREQ); - /* Always retry trusted exchanges in the background, so that we don't have - * to wait for a customer to trigger it and thus delay his response */ - if (GNUNET_YES == exchange->trusted) - { - exchange->retry_task = GNUNET_SCHEDULER_add_delayed (KEYS_RETRY_FREQ, - &retry_exchange, - exchange); - } - else - { - exchange->pending = GNUNET_SYSERR; /* failed hard */ - } + "Failed to fetch /keys from `%s', retrying in %s\n", + exchange->uri, + GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay, + GNUNET_YES)); + exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay, + &retry_exchange, + exchange); + return; } + expire = TALER_EXCHANGE_check_keys_current (exchange->conn); + if (0 == expire.abs_value_us) + delay = RELOAD_DELAY; + else + delay = GNUNET_TIME_absolute_get_remaining (expire); + exchange->retry_delay = GNUNET_TIME_UNIT_ZERO; + exchange->retry_task + = GNUNET_SCHEDULER_add_delayed (delay, + &retry_exchange, + exchange); + exchange->pending = GNUNET_NO; while (NULL != (fo = exchange->fo_head)) { GNUNET_CONTAINER_DLL_remove (exchange->fo_head, exchange->fo_tail, fo); fo->fc (fo->fc_cls, - (NULL != keys) ? exchange->conn : NULL, + exchange->conn, exchange->trusted); GNUNET_free (fo); } @@ -312,7 +336,7 @@ return_result (void *cls) */ struct TMH_EXCHANGES_FindOperation * TMH_EXCHANGES_find_exchange (const char *chosen_exchange, - TMH_EXCHANGES_FindContinuation fc, // process payment + TMH_EXCHANGES_FindContinuation fc, void *fc_cls) { struct Exchange *exchange; @@ -359,29 +383,6 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, chosen_exchange); } - if (GNUNET_SYSERR == exchange->pending) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Maybe retrying previously contacted exchange `%s'\n", - chosen_exchange); - /* check if we should resume this exchange */ - if (0 == GNUNET_TIME_absolute_get_remaining (exchange->retry_time).rel_value_us) - { - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Retrying exchange `%s'\n", - chosen_exchange); - exchange->pending = GNUNET_YES; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Not retrying exchange `%s', too early\n", - chosen_exchange); - } - } - - fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation); fo->fc = fc; fo->fc_cls = fc_cls; @@ -390,7 +391,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, exchange->fo_tail, fo); - if (GNUNET_YES != exchange->pending) // can post coins + if (GNUNET_YES != exchange->pending) { /* We are not currently waiting for a reply, immediately return result */ @@ -434,13 +435,14 @@ TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo) /** * Function called on each configuration section. Finds sections - * about exchanges and parses the entries. + * about exchanges, parses the entries and tries to connect to + * it in order to fetch /keys. * * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *` * @param section name of the section */ static void -parse_exchanges (void *cls, +accept_exchanges (void *cls, const char *section) { const struct GNUNET_CONFIGURATION_Handle *cfg = cls; @@ -524,10 +526,11 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) return GNUNET_SYSERR; } merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx); + /* get exchanges from the merchant configuration and try to connect to them */ GNUNET_CONFIGURATION_iterate_sections (cfg, - &parse_exchanges, + &accept_exchanges, (void *) cfg); - /* build JSON with list of trusted exchanges */ + /* build JSON with list of trusted exchanges (will be included in contracts) */ trusted_exchanges = json_array (); for (exchange = exchange_head; NULL != exchange; exchange = exchange->next) { diff --git a/src/backend/taler-merchant-httpd_mhd.c b/src/backend/taler-merchant-httpd_mhd.c index 23a47707..34fdb113 100644 --- a/src/backend/taler-merchant-httpd_mhd.c +++ b/src/backend/taler-merchant-httpd_mhd.c @@ -41,10 +41,10 @@ */ int TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) { struct MHD_Response *response; int ret; diff --git a/src/backend/taler-merchant-httpd_parsing.c b/src/backend/taler-merchant-httpd_parsing.c index 9b0a3863..c7cdfd15 100644 --- a/src/backend/taler-merchant-httpd_parsing.c +++ b/src/backend/taler-merchant-httpd_parsing.c @@ -27,12 +27,6 @@ #include "taler-merchant-httpd_parsing.h" #include "taler-merchant-httpd_responses.h" -/* Although the following declaration isn't in any case useful -to a merchant's activity, it's needed here to make the function -'TMH_PARSE_nagivate_json ()' compile fine; so its value will be -kept on some merchant's accepted currency. For multi currencies -merchants, that of course would require a patch */ -extern char *TMH_merchant_currency_string; /** * Initial size for POST request buffer. @@ -129,9 +123,9 @@ buffer_append (struct Buffer *buf, if (data_size + buf->fill > buf->alloc) { char *new_buf; - size_t new_size = buf->alloc; + size_t new_size = buf->alloc ? buf->alloc : 1; while (new_size < buf->fill + data_size) - new_size += 2; + new_size *= 2; if (new_size > max_size) return GNUNET_NO; new_buf = GNUNET_malloc (new_size); @@ -313,4 +307,49 @@ TMH_PARSE_json_data (struct MHD_Connection *connection, } + +/** + * Extract base32crockford encoded data from request. + * + * Queues an error response to the connection if the parameter is + * missing or invalid. + * + * @param connection the MHD connection + * @param param_name the name of the parameter with the key + * @param[out] out_data pointer to store the result + * @param out_size expected size of data + * @return + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) + */ +int +TMH_PARSE_mhd_request_arg_data (struct MHD_Connection *connection, + const char *param_name, + void *out_data, + size_t out_size) +{ + const char *str; + + str = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + param_name); + if (NULL == str) + { + return (MHD_NO == + TMH_RESPONSE_reply_arg_missing (connection, param_name)) + ? GNUNET_SYSERR : GNUNET_NO; + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (str, + strlen (str), + out_data, + out_size)) + return (MHD_NO == + TMH_RESPONSE_reply_arg_invalid (connection, param_name)) + ? GNUNET_SYSERR : GNUNET_NO; + return GNUNET_OK; +} + + /* end of taler-merchant-httpd_parsing.c */ diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index bd8dc742..8a32732d 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -18,6 +18,7 @@ * @brief handling of /pay requests * @author Marcello Stanisci * @author Christian Grothoff + * @author Florian Dold */ #include "platform.h" #include <jansson.h> @@ -33,6 +34,12 @@ /** + * How long to wait before giving up processing with the exchange? + */ +#define PAY_TIMEOUT (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)) + + +/** * Information we keep for an individual call to the /pay handler. */ struct PayContext; @@ -175,11 +182,11 @@ struct PayContext struct GNUNET_HashCode h_contract; /** - * Execution date. How soon would the merchant like the - * transaction to be executed? (Can be given by the frontend - * or be determined by our configuration via #edate_delay.) + * Wire transfer deadline. How soon would the merchant like the + * wire transfer to be executed? (Can be given by the frontend + * or be determined by our configuration via #wire_transfer_delay.) */ - struct GNUNET_TIME_Absolute edate; + struct GNUNET_TIME_Absolute wire_transfer_deadline; /** * Response to return, NULL if we don't have one yet. @@ -206,6 +213,13 @@ struct PayContext */ unsigned int response_code; + /** + * Task called when the (suspended) processing for + * the /pay request times out. + * Happens when we don't get a response from the exchange. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + }; @@ -228,10 +242,16 @@ resume_pay_with_response (struct PayContext *pc, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Resuming /pay handling as exchange interaction is done (%u)\n", response_code); + if (NULL != pc->timeout_task) + { + GNUNET_SCHEDULER_cancel (pc->timeout_task); + pc->timeout_task = NULL; + } MHD_resume_connection (pc->connection); TMH_trigger_daemon (); /* we resumed, kick MHD */ } + /** * Convert denomination key to its base32 representation * @@ -311,10 +331,13 @@ deposit_cb (void *cls, if (NULL == proof) { - /* FIXME: is this the right code for when the exchange fails? */ + /* We can't do anything meaningful here, the exchange did something wrong */ + /* FIXME: any useful information we can include? */ resume_pay_with_response (pc, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TMH_RESPONSE_make_internal_error ("Exchange failed, no proof available")); + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_json_pack ("{s:s, s:s}", + "error", "exchange failed", + "hint", "The exchange provided an unexpected response")); } else { @@ -375,6 +398,12 @@ pay_context_cleanup (struct TM_HandlerContext *hc) struct PayContext *pc = (struct PayContext *) hc; unsigned int i; + if (NULL != pc->timeout_task) + { + GNUNET_SCHEDULER_cancel (pc->timeout_task); + pc->timeout_task = NULL; + } + TMH_PARSE_post_cleanup_callback (pc->json_parse_context); for (i=0;i<pc->coins_cnt;i++) { @@ -466,9 +495,10 @@ process_pay_with_exchange (void *cls, GNUNET_break_op (0); resume_pay_with_response (pc, MHD_HTTP_BAD_REQUEST, - TMH_RESPONSE_make_json_pack ("{s:s, s:o}", - "hint", "unknown denom to exchange", - "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key))); + TMH_RESPONSE_make_json_pack ("{s:s, s:o, s:o}", + "error", "denomination not found", + "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key), + "exchange_keys", TALER_EXCHANGE_get_keys_raw (mh))); denom_enc = denomination_to_string_alloc (&dc->denom); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "unknown denom to exchange: %s\n", denom_enc); GNUNET_free (denom_enc); @@ -484,10 +514,10 @@ process_pay_with_exchange (void *cls, resume_pay_with_response (pc, MHD_HTTP_BAD_REQUEST, TMH_RESPONSE_make_json_pack ("{s:s, s:o}", - "hint", "no acceptable auditor for denomination", + "error", "invalid denomination", "denom_pub", GNUNET_JSON_from_rsa_public_key (dc->denom.rsa_public_key))); denom_enc = denomination_to_string_alloc (&dc->denom); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "no acceptable auditor for denomination: %s\n", denom_enc); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "client offered invalid denomination: %s\n", denom_enc); GNUNET_free (denom_enc); return; } @@ -593,7 +623,7 @@ process_pay_with_exchange (void *cls, dc->dh = TALER_EXCHANGE_deposit (mh, &dc->percoin_amount, - pc->edate, + pc->wire_transfer_deadline, j_wire, &pc->h_contract, &dc->coin_pub, @@ -623,6 +653,33 @@ process_pay_with_exchange (void *cls, /** + * Handle a timeout for the processing of the pay request. + * + * @param cls closure + */ +static void +handle_pay_timeout (void *cls) +{ + struct PayContext *pc = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Resuming /pay with error after timeout\n"); + + pc->timeout_task = NULL; + + if (NULL != pc->fo) + { + TMH_EXCHANGES_find_exchange_cancel (pc->fo); + pc->fo = NULL; + } + + resume_pay_with_response (pc, + MHD_HTTP_SERVICE_UNAVAILABLE, + TMH_RESPONSE_make_internal_error ("exchange not reachable")); +} + + +/** * Accomplish this payment. * * @param rh context of the handler @@ -748,18 +805,24 @@ MH_handler_pay (struct TMH_RequestHandler *rh, "invalid merchant signature supplied"); } - /* 'edate' is optional, if it is not present, generate it here; it - will be timestamp plus the edate_delay supplied in config - file */ - if (NULL == json_object_get (root, "edate")) + /* 'wire_transfer_deadline' is optional, if it is not present, + generate it here; it will be timestamp plus the + wire_transfer_delay supplied in config file */ + if (NULL == json_object_get (root, "wire_transfer_deadline")) { - pc->edate = GNUNET_TIME_absolute_add (pc->timestamp, - edate_delay); + pc->wire_transfer_deadline = GNUNET_TIME_absolute_add (pc->timestamp, + wire_transfer_delay); + if (pc->wire_transfer_deadline.abs_value_us < pc->refund_deadline.abs_value_us) + { + /* Refund value very large, delay wire transfer accordingly */ + pc->wire_transfer_deadline = pc->refund_deadline; + } } else { struct GNUNET_JSON_Specification espec[] = { - GNUNET_JSON_spec_absolute_time ("edate", &pc->edate), + GNUNET_JSON_spec_absolute_time ("wire_transfer_deadline", + &pc->wire_transfer_deadline), GNUNET_JSON_spec_end() }; @@ -772,6 +835,14 @@ MH_handler_pay (struct TMH_RequestHandler *rh, GNUNET_break (0); return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } + if (pc->wire_transfer_deadline.abs_value_us < pc->refund_deadline.abs_value_us) + { + GNUNET_break (0); + json_decref (root); + return TMH_RESPONSE_reply_external_error (connection, + "refund deadline after wire transfer deadline"); + } + } @@ -833,9 +904,6 @@ MH_handler_pay (struct TMH_RequestHandler *rh, /* Payment succeeded in the past; take short cut and accept immediately */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Transaction %llu already paid in the past, taking short cut.\n", - (unsigned long long) pc->transaction_id); resp = MHD_create_response_from_buffer (0, NULL, MHD_RESPMEM_PERSISTENT); @@ -859,6 +927,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Suspending /pay handling while working with the exchange\n"); MHD_suspend_connection (connection); + pc->timeout_task = GNUNET_SCHEDULER_add_delayed (PAY_TIMEOUT, handle_pay_timeout, pc); json_decref (root); return MHD_YES; } diff --git a/src/backend/taler-merchant-httpd_responses.c b/src/backend/taler-merchant-httpd_responses.c index 0f066ba9..d63eacb5 100644 --- a/src/backend/taler-merchant-httpd_responses.c +++ b/src/backend/taler-merchant-httpd_responses.c @@ -289,5 +289,41 @@ TMH_RESPONSE_make_external_error (const char *hint) "hint", hint); } +/** + * Send a response indicating a missing argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is missing + * @return a MHD result code + */ +int +TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection, + const char *param_name) +{ + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{ s:s, s:s}", + "error", "missing parameter", + "parameter", param_name); +} + + +/** + * Send a response indicating an invalid argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is invalid + * @return a MHD result code + */ +int +TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection, + const char *param_name) +{ + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s}", + "error", "invalid parameter", + "parameter", param_name); +} /* end of taler-exchange-httpd_responses.c */ diff --git a/src/backend/taler-merchant-httpd_responses.h b/src/backend/taler-merchant-httpd_responses.h index abf90949..7314e0bb 100644 --- a/src/backend/taler-merchant-httpd_responses.h +++ b/src/backend/taler-merchant-httpd_responses.h @@ -157,4 +157,25 @@ TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection); void TMH_RESPONSE_add_global_headers (struct MHD_Response *response); +/** + * Send a response indicating a missing argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is missing + * @return a MHD result code + */ +int +TMH_RESPONSE_reply_arg_missing (struct MHD_Connection *connection, + const char *param_name); + +/** + * Send a response indicating an invalid argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is invalid + * @return a MHD result code + */ +int +TMH_RESPONSE_reply_arg_invalid (struct MHD_Connection *connection, + const char *param_name); #endif diff --git a/src/backend/taler-merchant-httpd_track.c b/src/backend/taler-merchant-httpd_track.c new file mode 100644 index 00000000..47e5461a --- /dev/null +++ b/src/backend/taler-merchant-httpd_track.c @@ -0,0 +1,70 @@ +/* + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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 + 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, If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file backend/taler-merchant-httpd_track.c + * @brief implement API for tracking deposits and wire transfers + * @author Marcello Stanisci + */ +#include "platform.h" +#include <jansson.h> +#include <taler/taler_signatures.h> +#include <taler/taler_json_lib.h> +#include "taler-merchant-httpd.h" +#include "taler-merchant-httpd_mhd.h" +#include "taler-merchant-httpd_parsing.h" +#include "taler-merchant-httpd_auditors.h" +#include "taler-merchant-httpd_exchanges.h" +#include "taler-merchant-httpd_responses.h" + + +extern char *TMH_merchant_currency_string; + + +/** + * Manages a /track/deposit call, thus it calls the /wire/deposit + * offered by the exchange in order to return the set of deposits + * (of coins) associated with a given wire transfer + * + * @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 +MH_handler_track_deposit (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + static struct TMH_RequestHandler pong = + { + "", NULL, "text/html", + "/track/deposit served\n", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_OK + }; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "serving /track/deposit\n"); + return TMH_MHD_handler_static_response (&pong, + connection, + connection_cls, + upload_data, + upload_data_size); +} + +/* end of taler-merchant-httpd_contract.c */ diff --git a/src/backend/taler-merchant-httpd_util.h b/src/backend/taler-merchant-httpd_track.h index cbf4693a..51dde1b9 100644 --- a/src/backend/taler-merchant-httpd_util.h +++ b/src/backend/taler-merchant-httpd_track.h @@ -14,17 +14,17 @@ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/> */ /** - * @file backend/taler-merchant-httpd_contract.h - * @brief headers for /contract handler + * @file backend/taler-merchant-httpd_track.h + * @brief headers for /track/{deposit,wtid} handler * @author Marcello Stanisci */ -#ifndef TALER_EXCHANGE_HTTPD_UTIL_H -#define TALER_EXCHANGE_HTTPD_UTIL_H #include <microhttpd.h> #include "taler-merchant-httpd.h" /** - * Manage a contract request + * Manages a /track/deposit call, thus it calls the /wire/deposit + * offered by the exchange in order to return the set of deposits + * (of coins) associated with a given wire transfer * * @param rh context of the handler * @param connection the MHD connection to handle @@ -34,10 +34,8 @@ * @return MHD result code */ int -MH_handler_hash_contract (struct TMH_RequestHandler *rh, +MH_handler_track_deposit (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/backend/taler-merchant-httpd_util.c b/src/backend/taler-merchant-httpd_util.c deleted file mode 100644 index 9d62722c..00000000 --- a/src/backend/taler-merchant-httpd_util.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - This file is part of TALER - (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 Affero 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, If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file backend/taler-merchant-httpd_contract.c - * @brief HTTP serving layer mainly intended to communicate with the frontend - * @author Marcello Stanisci - */ -#include "platform.h" -#include <jansson.h> -#include <taler/taler_signatures.h> -#include <taler/taler_json_lib.h> -#include "taler-merchant-httpd.h" -#include "taler-merchant-httpd_parsing.h" -#include "taler-merchant-httpd_auditors.h" -#include "taler-merchant-httpd_exchanges.h" -#include "taler-merchant-httpd_responses.h" - - -/** - * Hashes a plain JSON contract sending the result to the other end of - * HTTP communication - * - * @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 -MH_handler_hash_contract (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) -{ - json_t *root; - json_t *jcontract; - int res; - struct GNUNET_HashCode hc; - struct TMH_JsonParseContext *ctx; - - if (NULL == *connection_cls) - { - ctx = GNUNET_new (struct TMH_JsonParseContext); - ctx->hc.cc = &TMH_json_parse_cleanup; - *connection_cls = ctx; - } - else - { - ctx = *connection_cls; - } - - res = TMH_PARSE_post_json (connection, - &ctx->json_parse_context, - upload_data, - upload_data_size, - &root); - - if (GNUNET_SYSERR == res) - return MHD_NO; - /* the POST's body has to be further fetched */ - if ((GNUNET_NO == res) || (NULL == root)) - return MHD_YES; - - jcontract = json_object_get (root, "contract"); - - if (NULL == jcontract) - { - return TMH_RESPONSE_reply_external_error (connection, - "missing 'contract' field"); - } - - if (GNUNET_OK != TALER_JSON_hash (jcontract, - &hc)) - { - return TMH_RESPONSE_reply_external_error (connection, - "expected object as contract"); - } - - GNUNET_assert (GNUNET_OK == - TALER_JSON_hash (jcontract, - &hc)); - - /* return final response */ - res = TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:O}", - "hash", GNUNET_JSON_from_data_auto (&hc)); - json_decref (root); - return res; -} |