diff options
22 files changed, 1292 insertions, 1488 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index d5bcda16..9da9164e 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -22,7 +22,6 @@ bin_PROGRAMS = \ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd.c taler-merchant-httpd.h \ - taler-merchant-httpd_auditors.c taler-merchant-httpd_auditors.h \ taler-merchant-httpd_config.c taler-merchant-httpd_config.h \ taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \ taler-merchant-httpd_get-orders-ID.c \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 8f455f58..a676a009 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -28,7 +28,6 @@ #include <taler/taler_mhd_lib.h> #include <taler/taler_templating_lib.h> #include <taler/taler_exchange_service.h> -#include "taler-merchant-httpd_auditors.h" #include "taler-merchant-httpd_config.h" #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_get-orders-ID.h" @@ -313,7 +312,6 @@ do_shutdown (void *cls) TMH_db = NULL; } TMH_EXCHANGES_done (); - TMH_AUDITORS_done (); if (NULL != TMH_by_id_map) { GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map, @@ -1892,7 +1890,6 @@ run (void *cls, int fh; enum TALER_MHD_GlobalOptions go; int elen; - int alen; const char *tok; (void) cls; @@ -1966,16 +1963,10 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - alen = TMH_AUDITORS_init (config); - if (GNUNET_SYSERR == alen) - { - GNUNET_SCHEDULER_shutdown (); - return; - } - if (0 == elen + alen) + if (0 == elen) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fatal: no trusted exchanges and no trusted auditors configured. Exiting.\n"); + "Fatal: no trusted exchanges configured. Exiting.\n"); GNUNET_SCHEDULER_shutdown (); return; } diff --git a/src/backend/taler-merchant-httpd_auditors.c b/src/backend/taler-merchant-httpd_auditors.c deleted file mode 100644 index 8a09af63..00000000 --- a/src/backend/taler-merchant-httpd_auditors.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - This file is part of TALER - (C) 2014-2023 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-merchant-httpd_auditors.c - * @brief logic this HTTPD keeps for each exchange we interact with - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#include "platform.h" -#include <taler/taler_json_lib.h> -#include "taler-merchant-httpd_auditors.h" - - -/** - * Auditors are currently not a supported feature, as having merchants - * allow all exchanges of an auditor creates problems when exchanges - * have restricted their bank accounts via ``/wire``. Thus, for now, - * merchants must specify the exact list of trusted exchanges. - */ -#define ENABLE_AUDITORS 0 - -/** - * Our representation of an auditor. - */ -struct Auditor -{ - /** - * Auditor's legal name. - */ - char *name; - - /** - * Auditor's URL. - */ - char *url; - - /** - * Public key of the auditor. - */ - struct TALER_AuditorPublicKeyP public_key; - -}; - - -#if ENABLE_AUDITORS -/** - * Array of the auditors this merchant is willing to accept. - */ -static struct Auditor *auditors; - -/** - * The length of the #auditors array. - */ -static unsigned int nauditors; - -#endif - -/** - * JSON representation of the auditors accepted by this exchange. - */ -json_t *j_auditors; - -enum GNUNET_GenericReturnValue -TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh, - const struct TALER_EXCHANGE_DenomPublicKey *dk, - bool exchange_trusted, - unsigned int *hc, - enum TALER_ErrorCode *ec) -{ - const struct TALER_EXCHANGE_Keys *keys; - - if (GNUNET_TIME_absolute_is_past (dk->expire_deposit.abs_time)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Denomination key offered by client has expired for deposits\n"); - *hc = MHD_HTTP_GONE; - *ec = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED; - return GNUNET_SYSERR; /* expired */ - } - if (exchange_trusted) - { - *ec = TALER_EC_NONE; - *hc = MHD_HTTP_OK; - return GNUNET_OK; - } - keys = TALER_EXCHANGE_get_keys (mh); - if (NULL == keys) - { - /* this should never happen, keys should have been successfully - obtained before we even got into this function */ - GNUNET_break (0); - *ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; - *hc = MHD_HTTP_INTERNAL_SERVER_ERROR; - return GNUNET_SYSERR; - } -#if ENABLE_AUDITORS - for (unsigned int i = 0; i<keys->num_auditors; i++) - { - const struct TALER_EXCHANGE_AuditorInformation *ai = &keys->auditors[i]; - - for (unsigned int j = 0; j<nauditors; j++) - { - if (0 == GNUNET_memcmp (&ai->auditor_pub, - &auditors[j].public_key)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Found supported auditor `%s' (%s)\n", - auditors[j].name, - TALER_B2S (&auditors[j].public_key)); - } - for (unsigned int k = 0; k<ai->num_denom_keys; k++) - if (&keys->denom_keys[k] == dk) - { - *ec = TALER_EC_NONE; - *hc = MHD_HTTP_OK; - return GNUNET_OK; - } - } - } -#endif - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Denomination key %s offered by client not audited by any accepted auditor\n", - GNUNET_h2s (&dk->h_key.hash)); - *hc = MHD_HTTP_BAD_REQUEST; - *ec = TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_AUDITOR_FAILURE; - return GNUNET_NO; -} - - -#if ENABLE_AUDITORS -/** - * Function called on each configuration section. Finds sections - * about auditors and parses the entries. - * - * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *` - * @param section name of the section - */ -static void -parse_auditors (void *cls, - const char *section) -{ - const struct GNUNET_CONFIGURATION_Handle *cfg = cls; - char *pks; - struct Auditor auditor; - char *currency; - - if (0 != strncasecmp (section, - "merchant-auditor-", - strlen ("merchant-auditor-"))) - return; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "CURRENCY", - ¤cy)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "CURRENCY"); - return; - } - if (0 != strcasecmp (currency, - TMH_currency)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Auditor given in section `%s' is for another currency. Skipping.\n", - section); - GNUNET_free (currency); - return; - } - GNUNET_free (currency); - auditor.name = GNUNET_strdup (§ion[strlen ("merchant-auditor-")]); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "AUDITOR_BASE_URL", - &auditor.url)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "URL"); - GNUNET_free (auditor.name); - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "AUDITOR_KEY", - &pks)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "AUDITOR_KEY"); - GNUNET_free (auditor.name); - GNUNET_free (auditor.url); - return; - } - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_public_key_from_string (pks, - strlen (pks), - &auditor.public_key.eddsa_pub)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "AUDITOR_KEY", - "need a valid EdDSA public key"); - GNUNET_free (auditor.name); - GNUNET_free (auditor.url); - GNUNET_free (pks); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Loaded key data of auditor `%s' (%s)\n", - auditor.name, - TALER_B2S (&auditor.public_key)); - GNUNET_free (pks); - GNUNET_array_append (auditors, - nauditors, - auditor); -} - - -#endif - - -int -TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ -#if ENABLE_AUDITORS - GNUNET_CONFIGURATION_iterate_sections (cfg, - &parse_auditors, - (void *) cfg); -#endif - /* Generate preferred exchange(s) array. */ - j_auditors = json_array (); -#if ENABLE_AUDITORS - for (unsigned int cnt = 0; cnt < nauditors; cnt++) - GNUNET_assert (0 == - json_array_append_new ( - j_auditors, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("name", - auditors[cnt].name), - GNUNET_JSON_pack_data_auto ("auditor_pub", - &auditors[cnt].public_key), - GNUNET_JSON_pack_string ("url", - auditors[cnt].url)))); - return nauditors; -#else - return 0; -#endif -} - - -/** - * Release auditor information state. - */ -void -TMH_AUDITORS_done () -{ - json_decref (j_auditors); - j_auditors = NULL; -#if ENABLE_AUDITORS - for (unsigned int i = 0; i<nauditors; i++) - { - GNUNET_free (auditors[i].name); - GNUNET_free (auditors[i].url); - } - GNUNET_free (auditors); - auditors = NULL; - nauditors = 0; -#endif -} - - -#if ENABLE_AUDITORS - -/* -Something like the following text should be added to the taler.5.conf man-page -if auditor support is brought back. - -KNOWN AUDITORS (for merchants) ------------------------------- - -The merchant configuration can include a list of known exchanges if the -merchant wants to specify that certain auditors are explicitly trusted. -For each trusted exchange, a section “[merchant-auditor-$NAME]” must exist, where -``$NAME`` is a merchant-given name for the auditor. The following options -must be given in each “[merchant-auditor-$NAME]” section. - -AUDITOR_BASE_URL - Base URL of the auditor, e.g. “https://auditor.demo.taler.net/” - -AUDITOR_KEY - Crockford Base32 encoded auditor public key. - -CURRENCY - Name of the currency for which this auditor is trusted, e.g. “KUDOS” - The entire section is ignored if the currency does not match the currency - we use, which must be given in the ``[taler]`` section. - -*/ - -#endif - -/* end of taler-merchant-httpd_auditors.c */ diff --git a/src/backend/taler-merchant-httpd_auditors.h b/src/backend/taler-merchant-httpd_auditors.h deleted file mode 100644 index 1d66c801..00000000 --- a/src/backend/taler-merchant-httpd_auditors.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - This file is part of TALER - (C) 2014, 2015 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, see <http://www.gnu.org/licenses/> -*/ -/** - * @file taler-merchant-httpd_auditors.h - * @brief logic this HTTPD keeps for each exchange we interact with - * @author Marcello Stanisci - * @author Christian Grothoff - */ -#ifndef TALER_MERCHANT_HTTPD_AUDITORS_H -#define TALER_MERCHANT_HTTPD_AUDITORS_H - -#include <jansson.h> -#include <gnunet/gnunet_util_lib.h> -#include <taler/taler_util.h> -#include <taler/taler_exchange_service.h> -#include "taler-merchant-httpd.h" - - -/** - * JSON representation of the auditors accepted by this exchange. - */ -extern json_t *j_auditors; - - -/** - * Parses auditor information from the configuration. - * - * @param cfg the configuration - * @return the number of auditors found; #GNUNET_SYSERR upon error in - * parsing. - */ -int -TMH_AUDITORS_init (const struct GNUNET_CONFIGURATION_Handle *cfg); - - -/** - * Check if the given @a dk issued by exchange @a mh is audited by - * an auditor that is acceptable for this merchant. (And if the - * denomination is not yet expired or something silly like that.) - * - * @param mh exchange issuing @a dk - * @param dk a denomination issued by @a mh - * @param exchange_trusted true if the exchange of @a dk is trusted by config - * @param[out] hc set to the HTTP status code to return - * @param[out] ec set to the Taler error code to return - * @return #GNUNET_OK on success - */ -int -TMH_AUDITORS_check_dk (struct TALER_EXCHANGE_Handle *mh, - const struct TALER_EXCHANGE_DenomPublicKey *dk, - bool exchange_trusted, - unsigned int *hc, - enum TALER_ErrorCode *ec); - - -/** - * Release auditor information state. - */ -void -TMH_AUDITORS_done (void); - - -#endif diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c index f4695184..e415bbf6 100644 --- a/src/backend/taler-merchant-httpd_exchanges.c +++ b/src/backend/taler-merchant-httpd_exchanges.c @@ -23,6 +23,7 @@ #include <taler/taler_json_lib.h> #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd.h" +#include <regex.h> /** * How often do we retry DB transactions with soft errors? @@ -63,12 +64,6 @@ /** - * Exchange - */ -struct Exchange; - - -/** * Information we keep for a pending #MMH_EXCHANGES_find_exchange() operation. */ struct TMH_EXCHANGES_FindOperation @@ -97,7 +92,7 @@ struct TMH_EXCHANGES_FindOperation /** * Exchange we wait for the /keys for. */ - struct Exchange *my_exchange; + struct TMH_Exchange *my_exchange; /** * Task scheduled to asynchronously return the result to @@ -138,20 +133,101 @@ struct FeesByWireMethod /** + * Restriction that applies to an exchange account. + */ +struct Restriction +{ + /** + * Kept in a DLL. + */ + struct Restriction *next; + + /** + * Kept in a DLL. + */ + struct Restriction *prev; + + /** + * Type of restriction imposed on the account. + */ + enum TALER_EXCHANGE_AccountRestrictionType type; + + /** + * Details depending on @e type. + */ + union + { + + /** + * Accounts must match the given regular expression. + */ + struct + { + + /** + * Pre-compiled regex. + */ + regex_t ex; + + } regex; + + } details; +}; + + +/** + * Information about a bank account of the exchange. + */ +struct ExchangeAccount +{ + /** + * Kept in a DLL. + */ + struct ExchangeAccount *next; + + /** + * Kept in a DLL. + */ + struct ExchangeAccount *prev; + + /** + * Wire method of this exchange account. + */ + char *wire_method; + + /** + * Currency conversion that applies to this account, + * NULL if none. + */ + char *conversion_url; + + /** + * Head of DLL of debit restrictions of this account. + */ + struct Restriction *d_head; + + /** + * Tail of DLL of debit restrictions of this account. + */ + struct Restriction *d_tail; +}; + + +/** * Exchange */ -struct Exchange +struct TMH_Exchange { /** * Kept in a DLL. */ - struct Exchange *next; + struct TMH_Exchange *next; /** * Kept in a DLL. */ - struct Exchange *prev; + struct TMH_Exchange *prev; /** * Head of FOs pending for this exchange. @@ -164,6 +240,16 @@ struct Exchange struct TMH_EXCHANGES_FindOperation *fo_tail; /** + * Head of accounts of this exchange. + */ + struct ExchangeAccount *acc_head; + + /** + * Tail of accounts of this exchange. + */ + struct ExchangeAccount *acc_tail; + + /** * (base) URL of the exchange. */ char *url; @@ -261,27 +347,28 @@ static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc; /** * Head of exchanges we know about. */ -static struct Exchange *exchange_head; +static struct TMH_Exchange *exchange_head; /** * Tail of exchanges we know about. */ -static struct Exchange *exchange_tail; +static struct TMH_Exchange *exchange_tail; /** - * List of our trusted exchanges for inclusion in contracts. + * How many exchanges do we trust (for our configured + * currency) as per our configuration? Used for a + * sanity-check on startup. */ -json_t *TMH_trusted_exchanges; - +static int trusted_exchange_count; /** * Function called with information about who is auditing * a particular exchange and what key the exchange is using. * - * @param cls closure, will be `struct Exchange` so that + * @param cls closure, will be `struct TMH_Exchange` so that * when this function gets called, it will change the flag 'have_keys' * to 'true'. Note: 'keys' is automatically saved inside the exchange's - * handle, which is contained inside 'struct Exchange', when + * handle, which is contained inside 'struct TMH_Exchange', when * this callback is called. Thus, once 'have_keys' turns 'true', * it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle, * in order to get the "good" keys. @@ -293,6 +380,121 @@ keys_mgmt_cb (void *cls, /** + * Free data structures within @a ea, but not @a ea + * pointer itself. + * + * @param[in] ea data structure to free + */ +static void +free_exchange_account (struct ExchangeAccount *ea) +{ + struct Restriction *r; + + while (NULL != (r = ea->d_head)) + { + GNUNET_CONTAINER_DLL_remove (ea->d_head, + ea->d_tail, + r); + switch (r->type) + { + case TALER_EXCHANGE_AR_INVALID: + GNUNET_assert (0); + break; + case TALER_EXCHANGE_AR_DENY: + break; + case TALER_EXCHANGE_AR_REGEX: + regfree (&r->details.regex.ex); + break; + } + GNUNET_free (r); + } + GNUNET_free (ea->wire_method); + GNUNET_free (ea->conversion_url); +} + + +/** + * Free list of all accounts in @a exchange. + * + * @param[in,out] exchange entry to free accounts for + */ +static void +purge_exchange_accounts (struct TMH_Exchange *exchange) +{ + struct ExchangeAccount *acc; + + while (NULL != (acc = exchange->acc_head)) + { + GNUNET_CONTAINER_DLL_remove (exchange->acc_head, + exchange->acc_tail, + acc); + free_exchange_account (acc); + GNUNET_free (acc); + } +} + + +/** + * Set the list of accounts of @a exchange. + * + * @param[in,out] exchange exchange to initialize or update + * @param accounts_len length of @a accounts array + * @param accounts array of accounts to convert + * @return #GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +set_exchange_accounts (struct TMH_Exchange *exchange, + unsigned int accounts_len, + const struct TALER_EXCHANGE_WireAccount *accounts) +{ + purge_exchange_accounts (exchange); + for (unsigned int i = 0; i<accounts_len; i++) + { + const struct TALER_EXCHANGE_WireAccount *account = &accounts[i]; + struct ExchangeAccount *acc; + + acc = GNUNET_new (struct ExchangeAccount); + acc->wire_method = TALER_payto_get_method (account->payto_uri); + if (NULL != account->conversion_url) + acc->conversion_url = GNUNET_strdup (account->conversion_url); + GNUNET_CONTAINER_DLL_insert (exchange->acc_head, + exchange->acc_tail, + acc); + for (unsigned int j = 0; j<account->debit_restrictions_length; j++) + { + const struct TALER_EXCHANGE_AccountRestriction *ar = + &account->debit_restrictions[j]; + struct Restriction *r; + + r = GNUNET_new (struct Restriction); + r->type = ar->type; + switch (ar->type) + { + case TALER_EXCHANGE_AR_INVALID: + GNUNET_assert (0); + break; + case TALER_EXCHANGE_AR_DENY: + break; + case TALER_EXCHANGE_AR_REGEX: + if (0 != regcomp (&r->details.regex.ex, + ar->details.regex.posix_egrep, + REG_NOSUB | REG_EXTENDED)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + break; + } + GNUNET_CONTAINER_DLL_insert (acc->d_head, + acc->d_tail, + r); + } + } + return GNUNET_OK; +} + + +/** * Retry getting information from the given exchange in * the closure. * @@ -301,7 +503,7 @@ keys_mgmt_cb (void *cls, static void retry_exchange (void *cls) { - struct Exchange *exchange = cls; + struct TMH_Exchange *exchange = cls; /* might be a scheduled reload and not our first attempt */ exchange->retry_task = NULL; @@ -347,7 +549,7 @@ retry_exchange (void *cls) * @return #TALER_EC_NONE on success */ static enum TALER_ErrorCode -process_wire_fees (struct Exchange *exchange, +process_wire_fees (struct TMH_Exchange *exchange, const struct TALER_MasterPublicKeyP *master_pub, unsigned int num_methods, const struct TALER_EXCHANGE_WireFeesByMethod *fbm) @@ -533,7 +735,7 @@ add_restriction (json_t *restrictions, * @return #TALER_EC_NONE on success */ static enum TALER_ErrorCode -process_wire_accounts (struct Exchange *exchange, +process_wire_accounts (struct TMH_Exchange *exchange, const struct TALER_MasterPublicKeyP *master_pub, unsigned int accounts_len, const struct TALER_EXCHANGE_WireAccount *accounts) @@ -623,6 +825,9 @@ process_wire_accounts (struct Exchange *exchange, GNUNET_break (0); return TALER_EC_GENERIC_DB_COMMIT_FAILED; } + set_exchange_accounts (exchange, + accounts_len, + accounts); return TALER_EC_NONE; outer:; } @@ -639,7 +844,7 @@ outer:; * @return NULL if we do not have fees for this method yet */ static const struct FeesByWireMethod * -get_wire_fees (struct Exchange *exchange, +get_wire_fees (struct TMH_Exchange *exchange, struct GNUNET_TIME_Timestamp now, const char *wire_method) { @@ -680,7 +885,7 @@ get_wire_fees (struct Exchange *exchange, * @return true if we need /wire data from @a exchange */ static bool -process_find_operations (struct Exchange *exchange) +process_find_operations (struct TMH_Exchange *exchange) { struct TMH_EXCHANGES_FindOperation *fn; struct GNUNET_TIME_Timestamp now; @@ -745,7 +950,7 @@ process_find_operations (struct Exchange *exchange) fo->fc (fo->fc_cls, &hr, exchange->conn, - exchange->trusted); + exchange); fn = fo->next; TMH_EXCHANGES_find_exchange_cancel (fo); } @@ -767,14 +972,14 @@ wire_task_cb (void *cls); * Must only be called if 'exchange->have_keys' is true. * that is #TALER_EXCHANGE_get_keys() will succeed. * - * @param cls closure, a `struct Exchange` + * @param cls closure, a `struct TMH_Exchange` * @param wr response details */ static void handle_wire_data (void *cls, const struct TALER_EXCHANGE_WireResponse *wr) { - struct Exchange *exchange = cls; + struct TMH_Exchange *exchange = cls; const struct TALER_EXCHANGE_Keys *keys; enum TALER_ErrorCode ecx; @@ -869,12 +1074,12 @@ handle_wire_data (void *cls, * Must only be called if 'exchange->have_keys' is true, * that is #TALER_EXCHANGE_get_keys() will succeed. * - * @param cls a `struct Exchange` to check + * @param cls a `struct TMH_Exchange` to check */ static void wire_task_cb (void *cls) { - struct Exchange *exchange = cls; + struct TMH_Exchange *exchange = cls; exchange->wire_task = NULL; GNUNET_assert (exchange->have_keys); @@ -895,13 +1100,14 @@ wire_task_cb (void *cls) * @param[in] exchange entry to free */ static void -free_exchange_entry (struct Exchange *exchange) +free_exchange_entry (struct TMH_Exchange *exchange) { struct FeesByWireMethod *f; GNUNET_CONTAINER_DLL_remove (exchange_head, exchange_tail, exchange); + purge_exchange_accounts (exchange); while (NULL != (f = exchange->wire_fees_head)) { struct TALER_EXCHANGE_WireAggregateFees *af; @@ -952,7 +1158,7 @@ free_exchange_entry (struct Exchange *exchange) * @param hr details about the HTTP reply */ static void -fail_and_retry (struct Exchange *exchange, +fail_and_retry (struct TMH_Exchange *exchange, const struct TALER_EXCHANGE_HttpResponse *hr) { struct TMH_EXCHANGES_FindOperation *fo; @@ -1005,7 +1211,7 @@ static void keys_mgmt_cb (void *cls, const struct TALER_EXCHANGE_KeysResponse *kr) { - struct Exchange *exchange = cls; + struct TMH_Exchange *exchange = cls; struct GNUNET_TIME_Relative delay; const struct TALER_EXCHANGE_Keys *keys; @@ -1029,7 +1235,7 @@ keys_mgmt_cb (void *cls, if (! exchange->trusted) { exchange->master_pub = keys->master_pub; - for (struct Exchange *e = exchange_head; + for (struct TMH_Exchange *e = exchange_head; NULL != e; e = e->next) { @@ -1112,7 +1318,7 @@ static void return_result (void *cls) { struct TMH_EXCHANGES_FindOperation *fo = cls; - struct Exchange *exchange = fo->my_exchange; + struct TMH_Exchange *exchange = fo->my_exchange; fo->at = NULL; if ( (process_find_operations (exchange)) && @@ -1139,10 +1345,10 @@ return_result (void *cls) * @param exchange_url base URL to match against * @return NULL if exchange is not yet known */ -static struct Exchange * +static struct TMH_Exchange * lookup_exchange (const char *exchange_url) { - for (struct Exchange *exchange = exchange_head; + for (struct TMH_Exchange *exchange = exchange_head; NULL != exchange; exchange = exchange->next) if (0 == strcmp (exchange->url, @@ -1158,7 +1364,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, TMH_EXCHANGES_FindContinuation fc, void *fc_cls) { - struct Exchange *exchange; + struct TMH_Exchange *exchange; struct TMH_EXCHANGES_FindOperation *fo; if (NULL == merchant_curl_ctx) @@ -1174,7 +1380,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, if (NULL == exchange) { /* This is a new exchange */ - exchange = GNUNET_new (struct Exchange); + exchange = GNUNET_new (struct TMH_Exchange); exchange->url = GNUNET_strdup (chosen_exchange); GNUNET_CONTAINER_DLL_insert (exchange_head, exchange_tail, @@ -1271,7 +1477,7 @@ TMH_EXCHANGES_find_exchange (const char *chosen_exchange, void TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo) { - struct Exchange *exchange = fo->my_exchange; + struct TMH_Exchange *exchange = fo->my_exchange; if (NULL != fo->at) { @@ -1300,7 +1506,7 @@ accept_exchanges (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg = cls; char *url; char *mks; - struct Exchange *exchange; + struct TMH_Exchange *exchange; char *currency; if (0 != strncasecmp (section, @@ -1339,7 +1545,7 @@ accept_exchanges (void *cls, "EXCHANGE_BASE_URL"); return; } - exchange = GNUNET_new (struct Exchange); + exchange = GNUNET_new (struct TMH_Exchange); exchange->url = url; if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, @@ -1354,6 +1560,7 @@ accept_exchanges (void *cls, eddsa_pub)) { exchange->trusted = true; + trusted_exchange_count++; } else { @@ -1385,22 +1592,17 @@ TMH_EXCHANGES_lookup_wire_fee (const char *exchange_url, const char *wire_method, struct TALER_Amount *wire_fee) { - struct Exchange *exchange; + struct TMH_Exchange *exchange; const struct FeesByWireMethod *fbm; const struct TALER_EXCHANGE_WireAggregateFees *af; exchange = lookup_exchange (exchange_url); if (NULL == exchange) { - fprintf (stderr, - "No %s yet\n", - exchange_url); return GNUNET_SYSERR; } if (! exchange->have_wire) { - fprintf (stderr, - "No wire yet\n"); return GNUNET_SYSERR; } fbm = get_wire_fees (exchange, @@ -1433,25 +1635,7 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) &accept_exchanges, (void *) cfg); /* build JSON with list of trusted exchanges (will be included in contracts) */ - TMH_trusted_exchanges = json_array (); - for (struct Exchange *exchange = exchange_head; - NULL != exchange; - exchange = exchange->next) - { - json_t *j_exchange; - - if (! exchange->trusted) - continue; - j_exchange = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("url", - exchange->url), - GNUNET_JSON_pack_data_auto ("master_pub", - &exchange->master_pub)); - GNUNET_assert (0 == - json_array_append_new (TMH_trusted_exchanges, - j_exchange)); - } - return json_array_size (TMH_trusted_exchanges); + return trusted_exchange_count; } @@ -1470,11 +1654,93 @@ TMH_EXCHANGES_done () GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc); merchant_curl_rc = NULL; } - if (NULL != TMH_trusted_exchanges) +} + + +enum GNUNET_GenericReturnValue +TMH_exchange_check_debit (struct TMH_Exchange *ex, + const struct TMH_WireMethod *wm) +{ + for (struct ExchangeAccount *acc = ex->acc_head; + NULL != acc; + acc = acc->next) { - json_decref (TMH_trusted_exchanges); - TMH_trusted_exchanges = NULL; + bool ok = true; + + if (0 != strcmp (acc->wire_method, + wm->wire_method)) + continue; + if (NULL != acc->conversion_url) + continue; /* never use accounts with conversion */ + for (struct Restriction *r = acc->d_head; + NULL != r; + r = r->next) + { + switch (r->type) + { + case TALER_EXCHANGE_AR_INVALID: + GNUNET_break (0); + ok = false; + break; + case TALER_EXCHANGE_AR_DENY: + ok = false; + break; + case TALER_EXCHANGE_AR_REGEX: + if (0 != regexec (&r->details.regex.ex, + wm->payto_uri, + 0, NULL, 0)) + ok = false; + break; + } + if (! ok) + break; + } + + if (ok) + return GNUNET_OK; + } + return GNUNET_NO; +} + + +json_t * +TMH_exchange_get_acceptable (const struct TMH_WireMethod *wm) +{ + json_t *te = json_array (); + + GNUNET_assert (NULL != te); + for (struct TMH_Exchange *exchange = exchange_head; + NULL != exchange; + exchange = exchange->next) + { + json_t *j_exchange; + unsigned int priority; + + if (! exchange->trusted) + continue; + priority = 512; /* medium */ + if (exchange->have_wire) + { + if (GNUNET_OK == + TMH_exchange_check_debit (exchange, + wm)) + priority = 1024; /* high */ + else + priority = 0; /* negative response */ + } + j_exchange = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("url", + exchange->url), + GNUNET_JSON_pack_uint64 ("priority", + priority), + GNUNET_JSON_pack_data_auto ("master_pub", + &exchange->master_pub)); + GNUNET_assert (NULL != j_exchange); + GNUNET_assert (0 == + json_array_append_new (te, + j_exchange)); } + return te; } diff --git a/src/backend/taler-merchant-httpd_exchanges.h b/src/backend/taler-merchant-httpd_exchanges.h index 2f4e69fe..23424f40 100644 --- a/src/backend/taler-merchant-httpd_exchanges.h +++ b/src/backend/taler-merchant-httpd_exchanges.h @@ -31,9 +31,37 @@ /** - * List of our trusted exchanges in JSON format for inclusion in contracts. + * Exchange */ -extern json_t *TMH_trusted_exchanges; +struct TMH_Exchange; + + +/** + * Check if we would trust @a ex to deposit funds + * into our account @a wm. Checks that both @a ex + * is trusted and that @a ex allows wire transfers + * into the account given in @a wm. + * + * @param ex the exchange to check + * @param wm the wire method to check with + * @return #GNUNET_OK if such a debit can happen + */ +enum GNUNET_GenericReturnValue +TMH_exchange_check_debit (struct TMH_Exchange *ex, + const struct TMH_WireMethod *wm); + + +/** + * Return the list of exchanges we would find + * acceptable for a payment given the @a wire_method. + * + * @param wm wire method the payment should be + * made with + * @return list of exchanges we accept for @a wire_method + * (and that would accept our bank account) + */ +json_t * +TMH_exchange_get_acceptable (const struct TMH_WireMethod *wm); /** @@ -61,13 +89,14 @@ TMH_EXCHANGES_done (void); * @param cls closure * @param hr HTTP response details * @param eh handle to the exchange context - * @param exchange_trusted true if this exchange is trusted by config + * @param ih internal handle to the exchange */ typedef void -(*TMH_EXCHANGES_FindContinuation)(void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted); +(*TMH_EXCHANGES_FindContinuation)( + void *cls, + const struct TALER_EXCHANGE_HttpResponse *hr, + struct TALER_EXCHANGE_Handle *eh, + struct TMH_Exchange *ih); /** diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c index ab4de015..498ad1c4 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -28,6 +28,7 @@ #include <taler/taler_templating_lib.h> #include <taler/taler_exchange_service.h> #include "taler-merchant-httpd_exchanges.h" +#include "taler-merchant-httpd_helper.h" #include "taler-merchant-httpd_get-orders-ID.h" #include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_qr.h" @@ -377,62 +378,26 @@ TMH_make_order_status_url (struct MHD_Connection *con, struct TALER_ClaimTokenP *claim_token, struct TALER_PrivateContractHashP *h_contract) { - const char *host; - const char *forwarded_host; - const char *uri_path; - struct GNUNET_Buffer buf = { 0 }; + struct GNUNET_Buffer buf; /* Number of query parameters written so far */ unsigned int num_qp = 0; - host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_HOST); - forwarded_host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - uri_path = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL != forwarded_host) - host = forwarded_host; - if (NULL == host) - { - GNUNET_break (0); - return NULL; - } - if (NULL != strchr (host, '/')) - { - GNUNET_break_op (0); - return NULL; - } GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != order_id); - - if (GNUNET_NO == TALER_mhd_is_https (con)) - GNUNET_buffer_write_str (&buf, - "http://"); - else - GNUNET_buffer_write_str (&buf, - "https://"); - GNUNET_buffer_write_str (&buf, - host); - if (NULL != uri_path) - GNUNET_buffer_write_path (&buf, - uri_path); - if (0 != strcmp ("default", - instance_id)) + if (GNUNET_OK != + TMH_base_url_by_connection (con, + instance_id, + &buf)) { - GNUNET_buffer_write_path (&buf, - "instances"); - GNUNET_buffer_write_path (&buf, - instance_id); + GNUNET_break (0); + return NULL; } GNUNET_buffer_write_path (&buf, "/orders"); GNUNET_buffer_write_path (&buf, order_id); - if ((NULL != claim_token) && - (GNUNET_NO == GNUNET_is_zero (claim_token))) + if ( (NULL != claim_token) && + (! GNUNET_is_zero (claim_token)) ) { /* 'token=' for human readability */ GNUNET_buffer_write_str (&buf, @@ -480,60 +445,27 @@ TMH_make_taler_pay_uri (struct MHD_Connection *con, const char *instance_id, struct TALER_ClaimTokenP *claim_token) { - const char *host; - const char *forwarded_host; - const char *uri_path; - struct GNUNET_Buffer buf = { 0 }; + struct GNUNET_Buffer buf; - host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_HOST); - forwarded_host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - uri_path = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL != forwarded_host) - host = forwarded_host; - if (NULL == host) - { - GNUNET_break (0); - return NULL; - } - if (NULL != strchr (host, '/')) - { - GNUNET_break_op (0); - return NULL; - } GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != order_id); - GNUNET_buffer_write_str (&buf, - "taler"); - if (GNUNET_NO == TALER_mhd_is_https (con)) - GNUNET_buffer_write_str (&buf, - "+http"); - GNUNET_buffer_write_str (&buf, - "://pay/"); - GNUNET_buffer_write_str (&buf, - host); - if (NULL != uri_path) - GNUNET_buffer_write_path (&buf, - uri_path); - if (0 != strcmp ("default", - instance_id)) + if (GNUNET_OK != + TMH_taler_uri_by_connection (con, + "pay", + instance_id, + &buf)) { - GNUNET_buffer_write_path (&buf, - "instances"); - GNUNET_buffer_write_path (&buf, - instance_id); + GNUNET_break (0); + return NULL; } GNUNET_buffer_write_path (&buf, order_id); GNUNET_buffer_write_path (&buf, - (session_id == NULL) ? "" : session_id); - if ((NULL != claim_token) && - (GNUNET_NO == GNUNET_is_zero (claim_token))) + (NULL == session_id) + ? "" + : session_id); + if ( (NULL != claim_token) && + (! GNUNET_is_zero (claim_token))) { /* Just 'c=' because this goes into QR codes, so this is more compact. */ diff --git a/src/backend/taler-merchant-httpd_get-tips-ID.c b/src/backend/taler-merchant-httpd_get-tips-ID.c index f9e6c8cd..71c66d51 100644 --- a/src/backend/taler-merchant-httpd_get-tips-ID.c +++ b/src/backend/taler-merchant-httpd_get-tips-ID.c @@ -25,6 +25,7 @@ #include <taler/taler_json_lib.h> #include <taler/taler_templating_lib.h> #include "taler-merchant-httpd_get-tips-ID.h" +#include "taler-merchant-httpd_helper.h" #include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_qr.h" @@ -34,52 +35,18 @@ TMH_make_taler_tip_uri (struct MHD_Connection *con, const struct TALER_TipIdentifierP *tip_id, const char *instance_id) { - const char *host; - const char *forwarded_host; - const char *uri_path; struct GNUNET_Buffer buf = { 0 }; - host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "Host"); - forwarded_host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - - uri_path = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL != forwarded_host) - host = forwarded_host; - - if (NULL == host) - { - GNUNET_break (0); - return NULL; - } - GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != tip_id); - - GNUNET_buffer_write_str (&buf, - "taler"); - if (GNUNET_NO == TALER_mhd_is_https (con)) - GNUNET_buffer_write_str (&buf, - "+http"); - GNUNET_buffer_write_str (&buf, - "://tip/"); - GNUNET_buffer_write_str (&buf, - host); - if (NULL != uri_path) - GNUNET_buffer_write_path (&buf, - uri_path); - if (0 != strcmp ("default", - instance_id)) + if (GNUNET_OK != + TMH_taler_uri_by_connection (con, + "tip", + instance_id, + &buf)) { - GNUNET_buffer_write_path (&buf, - "instances"); - GNUNET_buffer_write_path (&buf, - instance_id); + GNUNET_break (0); + return NULL; } /* Ensure previous part is slash-terminated */ GNUNET_buffer_write_path (&buf, @@ -96,51 +63,17 @@ TMH_make_tip_status_url (struct MHD_Connection *con, const struct TALER_TipIdentifierP *tip_id, const char *instance_id) { - const char *host; - const char *forwarded_host; - const char *uri_path; - struct GNUNET_Buffer buf = { 0 }; - - host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "Host"); - forwarded_host = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - - uri_path = MHD_lookup_connection_value (con, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL != forwarded_host) - host = forwarded_host; - - if (NULL == host) - { - GNUNET_break (0); - return NULL; - } + struct GNUNET_Buffer buf; GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != tip_id); - - if (GNUNET_NO == TALER_mhd_is_https (con)) - GNUNET_buffer_write_str (&buf, - "http://"); - else - GNUNET_buffer_write_str (&buf, - "https://"); - GNUNET_buffer_write_str (&buf, - host); - if (NULL != uri_path) - GNUNET_buffer_write_path (&buf, - uri_path); - if (0 != strcmp ("default", - instance_id)) + if (GNUNET_OK != + TMH_base_url_by_connection (con, + instance_id, + &buf)) { - GNUNET_buffer_write_path (&buf, - "instances"); - GNUNET_buffer_write_path (&buf, - instance_id); + GNUNET_break (0); + return NULL; } GNUNET_buffer_write_path (&buf, "tips/"); diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c index 0e026a87..9e7a1883 100644 --- a/src/backend/taler-merchant-httpd_helper.c +++ b/src/backend/taler-merchant-httpd_helper.c @@ -314,7 +314,6 @@ TMH_products_array_valid (const json_t *products) { const char *product_id = NULL; const char *description; - json_t *description_i18n = NULL; uint64_t quantity = 0; const char *unit = NULL; struct TALER_Amount price = { .value = 0 }; @@ -326,12 +325,8 @@ TMH_products_array_valid (const json_t *products) GNUNET_JSON_spec_string ("product_id", &product_id), NULL), - GNUNET_JSON_spec_string ("description", - &description), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("description_i18n", - &description_i18n), - NULL), + TALER_JSON_spec_i18n_str ("description", + &description), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_uint64 ("quantity", &quantity), @@ -386,12 +381,6 @@ TMH_products_array_valid (const json_t *products) GNUNET_break_op (0); valid = false; } - if ( (NULL != description_i18n) && - (! TALER_JSON_check_i18n (description_i18n)) ) - { - GNUNET_break_op (0); - valid = false; - } GNUNET_JSON_parse_free (spec); if (! valid) break; @@ -598,12 +587,6 @@ TMH_check_auth_config (struct MHD_Connection *connection, } -/** - * Generate binary UUID from client-provided UUID-string. - * - * @param uuids string intpu - * @param[out] uuid set to binary UUID - */ void TMH_uuid_from_string (const char *uuids, struct GNUNET_Uuid *uuid) @@ -855,3 +838,116 @@ TMH_exchange_accounts_by_method ( } return emc.accounts; } + + +enum GNUNET_GenericReturnValue +TMH_base_url_by_connection (struct MHD_Connection *connection, + const char *instance, + struct GNUNET_Buffer *buf) +{ + const char *host; + const char *forwarded_host; + const char *uri_path; + + memset (buf, + 0, + sizeof (*buf)); + if (GNUNET_YES == TALER_mhd_is_https (connection)) + GNUNET_buffer_write_str (buf, "https://"); + else + GNUNET_buffer_write_str (buf, "http://"); + host = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_HOST); + forwarded_host = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "X-Forwarded-Host"); + if (NULL != forwarded_host) + { + GNUNET_buffer_write_str (buf, + forwarded_host); + } + else + { + if (NULL == host) + { + GNUNET_buffer_clear (buf); + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_buffer_write_str (buf, + host); + } + uri_path = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "X-Forwarded-Prefix"); + if (NULL != uri_path) + GNUNET_buffer_write_path (buf, + uri_path); + if (0 != strcmp (instance, + "default")) + { + GNUNET_buffer_write_path (buf, + "/instances/"); + GNUNET_buffer_write_str (buf, + instance); + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TMH_taler_uri_by_connection (struct MHD_Connection *connection, + const char *method, + const char *instance, + struct GNUNET_Buffer *buf) +{ + const char *host; + const char *forwarded_host; + const char *uri_path; + + memset (buf, + 0, + sizeof (*buf)); + host = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "Host"); + forwarded_host = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "X-Forwarded-Host"); + uri_path = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + "X-Forwarded-Prefix"); + if (NULL != forwarded_host) + host = forwarded_host; + if (NULL == host) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_buffer_write_str (buf, + "taler"); + if (GNUNET_NO == TALER_mhd_is_https (connection)) + GNUNET_buffer_write_str (buf, + "+http"); + GNUNET_buffer_write_str (buf, + "://"); + GNUNET_buffer_write_str (buf, + method); + GNUNET_buffer_write_str (buf, + "/"); + GNUNET_buffer_write_str (buf, + host); + if (NULL != uri_path) + GNUNET_buffer_write_path (buf, + uri_path); + if (0 != strcmp ("default", + instance)) + { + GNUNET_buffer_write_path (buf, + "instances"); + GNUNET_buffer_write_path (buf, + instance); + } + return GNUNET_OK; +} diff --git a/src/backend/taler-merchant-httpd_helper.h b/src/backend/taler-merchant-httpd_helper.h index 3b64c04b..827cc03b 100644 --- a/src/backend/taler-merchant-httpd_helper.h +++ b/src/backend/taler-merchant-httpd_helper.h @@ -159,6 +159,40 @@ TMH_uuid_from_string (const char *uuids, /** + * Initializes a buffer with + * the ``http[s]://$HOST/[$PATH/][instances/$INSTANCE/]`` + * string using $HOST and $PATH from @a connection. + * + * @param[in] connection connection to base the construction on + * @param instance instance to set, NULL for none + * @param[out] buf buffer to initialize + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TMH_base_url_by_connection (struct MHD_Connection *connection, + const char *instance, + struct GNUNET_Buffer *buf); + + +/** + * Initializes a buffer with + * the ``taler[+http]://$METHOD/$HOST/[instances/$INSTANCE/]`` + * string using $HOST from @a connection. + * + * @param[in] connection connection to base the construction on + * @param method taler-URI method to inject + * @param instance instance to set, NULL for none + * @param[out] buf buffer to initialize + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TMH_taler_uri_by_connection (struct MHD_Connection *connection, + const char *method, + const char *instance, + struct GNUNET_Buffer *buf); + + +/** * Put data from an exchange's HTTP response into * a JSON reply * diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c index 9ab10939..1853e88a 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c @@ -498,18 +498,17 @@ refund_cb (void *cls, * @param cls the `struct AbortContext` * @param hr HTTP response details * @param exchange_handle NULL if exchange was not found to be acceptable - * @param exchange_trusted true if this exchange is - * trusted by config + * @param ih internal handle to the exchange */ static void process_abort_with_exchange (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *exchange_handle, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct AbortContext *ac = cls; - (void) exchange_trusted; + (void) ih; ac->fo = NULL; GNUNET_assert (GNUNET_YES == ac->suspended); if (NULL == hr) diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c index afec3b25..81189a6d 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -29,7 +29,6 @@ #include <taler/taler_signatures.h> #include <taler/taler_json_lib.h> #include <taler/taler_exchange_service.h> -#include "taler-merchant-httpd_auditors.h" #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_helper.h" #include "taler-merchant-httpd_post-orders-ID-pay.h" @@ -805,18 +804,18 @@ deposit_get_callback ( * @param cls the `struct KycContext` * @param hr HTTP response details * @param exchange_handle NULL if exchange was not found to be acceptable - * @param exchange_trusted true if this exchange is - * trusted by config + * @param ih internal handle to the exchange */ static void process_kyc_with_exchange ( void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *exchange_handle, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct KycContext *kc = cls; + (void) ih; kc->fo = NULL; if (NULL == exchange_handle) { @@ -1175,15 +1174,14 @@ batch_deposit_cb ( * @param cls the `struct ExchangeGroup` * @param hr HTTP response details * @param exchange_handle NULL if exchange was not found to be acceptable - * @param exchange_trusted true if this exchange is - * trusted by config + * @param ih internal handle to the exchange */ static void process_pay_with_exchange ( void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *exchange_handle, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct ExchangeGroup *eg = cls; struct PayContext *pc = eg->pc; @@ -1221,6 +1219,21 @@ process_pay_with_exchange ( TMH_pack_exchange_reply (hr))); return; } + if (GNUNET_OK != + TMH_exchange_check_debit (ih, + pc->wm)) + { + GNUNET_break_op (0); + pc->pending_at_eg--; + resume_pay_with_response ( + pc, + MHD_HTTP_CONFLICT, + TALER_MHD_MAKE_JSON_PACK ( + TALER_JSON_pack_ec ( + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED))); + return; + } + keys = TALER_EXCHANGE_get_keys (exchange_handle); if (NULL == keys) { @@ -1240,8 +1253,6 @@ process_pay_with_exchange ( { struct DepositConfirmation *dc = &pc->dc[i]; const struct TALER_EXCHANGE_DenomPublicKey *denom_details; - unsigned int http_status; - enum TALER_ErrorCode ec; bool is_age_restricted_denom = false; if (0 != strcmp (eg->exchange_url, @@ -1288,33 +1299,18 @@ process_pay_with_exchange ( dc->deposit_fee = denom_details->fees.deposit; dc->refund_fee = denom_details->fees.refund; - if (GNUNET_OK != - TMH_AUDITORS_check_dk (exchange_handle, - denom_details, - exchange_trusted, - &http_status, - &ec)) + if (GNUNET_TIME_absolute_is_past ( + denom_details->expire_deposit.abs_time)) { - if (! eg->tried_force_keys) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Denomination not audited by trusted auditor, re-fetching /keys\n"); - /* let's try *forcing* a re-download of /keys from the exchange. - Maybe the wallet has seen auditors that we missed. */ - eg->tried_force_keys = true; - eg->fo = TMH_EXCHANGES_find_exchange (eg->exchange_url, - true, - &process_pay_with_exchange, - eg); - if (NULL != eg->fo) - return; - } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Denomination key offered by client has expired for deposits\n"); pc->pending_at_eg--; resume_pay_with_response ( pc, - http_status, + MHD_HTTP_GONE, TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec (ec), + TALER_JSON_pack_ec ( + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_DEPOSIT_EXPIRED), GNUNET_JSON_pack_data_auto ("h_denom_pub", &denom_details->h_key))); return; diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c index 766c8814..ba562f9e 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c @@ -28,7 +28,6 @@ #include <taler/taler_json_lib.h> #include <taler/taler_exchange_service.h> #include "taler-merchant-httpd.h" -#include "taler-merchant-httpd_auditors.h" #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_post-orders-ID-refund.h" @@ -440,18 +439,18 @@ refund_cb (void *cls, * @param cls a `struct CoinRefund *` * @param hr HTTP response details * @param eh handle to the exchange context - * @param exchange_trusted true if this exchange is trusted by config + * @param ih internal handle to the exchange */ static void exchange_found_cb (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct CoinRefund *cr = cls; struct PostRefundData *prd = cr->prd; - (void) exchange_trusted; + (void) ih; cr->fo = NULL; if (NULL == hr) { diff --git a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c index fb560de6..da482cda 100644 --- a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c +++ b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c @@ -379,17 +379,18 @@ withdraw_cb (void *cls, * @param cls closure, with our `struct PlanchetOperation *` * @param hr HTTP response details * @param eh handle to the exchange context - * @param exchange_trusted true if this exchange is trusted by config + * @param ih internal handle to the exchange */ static void do_withdraw (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct PlanchetOperation *po = cls; struct PickupContext *pc = po->pc; + (void) ih; po->fo = NULL; TMH_db->preflight (TMH_db->cls); if (NULL == hr) @@ -501,17 +502,18 @@ do_timeout (void *cls) * @param cls closure, with our `struct PickupContext *` * @param hr HTTP response details * @param eh handle to the exchange context - * @param exchange_trusted true if this exchange is trusted by config + * @param ih internal handle to the exchange */ static void compute_total_requested (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct PickupContext *pc = cls; const struct TALER_EXCHANGE_Keys *keys; + (void) ih; pc->fo = NULL; stop_operations (pc); /* stops timeout job */ if (NULL == hr) diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c index 773415f8..3d807569 100644 --- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c +++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c @@ -651,19 +651,19 @@ exchange_check_cb (void *cls, * @param cls closure with our `struct ExchangeKycRequest *` * @param hr HTTP response details * @param eh handle to the exchange context - * @param exchange_trusted true if this exchange is trusted by config + * @param ih internal handle to the exchange */ static void kyc_with_exchange (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct ExchangeKycRequest *ekr = cls; struct KycContext *kc = ekr->kc; struct TALER_PaytoHashP h_payto; - (void) exchange_trusted; + (void) ih; ekr->fo = NULL; if (MHD_HTTP_OK != hr->http_status) { diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c index cff03c7e..960058ea 100644 --- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c @@ -598,11 +598,12 @@ static void exchange_found_cb (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct TransferQuery *tq = cls; struct GetOrderRequestContext *gorc = tq->gorc; + (void) ih; tq->fo = NULL; if (NULL == hr) { diff --git a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c index 3953fa06..5b5f6b05 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c +++ b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c @@ -26,6 +26,7 @@ #include <taler/taler_json_lib.h> #include "taler-merchant-httpd_private-post-orders-ID-refund.h" #include "taler-merchant-httpd_private-get-orders.h" +#include "taler-merchant-httpd_helper.h" /** @@ -82,43 +83,21 @@ make_taler_refund_uri (struct MHD_Connection *connection, const char *instance_id, const char *order_id) { - const char *host; - const char *forwarded_host; - const char *uri_path; - struct GNUNET_Buffer buf = { 0 }; + struct GNUNET_Buffer buf; GNUNET_assert (NULL != instance_id); GNUNET_assert (NULL != order_id); - host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "Host"); - forwarded_host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - uri_path = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL != forwarded_host) - host = forwarded_host; - if (NULL == host) + if (GNUNET_OK != + TMH_taler_uri_by_connection (connection, + "refund", + instance_id, + &buf)) { - /* Should never happen, at least the host header should be defined */ GNUNET_break (0); return NULL; } - GNUNET_buffer_write_str (&buf, "taler"); - if (GNUNET_NO == TALER_mhd_is_https (connection)) - GNUNET_buffer_write_str (&buf, "+http"); - GNUNET_buffer_write_str (&buf, "://refund/"); - GNUNET_buffer_write_str (&buf, host); - if (NULL != uri_path) - GNUNET_buffer_write_path (&buf, uri_path); - if (0 != strcmp ("default", instance_id)) - { - GNUNET_buffer_write_path (&buf, "instances"); - GNUNET_buffer_write_path (&buf, instance_id); - } - GNUNET_buffer_write_path (&buf, order_id); + GNUNET_buffer_write_path (&buf, + order_id); GNUNET_buffer_write_path (&buf, ""); /* Trailing slash */ return GNUNET_buffer_reap_str (&buf); diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index e75049d0..718909d3 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -28,7 +28,6 @@ #include <taler/taler_signatures.h> #include <taler/taler_json_lib.h> #include "taler-merchant-httpd_private-post-orders.h" -#include "taler-merchant-httpd_auditors.h" #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_helper.h" #include "taler-merchant-httpd_private-get-orders.h" @@ -53,109 +52,6 @@ /** - * Check that the given JSON array of products is well-formed. - * - * @param products JSON array to check - * @return #GNUNET_OK if all is fine - */ -static enum GNUNET_GenericReturnValue -check_products (const json_t *products) -{ - size_t index; - json_t *value; - - if (! json_is_array (products)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - json_array_foreach (products, index, value) { - const char *description; - const char *product_id = NULL; - uint64_t quantity; - const char *unit = NULL; - struct TALER_Amount price; - const char *image = NULL; - json_t *taxes = NULL; - struct GNUNET_TIME_Timestamp delivery_date; - const char *error_name; - unsigned int error_line; - enum GNUNET_GenericReturnValue res; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("product_id", - &product_id), - NULL), - TALER_JSON_spec_i18n_str ("description", - &description), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_uint64 ("quantity", - &quantity), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("unit", - &unit), - NULL), - GNUNET_JSON_spec_mark_optional ( - TALER_JSON_spec_amount ("price", - TMH_currency, - &price), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_string ("image", - &image), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("taxes", - &taxes), - NULL), - GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_timestamp ("delivery_date", - &delivery_date), - NULL), - GNUNET_JSON_spec_end () - }; - - /* extract fields we need to sign separately */ - res = GNUNET_JSON_parse (value, - spec, - &error_name, - &error_line); - if (GNUNET_OK != res) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Product parsing failed at #%u: %s:%u\n", - (unsigned int) index, - error_name, - error_line); - return GNUNET_SYSERR; - } - if ( (NULL != taxes) && - (! TMH_taxes_array_valid (taxes) ) ) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Product parsing failed for taxes\n"); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - if ( (NULL != image) && - (! TMH_image_data_url_valid (image) ) ) - { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Product parsing failed for image\n"); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - GNUNET_JSON_parse_free (spec); - } - return GNUNET_OK; -} - - -/** * Generate the base URL for the given merchant instance. * * @param connection the MHD connection @@ -166,46 +62,13 @@ static char * make_merchant_base_url (struct MHD_Connection *connection, const char *instance_id) { - const char *host; - const char *forwarded_host; - const char *uri_path; - struct GNUNET_Buffer buf = { 0 }; - - if (GNUNET_YES == TALER_mhd_is_https (connection)) - GNUNET_buffer_write_str (&buf, "https://"); - else - GNUNET_buffer_write_str (&buf, "http://"); - host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_HOST); - forwarded_host = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Host"); - if (NULL != forwarded_host) - { - GNUNET_buffer_write_str (&buf, - forwarded_host); - } - else - { - GNUNET_assert (NULL != host); - GNUNET_buffer_write_str (&buf, - host); - } - uri_path = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - "X-Forwarded-Prefix"); - if (NULL != uri_path) - GNUNET_buffer_write_path (&buf, uri_path); - - if (0 != strcmp (instance_id, - "default")) - { - GNUNET_buffer_write_path (&buf, - "/instances/"); - GNUNET_buffer_write_str (&buf, - instance_id); - } + struct GNUNET_Buffer buf; + + if (GNUNET_OK != + TMH_base_url_by_connection (connection, + instance_id, + &buf)) + return NULL; GNUNET_buffer_write_path (&buf, ""); return GNUNET_buffer_reap_str (&buf); @@ -231,37 +94,218 @@ struct InventoryProduct /** + * Information we keep per order we are processing. + */ +struct OrderContext +{ + + /** + * Connection of the request. + */ + struct MHD_Connection *connection; + + /** + * Handler context for the request. + */ + struct TMH_HandlerContext *hc; + + /** + * Hash of the POST request data, used to detect + * idempotent requests. + */ + struct TALER_MerchantPostDataHashP h_post_data; + + /** + * Payment deadline. + */ + struct GNUNET_TIME_Timestamp pay_deadline; + + /** + * Set to how long refunds will be allowed. + */ + struct GNUNET_TIME_Relative refund_delay; + + /** + * Order we are building (modified as we process + * the request). + */ + json_t *order; + + /** + * RFC8905 payment target type to find a matching merchant account + */ + const char *payment_target; + + /** + * Wire method (and our bank account) we have selected + * to be included for this order. + */ + const struct TMH_WireMethod *wm; + + /** + * Claim token for the request. + */ + struct TALER_ClaimTokenP claim_token; + + /** + * Length of the @e inventory_products array. + */ + unsigned int inventory_products_length; + + /** + * Array of inventory products in the @e order. + */ + struct InventoryProduct *inventory_products; + + /** + * Length of the @e uuids array. + */ + unsigned int uuids_length; + + /** + * array of UUIDs used to reserve products from @a inventory_products. + */ + struct GNUNET_Uuid *uuids; + + /** + * which product (by offset) is out of stock, UINT_MAX if all were in-stock + */ + unsigned int out_of_stock_index; + + /** + * Shared key to use with @e pos_algorithm. + */ + char *pos_key; + + /** + * Our order ID. Pointer into @e order. + */ + const char *order_id; + + /** + * Selected algorithm (by template) when we are to + * generate an OTP code for payment confirmation. + */ + enum TALER_MerchantConfirmationAlgorithm pos_algorithm; + + /** + * Current phase of setting up the order. + */ + enum + { + ORDER_PHASE_INIT, + ORDER_PHASE_MERGE_INVENTORY, + ORDER_PHASE_ADD_PAYMENT_DETAILS, + ORDER_PHASE_PATCH_ORDER, + ORDER_PHASE_SET_EXCHANGES, + ORDER_PHASE_CHECK_CONTRACT, + ORDER_PHASE_EXECUTE_ORDER, + + /** + * Processing is done, we should return #MHD_YES. + */ + ORDER_PHASE_FINISHED_MHD_YES, + + /** + * Processing is done, we should return #MHD_NO. + */ + ORDER_PHASE_FINISHED_MHD_NO + } phase; + +}; + + +/** + * Update the phase of @a oc based on @a mret. + * + * @param[in,out] oc order to update phase for + * @param mret #MHD_NO to close with #MHD_NO + * #MHD_YES to close with #MHD_YES + */ +static void +finalize_order (struct OrderContext *oc, + MHD_RESULT mret) +{ + oc->phase = (MHD_YES == mret) + ? ORDER_PHASE_FINISHED_MHD_YES + : ORDER_PHASE_FINISHED_MHD_NO; +} + + +/** + * Update the phase of @a oc based on @a ret. + * + * @param[in,out] oc order to update phase for + * @param ret #GNUNET_SYSERR to close with #MHD_NO + * #GNUNET_NO to close with #MHD_YES + * #GNUNET_OK is not allowed! + */ +static void +finalize_order2 (struct OrderContext *oc, + enum GNUNET_GenericReturnValue ret) +{ + GNUNET_assert (GNUNET_OK != ret); + oc->phase = (GNUNET_NO == ret) + ? ORDER_PHASE_FINISHED_MHD_YES + : ORDER_PHASE_FINISHED_MHD_NO; +} + + +/** + * Generate an error response for @a oc. + * + * @param[in,out] oc order context to respond to + * @param http_status HTTP status code to set + * @param ec error code to set + * @param detail error message detail to set + */ +static void +reply_with_error (struct OrderContext *oc, + unsigned int http_status, + enum TALER_ErrorCode ec, + const char *detail) +{ + MHD_RESULT mret; + + mret = TALER_MHD_reply_with_error (oc->connection, + http_status, + ec, + detail); + finalize_order (oc, + mret); +} + + +/** + * Clean up memory used by @a cls. + * + * @param[in] cls the `struct OrderContext` to clean up + */ +static void +clean_order (void *cls) +{ + struct OrderContext *oc = cls; + + GNUNET_array_grow (oc->inventory_products, + oc->inventory_products_length, + 0); + GNUNET_array_grow (oc->uuids, + oc->uuids_length, + 0); + json_decref (oc->order); + GNUNET_free (oc->pos_key); + GNUNET_free (oc); +} + + +/** * Execute the database transaction to setup the order. * - * @param hc handler context for the request - * @param order_id unique ID for the order - * @param h_post_data hash of the client's POST request, for idempotency checks - * @param pay_deadline until when does the order have to be paid - * @param[in] order order to process (not modified) - * @param claim_token token to use for access control - * @param inventory_products_length length of the @a inventory_products array - * @param inventory_products array of products to add to @a order from our inventory - * @param uuids_length length of the @a uuids array - * @param uuids array of UUIDs used to reserve products from @a inventory_products - * @param[out] out_of_stock_index which product (by offset) is out of stock, UINT_MAX if all were in-stock - * @param pos_key shared secret with the POS - * @param pos_algorithm algorithm for payment confirmation generation - * @return transaction status, 0 if @a uuids were insufficient to reserve required inventory + * @param[in,out] oc order context + * @return transaction status, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a uuids were insufficient to reserve required inventory */ static enum GNUNET_DB_QueryStatus -execute_transaction (struct TMH_HandlerContext *hc, - const char *order_id, - const struct TALER_MerchantPostDataHashP *h_post_data, - struct GNUNET_TIME_Timestamp pay_deadline, - const json_t *order, - const struct TALER_ClaimTokenP *claim_token, - unsigned int inventory_products_length, - const struct InventoryProduct inventory_products[], - unsigned int uuids_length, - const struct GNUNET_Uuid uuids[], - unsigned int *out_of_stock_index, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm) +execute_transaction (struct OrderContext *oc) { enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Timestamp timestamp; @@ -276,14 +320,14 @@ execute_transaction (struct TMH_HandlerContext *hc, } /* Setup order */ qs = TMH_db->insert_order (TMH_db->cls, - hc->instance->settings.id, - order_id, - h_post_data, - pay_deadline, - claim_token, - order, /* called 'contract terms' at database. */ - pos_key, - pos_algorithm); + oc->hc->instance->settings.id, + oc->order_id, + &oc->h_post_data, + oc->pay_deadline, + &oc->claim_token, + oc->order, /* called 'contract terms' at database. */ + oc->pos_key, + oc->pos_algorithm); if (qs <= 0) { /* qs == 0: probably instance does not exist (anymore) */ @@ -291,10 +335,10 @@ execute_transaction (struct TMH_HandlerContext *hc, return qs; } /* Migrate locks from UUIDs to new order: first release old locks */ - for (unsigned int i = 0; i<uuids_length; i++) + for (unsigned int i = 0; i<oc->uuids_length; i++) { qs = TMH_db->unlock_inventory (TMH_db->cls, - &uuids[i]); + &oc->uuids[i]); if (qs < 0) { TMH_db->rollback (TMH_db->cls); @@ -307,13 +351,14 @@ execute_transaction (struct TMH_HandlerContext *hc, (note: this can basically ONLY fail on serializability OR because the UUID locks were insufficient for the desired quantities). */ - for (unsigned int i = 0; i<inventory_products_length; i++) + for (unsigned int i = 0; i<oc->inventory_products_length; i++) { - qs = TMH_db->insert_order_lock (TMH_db->cls, - hc->instance->settings.id, - order_id, - inventory_products[i].product_id, - inventory_products[i].quantity); + qs = TMH_db->insert_order_lock ( + TMH_db->cls, + oc->hc->instance->settings.id, + oc->order_id, + oc->inventory_products[i].product_id, + oc->inventory_products[i].quantity); if (qs < 0) { TMH_db->rollback (TMH_db->cls); @@ -323,17 +368,17 @@ execute_transaction (struct TMH_HandlerContext *hc, { /* qs == 0: lock acquisition failed due to insufficient stocks */ TMH_db->rollback (TMH_db->cls); - *out_of_stock_index = i; /* indicate which product is causing the issue */ + oc->out_of_stock_index = i; /* indicate which product is causing the issue */ return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } } - *out_of_stock_index = UINT_MAX; + oc->out_of_stock_index = UINT_MAX; /* Get the order serial and timestamp for the order we just created to update long-poll clients. */ qs = TMH_db->lookup_order_summary (TMH_db->cls, - hc->instance->settings.id, - order_id, + oc->hc->instance->settings.id, + oc->order_id, ×tamp, &order_serial); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) @@ -341,7 +386,7 @@ execute_transaction (struct TMH_HandlerContext *hc, TMH_db->rollback (TMH_db->cls); return qs; } - TMH_notify_order_change (hc->instance, + TMH_notify_order_change (oc->hc->instance, TMH_OSF_NONE, timestamp, order_serial); @@ -359,36 +404,14 @@ execute_transaction (struct TMH_HandlerContext *hc, * database. Write the resulting proposal or an error message * of a MHD connection. * - * @param connection connection to write the result or error to - * @param hc handler context for the request - * @param h_post_data hash of the client's POST request, for idempotency checks - * @param[in,out] order order to process (can be modified) - * @param claim_token token to use for access control - * @param inventory_products_length length of the @a inventory_products array - * @param inventory_products array of products to add to @a order from our inventory - * @param uuids_length length of the @a uuids array - * @param uuids array of UUIDs used to reserve products from @a inventory_products - * @param pos_key encoded key for verification payment - * @param pos_algorithm algorithm to compute the payment verification - * @return MHD result code + * @param[in,out] oc order context */ -static MHD_RESULT -execute_order (struct MHD_Connection *connection, - struct TMH_HandlerContext *hc, - const struct TALER_MerchantPostDataHashP *h_post_data, - json_t *order, - const struct TALER_ClaimTokenP *claim_token, - unsigned int inventory_products_length, - const struct InventoryProduct inventory_products[], - unsigned int uuids_length, - const struct GNUNET_Uuid uuids[], - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm) +static void +execute_order (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = - &hc->instance->settings; + &oc->hc->instance->settings; struct TALER_Amount total; - const char *order_id; const char *summary; const char *fulfillment_msg = NULL; json_t *products; @@ -398,13 +421,12 @@ execute_order (struct MHD_Connection *connection, struct GNUNET_TIME_Timestamp timestamp; struct GNUNET_TIME_Timestamp refund_deadline = { 0 }; struct GNUNET_TIME_Timestamp wire_transfer_deadline; - struct GNUNET_TIME_Timestamp pay_deadline; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount ("amount", TMH_currency, &total), GNUNET_JSON_spec_string ("order_id", - &order_id), + &oc->order_id), GNUNET_JSON_spec_string ("summary", &summary), /** @@ -433,57 +455,62 @@ execute_order (struct MHD_Connection *connection, &refund_deadline), NULL), GNUNET_JSON_spec_timestamp ("pay_deadline", - &pay_deadline), + &oc->pay_deadline), GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", &wire_transfer_deadline), GNUNET_JSON_spec_end () }; enum GNUNET_DB_QueryStatus qs; - unsigned int out_of_stock_index; /* extract fields we need to sign separately */ { enum GNUNET_GenericReturnValue res; - res = TALER_MHD_parse_json_data (connection, - order, + res = TALER_MHD_parse_json_data (oc->connection, + oc->order, spec); if (GNUNET_OK != res) { GNUNET_break_op (0); - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; + finalize_order2 (oc, + res); } } /* check product list in contract is well-formed */ - if (GNUNET_OK != check_products (products)) + if (! TMH_products_array_valid (products)) { GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "order:products"); + GNUNET_break_op (0); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "order:products"); + return; } + // FIXME: we can do this better now... if ( (NULL != fulfillment_i18n) && (! TALER_JSON_check_i18n (fulfillment_i18n)) ) { + GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "order:fulfillment_message_i18n"); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "order:fulfillment_message_i18n"); + return; } if ( (NULL != summary_i18n) && (! TALER_JSON_check_i18n (summary_i18n)) ) { + GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "order:summary_i18n"); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "order:summary_i18n"); + return; } /* Test if we already have an order with this id */ @@ -494,8 +521,8 @@ execute_order (struct MHD_Connection *connection, TMH_db->preflight (TMH_db->cls); qs = TMH_db->lookup_order (TMH_db->cls, - hc->instance->settings.id, - order_id, + oc->hc->instance->settings.id, + oc->order_id, &token, &orig_post, &contract_terms); @@ -505,10 +532,11 @@ execute_order (struct MHD_Connection *connection, GNUNET_break (0); TMH_db->rollback (TMH_db->cls); GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "lookup_order"); + reply_with_error (oc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_order"); + return; } if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { @@ -518,13 +546,15 @@ execute_order (struct MHD_Connection *connection, /* Comparing the contract terms is sufficient because all the other params get added to it at some point. */ if (0 == GNUNET_memcmp (&orig_post, - h_post_data)) + &oc->h_post_data)) { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order creation idempotent\n"); ret = TALER_MHD_REPLY_JSON_PACK ( - connection, + oc->connection, MHD_HTTP_OK, GNUNET_JSON_pack_string ("order_id", - order_id), + oc->order_id), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_data_varsize ( "token", @@ -532,40 +562,29 @@ execute_order (struct MHD_Connection *connection, ? NULL : &token, sizeof (token)))); + GNUNET_JSON_parse_free (spec); + finalize_order (oc, + ret); + return; } - else - { - /* This request is not idempotent */ - ret = TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, - order_id); - } - GNUNET_JSON_parse_free (spec); - return ret; + /* This request is not idempotent */ + GNUNET_break_op (0); + reply_with_error ( + oc, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, + oc->order_id); + return; } } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing database transaction to create order '%s' for instance '%s'\n", - order_id, + oc->order_id, settings->id); for (unsigned int i = 0; i<MAX_RETRIES; i++) { TMH_db->preflight (TMH_db->cls); - qs = execute_transaction (hc, - order_id, - h_post_data, - pay_deadline, - order, - claim_token, - inventory_products_length, - inventory_products, - uuids_length, - uuids, - &out_of_stock_index, - pos_key, - pos_algorithm); + qs = execute_transaction (oc); if (GNUNET_DB_STATUS_SOFT_ERROR != qs) break; } @@ -576,57 +595,64 @@ execute_order (struct MHD_Connection *connection, if (GNUNET_DB_STATUS_SOFT_ERROR == qs) { GNUNET_break (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_SOFT_FAILURE, - NULL); + reply_with_error (oc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_SOFT_FAILURE, + NULL); + return; } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { /* should be: contract (!) with same order ID already exists */ - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_CONFLICT, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ALREADY_EXISTS, - order_id); + oc->order_id); + return; } /* Other hard transaction error (disk full, etc.) */ GNUNET_break (0); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_COMMIT_FAILED, NULL); + return; } /* DB transaction succeeded, check for out-of-stock */ - if (out_of_stock_index < UINT_MAX) + if (oc->out_of_stock_index < UINT_MAX) { /* We had a product that has insufficient quantities, generate the details for the response. */ struct TALER_MERCHANTDB_ProductDetails pd; MHD_RESULT ret; - memset (&pd, 0, sizeof (pd)); + memset (&pd, + 0, + sizeof (pd)); qs = TMH_db->lookup_product ( TMH_db->cls, - hc->instance->settings.id, - inventory_products[out_of_stock_index].product_id, + oc->hc->instance->settings.id, + oc->inventory_products[oc->out_of_stock_index].product_id, &pd); GNUNET_JSON_parse_free (spec); switch (qs) { case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order creation failed: product out of stock\n"); ret = TALER_MHD_REPLY_JSON_PACK ( - connection, + oc->connection, MHD_HTTP_GONE, GNUNET_JSON_pack_string ( "product_id", - inventory_products[out_of_stock_index].product_id), + oc->inventory_products[oc->out_of_stock_index].product_id), GNUNET_JSON_pack_uint64 ( "requested_quantity", - inventory_products[out_of_stock_index].quantity), + oc->inventory_products[oc->out_of_stock_index].quantity), GNUNET_JSON_pack_uint64 ( "available_quantity", pd.total_stock - pd.total_sold - pd.total_lost), @@ -635,57 +661,145 @@ execute_order (struct MHD_Connection *connection, "restock_expected", pd.next_restock))); TALER_MERCHANTDB_product_details_free (&pd); - return ret; + finalize_order (oc, + ret); + return; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - return TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_GONE, - GNUNET_JSON_pack_string ( - "product_id", - inventory_products[out_of_stock_index].product_id), - GNUNET_JSON_pack_uint64 ( - "requested_quantity", - inventory_products[out_of_stock_index].quantity), - GNUNET_JSON_pack_uint64 ( - "available_quantity", - 0)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order creation failed: unknown product out of stock\n"); + finalize_order (oc, + TALER_MHD_REPLY_JSON_PACK ( + oc->connection, + MHD_HTTP_GONE, + GNUNET_JSON_pack_string ( + "product_id", + oc->inventory_products[oc->out_of_stock_index]. + product_id), + GNUNET_JSON_pack_uint64 ( + "requested_quantity", + oc->inventory_products[oc->out_of_stock_index]. + quantity), + GNUNET_JSON_pack_uint64 ( + "available_quantity", + 0))); + return; case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_break (0); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_SOFT_FAILURE, NULL); + return; case GNUNET_DB_STATUS_HARD_ERROR: - return TALER_MHD_reply_with_error ( - connection, + GNUNET_break (0); + reply_with_error ( + oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, NULL); + return; } GNUNET_break (0); - return MHD_NO; - } + oc->phase = ORDER_PHASE_FINISHED_MHD_NO; + return; + } /* end 'out of stock' case */ /* Everything in-stock, generate positive response */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Order creation succeeded\n"); { MHD_RESULT ret; ret = TALER_MHD_REPLY_JSON_PACK ( - connection, + oc->connection, MHD_HTTP_OK, GNUNET_JSON_pack_string ("order_id", - order_id), + oc->order_id), GNUNET_JSON_pack_allow_null ( GNUNET_JSON_pack_data_varsize ( "token", - GNUNET_is_zero (claim_token) + GNUNET_is_zero (&oc->claim_token) ? NULL - : claim_token, - sizeof (*claim_token)))); + : &oc->claim_token, + sizeof (oc->claim_token)))); GNUNET_JSON_parse_free (spec); - return ret; + finalize_order (oc, + ret); + } +} + + +/** + * Check that the contract is now well-formed. + * + * @param[in,out] oc order context + */ +static void +check_contract (struct OrderContext *oc) +{ + struct TALER_PrivateContractHashP h_control; + + switch (TALER_JSON_contract_hash (oc->order, + &h_control)) + { + case GNUNET_SYSERR: + GNUNET_break (0); + reply_with_error ( + oc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, + "could not compute hash of patched order"); + return; + case GNUNET_NO: + GNUNET_break_op (0); + reply_with_error ( + oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, + "order contained unallowed values"); + return; + case GNUNET_OK: + oc->phase++; + return; } + GNUNET_assert (0); +} + + +/** + * Set list of acceptable exchanges in @a oc. + * + * @param[in,out] oc order context + */ +static void +set_exchanges (struct OrderContext *oc) +{ + json_t *exchanges; + + exchanges = TMH_exchange_get_acceptable (oc->wm); + if (0 == json_array_size (exchanges)) + { + json_decref (exchanges); + /* FIXME: maybe there are exchanges for which we simply + did not yet get /wire replies. We should *try* to + run find_exchange() or something to give those + exchanges a chance to respond => need to suspend! */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Cannot create order: lacking trusted exchanges for wire method `%s'\n", + oc->wm->wire_method); + reply_with_error ( + oc, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, + oc->wm->wire_method); + return; + } + GNUNET_assert (0 == + json_object_set (oc->order, + "exchanges", + exchanges)); + oc->phase++; } @@ -693,41 +807,18 @@ execute_order (struct MHD_Connection *connection, * Add missing fields to the order. Upon success, continue * processing with execute_order(). * - * @param connection connection to write the result or error to - * @param hc handler context for the request - * @param h_post_data hash of the client's POST request, for idempotency checks - * @param[in,out] order order to process (can be modified) - * @param claim_token token to use for access control - * @param refund_delay refund delay - * @param inventory_products_length length of the @a inventory_products array - * @param inventory_products array of products to add to @a order from our inventory - * @param uuids_length length of the @a uuids array - * @param uuids array of UUIDs used to reserve products from @a inventory_products - * @param pos_key encoded key for verification payment - * @param pos_algorithm algorithm to compute the payment verification - * @return MHD result code + * @param[in,out] oc order context */ -static MHD_RESULT -patch_order (struct MHD_Connection *connection, - struct TMH_HandlerContext *hc, - const struct TALER_MerchantPostDataHashP *h_post_data, - json_t *order, - const struct TALER_ClaimTokenP *claim_token, - struct GNUNET_TIME_Relative refund_delay, - unsigned int inventory_products_length, - const struct InventoryProduct inventory_products[], - unsigned int uuids_length, - const struct GNUNET_Uuid uuids[], - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm) +static void +patch_order (struct OrderContext *oc) { const struct TALER_MERCHANTDB_InstanceSettings *settings = - &hc->instance->settings; + &oc->hc->instance->settings; const char *order_id = NULL; const char *fulfillment_url = NULL; const char *merchant_base_url = NULL; - json_t *jmerchant = NULL; - json_t *delivery_location = NULL; + const json_t *jmerchant = NULL; + const json_t *delivery_location = NULL; struct TALER_Amount max_wire_fee = { 0 }; struct TALER_Amount max_fee = { 0 }; uint32_t wire_fee_amortization = 0; @@ -737,8 +828,6 @@ patch_order (struct MHD_Connection *connection, = GNUNET_TIME_UNIT_ZERO_TS; struct GNUNET_TIME_Timestamp refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS; - struct GNUNET_TIME_Timestamp pay_deadline - = GNUNET_TIME_UNIT_ZERO_TS; struct GNUNET_TIME_Timestamp wire_deadline = GNUNET_TIME_UNIT_FOREVER_TS; /* auto_refund only needs to be type-checked, @@ -751,8 +840,8 @@ patch_order (struct MHD_Connection *connection, &merchant_base_url), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("merchant", - &jmerchant), + GNUNET_JSON_spec_object_const ("merchant", + &jmerchant), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("order_id", @@ -772,7 +861,7 @@ patch_order (struct MHD_Connection *connection, NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("pay_deadline", - &pay_deadline), + &oc->pay_deadline), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", @@ -801,22 +890,22 @@ patch_order (struct MHD_Connection *connection, &auto_refund), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("delivery_location", - &delivery_location), + GNUNET_JSON_spec_object_const ("delivery_location", + &delivery_location), NULL), GNUNET_JSON_spec_end () }; enum GNUNET_GenericReturnValue ret; - ret = TALER_MHD_parse_json_data (connection, - order, + ret = TALER_MHD_parse_json_data (oc->connection, + oc->order, spec); if (GNUNET_OK != ret) { GNUNET_break_op (0); - return (GNUNET_NO == ret) - ? MHD_YES - : MHD_NO; + finalize_order2 (oc, + ret); + return; } /* Add order_id if it doesn't exist. */ @@ -835,8 +924,8 @@ patch_order (struct MHD_Connection *connection, if (NULL == tm_info) { GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_LOCALTIME, NULL); @@ -862,7 +951,7 @@ patch_order (struct MHD_Connection *connection, "Assigning order ID `%s' server-side\n", buf); GNUNET_break (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "order_id", jbuf)); order_id = json_string_value (jbuf); @@ -886,11 +975,11 @@ patch_order (struct MHD_Connection *connection, "${ORDER_ID}")) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "fulfillment_url"); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "fulfillment_url"); + return; } GNUNET_asprintf (&nurl, @@ -904,7 +993,7 @@ patch_order (struct MHD_Connection *connection, pos + strlen ("${ORDER_ID}")); /* replace in JSON of the order */ GNUNET_break (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "fulfillment_url", json_string (nurl))); GNUNET_free (nurl); @@ -920,7 +1009,7 @@ patch_order (struct MHD_Connection *connection, if (GNUNET_TIME_absolute_is_zero (timestamp.abs_time)) { GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "timestamp", GNUNET_JSON_from_timestamp (now))); } @@ -928,7 +1017,7 @@ patch_order (struct MHD_Connection *connection, /* If no refund_deadline given, set one based on refund_delay. */ if (GNUNET_TIME_absolute_is_never (refund_deadline.abs_time)) { - if (GNUNET_TIME_relative_is_zero (refund_delay)) + if (GNUNET_TIME_relative_is_zero (oc->refund_delay)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Refund delay is zero, no refunds are possible for this order\n"); @@ -936,10 +1025,10 @@ patch_order (struct MHD_Connection *connection, } else { - refund_deadline = GNUNET_TIME_relative_to_timestamp (refund_delay); + refund_deadline = GNUNET_TIME_relative_to_timestamp (oc->refund_delay); } GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "refund_deadline", GNUNET_JSON_from_timestamp ( refund_deadline))); @@ -948,45 +1037,45 @@ patch_order (struct MHD_Connection *connection, (GNUNET_TIME_absolute_is_past (delivery_date.abs_time)) ) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_DELIVERY_DATE_IN_PAST, NULL); + return; } } - if (GNUNET_TIME_absolute_is_zero (pay_deadline.abs_time)) + if (GNUNET_TIME_absolute_is_zero (oc->pay_deadline.abs_time)) { - struct GNUNET_TIME_Timestamp t; - - t = GNUNET_TIME_relative_to_timestamp (settings->default_pay_delay); + oc->pay_deadline = GNUNET_TIME_relative_to_timestamp ( + settings->default_pay_delay); GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "pay_deadline", - GNUNET_JSON_from_timestamp (t))); + GNUNET_JSON_from_timestamp ( + oc->pay_deadline))); } - else if (GNUNET_TIME_absolute_is_past (pay_deadline.abs_time)) + else if (GNUNET_TIME_absolute_is_past (oc->pay_deadline.abs_time)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PAY_DEADLINE_IN_PAST, NULL); + return; } if ( (! GNUNET_TIME_absolute_is_zero (refund_deadline.abs_time)) && (GNUNET_TIME_absolute_is_past (refund_deadline.abs_time)) ) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_DEADLINE_IN_PAST, NULL); + return; } if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time)) @@ -995,22 +1084,21 @@ patch_order (struct MHD_Connection *connection, t = GNUNET_TIME_relative_to_timestamp ( GNUNET_TIME_relative_max (settings->default_wire_transfer_delay, - refund_delay)); + oc->refund_delay)); wire_deadline = GNUNET_TIME_timestamp_max (refund_deadline, t); if (GNUNET_TIME_absolute_is_never (wire_deadline.abs_time)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_WIRE_DEADLINE_IS_NEVER, "order:wire_transfer_deadline"); - + return; } GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "wire_transfer_deadline", GNUNET_JSON_from_timestamp ( wire_deadline))); @@ -1020,12 +1108,12 @@ patch_order (struct MHD_Connection *connection, refund_deadline)) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_REFUND_AFTER_WIRE_DEADLINE, "order:wire_transfer_deadline;order:refund_deadline"); + return; } /* Note: total amount currency match checked @@ -1035,7 +1123,7 @@ patch_order (struct MHD_Connection *connection, { GNUNET_assert (0 == json_object_set_new ( - order, + oc->order, "max_wire_fee", TALER_JSON_from_amount (&settings->default_max_wire_fee))); } @@ -1045,7 +1133,7 @@ patch_order (struct MHD_Connection *connection, { GNUNET_assert (0 == json_object_set_new ( - order, + oc->order, "max_fee", TALER_JSON_from_amount ( &settings->default_max_deposit_fee))); @@ -1054,7 +1142,7 @@ patch_order (struct MHD_Connection *connection, { GNUNET_assert (0 == json_object_set_new ( - order, + oc->order, "wire_fee_amortization", json_integer ( (json_int_t) settings->default_wire_fee_amortization))); @@ -1063,10 +1151,20 @@ patch_order (struct MHD_Connection *connection, { char *url; - url = make_merchant_base_url (connection, + url = make_merchant_base_url (oc->connection, settings->id); + if (NULL == url) + { + GNUNET_break_op (0); + reply_with_error ( + oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "order:merchant_base_url"); + return; + } GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "merchant_base_url", json_string (url))); GNUNET_free (url); @@ -1075,24 +1173,24 @@ patch_order (struct MHD_Connection *connection, ('/' != merchant_base_url[strlen (merchant_base_url) - 1])) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR, "merchant_base_url is not valid"); + return; } - /* Fill in merchant information if necessary */ + /* Merchant information must not already be present */ if (NULL != jmerchant) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_PROPOSAL_PARSE_ERROR, "'merchant' field already set, but must be provided by backend"); + return; } { @@ -1140,91 +1238,39 @@ patch_order (struct MHD_Connection *connection, } } GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "merchant", jm)); } - /* add fields to the contract that the backend should provide */ GNUNET_assert (0 == - json_object_set (order, - "exchanges", - TMH_trusted_exchanges)); - GNUNET_assert (0 == - json_object_set (order, - "auditors", - j_auditors)); - GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "merchant_pub", GNUNET_JSON_from_data_auto ( - &hc->instance->merchant_pub))); + &oc->hc->instance->merchant_pub))); + if (GNUNET_OK != - TALER_JSON_contract_seed_forgettable (order)) + TALER_JSON_contract_seed_forgettable (oc->order)) { - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, + reply_with_error ( + oc, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_JSON_INVALID, "could not compute hash of order due to bogus forgettable fields"); + return; } if ( (NULL != delivery_location) && (! TMH_location_object_valid (delivery_location)) ) { GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "delivery_location"); - } - - /* sanity check result */ - { - struct TALER_PrivateContractHashP h_control; - - switch (TALER_JSON_contract_hash (order, - &h_control)) - { - case GNUNET_SYSERR: - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, - "could not compute hash of patched order"); - case GNUNET_NO: - GNUNET_JSON_parse_free (spec); - GNUNET_break_op (0); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, - "order contained unallowed values"); - case GNUNET_OK: - break; - } - } - { - MHD_RESULT mres; - - mres = execute_order (connection, - hc, - h_post_data, - order, - claim_token, - inventory_products_length, - inventory_products, - uuids_length, - uuids, - pos_key, - pos_algorithm); - GNUNET_JSON_parse_free (spec); - return mres; + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "delivery_location"); + return; } + oc->phase++; } @@ -1233,77 +1279,44 @@ patch_order (struct MHD_Connection *connection, * order could be paid to @a order. On success, continue * processing with patch_order(). * - * @param connection connection to write the result or error to - * @param hc handler context for the request - * @param h_post_data hash of the client's POST request, for idempotency checks - * @param[in,out] order order to process (can be modified) - * @param claim_token token to use for access control - * @param refund_delay refund delay - * @param payment_target desired wire method, NULL for no preference - * @param inventory_products_length length of the @a inventory_products array - * @param inventory_products array of products to add to @a order from our inventory - * @param uuids_length length of the @a uuids array - * @param uuids array of UUIDs used to reserve products from @a inventory_products - * @param pos_key encoded key for verification payment - * @param pos_algorithm algorithm to compute the payment verification + * @param[in,out] oc order context * @return MHD result code */ -static MHD_RESULT -add_payment_details (struct MHD_Connection *connection, - struct TMH_HandlerContext *hc, - const struct TALER_MerchantPostDataHashP *h_post_data, - json_t *order, - const struct TALER_ClaimTokenP *claim_token, - struct GNUNET_TIME_Relative refund_delay, - const char *payment_target, - unsigned int inventory_products_length, - const struct InventoryProduct inventory_products[], - unsigned int uuids_length, - const struct GNUNET_Uuid uuids[], - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm) +static void +add_payment_details (struct OrderContext *oc) { struct TMH_WireMethod *wm; - wm = hc->instance->wm_head; + wm = oc->hc->instance->wm_head; /* Locate wire method that has a matching payment target */ while ( (NULL != wm) && ( (! wm->active) || - ( (NULL != payment_target) && - (0 != strcasecmp (payment_target, + ( (NULL != oc->payment_target) && + (0 != strcasecmp (oc->payment_target, wm->wire_method) ) ) ) ) wm = wm->next; if (NULL == wm) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "No wire method available for instance '%s'\n", - hc->instance->settings.id); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE, - payment_target); + oc->hc->instance->settings.id); + reply_with_error (oc, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_INSTANCE_CONFIGURATION_LACKS_WIRE, + oc->payment_target); + return; } + oc->wm = wm; GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "h_wire", GNUNET_JSON_from_data_auto ( &wm->h_wire))); GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "wire_method", json_string (wm->wire_method))); - return patch_order (connection, - hc, - h_post_data, - order, - claim_token, - refund_delay, - inventory_products_length, - inventory_products, - uuids_length, - uuids, - pos_key, - pos_algorithm); + oc->phase++; } @@ -1312,57 +1325,32 @@ add_payment_details (struct MHD_Connection *connection, * database about the details of those products. Upon success, * continue processing by calling add_payment_details(). * - * @param connection connection to write the result or error to - * @param hc handler context for the request - * @param h_post_data hash of the client's POST request, for idempotency checks - * @param[in,out] order order to process (can be modified) - * @param claim_token token to use for access control - * @param refund_delay time window where it is possible to ask a refund - * @param payment_target RFC8905 payment target type to find a matching merchant account - * @param inventory_products_length length of the @a inventory_products array - * @param inventory_products array of products to add to @a order from our inventory - * @param uuids_length length of the @a uuids array - * @param uuids array of UUIDs used to reserve products from @a inventory_products - * @param pos_key encoded key for verification payment - * @param pos_algorithm algorithm to compute the payment verification - * @return MHD result code + * @param[in,out] oc order context to process */ -static MHD_RESULT -merge_inventory (struct MHD_Connection *connection, - struct TMH_HandlerContext *hc, - const struct TALER_MerchantPostDataHashP *h_post_data, - json_t *order, - const struct TALER_ClaimTokenP *claim_token, - struct GNUNET_TIME_Relative refund_delay, - const char *payment_target, - unsigned int inventory_products_length, - const struct InventoryProduct inventory_products[], - unsigned int uuids_length, - const struct GNUNET_Uuid uuids[], - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm) +static void +merge_inventory (struct OrderContext *oc) { /** * inventory_products => instructions to add products to contract terms * order.products => contains products that are not from the backend-managed inventory. */ - GNUNET_assert (NULL != order); { - json_t *jprod = json_object_get (order, + json_t *jprod = json_object_get (oc->order, "products"); if (NULL == jprod) { GNUNET_assert (0 == - json_object_set_new (order, + json_object_set_new (oc->order, "products", json_array ())); } else if (! TMH_products_array_valid (jprod)) { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "order.products"); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "order.products"); + return; } } @@ -1370,14 +1358,15 @@ merge_inventory (struct MHD_Connection *connection, { json_t *np = json_array (); - for (unsigned int i = 0; i<inventory_products_length; i++) + GNUNET_assert (NULL != np); + for (unsigned int i = 0; i<oc->inventory_products_length; i++) { struct TALER_MERCHANTDB_ProductDetails pd; enum GNUNET_DB_QueryStatus qs; qs = TMH_db->lookup_product (TMH_db->cls, - hc->instance->settings.id, - inventory_products[i].product_id, + oc->hc->instance->settings.id, + oc->inventory_products[i].product_id, &pd); if (qs <= 0) { @@ -1399,7 +1388,7 @@ merge_inventory (struct MHD_Connection *connection, case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Product %s from order unknown\n", - inventory_products[i].product_id); + oc->inventory_products[i].product_id); http_status = MHD_HTTP_NOT_FOUND; ec = TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN; break; @@ -1408,10 +1397,11 @@ merge_inventory (struct MHD_Connection *connection, GNUNET_assert (0); } json_decref (np); - return TALER_MHD_reply_with_error (connection, - http_status, - ec, - inventory_products[i].product_id); + reply_with_error (oc, + http_status, + ec, + oc->inventory_products[i].product_id); + return; } { json_t *p; @@ -1429,9 +1419,9 @@ merge_inventory (struct MHD_Connection *connection, pd.taxes), GNUNET_JSON_pack_string ("image", pd.image), - GNUNET_JSON_pack_uint64 ("quantity", - inventory_products[i]. - quantity)); + GNUNET_JSON_pack_uint64 ( + "quantity", + oc->inventory_products[i].quantity)); GNUNET_assert (NULL != p); GNUNET_assert (0 == json_array_append_new (np, @@ -1446,66 +1436,41 @@ merge_inventory (struct MHD_Connection *connection, { json_t *xp; - xp = json_object_get (order, + xp = json_object_get (oc->order, "products"); GNUNET_assert (NULL != xp); json_array_extend (xp, np); json_decref (np); } } - return add_payment_details (connection, - hc, - h_post_data, - order, - claim_token, - refund_delay, - payment_target, - inventory_products_length, - inventory_products, - uuids_length, - uuids, - pos_key, - pos_algorithm); + oc->phase++; } -MHD_RESULT -TMH_private_post_orders_with_pos_secrets ( - const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc, - const char *pos_key, - enum TALER_MerchantConfirmationAlgorithm pos_algorithm) +static void +parse_order_request (struct OrderContext *oc) { - json_t *order; - struct GNUNET_TIME_Relative refund_delay = GNUNET_TIME_UNIT_ZERO; - const char *payment_target = NULL; - json_t *ip = NULL; - unsigned int ips_len = 0; - struct InventoryProduct *ips = NULL; - unsigned int uuids_len = 0; - json_t *uuid; - struct GNUNET_Uuid *uuids = NULL; - struct TALER_ClaimTokenP claim_token; + const json_t *ip = NULL; + const json_t *uuid = NULL; bool create_token = true; /* default */ struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_json ("order", - &order), + &oc->order), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_relative_time ("refund_delay", - &refund_delay), + &oc->refund_delay), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_string ("payment_target", - &payment_target), + &oc->payment_target), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("inventory_products", - &ip), + GNUNET_JSON_spec_array_const ("inventory_products", + &ip), NULL), GNUNET_JSON_spec_mark_optional ( - GNUNET_JSON_spec_json ("lock_uuids", - &uuid), + GNUNET_JSON_spec_array_const ("lock_uuids", + &uuid), NULL), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_bool ("create_token", @@ -1514,83 +1479,69 @@ TMH_private_post_orders_with_pos_secrets ( GNUNET_JSON_spec_end () }; enum GNUNET_GenericReturnValue ret; - struct TALER_MerchantPostDataHashP h_post_data; - (void) rh; - ret = TALER_MHD_parse_json_data (connection, - hc->request_body, + ret = TALER_MHD_parse_json_data (oc->connection, + oc->hc->request_body, spec); if (GNUNET_OK != ret) - return (GNUNET_NO == ret) - ? MHD_YES - : MHD_NO; - + { + GNUNET_break_op (0); + finalize_order2 (oc, + ret); + return; + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Refund delay is %s\n", - GNUNET_TIME_relative2s (refund_delay, + GNUNET_TIME_relative2s (oc->refund_delay, false)); - TMH_db->expire_locks (TMH_db->cls); if (create_token) { GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &claim_token, - sizeof (claim_token)); - } - else - { - /* we use all-zeros for 'no token' */ - memset (&claim_token, - 0, - sizeof (claim_token)); + &oc->claim_token, + sizeof (oc->claim_token)); } - /* Compute h_post_data (for idempotency check) */ { char *req_body_enc; /* Dump normalized JSON to string. */ - if (NULL == (req_body_enc = json_dumps (hc->request_body, - JSON_ENCODE_ANY - | JSON_COMPACT - | JSON_SORT_KEYS))) + if (NULL == (req_body_enc + = json_dumps (oc->hc->request_body, + JSON_ENCODE_ANY + | JSON_COMPACT + | JSON_SORT_KEYS))) { GNUNET_break (0); GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_ALLOCATION_FAILURE, - "request body normalization for hashing"); + reply_with_error (oc, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_ALLOCATION_FAILURE, + "request body normalization for hashing"); + return; } GNUNET_CRYPTO_hash (req_body_enc, strlen (req_body_enc), - &h_post_data.hash); + &oc->h_post_data.hash); GNUNET_free (req_body_enc); } /* parse the inventory_products (optionally given) */ if (NULL != ip) { - if (! json_is_array (ip)) - { - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "inventory_products"); - } - GNUNET_array_grow (ips, - ips_len, + GNUNET_array_grow (oc->inventory_products, + oc->inventory_products_length, json_array_size (ip)); - for (unsigned int i = 0; i<ips_len; i++) + for (unsigned int i = 0; i<oc->inventory_products_length; i++) { + struct InventoryProduct *ipr = &oc->inventory_products[i]; const char *error_name; unsigned int error_line; struct GNUNET_JSON_Specification ispec[] = { GNUNET_JSON_spec_string ("product_id", - &ips[i].product_id), + &ipr->product_id), GNUNET_JSON_spec_uint32 ("quantity", - &ips[i].quantity), + &ipr->quantity), GNUNET_JSON_spec_end () }; @@ -1602,19 +1553,16 @@ TMH_private_post_orders_with_pos_secrets ( if (GNUNET_OK != ret) { GNUNET_break_op (0); - GNUNET_array_grow (ips, - ips_len, - 0); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Product parsing failed at #%u: %s:%u\n", i, error_name, error_line); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "inventory_products"); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "inventory_products"); + return; } } } @@ -1622,21 +1570,10 @@ TMH_private_post_orders_with_pos_secrets ( /* parse the lock_uuids (optionally given) */ if (NULL != uuid) { - if (! json_is_array (uuid)) - { - GNUNET_array_grow (ips, - ips_len, - 0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "lock_uuids"); - } - GNUNET_array_grow (uuids, - uuids_len, + GNUNET_array_grow (oc->uuids, + oc->uuids_length, json_array_size (uuid)); - for (unsigned int i = 0; i<uuids_len; i++) + for (unsigned int i = 0; i<oc->uuids_length; i++) { json_t *ui = json_array_get (uuid, i); @@ -1644,51 +1581,81 @@ TMH_private_post_orders_with_pos_secrets ( if (! json_is_string (ui)) { GNUNET_break_op (0); - GNUNET_array_grow (ips, - ips_len, - 0); - GNUNET_array_grow (uuids, - uuids_len, - 0); - GNUNET_JSON_parse_free (spec); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "UUID parsing failed at #%u\n", i); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "lock_uuids"); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "lock_uuids"); + return; } TMH_uuid_from_string (json_string_value (ui), - &uuids[i]); + &oc->uuids[i]); } } + oc->phase++; +} - /* Finally, start by completing the order */ + +MHD_RESULT +TMH_private_post_orders_with_pos_secrets ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc, + const char *pos_key, + enum TALER_MerchantConfirmationAlgorithm pos_algorithm) +{ + struct OrderContext *oc = hc->ctx; + + if (NULL == oc) { - MHD_RESULT res; - - res = merge_inventory (connection, - hc, - &h_post_data, - order, - &claim_token, - refund_delay, - payment_target, - ips_len, - ips, - uuids_len, - uuids, - pos_key, - pos_algorithm); - GNUNET_array_grow (ips, - ips_len, - 0); - GNUNET_array_grow (uuids, - uuids_len, - 0); - GNUNET_JSON_parse_free (spec); - return res; + oc = GNUNET_new (struct OrderContext); + hc->ctx = oc; + hc->cc = &clean_order; + oc->connection = connection; + oc->hc = hc; + if (NULL != pos_key) + oc->pos_key = GNUNET_strdup (pos_key); + oc->pos_algorithm = pos_algorithm; + } + while (1) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Processing order in phase %d\n", + oc->phase); + switch (oc->phase) + { + case ORDER_PHASE_INIT: + parse_order_request (oc); + break; + case ORDER_PHASE_MERGE_INVENTORY: + merge_inventory (oc); + break; + case ORDER_PHASE_ADD_PAYMENT_DETAILS: + add_payment_details (oc); + break; + case ORDER_PHASE_PATCH_ORDER: + patch_order (oc); + break; + case ORDER_PHASE_SET_EXCHANGES: + set_exchanges (oc); + break; + case ORDER_PHASE_CHECK_CONTRACT: + check_contract (oc); + break; + case ORDER_PHASE_EXECUTE_ORDER: + execute_order (oc); + break; + case ORDER_PHASE_FINISHED_MHD_YES: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Finished processing order (1)\n"); + return MHD_YES; + case ORDER_PHASE_FINISHED_MHD_NO: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Finished processing order (0)\n"); + return MHD_NO; + } } } diff --git a/src/backend/taler-merchant-httpd_private-post-reserves.c b/src/backend/taler-merchant-httpd_private-post-reserves.c index 4001badd..801cbf9c 100644 --- a/src/backend/taler-merchant-httpd_private-post-reserves.c +++ b/src/backend/taler-merchant-httpd_private-post-reserves.c @@ -210,17 +210,18 @@ reserve_context_cleanup (void *cls) * @param cls closure with our `struct PostReserveContext *` * @param hr HTTP response details * @param eh handle to the exchange context - * @param exchange_trusted true if this exchange is trusted by config + * @param ih internal handle to the exchange */ static void handle_exchange (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct PostReserveContext *rc = cls; const struct TALER_EXCHANGE_Keys *keys; + (void) ih; rc->fo = NULL; if (NULL != rc->timeout_task) { diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c b/src/backend/taler-merchant-httpd_private-post-transfers.c index a57c5e3b..4a324b6a 100644 --- a/src/backend/taler-merchant-httpd_private-post-transfers.c +++ b/src/backend/taler-merchant-httpd_private-post-transfers.c @@ -23,7 +23,6 @@ #include <jansson.h> #include <taler/taler_signatures.h> #include <taler/taler_json_lib.h> -#include "taler-merchant-httpd_auditors.h" #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd_helper.h" #include "taler-merchant-httpd_private-post-transfers.h" @@ -552,17 +551,17 @@ wire_transfer_cb (void *cls, * @param cls the `struct PostTransfersContext` * @param hr HTTP response details * @param eh NULL if exchange was not found to be acceptable - * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config + * @param ih internal handle to the exchange */ static void process_transfer_with_exchange (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct PostTransfersContext *ptc = cls; - (void) exchange_trusted; + (void) ih; ptc->fo = NULL; if (NULL == hr) { diff --git a/src/backend/taler-merchant-httpd_reserves.c b/src/backend/taler-merchant-httpd_reserves.c index fec18440..3d11a954 100644 --- a/src/backend/taler-merchant-httpd_reserves.c +++ b/src/backend/taler-merchant-httpd_reserves.c @@ -232,16 +232,17 @@ reserve_cb (void *cls, * @param cls closure * @param hr HTTP response details * @param eh handle to the exchange context - * @param exchange_trusted true if this exchange is trusted by config + * @param ih internal handle to the exchange */ static void find_cb (void *cls, const struct TALER_EXCHANGE_HttpResponse *hr, struct TALER_EXCHANGE_Handle *eh, - bool exchange_trusted) + struct TMH_Exchange *ih) { struct Reserve *r = cls; + (void) ih; r->fo = NULL; if (NULL == eh) { diff --git a/src/testing/test_merchant_api.conf b/src/testing/test_merchant_api.conf index e0451124..f270d670 100644 --- a/src/testing/test_merchant_api.conf +++ b/src/testing/test_merchant_api.conf @@ -64,28 +64,6 @@ CURRENCY = EUR BASE_URL = http://the.auditor/ -####################################################### -# Configuration for ??? Is this used? -####################################################### - -# Auditors must be in sections "auditor-", the rest of the section -# name could be anything. -[auditor-ezb] -# Informal name of the auditor. Just for the user. -NAME = European Central Bank - -# URL of the auditor (especially for in the future, when the -# auditor offers an automated issue reporting system). -# Not really used today. -URL = http://taler.ezb.eu/ - -# This is the important bit: the signing key of the auditor. -PUBLIC_KEY = 9QXF7XY7E9VPV47B5Z806NDFSX2VJ79SVHHD29QEQ3BG31ANHZ60 - -# Which currency is this auditor trusted for? -CURRENCY = EUR - - ################################################### # Configuration for the exchange for the testcase # ################################################### @@ -103,15 +81,12 @@ MASTER_PUBLIC_KEY = T1VVFQZZARQ1CMF4BN58EE7SKTW5AV2BS18S87ZEGYS4S29J6DNG # Base URL of the exchange. BASE_URL = "http://localhost:8081/" - [exchangedb-postgres] CONFIG = "postgres:///talercheck" - [auditordb-postgres] CONFIG = postgres:///talercheck - # Account of the EXCHANGE [exchange-account-exchange] # What is the exchange's bank account (with the "Taler Bank" demo system)? |