summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2020-04-17 14:07:06 +0200
committerChristian Grothoff <christian@grothoff.org>2020-04-17 14:07:24 +0200
commit0b8e550d09635f762033626c8c12b7b4a0d0faf7 (patch)
tree986a09d537a5dfde4b65fabd018c0f74d2778457 /src/backend
parent0a327ceebd3126d4adf69916e92702fe3c7a22e2 (diff)
downloadmerchant-0b8e550d09635f762033626c8c12b7b4a0d0faf7.tar.gz
merchant-0b8e550d09635f762033626c8c12b7b4a0d0faf7.tar.bz2
merchant-0b8e550d09635f762033626c8c12b7b4a0d0faf7.zip
starting v1 protocol dispatching logic
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/Makefile.am6
-rw-r--r--src/backend/legacy.c509
-rw-r--r--src/backend/taler-merchant-httpd.c1335
-rw-r--r--src/backend/taler-merchant-httpd.h308
-rw-r--r--src/backend/taler-merchant-httpd_config.c95
-rw-r--r--src/backend/taler-merchant-httpd_config.h12
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.c28
-rw-r--r--src/backend/taler-merchant-httpd_mhd.c38
-rw-r--r--src/backend/taler-merchant-httpd_mhd.h26
-rw-r--r--src/backend/taler-merchant-httpd_private-get-instances.c160
-rw-r--r--src/backend/taler-merchant-httpd_transfers-get.h49
11 files changed, 1214 insertions, 1352 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 082c35f9..f47cf088 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -21,10 +21,12 @@ 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_check-payment.c taler-merchant-httpd_check-payment.h \
taler-merchant-httpd_exchanges.c taler-merchant-httpd_exchanges.h \
+ taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h
+
+DEAD = \
+ taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \
taler-merchant-httpd_history.c taler-merchant-httpd_history.h \
- taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
taler-merchant-httpd_order.c taler-merchant-httpd_order.h \
taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \
taler-merchant-httpd_poll-payment.c taler-merchant-httpd_poll-payment.h \
diff --git a/src/backend/legacy.c b/src/backend/legacy.c
new file mode 100644
index 00000000..c105ecb3
--- /dev/null
+++ b/src/backend/legacy.c
@@ -0,0 +1,509 @@
+/**
+ * Create a taler://pay/ URI for the given @a con and @a order_id
+ * and @a session_id and @a instance_id.
+ *
+ * @param con HTTP connection
+ * @param order_id the order id
+ * @param session_id session, may be NULL
+ * @param instance_id instance, may be "default"
+ * @return corresponding taler://pay/ URI, or NULL on missing "host"
+ */
+char *
+TMH_make_taler_pay_uri (struct MHD_Connection *con,
+ const char *order_id,
+ const char *session_id,
+ const char *instance_id)
+{
+ const char *host;
+ const char *forwarded_host;
+ const char *uri_path;
+ const char *uri_instance_id;
+ const char *query;
+ char *result;
+
+ 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 == uri_path)
+ uri_path = "-";
+ if (NULL != forwarded_host)
+ host = forwarded_host;
+ if (0 == strcmp (instance_id,
+ "default"))
+ uri_instance_id = "-";
+ else
+ uri_instance_id = instance_id;
+ if (NULL == host)
+ {
+ /* Should never happen, at least the host header should be defined */
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ if (GNUNET_YES == TALER_mhd_is_https (con))
+ query = "";
+ else
+ query = "?insecure=1";
+ GNUNET_assert (NULL != order_id);
+ GNUNET_assert (0 < GNUNET_asprintf (&result,
+ "taler://pay/%s/%s/%s/%s%s%s%s",
+ host,
+ uri_path,
+ uri_instance_id,
+ order_id,
+ (NULL == session_id) ? "" : "/",
+ (NULL == session_id) ? "" : session_id,
+ query));
+ return result;
+}
+
+
+/**
+ * Closure for the #wireformat_iterator_cb().
+ */
+struct WireFormatIteratorContext
+{
+ /**
+ * The global iteration context.
+ */
+ struct IterateInstancesCls *iic;
+
+ /**
+ * The merchant instance we are currently building.
+ */
+ struct MerchantInstance *mi;
+
+ /**
+ * Set to #GNUNET_YES if the default instance was found.
+ */
+ int default_instance;
+};
+
+
+/**
+ * Callback that looks for 'merchant-account-*' sections,
+ * and populates our wire method according to the data
+ *
+ * @param cls closure with a `struct WireFormatIteratorContext *`
+ * @section section name this callback gets
+ */
+static void
+wireformat_iterator_cb (void *cls,
+ const char *section)
+{
+ struct WireFormatIteratorContext *wfic = cls;
+ struct MerchantInstance *mi = wfic->mi;
+ struct IterateInstancesCls *iic = wfic->iic;
+ char *instance_option;
+ struct WireMethod *wm;
+ char *payto;
+ char *fn;
+ json_t *j;
+ struct GNUNET_HashCode jh_wire;
+ char *wire_file_mode;
+
+ if (0 != strncasecmp (section,
+ "merchant-account-",
+ strlen ("merchant-account-")))
+ return;
+ GNUNET_asprintf (&instance_option,
+ "HONOR_%s",
+ mi->id);
+ if (GNUNET_YES !=
+ GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ section,
+ instance_option))
+ {
+ GNUNET_free (instance_option);
+ return;
+ }
+ GNUNET_free (instance_option);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "PAYTO_URI",
+ &payto))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "PAYTO_URI");
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg,
+ section,
+ "WIRE_RESPONSE",
+ &fn))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "WIRE_RESPONSE");
+ GNUNET_free (payto);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+
+ /* Try loading existing JSON from file */
+ if (GNUNET_YES ==
+ GNUNET_DISK_file_test (fn))
+ {
+ json_error_t err;
+ char *url;
+
+ if (NULL ==
+ (j = json_load_file (fn,
+ JSON_REJECT_DUPLICATES,
+ &err)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to load JSON from `%s': %s at %d:%d\n",
+ fn,
+ err.text,
+ err.line,
+ err.column);
+ GNUNET_free (fn);
+ GNUNET_free (payto);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+ url = TALER_JSON_wire_to_payto (j);
+ if (NULL == url)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "URL missing in `%s', disabling account `%s'\n",
+ fn,
+ section);
+ GNUNET_free (fn);
+ GNUNET_free (payto);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+ if (0 != strcasecmp (url,
+ payto))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "URL `%s' does not match configuration `%s', disabling account `%s'\n",
+ url,
+ payto,
+ section);
+ GNUNET_free (fn);
+ GNUNET_free (payto);
+ GNUNET_free (url);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+ GNUNET_free (url);
+ }
+ else /* need to generate JSON */
+ {
+ struct GNUNET_HashCode salt;
+ char *salt_str;
+
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &salt,
+ sizeof (salt));
+ salt_str = GNUNET_STRINGS_data_to_string_alloc (&salt,
+ sizeof (salt));
+ j = json_pack ("{s:s, s:s}",
+ "payto_uri", payto,
+ "salt", salt_str);
+ GNUNET_free (salt_str);
+
+ /* Make sure every path component exists. */
+ if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "mkdir",
+ fn);
+ GNUNET_free (fn);
+ GNUNET_free (payto);
+ json_decref (j);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+
+ if (0 != json_dump_file (j,
+ fn,
+ JSON_COMPACT | JSON_SORT_KEYS))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to write hashed wire details to `%s'\n",
+ fn);
+ GNUNET_free (fn);
+ GNUNET_free (payto);
+ json_decref (j);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "WIRE_FILE_MODE",
+ &wire_file_mode))
+ {
+ errno = 0;
+ mode_t mode = (mode_t) strtoul (wire_file_mode,
+ NULL,
+ 8);
+ if (0 != errno)
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "WIRE_FILE_MODE",
+ "Must be octal number\n");
+ iic->ret = GNUNET_SYSERR;
+ GNUNET_free (fn);
+ return;
+ }
+ if (0 != chmod (fn, mode))
+ {
+ TALER_LOG_ERROR ("chmod failed on %s\n", fn);
+ iic->ret = GNUNET_SYSERR;
+ GNUNET_free (fn);
+ return;
+ }
+ }
+ }
+
+ GNUNET_free (fn);
+
+ if (GNUNET_OK !=
+ TALER_JSON_merchant_wire_signature_hash (j,
+ &jh_wire))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to hash wire input\n");
+ GNUNET_free (fn);
+ GNUNET_free (payto);
+ json_decref (j);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+
+ wm = GNUNET_new (struct WireMethod);
+ wm->wire_method = TALER_payto_get_method (payto);
+ GNUNET_free (payto);
+ GNUNET_asprintf (&instance_option,
+ "ACTIVE_%s",
+ mi->id);
+ wm->active = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ section,
+ instance_option);
+ GNUNET_free (instance_option);
+ if (GNUNET_YES == wm->active)
+ GNUNET_CONTAINER_DLL_insert (mi->wm_head,
+ mi->wm_tail,
+ wm);
+ else
+ GNUNET_CONTAINER_DLL_insert_tail (mi->wm_head,
+ mi->wm_tail,
+ wm);
+ wm->j_wire = j;
+ wm->h_wire = jh_wire;
+}
+
+
+/**
+ * Callback that looks for 'instance-*' sections,
+ * and populates accordingly each instance's data
+ *
+ * @param cls closure of type `struct IterateInstancesCls`
+ * @section section name this callback gets
+ */
+static void
+instances_iterator_cb (void *cls,
+ const char *section)
+{
+ struct IterateInstancesCls *iic = cls;
+ char *token;
+ struct MerchantInstance *mi;
+ /* used as hashmap keys */
+ struct GNUNET_HashCode h_pk;
+ struct GNUNET_HashCode h_id;
+
+ if (0 != strncasecmp (section,
+ "instance-",
+ strlen ("instance-")))
+ return;
+ /** Get id **/
+ token = strrchr (section, '-');
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Extracted token: %s\n",
+ token + 1);
+ mi = GNUNET_new (struct MerchantInstance);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "NAME",
+ &mi->name))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "NAME");
+ GNUNET_free (mi);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg,
+ section,
+ "KEYFILE",
+ &mi->keyfile))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "KEYFILE");
+ GNUNET_free (mi->name);
+ GNUNET_free (mi);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "TIP_EXCHANGE",
+ &mi->tip_exchange))
+ {
+ char *tip_reserves;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg,
+ section,
+ "TIP_RESERVE_PRIV_FILENAME",
+ &tip_reserves))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "TIP_RESERVE_PRIV_FILENAME");
+ GNUNET_free (mi->keyfile);
+ GNUNET_free (mi->name);
+ GNUNET_free (mi);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_key_from_file (tip_reserves,
+ GNUNET_NO,
+ &mi->tip_reserve.eddsa_priv))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "TIP_RESERVE_PRIV_FILENAME",
+ "Failed to read private key");
+ GNUNET_free (tip_reserves);
+ GNUNET_free (mi->keyfile);
+ GNUNET_free (mi->name);
+ GNUNET_free (mi);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+ GNUNET_free (tip_reserves);
+ }
+
+ if (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_eddsa_key_from_file (mi->keyfile,
+ GNUNET_YES,
+ &mi->privkey.eddsa_priv))
+ {
+ GNUNET_break (0);
+ GNUNET_free (mi->keyfile);
+ GNUNET_free (mi->name);
+ GNUNET_free (mi);
+ iic->ret = GNUNET_SYSERR;
+ return;
+ }
+ GNUNET_CRYPTO_eddsa_key_get_public (&mi->privkey.eddsa_priv,
+ &mi->pubkey.eddsa_pub);
+
+ mi->id = GNUNET_strdup (token + 1);
+ if (0 == strcasecmp ("default",
+ mi->id))
+ iic->default_instance = GNUNET_YES;
+
+ GNUNET_CRYPTO_hash (mi->id,
+ strlen (mi->id),
+ &h_id);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_multihashmap_put (by_id_map,
+ &h_id,
+ mi,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to put an entry into the 'by_id' hashmap\n");
+ iic->ret = GNUNET_SYSERR;
+ GNUNET_free (mi->keyfile);
+ GNUNET_free (mi->name);
+ GNUNET_free (mi);
+ return;
+ }
+ GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
+ &h_pk);
+
+
+ /* Initialize wireformats */
+ {
+ struct WireFormatIteratorContext wfic = {
+ .iic = iic,
+ .mi = mi
+ };
+
+ GNUNET_CONFIGURATION_iterate_sections (cfg,
+ &wireformat_iterator_cb,
+ &wfic);
+ }
+ if (NULL == mi->wm_head)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to load wire formats for instance `%s'\n",
+ mi->id);
+ iic->ret = GNUNET_SYSERR;
+ }
+
+}
+
+
+/**
+ * Iterate over each merchant instance, in order to populate
+ * each instance's own data
+ *
+ * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors
+ * (for example, if no "default" instance is defined)
+ */
+static int
+iterate_instances (void)
+{
+ struct IterateInstancesCls iic;
+
+ iic.default_instance = GNUNET_NO;
+ iic.ret = GNUNET_OK;
+ GNUNET_CONFIGURATION_iterate_sections (cfg,
+ &instances_iterator_cb,
+ &iic);
+
+ if (GNUNET_NO == iic.default_instance)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "No default merchant instance found\n");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != iic.ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "At least one instance was not successfully parsed\n");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 86b36306..60979d2e 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2014-2018 Taler Systems SA
+ (C) 2014-2020 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
@@ -32,23 +32,8 @@
#include "taler_merchantdb_lib.h"
#include "taler-merchant-httpd.h"
#include "taler-merchant-httpd_auditors.h"
-#include "taler-merchant-httpd_check-payment.h"
#include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_history.h"
#include "taler-merchant-httpd_mhd.h"
-#include "taler-merchant-httpd_order.h"
-#include "taler-merchant-httpd_pay.h"
-#include "taler-merchant-httpd_poll-payment.h"
-#include "taler-merchant-httpd_proposal.h"
-#include "taler-merchant-httpd_refund.h"
-#include "taler-merchant-httpd_refund_increase.h"
-#include "taler-merchant-httpd_refund_lookup.h"
-#include "taler-merchant-httpd_track-transaction.h"
-#include "taler-merchant-httpd_track-transfer.h"
-#include "taler-merchant-httpd_tip-authorize.h"
-#include "taler-merchant-httpd_tip-pickup.h"
-#include "taler-merchant-httpd_tip-query.h"
-#include "taler-merchant-httpd_tip-reserve-helper.h"
#include "taler-merchant-httpd_config.h"
/**
@@ -58,45 +43,26 @@
/**
- * Used by the iterator of the various merchant's instances given
- * in configuration
+ * Which currency do we use?
*/
-struct IterateInstancesCls
-{
-
- /**
- * Current index in the global array of #MerchantInstance
- * types. Used by the callback in order to know which index
- * is associated to the element being processed.
- */
- unsigned int current_index;
-
- /**
- * Flag indicating whether config contains a default instance
- */
- unsigned int default_instance;
+char *TMH_currency;
- /**
- * Tells if the parsing encountered any error. We need this
- * field since the iterator must return void
- */
- unsigned int ret;
-};
+/**
+ * Inform the auditor for all deposit confirmations (global option)
+ */
+int TMH_force_audit;
+/**
+ * Connection handle to the our database
+ */
+struct TALER_MERCHANTDB_Plugin *TMH_db;
/**
* Hashmap pointing at merchant instances by 'id'. An 'id' is
* just a string that identifies a merchant instance. When a frontend
* needs to specify an instance to the backend, it does so by 'id'
*/
-struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
-
-/**
- * Hashmap pointing at merchant instances by public key. This map
- * is mainly used to check whether there is more than one instance
- * using the same key
- */
-struct GNUNET_CONTAINER_MultiHashMap *by_kpub_map;
+static struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
/**
* The port we are running on
@@ -104,55 +70,9 @@ struct GNUNET_CONTAINER_MultiHashMap *by_kpub_map;
static uint16_t port;
/**
- * This value tells the exchange by which date this merchant would like
- * to receive the funds for a deposited payment
- */
-struct GNUNET_TIME_Relative default_wire_transfer_delay;
-
-/**
- * Locations from the configuration. Mapping from
- * label to location data.
- */
-json_t *default_locations;
-
-/**
- * If the frontend does NOT specify a payment deadline, how long should
- * offers we make be valid by default?
- */
-struct GNUNET_TIME_Relative default_pay_deadline;
-
-/**
- * Default maximum wire fee to assume, unless stated differently in the proposal
- * already.
- */
-struct TALER_Amount default_max_wire_fee;
-
-/**
- * Default max deposit fee that the merchant is willing to
- * pay; if deposit costs more, then the customer will cover
- * the difference.
- */
-struct TALER_Amount default_max_deposit_fee;
-
-/**
- * Default factor for wire fee amortization.
- */
-unsigned long long default_wire_fee_amortization;
-
-/**
* Should a "Connection: close" header be added to each HTTP response?
*/
-static int TMH_merchant_connection_close;
-
-/**
- * Which currency do we use?
- */
-char *TMH_currency;
-
-/**
- * Inform the auditor for all deposit confirmations (global option)
- */
-int TMH_force_audit;
+static int merchant_connection_close;
/**
* Task running the HTTP server.
@@ -165,11 +85,6 @@ static struct GNUNET_SCHEDULER_Task *mhd_task;
static int result;
/**
- * Connection handle to the our database
- */
-struct TALER_MERCHANTDB_Plugin *db;
-
-/**
* The MHD Daemon
*/
static struct MHD_Daemon *mhd;
@@ -178,39 +93,39 @@ static struct MHD_Daemon *mhd;
* MIN-Heap of suspended connections to resume when the timeout expires,
* ordered by timeout. Values are of type `struct MHD_Connection`
*/
-struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
+static struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
/**
* Hash map from H(order_id,merchant_pub) to `struct MHD_Connection`
* entries to resume when a payment is made for the given order.
*/
-struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
+static struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
/**
* Task responsible for timeouts in the #resume_timeout_heap.
*/
-struct GNUNET_SCHEDULER_Task *resume_timeout_task;
+static struct GNUNET_SCHEDULER_Task *resume_timeout_task;
/**
* Our configuration.
*/
-static struct GNUNET_CONFIGURATION_Handle *cfg;
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
- * Callback that frees all the elements in the hashmap
+ * Callback that frees all the instances in the hashmap
*
* @param cls closure, NULL
* @param key current key
- * @param value a `struct MerchantInstance`
+ * @param value a `struct TMH_MerchantInstance`
*/
static int
-hashmap_free (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
+instance_free (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
{
- struct MerchantInstance *mi = value;
- struct WireMethod *wm;
+ struct TMH_MerchantInstance *mi = value;
+ struct TMH_WireMethod *wm;
(void) cls;
(void) key;
@@ -266,10 +181,10 @@ payment_trigger_free (void *cls,
* @param mpub an instance public key
* @param key[out] set to the hash map key to use
*/
-void
-TMH_compute_pay_key (const char *order_id,
- const struct TALER_MerchantPublicKeyP *mpub,
- struct GNUNET_HashCode *key)
+static void
+compute_pay_key (const char *order_id,
+ const struct TALER_MerchantPublicKeyP *mpub,
+ struct GNUNET_HashCode *key)
{
size_t olen = strlen (order_id);
char buf[sizeof (*mpub) + olen];
@@ -287,7 +202,6 @@ TMH_compute_pay_key (const char *order_id,
"Pay key for %s is %s\n",
order_id,
GNUNET_h2s (key));
-
}
@@ -332,14 +246,21 @@ do_resume (void *cls)
/**
* Suspend connection from @a sc until payment has been received.
*
+ * @param order_id the order that we are waiting on
+ * @param mi the merchant instance we are waiting on
* @param sc connection to suspend
* @param min_refund refund amount we are waiting on to be exceeded before resuming,
* NULL if we are not waiting for refunds
*/
void
-TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
+TMH_long_poll_suspend (const char *order_id,
+ const struct TMH_MerchantInstance *mi,
+ struct TMH_SuspendedConnection *sc,
const struct TALER_Amount *min_refund)
{
+ compute_pay_key (order_id,
+ &mi->pubkey,
+ &sc->key);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Suspending operation on key %s\n",
GNUNET_h2s (&sc->key));
@@ -350,7 +271,7 @@ TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
if (NULL != min_refund)
{
- sc->awaiting_refund = GNUNET_YES;
+ sc->awaiting_refund = true;
sc->refund_expected = *min_refund;
}
sc->hn = GNUNET_CONTAINER_heap_insert (resume_timeout_heap,
@@ -385,7 +306,7 @@ resume_operation (void *cls,
const struct TALER_Amount *have_refund = cls;
struct TMH_SuspendedConnection *sc = value;
- if ( (GNUNET_YES == sc->awaiting_refund) &&
+ if ( (sc->awaiting_refund) &&
( (NULL == have_refund) ||
(1 != TALER_amount_cmp (have_refund,
&sc->refund_expected)) ) )
@@ -415,20 +336,20 @@ resume_operation (void *cls,
* Find out if we have any clients long-polling for @a order_id to be
* confirmed at merchant @a mpub, and if so, tell them to resume.
*
- * @param order_id the order that was paid
- * @param mpub the merchant's public key of the instance where the payment happened
+ * @param order_id the order that was paid or refunded
+ * @param mi the merchant instance where the payment or refund happened
* @param have_refund refunded amount, NULL if there was no refund
*/
void
TMH_long_poll_resume (const char *order_id,
- const struct TALER_MerchantPublicKeyP *mpub,
+ const struct TMH_MerchantInstance *mi,
const struct TALER_Amount *have_refund)
{
struct GNUNET_HashCode key;
- TMH_compute_pay_key (order_id,
- mpub,
- &key);
+ compute_pay_key (order_id,
+ &mi->pubkey,
+ &key);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Resuming operations suspended pending payment on key %s\n",
GNUNET_h2s (&key));
@@ -443,73 +364,6 @@ TMH_long_poll_resume (const char *order_id,
/**
- * Create a taler://pay/ URI for the given @a con and @a order_id
- * and @a session_id and @a instance_id.
- *
- * @param con HTTP connection
- * @param order_id the order id
- * @param session_id session, may be NULL
- * @param instance_id instance, may be "default"
- * @return corresponding taler://pay/ URI, or NULL on missing "host"
- */
-char *
-TMH_make_taler_pay_uri (struct MHD_Connection *con,
- const char *order_id,
- const char *session_id,
- const char *instance_id)
-{
- const char *host;
- const char *forwarded_host;
- const char *uri_path;
- const char *uri_instance_id;
- const char *query;
- char *result;
-
- 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 == uri_path)
- uri_path = "-";
- if (NULL != forwarded_host)
- host = forwarded_host;
- if (0 == strcmp (instance_id,
- "default"))
- uri_instance_id = "-";
- else
- uri_instance_id = instance_id;
- if (NULL == host)
- {
- /* Should never happen, at least the host header should be defined */
- GNUNET_break (0);
- return NULL;
- }
-
- if (GNUNET_YES == TALER_mhd_is_https (con))
- query = "";
- else
- query = "?insecure=1";
- GNUNET_assert (NULL != order_id);
- GNUNET_assert (0 < GNUNET_asprintf (&result,
- "taler://pay/%s/%s/%s/%s%s%s%s",
- host,
- uri_path,
- uri_instance_id,
- order_id,
- (NULL == session_id) ? "" : "/",
- (NULL == session_id) ? "" : session_id,
- query));
- return result;
-}
-
-
-/**
* Shutdown task (magically invoked when the application is being
* quit)
*
@@ -521,10 +375,12 @@ do_shutdown (void *cls)
struct TMH_SuspendedConnection *sc;
(void) cls;
- MH_force_pc_resume ();
- MH_force_trh_resume ();
- MH_force_refund_resume ();
- MH_force_tip_pickup_resume ();
+#if 0
+ TMH_force_pc_resume ();
+ TMH_force_trh_resume ();
+ TMH_force_refund_resume ();
+ TMH_force_tip_pickup_resume ();
+#endif
if (NULL != mhd_task)
{
GNUNET_SCHEDULER_cancel (mhd_task);
@@ -556,10 +412,10 @@ do_shutdown (void *cls)
MHD_stop_daemon (mhd);
mhd = NULL;
}
- if (NULL != db)
+ if (NULL != TMH_db)
{
- TALER_MERCHANTDB_plugin_unload (db);
- db = NULL;
+ TALER_MERCHANTDB_plugin_unload (TMH_db);
+ TMH_db = NULL;
}
TMH_EXCHANGES_done ();
TMH_AUDITORS_done ();
@@ -574,16 +430,11 @@ do_shutdown (void *cls)
if (NULL != by_id_map)
{
GNUNET_CONTAINER_multihashmap_iterate (by_id_map,
- &hashmap_free,
+ &instance_free,
NULL);
GNUNET_CONTAINER_multihashmap_destroy (by_id_map);
by_id_map = NULL;
}
- if (NULL != by_kpub_map)
- {
- GNUNET_CONTAINER_multihashmap_destroy (by_kpub_map);
- by_kpub_map = NULL;
- }
}
@@ -607,15 +458,21 @@ handle_mhd_completion_callback (void *cls,
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
- struct TM_HandlerContext *hc = *con_cls;
+ struct TMH_HandlerContext *hc = *con_cls;
if (NULL == hc)
return;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Finished handling request for `%s' with status %d\n",
- hc->rh->url,
+ hc->url,
(int) toe);
- hc->cc (hc);
+ if (NULL != hc->cc)
+ hc->cc (hc->ctx);
+ TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
+ GNUNET_free_non_null (hc->infix);
+ if (NULL != hc->json)
+ json_decref (hc->json);
+ GNUNET_free (hc);
*con_cls = NULL;
}
@@ -625,7 +482,6 @@ handle_mhd_completion_callback (void *cls,
* starts the task waiting for them.
*/
static struct GNUNET_SCHEDULER_Task *
-
prepare_daemon (void);
@@ -729,504 +585,18 @@ prepare_daemon (void)
/**
- * Callback that looks for 'merchant-location-*' sections,
- * and populates @a default_locations.
- *
- * @param cls closure
- * @section section name this callback gets
- */
-static void
-locations_iterator_cb (void *cls,
- const char *section)
-{
- static const char *keys[] = {
- "country",
- "city",
- "state",
- "region",
- "province",
- "zip_code",
- "street",
- "street_number",
- NULL,
- };
- const char *prefix = "merchant-location-";
- const char *substr = strstr (section, prefix);
- const char *locname;
- json_t *loc;
-
- (void) cls;
- if ( (NULL == substr) || (substr != section) )
- return;
- locname = section + strlen (prefix);
- if (0 == strlen (locname))
- return;
- GNUNET_assert (json_is_object (default_locations));
-
- loc = json_object ();
- json_object_set_new (default_locations,
- locname,
- loc);
- for (unsigned int pos = 0; NULL != keys[pos]; pos++)
- {
- char *val;
-
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (cfg,
- section,
- keys[pos],
- &val))
- {
- json_object_set_new (loc,
- keys[pos],
- json_string (val));
- GNUNET_free (val);
- }
- }
-}
-
-
-/**
- * Closure for the #wireformat_iterator_cb().
- */
-struct WireFormatIteratorContext
-{
- /**
- * The global iteration context.
- */
- struct IterateInstancesCls *iic;
-
- /**
- * The merchant instance we are currently building.
- */
- struct MerchantInstance *mi;
-
- /**
- * Set to #GNUNET_YES if the default instance was found.
- */
- int default_instance;
-};
-
-
-/**
- * Callback that looks for 'merchant-account-*' sections,
- * and populates our wire method according to the data
- *
- * @param cls closure with a `struct WireFormatIteratorContext *`
- * @section section name this callback gets
- */
-static void
-wireformat_iterator_cb (void *cls,
- const char *section)
-{
- struct WireFormatIteratorContext *wfic = cls;
- struct MerchantInstance *mi = wfic->mi;
- struct IterateInstancesCls *iic = wfic->iic;
- char *instance_option;
- struct WireMethod *wm;
- char *payto;
- char *fn;
- json_t *j;
- struct GNUNET_HashCode jh_wire;
- char *wire_file_mode;
-
- if (0 != strncasecmp (section,
- "merchant-account-",
- strlen ("merchant-account-")))
- return;
- GNUNET_asprintf (&instance_option,
- "HONOR_%s",
- mi->id);
- if (GNUNET_YES !=
- GNUNET_CONFIGURATION_get_value_yesno (cfg,
- section,
- instance_option))
- {
- GNUNET_free (instance_option);
- return;
- }
- GNUNET_free (instance_option);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- section,
- "PAYTO_URI",
- &payto))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- section,
- "PAYTO_URI");
- iic->ret = GNUNET_SYSERR;
- return;
- }
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- section,
- "WIRE_RESPONSE",
- &fn))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- section,
- "WIRE_RESPONSE");
- GNUNET_free (payto);
- iic->ret = GNUNET_SYSERR;
- return;
- }
-
- /* Try loading existing JSON from file */
- if (GNUNET_YES ==
- GNUNET_DISK_file_test (fn))
- {
- json_error_t err;
- char *url;
-
- if (NULL ==
- (j = json_load_file (fn,
- JSON_REJECT_DUPLICATES,
- &err)))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to load JSON from `%s': %s at %d:%d\n",
- fn,
- err.text,
- err.line,
- err.column);
- GNUNET_free (fn);
- GNUNET_free (payto);
- iic->ret = GNUNET_SYSERR;
- return;
- }
- url = TALER_JSON_wire_to_payto (j);
- if (NULL == url)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "URL missing in `%s', disabling account `%s'\n",
- fn,
- section);
- GNUNET_free (fn);
- GNUNET_free (payto);
- iic->ret = GNUNET_SYSERR;
- return;
- }
- if (0 != strcasecmp (url,
- payto))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "URL `%s' does not match configuration `%s', disabling account `%s'\n",
- url,
- payto,
- section);
- GNUNET_free (fn);
- GNUNET_free (payto);
- GNUNET_free (url);
- iic->ret = GNUNET_SYSERR;
- return;
- }
- GNUNET_free (url);
- }
- else /* need to generate JSON */
- {
- struct GNUNET_HashCode salt;
- char *salt_str;
-
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
- &salt,
- sizeof (salt));
- salt_str = GNUNET_STRINGS_data_to_string_alloc (&salt,
- sizeof (salt));
- j = json_pack ("{s:s, s:s}",
- "payto_uri", payto,
- "salt", salt_str);
- GNUNET_free (salt_str);
-
- /* Make sure every path component exists. */
- if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn))
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "mkdir",
- fn);
- GNUNET_free (fn);
- GNUNET_free (payto);
- json_decref (j);
- iic->ret = GNUNET_SYSERR;
- return;
- }
-
- if (0 != json_dump_file (j,
- fn,
- JSON_COMPACT | JSON_SORT_KEYS))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to write hashed wire details to `%s'\n",
- fn);
- GNUNET_free (fn);
- GNUNET_free (payto);
- json_decref (j);
- iic->ret = GNUNET_SYSERR;
- return;
- }
-
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (cfg,
- section,
- "WIRE_FILE_MODE",
- &wire_file_mode))
- {
- errno = 0;
- mode_t mode = (mode_t) strtoul (wire_file_mode,
- NULL,
- 8);
- if (0 != errno)
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- section,
- "WIRE_FILE_MODE",
- "Must be octal number\n");
- iic->ret = GNUNET_SYSERR;
- GNUNET_free (fn);
- return;
- }
- if (0 != chmod (fn, mode))
- {
- TALER_LOG_ERROR ("chmod failed on %s\n", fn);
- iic->ret = GNUNET_SYSERR;
- GNUNET_free (fn);
- return;
- }
- }
- }
-
- GNUNET_free (fn);
-
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (j,
- &jh_wire))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to hash wire input\n");
- GNUNET_free (fn);
- GNUNET_free (payto);
- json_decref (j);
- iic->ret = GNUNET_SYSERR;
- return;
- }
-
- wm = GNUNET_new (struct WireMethod);
- wm->wire_method = TALER_payto_get_method (payto);
- GNUNET_free (payto);
- GNUNET_asprintf (&instance_option,
- "ACTIVE_%s",
- mi->id);
- wm->active = GNUNET_CONFIGURATION_get_value_yesno (cfg,
- section,
- instance_option);
- GNUNET_free (instance_option);
- if (GNUNET_YES == wm->active)
- GNUNET_CONTAINER_DLL_insert (mi->wm_head,
- mi->wm_tail,
- wm);
- else
- GNUNET_CONTAINER_DLL_insert_tail (mi->wm_head,
- mi->wm_tail,
- wm);
- wm->j_wire = j;
- wm->h_wire = jh_wire;
-}
-
-
-/**
- * Callback that looks for 'instance-*' sections,
- * and populates accordingly each instance's data
- *
- * @param cls closure of type `struct IterateInstancesCls`
- * @section section name this callback gets
- */
-static void
-instances_iterator_cb (void *cls,
- const char *section)
-{
- struct IterateInstancesCls *iic = cls;
- char *token;
- struct MerchantInstance *mi;
- /* used as hashmap keys */
- struct GNUNET_HashCode h_pk;
- struct GNUNET_HashCode h_id;
-
- if (0 != strncasecmp (section,
- "instance-",
- strlen ("instance-")))
- return;
- /** Get id **/
- token = strrchr (section, '-');
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Extracted token: %s\n",
- token + 1);
- mi = GNUNET_new (struct MerchantInstance);
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- section,
- "NAME",
- &mi->name))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- section,
- "NAME");
- GNUNET_free (mi);
- iic->ret = GNUNET_SYSERR;
- return;
- }
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- section,
- "KEYFILE",
- &mi->keyfile))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- section,
- "KEYFILE");
- GNUNET_free (mi->name);
- GNUNET_free (mi);
- iic->ret = GNUNET_SYSERR;
- return;
- }
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_get_value_string (cfg,
- section,
- "TIP_EXCHANGE",
- &mi->tip_exchange))
- {
- char *tip_reserves;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- section,
- "TIP_RESERVE_PRIV_FILENAME",
- &tip_reserves))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- section,
- "TIP_RESERVE_PRIV_FILENAME");
- GNUNET_free (mi->keyfile);
- GNUNET_free (mi->name);
- GNUNET_free (mi);
- iic->ret = GNUNET_SYSERR;
- return;
- }
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_key_from_file (tip_reserves,
- GNUNET_NO,
- &mi->tip_reserve.eddsa_priv))
- {
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
- section,
- "TIP_RESERVE_PRIV_FILENAME",
- "Failed to read private key");
- GNUNET_free (tip_reserves);
- GNUNET_free (mi->keyfile);
- GNUNET_free (mi->name);
- GNUNET_free (mi);
- iic->ret = GNUNET_SYSERR;
- return;
- }
- GNUNET_free (tip_reserves);
- }
-
- if (GNUNET_SYSERR ==
- GNUNET_CRYPTO_eddsa_key_from_file (mi->keyfile,
- GNUNET_YES,
- &mi->privkey.eddsa_priv))
- {
- GNUNET_break (0);
- GNUNET_free (mi->keyfile);
- GNUNET_free (mi->name);
- GNUNET_free (mi);
- iic->ret = GNUNET_SYSERR;
- return;
- }
- GNUNET_CRYPTO_eddsa_key_get_public (&mi->privkey.eddsa_priv,
- &mi->pubkey.eddsa_pub);
-
- mi->id = GNUNET_strdup (token + 1);
- if (0 == strcasecmp ("default",
- mi->id))
- iic->default_instance = GNUNET_YES;
-
- GNUNET_CRYPTO_hash (mi->id,
- strlen (mi->id),
- &h_id);
- if (GNUNET_OK !=
- GNUNET_CONTAINER_multihashmap_put (by_id_map,
- &h_id,
- mi,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to put an entry into the 'by_id' hashmap\n");
- iic->ret = GNUNET_SYSERR;
- GNUNET_free (mi->keyfile);
- GNUNET_free (mi->name);
- GNUNET_free (mi);
- return;
- }
- GNUNET_CRYPTO_hash (&mi->pubkey.eddsa_pub,
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey),
- &h_pk);
- if (GNUNET_OK !=
- GNUNET_CONTAINER_multihashmap_put (by_kpub_map,
- &h_pk,
- mi,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to put an entry into the 'by_kpub_map' hashmap\n");
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_remove (by_id_map,
- &h_id,
- mi));
- iic->ret = GNUNET_SYSERR;
- GNUNET_free (mi->keyfile);
- GNUNET_free (mi->name);
- GNUNET_free (mi);
- return;
- }
-
- /* Initialize wireformats */
- {
- struct WireFormatIteratorContext wfic = {
- .iic = iic,
- .mi = mi
- };
-
- GNUNET_CONFIGURATION_iterate_sections (cfg,
- &wireformat_iterator_cb,
- &wfic);
- }
- if (NULL == mi->wm_head)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to load wire formats for instance `%s'\n",
- mi->id);
- iic->ret = GNUNET_SYSERR;
- }
-
-}
-
-
-/**
* Lookup a merchant instance by its instance ID.
*
* @param instance_id identifier of the instance to resolve
* @return NULL if that instance is unknown to us
*/
-static struct MerchantInstance *
+static struct TMH_MerchantInstance *
lookup_instance (const char *instance_id)
{
struct GNUNET_HashCode h_instance;
if (NULL == instance_id)
instance_id = "default";
-
GNUNET_CRYPTO_hash (instance_id,
strlen (instance_id),
&h_instance);
@@ -1242,57 +612,6 @@ lookup_instance (const char *instance_id)
/**
- * Iterate over locations in config in order to populate
- * the location data.
- *
- * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors
- */
-static void
-iterate_locations (void)
-{
- GNUNET_assert (NULL == default_locations);
- default_locations = json_object ();
- GNUNET_CONFIGURATION_iterate_sections (cfg,
- &locations_iterator_cb,
- NULL);
-}
-
-
-/**
- * Iterate over each merchant instance, in order to populate
- * each instance's own data
- *
- * @return #GNUNET_OK if successful, #GNUNET_SYSERR upon errors
- * (for example, if no "default" instance is defined)
- */
-static int
-iterate_instances (void)
-{
- struct IterateInstancesCls iic;
-
- iic.default_instance = GNUNET_NO;
- iic.ret = GNUNET_OK;
- GNUNET_CONFIGURATION_iterate_sections (cfg,
- &instances_iterator_cb,
- &iic);
-
- if (GNUNET_NO == iic.default_instance)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "No default merchant instance found\n");
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK != iic.ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "At least one instance was not successfully parsed\n");
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
* A client has requested the given url using the given method
* (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
* #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
@@ -1341,158 +660,143 @@ url_handler (void *cls,
size_t *upload_data_size,
void **con_cls)
{
- static struct TMH_RequestHandler handlers[] = {
- /* Landing page, tell humans to go away. */
- { "/", MHD_HTTP_METHOD_GET, "text/plain",
- "Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.\n",
- 0,
- &TMH_MHD_handler_static_response, MHD_HTTP_OK },
- { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &TMH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
- { "/track/transfer", MHD_HTTP_METHOD_GET, "application/json",
- NULL, 0,
- &MH_handler_track_transfer, MHD_HTTP_OK},
- { "/track/transfer", NULL, "text/plain",
- "Only GET is allowed", 0,
- &TMH_MHD_handler_static_response, MHD_HTTP_OK},
- { "/track/transaction", MHD_HTTP_METHOD_GET, "application/json",
- NULL, 0,
- &MH_handler_track_transaction, MHD_HTTP_OK},
- { "/track/transaction", NULL, "text/plain",
- "Only GET is allowed", 0,
- &TMH_MHD_handler_static_response, MHD_HTTP_OK},
- { "/history", MHD_HTTP_METHOD_GET, "text/plain",
- "Only GET is allowed", 0,
- &MH_handler_history, MHD_HTTP_OK},
- { "/order", MHD_HTTP_METHOD_POST, "application/json",
- NULL, 0,
- &MH_handler_order_post, MHD_HTTP_OK },
- { "/refund", MHD_HTTP_METHOD_POST, "application/json",
- NULL, 0,
- &MH_handler_refund_increase, MHD_HTTP_OK},
- { "/tip-authorize", MHD_HTTP_METHOD_POST, "text/plain",
- NULL, 0,
- &MH_handler_tip_authorize, MHD_HTTP_OK},
- { "/tip-query", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_tip_query, MHD_HTTP_OK},
- { "/check-payment", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_check_payment, MHD_HTTP_OK},
- { "/config", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_config, MHD_HTTP_OK},
- {NULL, NULL, NULL, NULL, 0, 0 }
+ static struct TMH_RequestHandler private_handlers[] = {
+ {
+ .url_prefix = "/",
+ .method = MHD_HTTP_METHOD_GET,
+ .mime_type = "text/plain",
+ .skip_instance = true,
+ .data = "This is a GNU Taler merchant backend. See https://taler.net/.\n",
+ .data_size = strlen (
+ "This is a GNU Taler merchant backend. See https://taler.net/.\n"),
+ .handler = &TMH_MHD_handler_static_response,
+ .response_code = MHD_HTTP_OK
+ },
+ {
+ .url_prefix = "/agpl",
+ .method = MHD_HTTP_METHOD_GET,
+ .skip_instance = true,
+ .handler = &TMH_MHD_handler_agpl_redirect
+ },
+ {
+ NULL
+ }
};
static struct TMH_RequestHandler public_handlers[] = {
- { "/pay", MHD_HTTP_METHOD_POST, "application/json",
- NULL, 0,
- &MH_handler_pay, MHD_HTTP_OK },
- { "/proposal", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_proposal_lookup, MHD_HTTP_OK },
- { "/tip-pickup", MHD_HTTP_METHOD_POST, "text/plain",
- NULL, 0,
- &MH_handler_tip_pickup, MHD_HTTP_OK },
- { "/refund", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_refund_lookup, MHD_HTTP_OK },
- { "/tip-pickup", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_tip_pickup_get, MHD_HTTP_OK },
- { "/poll-payment", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_poll_payment, MHD_HTTP_OK},
- { "/config", MHD_HTTP_METHOD_GET, "text/plain",
- NULL, 0,
- &MH_handler_config, MHD_HTTP_OK},
- {NULL, NULL, NULL, NULL, 0, 0 }
+ {
+ .url_prefix = "/",
+ .method = MHD_HTTP_METHOD_GET,
+ .mime_type = "text/plain",
+ .skip_instance = true,
+ .data = "This is a GNU Taler merchant backend. See https://taler.net/.\n",
+ .data_size = strlen (
+ "This is a GNU Taler merchant backend. See https://taler.net/.\n"),
+ .handler = &TMH_MHD_handler_static_response,
+ .response_code = MHD_HTTP_OK
+ },
+ {
+ .url_prefix = "/agpl",
+ .method = MHD_HTTP_METHOD_GET,
+ .skip_instance = true,
+ .handler = &TMH_MHD_handler_agpl_redirect
+ },
+ {
+ .url_prefix = "/config",
+ .method = MHD_HTTP_METHOD_GET,
+ .skip_instance = true,
+ .handler = &MH_handler_config
+ },
+ {
+ NULL
+ }
};
static struct TMH_RequestHandler h404 = {
- "", NULL, "text/html",
- "<html><title>404: not found</title><body>404: not found</body></html>", 0,
- &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
+ .mime_type = "text/html",
+ .data = "<html><title>404: not found</title>"
+ "<body>404: not found</body></html>",
+ .data_size = strlen ("<html><title>404: not found</title>"
+ "<body>404: not found</body></html>"),
+ .handler = &TMH_MHD_handler_static_response,
+ .response_code = MHD_HTTP_NOT_FOUND
};
-
- struct TM_HandlerContext *hc = *con_cls;
- struct GNUNET_AsyncScopeId aid;
- const char *correlation_id = NULL;
- struct MerchantInstance *instance;
- const char *effective_url;
- /* Is a publicly facing endpoint being requested? */
- int is_public;
- /* Matching URL found, but maybe method doesn't match */
- int url_found = GNUNET_NO;
- MHD_RESULT ret;
- struct TMH_RequestHandler *selected_handler = NULL;
+ struct TMH_HandlerContext *hc = *con_cls;
+ struct TMH_RequestHandler *handlers;
(void) cls;
(void) version;
- if (NULL == hc)
+ if (NULL != hc)
{
- GNUNET_async_scope_fresh (&aid);
- /* We only read the correlation ID on the first callback for every client */
+ GNUNET_assert (NULL != hc->rh);
+ GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
+ if ( (hc->is_post) &&
+ (NULL == hc->json) )
+ {
+ int res;
+
+ res = TALER_MHD_parse_post_json (connection,
+ &hc->json_parse_context,
+ upload_data,
+ upload_data_size,
+ &hc->json);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ /* A error response was already generated */
+ if ( (GNUNET_NO == res) ||
+ /* or, need more data to accomplish parsing */
+ (NULL == hc->json) )
+ return MHD_YES;
+ }
+ return hc->rh->handler (hc->rh,
+ connection,
+ hc);
+ }
+ hc = GNUNET_new (struct TMH_HandlerContext);
+ *con_cls = hc;
+ GNUNET_async_scope_fresh (&hc->async_scope_id);
+ GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
+ hc->url = url;
+ {
+ const char *correlation_id;
+
correlation_id = MHD_lookup_connection_value (connection,
MHD_HEADER_KIND,
"Taler-Correlation-Id");
- if ((NULL != correlation_id) &&
- (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)))
+ if ( (NULL != correlation_id) &&
+ (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"illegal incoming correlation ID\n");
correlation_id = NULL;
}
- }
- else
- {
- aid = hc->async_scope_id;
- }
-
- GNUNET_SCHEDULER_begin_async_scope (&aid);
-
- if (NULL != correlation_id)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling request for (%s) URL '%s', correlation_id=%s\n",
- method,
- url,
- correlation_id);
- else
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling request (%s) for URL '%s'\n",
- method,
- url);
-
- effective_url = url;
-
- {
- const char *public_prefix = "/public/";
-
- if (0 == strncmp (effective_url,
- public_prefix,
- strlen (public_prefix)))
- {
- is_public = GNUNET_YES;
- effective_url = effective_url + strlen (public_prefix) - 1;
- }
+ if (NULL != correlation_id)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling request for (%s) URL '%s', correlation_id=%s\n",
+ method,
+ url,
+ correlation_id);
else
- {
- is_public = GNUNET_NO;
- }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling request (%s) for URL '%s'\n",
+ method,
+ url);
}
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_HEAD))
+ method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
+
/* Find out the merchant backend instance for the request.
* If there is an instance, remove the instance specification
* from the beginning of the request URL. */
{
const char *instance_prefix = "/instances/";
- if (0 == strncmp (effective_url,
+ if (0 == strncmp (url,
instance_prefix,
strlen (instance_prefix)))
{
/* url starts with "/instances/" */
- const char *istart = effective_url + strlen (instance_prefix);
+ const char *istart = url + strlen (instance_prefix);
const char *slash = strchr (istart, '/');
char *instance_id;
@@ -1500,114 +804,149 @@ url_handler (void *cls,
{
return TMH_MHD_handler_static_response (&h404,
connection,
- con_cls,
- upload_data,
- upload_data_size,
- NULL);
+ hc);
}
instance_id = GNUNET_strndup (istart,
slash - istart);
- instance = lookup_instance (instance_id);
+ hc->instance = lookup_instance (instance_id);
GNUNET_free (instance_id);
- effective_url = slash;
+ url = slash;
}
else
{
- instance = lookup_instance (NULL);
+ /* use 'default' */
+ hc->instance = lookup_instance (NULL);
}
}
- if (NULL == instance)
- return TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_NOT_FOUND,
- "{s:I, s:s}",
- "code",
- (json_int_t) TALER_EC_INSTANCE_UNKNOWN,
- "error",
- "merchant instance unknown");
- if (GNUNET_NO == is_public)
{
- for (unsigned int i = 0; NULL != handlers[i].url; i++)
- {
- struct TMH_RequestHandler *rh = &handlers[i];
+ const char *private_prefix = "/private/";
- if ( (0 != strcasecmp (effective_url, rh->url)) )
- continue;
- url_found = GNUNET_YES;
- if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_OPTIONS))
- {
- return TALER_MHD_reply_cors_preflight (connection);
- }
- if ( (rh->method != NULL) &&
- (0 != strcasecmp (method, rh->method)) )
- continue;
- selected_handler = rh;
- break;
+ if (0 == strncmp (url,
+ private_prefix,
+ strlen (private_prefix)))
+ {
+ handlers = private_handlers;
+ url += strlen (private_prefix) - 1;
+ }
+ else
+ {
+ handlers = public_handlers;
}
}
+ if (strcmp (url,
+ ""))
+ url = "/"; /* code below does not like empty string */
- if (NULL == selected_handler)
{
- for (unsigned int i = 0; NULL != public_handlers[i].url; i++)
+ /* Matching URL found, but maybe method doesn't match */
+ size_t prefix_strlen; /* i.e. 8 for "/orders/", or 7 for "/config" */
+ const char *infix_url = NULL; /* i.e. "$ORDER_ID", no '/'-es */
+ size_t infix_strlen = 0; /* number of characters in infix_url */
+ const char *suffix_url = NULL; /* i.e. "/refund", includes '/' at the beginning */
+ size_t suffix_strlen = 0; /* number of characters in suffix_url */
+
{
- struct TMH_RequestHandler *rh = &public_handlers[i];
+ const char *slash;
- if ( (0 != strcasecmp (effective_url, rh->url)) )
- continue;
- url_found = GNUNET_YES;
- if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_OPTIONS))
+ slash = strchr (&url[1], '/');
+ if (NULL == slash)
{
- return TALER_MHD_reply_cors_preflight (connection);
+ prefix_strlen = strlen (url);
+ }
+ else
+ {
+ prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */
+ infix_url = slash + 1;
+ slash = strchr (&infix_url[1], '/');
+ if (NULL == slash)
+ {
+ infix_strlen = strlen (infix_url);
+ }
+ else
+ {
+ infix_strlen = slash - infix_url;
+ suffix_url = slash;
+ suffix_strlen = strlen (suffix_url);
+ }
+ hc->infix = GNUNET_strndup (infix_url,
+ infix_strlen);
}
- if ( (rh->method != NULL) && (0 != strcasecmp (method, rh->method)) )
- continue;
- selected_handler = rh;
- break;
}
- }
- if (NULL == selected_handler)
- {
- if (GNUNET_YES == url_found)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "invalid request: method '%s' for '%s' not allowed\n",
- method,
- url);
- return TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_METHOD_NOT_ALLOWED,
- "{s:s}",
- "error",
- "method not allowed");
+ bool url_found = false;
+
+ for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
+ {
+ struct TMH_RequestHandler *rh = &handlers[i];
+
+ if ( (prefix_strlen != strlen (rh->url_prefix)) ||
+ (0 != memcmp (url,
+ rh->url_prefix,
+ prefix_strlen)) )
+ continue;
+ if ( (NULL == infix_url)
+ ^ (GNUNET_NO == rh->have_id_segment) )
+ continue; /* infix existence missmatch */
+ if ( (NULL == suffix_url)
+ ^ (NULL != rh->url_suffix) )
+ continue; /* suffix existence missmatch */
+ if ( (NULL != suffix_url) &&
+ ( (suffix_strlen != strlen (rh->url_suffix)) ||
+ (0 != memcmp (suffix_url,
+ rh->url_suffix,
+ suffix_strlen)) ) )
+ continue; /* suffix content missmatch */
+ url_found = true;
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_OPTIONS))
+ {
+ return TALER_MHD_reply_cors_preflight (connection);
+ }
+ if ( (rh->method != NULL) &&
+ (0 != strcasecmp (method, rh->method)) )
+ continue;
+ hc->rh = rh;
+ break;
+ }
+ if ( (NULL == hc->rh) &&
+ (url_found) )
+ return TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_METHOD_NOT_ALLOWED,
+ "{s:s}",
+ "error",
+ "method not allowed");
+ if (NULL == hc->rh)
+ return TMH_MHD_handler_static_response (&h404,
+ connection,
+ hc);
}
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "invalid request: URL '%s' not handled\n",
- url);
- return TMH_MHD_handler_static_response (&h404,
- connection,
- con_cls,
- upload_data,
- upload_data_size,
- instance);
}
-
- ret = selected_handler->handler (selected_handler,
- connection,
- con_cls,
- upload_data,
- upload_data_size,
- instance);
- hc = *con_cls;
- if (NULL != hc)
+ /* At this point, we must have found a handler */
+ GNUNET_assert (NULL != hc->rh);
+ if ( (NULL == hc->instance) &&
+ (GNUNET_YES != hc->rh->skip_instance) )
+ return TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_NOT_FOUND,
+ "{s:I, s:s}",
+ "code",
+ (json_int_t) TALER_EC_INSTANCE_UNKNOWN,
+ "error",
+ "merchant instance unknown");
+ hc->is_post = (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST));
+ if (hc->is_post)
{
- hc->rh = selected_handler;
- /* Store the async context ID, so we can restore it if
- * we get another callback for this request. */
- hc->async_scope_id = aid;
+ /* FIXME: Maybe check for maximum upload size here
+ and refuse if it is too big? */
+
+ GNUNET_break (NULL == hc->json); /* can't have it already */
+ return MHD_YES; /* proceed with upload */
}
- return ret;
+ return hc->rh->handler (hc->rh,
+ connection,
+ hc);
}
@@ -1632,10 +971,11 @@ run (void *cls,
(void) cls;
(void) args;
(void) cfgfile;
+ cfg = config;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Starting taler-merchant-httpd\n");
go = TALER_MHD_GO_NONE;
- if (TMH_merchant_connection_close)
+ if (merchant_connection_close)
go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
TALER_MHD_setup (go);
@@ -1643,14 +983,14 @@ run (void *cls,
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
if (GNUNET_OK !=
- TALER_config_get_currency (config,
+ TALER_config_get_currency (cfg,
&TMH_currency))
{
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_YES ==
- GNUNET_CONFIGURATION_get_value_yesno (config,
+ GNUNET_CONFIGURATION_get_value_yesno (cfg,
"merchant",
"FORCE_AUDIT"))
TMH_force_audit = GNUNET_YES;
@@ -1666,7 +1006,6 @@ run (void *cls,
GNUNET_SCHEDULER_shutdown ();
return;
}
-
if (NULL ==
(by_id_map = GNUNET_CONTAINER_multihashmap_create (1,
GNUNET_NO)))
@@ -1674,97 +1013,14 @@ run (void *cls,
GNUNET_SCHEDULER_shutdown ();
return;
}
-
if (NULL ==
- (by_kpub_map = GNUNET_CONTAINER_multihashmap_create (1,
- GNUNET_NO)))
- {
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-
- if (GNUNET_SYSERR ==
- GNUNET_CONFIGURATION_get_value_time (config,
- "merchant",
- "WIRE_TRANSFER_DELAY",
- &default_wire_transfer_delay))
+ (TMH_db = TALER_MERCHANTDB_plugin_load (cfg)))
{
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "merchant",
- "WIRE_TRANSFER_DELAY");
GNUNET_SCHEDULER_shutdown ();
return;
}
- if (GNUNET_SYSERR ==
- GNUNET_CONFIGURATION_get_value_time (config,
- "merchant",
- "DEFAULT_PAY_DEADLINE",
- &default_pay_deadline))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "merchant",
- "DEFAULT_PAY_DEADLINE");
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-
- if (GNUNET_OK !=
- TALER_config_get_amount (config,
- "merchant",
- "DEFAULT_MAX_WIRE_FEE",
- &default_max_wire_fee))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "merchant",
- "DEFAULT_MAX_WIRE_FEE");
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-
- if (GNUNET_OK !=
- TALER_config_get_amount (config,
- "merchant",
- "DEFAULT_MAX_DEPOSIT_FEE",
- &default_max_deposit_fee))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "merchant",
- "DEFAULT_MAX_DEPOSIT_FEE");
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (config,
- "merchant",
- "DEFAULT_WIRE_FEE_AMORTIZATION",
- &default_wire_fee_amortization))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "merchant",
- "DEFAULT_WIRE_FEE_AMORTIZATION");
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-
- cfg = GNUNET_CONFIGURATION_dup (config);
- if (GNUNET_OK !=
- iterate_instances ())
- {
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- iterate_locations ();
-
- if (NULL ==
- (db = TALER_MERCHANTDB_plugin_load (cfg)))
- {
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-
- fh = TALER_MHD_bind (config,
+ fh = TALER_MHD_bind (cfg,
"merchant",
&port);
if ( (0 == port) &&
@@ -1805,7 +1061,7 @@ run (void *cls,
*
* @param argc number of arguments from the command line
* @param argv command line arguments
- * @return 0 ok, 1 on error
+ * @return 0 ok, non-zero on error
*/
int
main (int argc,
@@ -1815,12 +1071,11 @@ main (int argc,
GNUNET_GETOPT_option_flag ('C',
"connection-close",
"force HTTP connections to be closed after each request",
- &TMH_merchant_connection_close),
+ &merchant_connection_close),
GNUNET_GETOPT_option_timetravel ('T',
"timetravel"),
GNUNET_GETOPT_OPTION_END
};
-
if (GNUNET_OK !=
GNUNET_PROGRAM_run (argc, argv,
"taler-merchant-httpd",
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index 28a0f2ea..b2e1a599 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -39,17 +39,17 @@
/**
* Supported wire method. Kept in a DLL.
*/
-struct WireMethod
+struct TMH_WireMethod
{
/**
* Next entry in DLL.
*/
- struct WireMethod *next;
+ struct TMH_WireMethod *next;
/**
* Previous entry in DLL.
*/
- struct WireMethod *prev;
+ struct TMH_WireMethod *prev;
/**
* Which wire method / payment target identifier is @e j_wire using?
@@ -69,7 +69,7 @@ struct WireMethod
/**
* Is this wire method active (should it be included in new contracts)?
*/
- int active;
+ bool active;
};
@@ -79,7 +79,7 @@ struct WireMethod
* backend can account for several merchants, as used to do in donation
* shops
*/
-struct MerchantInstance
+struct TMH_MerchantInstance
{
/**
@@ -102,12 +102,12 @@ struct MerchantInstance
/**
* Next entry in DLL.
*/
- struct WireMethod *wm_head;
+ struct TMH_WireMethod *wm_head;
/**
* Previous entry in DLL.
*/
- struct WireMethod *wm_tail;
+ struct TMH_WireMethod *wm_tail;
/**
* Merchant's private key
@@ -120,12 +120,50 @@ struct MerchantInstance
struct TALER_MerchantPublicKeyP pubkey;
/**
+ * Default maximum wire fee to assume, unless stated differently in the proposal
+ * already.
+ */
+ struct TALER_Amount default_max_wire_fee;
+
+ /**
+ * Default max deposit fee that the merchant is willing to
+ * pay; if deposit costs more, then the customer will cover
+ * the difference.
+ */
+ struct TALER_Amount default_max_deposit_fee;
+
+ /**
+ * Default factor for wire fee amortization.
+ */
+ unsigned long long default_wire_fee_amortization;
+
+ /**
+ * If the frontend does NOT specify an execution date, how long should
+ * we tell the exchange to wait to aggregate transactions before
+ * executing the wire transfer? This delay is added to the current
+ * time when we generate the advisory execution time for the exchange.
+ */
+ struct GNUNET_TIME_Relative default_wire_transfer_delay;
+
+ /**
+ * If the frontend does NOT specify a payment deadline, how long should
+ * offers we make be valid by default?
+ */
+ struct GNUNET_TIME_Relative default_pay_deadline;
+
+ /**
* Exchange this instance uses for tipping, NULL if tipping
* is not supported.
*/
char *tip_exchange;
/**
+ * Locations from the configuration. Mapping from
+ * label to location data.
+ */
+ json_t *default_locations;
+
+ /**
* What is the private key of the reserve used for signing tips by this exchange?
* Only valid if @e tip_exchange is non-null.
*/
@@ -135,14 +173,56 @@ struct MerchantInstance
/**
* @brief Struct describing an URL and the handler for it.
+ *
+ * The overall URL is always @e url_prefix, optionally followed by the
+ * id_segment, which is optionally followed by the @e url_suffix. It is NOT
+ * allowed for the @e url_prefix to be directly followed by the @e url_suffix.
+ * A @e url_suffix SHOULD only be used with a @e method of #MHD_HTTP_METHOD_POST.
+ */
+struct TMH_RequestHandler;
+
+/**
+ * This information is stored in the "connection_cls" of MHD for
+ * every request that we process.
+ * Individual handlers can evaluate tis memebers and
+ * are allowed to update @e cc and @e ctx to store and clean up
+ * handler-specific data.
+ */
+struct TMH_HandlerContext;
+
+
+/**
+ * @brief Struct describing an URL and the handler for it.
+ *
+ * The overall URL is always @e url_prefix, optionally followed by the
+ * id_segment, which is optionally followed by the @e url_suffix. It is NOT
+ * allowed for the @e url_prefix to be directly followed by the @e url_suffix.
+ * A @e url_suffix SHOULD only be used with a @e method of #MHD_HTTP_METHOD_POST.
*/
struct TMH_RequestHandler
{
/**
- * URL the handler is for.
+ * URL prefix the handler is for.
*/
- const char *url;
+ const char *url_prefix;
+
+ /**
+ * Does this request include an identifier segment
+ * (product_id, reserve_pub, order_id, tip_id) in the
+ * second segment?
+ */
+ bool have_id_segment;
+
+ /**
+ * Does this request handler expect an instance?
+ */
+ bool skip_instance;
+
+ /**
+ * URL suffix the handler is for.
+ */
+ const char *url_suffix;
/**
* Method the handler is for, NULL for "all".
@@ -155,36 +235,30 @@ struct TMH_RequestHandler
const char *mime_type;
/**
- * Raw data for the @e handler
+ * Raw data for the @e handler (can be NULL).
*/
const void *data;
/**
- * Number of bytes in @e data, 0 for 0-terminated.
+ * Number of bytes in @e data.
*/
size_t data_size;
/**
- * Function to call to handle the request.
+ * Handler to be called for this URL/METHOD combination.
*
* @param rh this struct
- * @param mime_type the @e mime_type for the reply (hint, can be NULL)
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc context with further information about the request
* @return MHD result code
*/
- MHD_RESULT (*handler)(struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
+ MHD_RESULT
+ (*handler)(const struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ struct TMH_HandlerContext *hc);
/**
- * Default response code.
+ * Default response code to use.
*/
unsigned int response_code;
};
@@ -196,33 +270,39 @@ struct TMH_RequestHandler
* member. This struct contains a single callback, which will be
* invoked to clean up the memory when the contection is completed.
*/
-struct TM_HandlerContext;
+struct TMH_HandlerContext;
/**
* Signature of a function used to clean up the context
* we keep in the "connection_cls" of MHD when handling
* a request.
*
- * @param hc header of the context to clean up.
+ * @param ctxt the context to clean up.
*/
typedef void
-(*TM_ContextCleanup)(struct TM_HandlerContext *hc);
+(*TMH_ContextCleanup)(void *ctx);
/**
- * Each MHD response handler that sets the "connection_cls" to a
- * non-NULL value must use a struct that has this struct as its first
- * member. This struct contains a single callback, which will be
- * invoked to clean up the memory when the connection is completed.
+ * This information is stored in the "connection_cls" of MHD for
+ * every request that we process.
+ * Individual handlers can evaluate tis memebers and
+ * are allowed to update @e cc and @e ctx to store and clean up
+ * handler-specific data.
*/
-struct TM_HandlerContext
+struct TMH_HandlerContext
{
/**
* Function to execute the handler-specific cleanup of the
- * (typically larger) context.
+ * (request-specific) context in @e ctx.
+ */
+ TMH_ContextCleanup cc;
+
+ /**
+ * Client-specific context we keep. Passed to @e cc.
*/
- TM_ContextCleanup cc;
+ void *ctx;
/**
* Which request handler is handling this request?
@@ -230,9 +310,40 @@ struct TM_HandlerContext
const struct TMH_RequestHandler *rh;
/**
+ * Which instance is handling this request?
+ */
+ struct TMH_MerchantInstance *instance;
+
+ /**
* Asynchronous request context id.
*/
struct GNUNET_AsyncScopeId async_scope_id;
+
+ /**
+ * Our original URL, for logging.
+ */
+ const char *url;
+
+ /**
+ * Infix part of @a url.
+ */
+ char *infix;
+
+ /**
+ * JSON body that was uploaded, NULL if @e is_post is false.
+ */
+ json_t *json;
+
+ /**
+ * Placeholder for #TALER_MHD_parse_post_json() to keep its internal state.
+ * Used when we parse the POSTed data.
+ */
+ void *json_parse_context;
+
+ /**
+ * Set to true if this is an #MHD_HTTP_METHOD_POST request.
+ */
+ bool is_post;
};
@@ -247,12 +358,14 @@ struct TMH_SuspendedConnection
struct MHD_Connection *con;
/**
- * Associated heap node.
+ * Associated heap node. Used internally by #TMH_long_poll_suspend()
+ * and TMH_long_poll_resume().
*/
struct GNUNET_CONTAINER_HeapNode *hn;
/**
- * Key of this entry in the #payment_trigger_map
+ * Key of this entry in the #payment_trigger_map. Used internally by
+ * #TMH_long_poll_suspend() and TMH_long_poll_resume().
*/
struct GNUNET_HashCode key;
@@ -270,53 +383,12 @@ struct TMH_SuspendedConnection
/**
* #GNUNET_YES if we are waiting for a refund.
*/
- int awaiting_refund;
+ bool awaiting_refund;
};
/**
- * Locations from the configuration. Mapping from
- * label to location data.
- */
-extern json_t *default_locations;
-
-/**
- * Default maximum wire fee to assume, unless stated differently in the proposal
- * already.
- */
-extern struct TALER_Amount default_max_wire_fee;
-
-/**
- * Default max deposit fee that the merchant is willing to
- * pay; if deposit costs more, then the customer will cover
- * the difference.
- */
-extern struct TALER_Amount default_max_deposit_fee;
-
-/**
- * Default factor for wire fee amortization.
- */
-extern unsigned long long default_wire_fee_amortization;
-
-/**
- * MIN-Heap of suspended connections to resume when the timeout expires,
- * ordered by timeout. Values are of type `struct TMH_SuspendedConnection`
- */
-extern struct GNUNET_CONTAINER_Heap *resume_timeout_heap;
-
-/**
- * Task responsible for timeouts in the #resume_timeout_heap.
- */
-extern struct GNUNET_SCHEDULER_Task *resume_timeout_task;
-
-/**
- * Hash map from H(order_id,merchant_pub) to `struct TMH_SuspendedConnection`
- * entries to resume when a payment is made for the given order.
- */
-extern struct GNUNET_CONTAINER_MultiHashMap *payment_trigger_map;
-
-/**
* Which currency do we use?
*/
extern char *TMH_currency;
@@ -327,45 +399,10 @@ extern char *TMH_currency;
extern int TMH_force_audit;
/**
- * Hash of our wire format details as given in #j_wire.
- */
-extern struct GNUNET_HashCode h_wire;
-
-/**
- * Our private key, corresponds to #pubkey.
- */
-extern struct TALER_MerchantPrivateKeyP privkey;
-
-/**
- * Our public key, corresponds to #privkey.
- */
-extern struct TALER_MerchantPublicKeyP pubkey;
-
-/**
- * Hashmap pointing at merchant instances by 'id'. An 'id' is
- * just a string that identifies a merchant instance. When a frontend
- * needs to specify an instance to the backend, it does so by 'id'
- */
-extern struct GNUNET_CONTAINER_MultiHashMap *by_id_map;
-
-/**
* Handle to the database backend.
*/
-extern struct TALER_MERCHANTDB_Plugin *db;
-
-/**
- * If the frontend does NOT specify an execution date, how long should
- * we tell the exchange to wait to aggregate transactions before
- * executing the wire transfer? This delay is added to the current
- * time when we generate the advisory execution time for the exchange.
- */
-extern struct GNUNET_TIME_Relative default_wire_transfer_delay;
+extern struct TALER_MERCHANTDB_Plugin *TMH_db;
-/**
- * If the frontend does NOT specify a payment deadline, how long should
- * offers we make be valid by default?
- */
-extern struct GNUNET_TIME_Relative default_pay_deadline;
/**
* Kick MHD to run now, to be called after MHD_resume_connection().
@@ -378,28 +415,18 @@ TMH_trigger_daemon (void);
/**
- * Compute @a key to use for @a order_id and @a mpub in our
- * #payment_trigger_map.
- *
- * @param order_id an order ID
- * @param mpub an instance public key
- * @param key[out] set to the hash map key to use
- */
-void
-TMH_compute_pay_key (const char *order_id,
- const struct TALER_MerchantPublicKeyP *mpub,
- struct GNUNET_HashCode *key);
-
-
-/**
* Suspend connection from @a sc until payment has been received.
*
+ * @param order_id the order that we are waiting on
+ * @param mi the merchant instance we are waiting on
* @param sc connection to suspend
* @param min_refund refund amount we are waiting on to be exceeded before resuming,
* NULL if we are not waiting for refunds
*/
void
-TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
+TMH_long_poll_suspend (const char *order_id,
+ const struct TMH_MerchantInstance *mi,
+ struct TMH_SuspendedConnection *sc,
const struct TALER_Amount *min_refund);
@@ -407,31 +434,14 @@ TMH_long_poll_suspend (struct TMH_SuspendedConnection *sc,
* Find out if we have any clients long-polling for @a order_id to be
* confirmed at merchant @a mpub, and if so, tell them to resume.
*
- * @param order_id the order that was paid
- * @param mpub the merchant's public key of the instance where the payment happened
+ * @param order_id the order that was paid or refunded
+ * @param mi the merchant instance where the payment or refund happened
* @param refund_amount refunded amount, if the trigger was a refund, otherwise NULL
*/
void
TMH_long_poll_resume (const char *order_id,
- const struct TALER_MerchantPublicKeyP *mpub,
+ const struct TMH_MerchantInstance *mi,
const struct TALER_Amount *refund_amount);
-/**
- * Create a taler://pay/ URI for the given @a con and @a order_id
- * and @a session_id and @a instance_id.
- *
- * @param con HTTP connection
- * @param order_id the order id
- * @param session_id session, may be NULL
- * @param instance_id instance, may be "default"
- * @return corresponding taler://pay/ URI, or NULL on missing "host"
- */
-char *
-TMH_make_taler_pay_uri (struct MHD_Connection *con,
- const char *order_id,
- const char *session_id,
- const char *instance_id);
-
-
#endif
diff --git a/src/backend/taler-merchant-httpd_config.c b/src/backend/taler-merchant-httpd_config.c
index d6f6c922..c4fed585 100644
--- a/src/backend/taler-merchant-httpd_config.c
+++ b/src/backend/taler-merchant-httpd_config.c
@@ -25,8 +25,6 @@
#include "taler-merchant-httpd.h"
#include "taler-merchant-httpd_mhd.h"
#include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_tip-query.h"
-#include "taler-merchant-httpd_tip-reserve-helper.h"
/**
@@ -47,111 +45,28 @@
#define MERCHANT_PROTOCOL_VERSION "0:0:0"
-static int
-add_instance (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
-{
- json_t *ja = cls;
- struct MerchantInstance *mi = value;
- char *url;
- json_t *pta;
-
- /* Compile array of all unique wire methods supported by this
- instance */
- pta = json_array ();
- GNUNET_assert (NULL != pta);
- for (struct WireMethod *wm = mi->wm_head;
- NULL != wm;
- wm = wm->next)
- {
- int duplicate = GNUNET_NO;
-
- if (! wm->active)
- break;
- /* Yes, O(n^2), but really how many bank accounts can an
- instance realistically have for this to matter? */
- for (struct WireMethod *pm = mi->wm_head;
- pm != wm;
- pm = pm->next)
- if (0 == strcasecmp (pm->wire_method,
- wm->wire_method))
- {
- duplicate = GNUNET_YES;
- break;
- }
- if (duplicate)
- continue;
- GNUNET_assert (0 ==
- json_array_append_new (pta,
- json_string (wm->wire_method)));
- }
- GNUNET_asprintf (&url,
- "/%s/",
- mi->id);
- GNUNET_assert (0 ==
- json_array_append_new (
- ja,
- json_pack (
- (NULL != mi->tip_exchange)
- ? "{s:s, s:s, s:o, s:o, s:s}"
- : "{s:s, s:s, s:o, s:o}",
- "name",
- mi->name,
- "backend_base_url",
- url,
- "merchant_pub",
- GNUNET_JSON_from_data_auto (&mi->pubkey),
- "payment_targets",
- pta,
- /* optional: */
- "tipping_exchange_baseurl",
- mi->tip_exchange)));
- GNUNET_free (url);
- return GNUNET_OK;
-}
-
-
/**
* Handle a "/config" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc handler context (can be updated)
* @return MHD result code
*/
MHD_RESULT
MH_handler_config (struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi)
+ struct TMH_HandlerContext *hc)
{
static struct MHD_Response *response;
(void) rh;
- (void) connection_cls;
- (void) upload_data;
- (void) upload_data_size;
- (void) mi;
+ (void) hc;
if (NULL == response)
{
- json_t *ia;
-
- ia = json_array ();
- GNUNET_assert (NULL != ia);
- GNUNET_CONTAINER_multihashmap_iterate (by_id_map,
- &add_instance,
- ia);
- response = TALER_MHD_make_json_pack ("{s:s, s:s, s:o}",
+ response = TALER_MHD_make_json_pack ("{s:s, s:s }",
"currency", TMH_currency,
- "version", MERCHANT_PROTOCOL_VERSION,
- "instances", ia);
-
+ "version", MERCHANT_PROTOCOL_VERSION);
}
return MHD_queue_response (connection,
MHD_HTTP_OK,
diff --git a/src/backend/taler-merchant-httpd_config.h b/src/backend/taler-merchant-httpd_config.h
index 1a5dfd68..8d21e47c 100644
--- a/src/backend/taler-merchant-httpd_config.h
+++ b/src/backend/taler-merchant-httpd_config.h
@@ -28,18 +28,12 @@
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc handler context (can be updated)
* @return MHD result code
*/
MHD_RESULT
-MH_handler_config (struct TMH_RequestHandler *rh,
+MH_handler_config (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
+ struct TMH_HandlerContext *hc);
#endif
diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c
index 190f5db0..11f845f1 100644
--- a/src/backend/taler-merchant-httpd_exchanges.c
+++ b/src/backend/taler-merchant-httpd_exchanges.c
@@ -380,10 +380,10 @@ process_wire_fees (struct Exchange *exchange,
wire_method,
GNUNET_STRINGS_absolute_time_to_string (af->start_date),
TALER_amount2s (&af->wire_fee));
- db->preflight (db->cls);
+ TMH_db->preflight (TMH_db->cls);
if (GNUNET_OK !=
- db->start (db->cls,
- "store wire fee"))
+ TMH_db->start (TMH_db->cls,
+ "store wire fee"))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to start database transaction to store exchange wire fees (will try to continue anyway)!\n");
@@ -391,21 +391,21 @@ process_wire_fees (struct Exchange *exchange,
fees = fees->next;
continue;
}
- qs = db->store_wire_fee_by_exchange (db->cls,
- master_pub,
- &h_wire_method,
- &af->wire_fee,
- &af->closing_fee,
- af->start_date,
- af->end_date,
- &af->master_sig);
+ qs = TMH_db->store_wire_fee_by_exchange (TMH_db->cls,
+ master_pub,
+ &h_wire_method,
+ &af->wire_fee,
+ &af->closing_fee,
+ af->start_date,
+ af->end_date,
+ &af->master_sig);
if (0 > qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
GNUNET_free (af);
fees = fees->next;
- db->rollback (db->cls);
+ TMH_db->rollback (TMH_db->cls);
continue;
}
if (0 == qs)
@@ -413,12 +413,12 @@ process_wire_fees (struct Exchange *exchange,
/* Entry was already in DB, fine, continue as if we had succeeded */
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Fees already in DB, rolling back transaction attempt!\n");
- db->rollback (db->cls);
+ TMH_db->rollback (TMH_db->cls);
}
if (0 < qs)
{
/* Inserted into DB, make sure transaction completes */
- qs = db->commit (db->cls);
+ qs = TMH_db->commit (TMH_db->cls);
if (0 > qs)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
diff --git a/src/backend/taler-merchant-httpd_mhd.c b/src/backend/taler-merchant-httpd_mhd.c
index 08608604..79f40714 100644
--- a/src/backend/taler-merchant-httpd_mhd.c
+++ b/src/backend/taler-merchant-httpd_mhd.c
@@ -33,27 +33,15 @@
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, could be NULL in this specific case!
+ * @param[in,out] hc handler context (can be updated)
* @return MHD result code
*/
MHD_RESULT
-TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_static_response (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *instance)
+ struct TMH_HandlerContext *hc)
{
- (void) instance;
- (void) connection_cls;
- (void) upload_data;
- (void) upload_data_size;
- (void) instance;
- if (0 == rh->data_size)
- rh->data_size = strlen ((const char *) rh->data);
+ (void) hc;
return TALER_MHD_reply_static (connection,
rh->response_code,
rh->mime_type,
@@ -68,24 +56,16 @@ TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL (but unused)
+ * @param[in,out] hc handler context (can be updated)
* @return MHD result code
*/
MHD_RESULT
-TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_agpl_redirect (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi)
+ struct TMH_HandlerContext *hc)
{
- (void) mi;
- (void) connection_cls;
- (void) upload_data;
- (void) upload_data_size;
+ (void) rh;
+ (void) hc;
return TALER_MHD_reply_agpl (connection,
"http://www.git.taler.net/?p=merchant.git");
}
diff --git a/src/backend/taler-merchant-httpd_mhd.h b/src/backend/taler-merchant-httpd_mhd.h
index 8fc78a21..cbf83add 100644
--- a/src/backend/taler-merchant-httpd_mhd.h
+++ b/src/backend/taler-merchant-httpd_mhd.h
@@ -34,19 +34,13 @@
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, NULL is allowed in this case!
+ * @param[in,out] hc handler context (can be updated)
* @return MHD result code
*/
MHD_RESULT
-TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_static_response (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
+ struct TMH_HandlerContext *hc);
/**
@@ -55,19 +49,13 @@ TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
*
* @param rh context of the handler
* @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @param mi merchant backend instance, never NULL
+ * @param[in,out] hc handler context (can be updated)
* @return MHD result code
*/
MHD_RESULT
-TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+TMH_MHD_handler_agpl_redirect (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
- void **connection_cls,
- const char *upload_data,
- size_t *upload_data_size,
- struct MerchantInstance *mi);
+ struct TMH_HandlerContext *hc);
/**
@@ -111,7 +99,7 @@ TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh,
void **connection_cls,
const char *upload_data,
size_t *upload_data_size,
- struct MerchantInstance *mi);
+ struct TMH_MerchantInstance *mi);
#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-instances.c b/src/backend/taler-merchant-httpd_private-get-instances.c
new file mode 100644
index 00000000..8a08ab14
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-instances.c
@@ -0,0 +1,160 @@
+/*
+ This file is part of TALER
+ (C) 2019, 2020 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 backend/taler-merchant-httpd_config.c
+ * @brief implement API for querying configuration data of the backend
+ * @author Florian Dold
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <taler/taler_util.h>
+#include <taler/taler_json_lib.h>
+#include "taler-merchant-httpd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_exchanges.h"
+
+
+/**
+ * Taler protocol version in the format CURRENT:REVISION:AGE
+ * as used by GNU libtool. See
+ * https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
+ *
+ * Please be very careful when updating and follow
+ * https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
+ * precisely. Note that this version has NOTHING to do with the
+ * release version, and the format is NOT the same that semantic
+ * versioning uses either.
+ *
+ * When changing this version, you likely want to also update
+ * #MERCHANT_PROTOCOL_CURRENT and #MERCHANT_PROTOCOL_AGE in
+ * merchant_api_config.c!
+ */
+#define MERCHANT_PROTOCOL_VERSION "0:0:0"
+
+
+static int
+add_instance (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ json_t *ja = cls;
+ struct MerchantInstance *mi = value;
+ char *url;
+ json_t *pta;
+
+ /* Compile array of all unique wire methods supported by this
+ instance */
+ pta = json_array ();
+ GNUNET_assert (NULL != pta);
+ for (struct WireMethod *wm = mi->wm_head;
+ NULL != wm;
+ wm = wm->next)
+ {
+ int duplicate = GNUNET_NO;
+
+ if (! wm->active)
+ break;
+ /* Yes, O(n^2), but really how many bank accounts can an
+ instance realistically have for this to matter? */
+ for (struct WireMethod *pm = mi->wm_head;
+ pm != wm;
+ pm = pm->next)
+ if (0 == strcasecmp (pm->wire_method,
+ wm->wire_method))
+ {
+ duplicate = GNUNET_YES;
+ break;
+ }
+ if (duplicate)
+ continue;
+ GNUNET_assert (0 ==
+ json_array_append_new (pta,
+ json_string (wm->wire_method)));
+ }
+ GNUNET_asprintf (&url,
+ "/%s/",
+ mi->id);
+ GNUNET_assert (0 ==
+ json_array_append_new (
+ ja,
+ json_pack (
+ (NULL != mi->tip_exchange)
+ ? "{s:s, s:s, s:o, s:o, s:s}"
+ : "{s:s, s:s, s:o, s:o}",
+ "name",
+ mi->name,
+ "backend_base_url",
+ url,
+ "merchant_pub",
+ GNUNET_JSON_from_data_auto (&mi->pubkey),
+ "payment_targets",
+ pta,
+ /* optional: */
+ "tipping_exchange_baseurl",
+ mi->tip_exchange)));
+ GNUNET_free (url);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle a "/config" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param mi merchant backend instance, never NULL
+ * @return MHD result code
+ */
+MHD_RESULT
+MH_handler_config (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ struct MerchantInstance *mi)
+{
+ static struct MHD_Response *response;
+
+ (void) rh;
+ (void) connection_cls;
+ (void) upload_data;
+ (void) upload_data_size;
+ (void) mi;
+ if (NULL == response)
+ {
+ json_t *ia;
+
+ ia = json_array ();
+ GNUNET_assert (NULL != ia);
+ GNUNET_CONTAINER_multihashmap_iterate (by_id_map,
+ &add_instance,
+ ia);
+ response = TALER_MHD_make_json_pack ("{s:s, s:s, s:o}",
+ "currency", TMH_currency,
+ "version", MERCHANT_PROTOCOL_VERSION,
+ "instances", ia);
+
+ }
+ return MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ response);
+}
+
+
+/* end of taler-merchant-httpd_config.c */
diff --git a/src/backend/taler-merchant-httpd_transfers-get.h b/src/backend/taler-merchant-httpd_transfers-get.h
new file mode 100644
index 00000000..0463295e
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_transfers-get.h
@@ -0,0 +1,49 @@
+/*
+ This file is part of TALER
+ (C) 2014-2020 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ 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 backend/taler-merchant-httpd_track-transfer.h
+ * @brief headers for /track/transfer handler
+ * @author Christian Grothoff
+ * @author Marcello Stanisci
+ */
+#ifndef TALER_MERCHANT_HTTPD_TRACK_TRANSFER_H
+#define TALER_MERCHANT_HTTPD_TRACK_TRANSFER_H
+#include <microhttpd.h>
+#include "taler-merchant-httpd.h"
+
+/**
+ * Manages a /track/transfer call, thus it calls the /wire/transfer
+ * offered by the exchange in order to return the set of transfers
+ * (of coins) associated with a given wire transfer
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param mi merchant backend instance, never NULL
+ * @return MHD result code
+ */
+MHD_RESULT
+MH_handler_track_transfer (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ struct MerchantInstance *mi);
+
+
+#endif