summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/Makefile.am1
-rw-r--r--src/backend/taler-merchant-httpd.c13
-rw-r--r--src/backend/taler-merchant-httpd_auditors.c319
-rw-r--r--src/backend/taler-merchant-httpd_auditors.h76
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.c400
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.h43
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID.c114
-rw-r--r--src/backend/taler-merchant-httpd_get-tips-ID.c97
-rw-r--r--src/backend/taler-merchant-httpd_helper.c134
-rw-r--r--src/backend/taler-merchant-httpd_helper.h34
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-abort.c7
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-pay.c58
-rw-r--r--src/backend/taler-merchant-httpd_post-orders-ID-refund.c7
-rw-r--r--src/backend/taler-merchant-httpd_post-tips-ID-pickup.c10
-rw-r--r--src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c6
-rw-r--r--src/backend/taler-merchant-httpd_private-get-orders-ID.c3
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c39
-rw-r--r--src/backend/taler-merchant-httpd_private-post-orders.c1377
-rw-r--r--src/backend/taler-merchant-httpd_private-post-reserves.c5
-rw-r--r--src/backend/taler-merchant-httpd_private-post-transfers.c7
-rw-r--r--src/backend/taler-merchant-httpd_reserves.c5
-rw-r--r--src/testing/test_merchant_api.conf25
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",
- &currency))
- {
- 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 (&section[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,
&timestamp,
&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)?