summaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/taler-merchant-httpd.c')
-rw-r--r--src/backend/taler-merchant-httpd.c421
1 files changed, 175 insertions, 246 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index ba359bd1..2cc553e9 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 Christian Grothoff (and other contributing authors)
+ (C) 2014, 2015 Christian Grothoff (and other contributing authors)
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
@@ -13,40 +13,40 @@
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
-
/**
* @file merchant/backend/taler-merchant-httpd.c
* @brief HTTP serving layer intended to perform crypto-work and
* communication with the mint
* @author Marcello Stanisci
+ * @author Christian Grothoff
*/
-
#include "platform.h"
#include <microhttpd.h>
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
-#include <curl/curl.h>
#include <taler/taler_util.h>
#include <taler/taler_mint_service.h>
-#include "taler-mint-httpd_parsing.h"
-#include "taler-mint-httpd_responses.h"
-#include "merchant_db.h"
-#include "merchant.h"
-#include "taler_merchant_lib.h"
+#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_responses.h"
+#include "taler_merchantdb_lib.h"
#include "taler-merchant-httpd.h"
-#include "taler-mint-httpd_mhd.h"
+#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_auditors.h"
+#include "taler-merchant-httpd_mints.h"
#include "taler-merchant-httpd_contract.h"
#include "taler-merchant-httpd_pay.h"
+
+
/**
- * Our hostname
+ * Our wire format details in JSON format (with salt).
*/
-static char *hostname;
+struct json_t *j_wire;
/**
- * The port we are running on
+ * Hash of our wire format details as given in #j_wire.
*/
-static long long unsigned port;
+struct GNUNET_HashCode h_wire;
/**
* Merchant's private key
@@ -54,30 +54,35 @@ static long long unsigned port;
struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
/**
- * File holding the merchant's private key
+ * Merchant's public key
*/
-char *keyfile;
+struct TALER_MerchantPublicKeyP pubkey;
/**
- * This value tells the mint by which date this merchant would like
- * to receive the funds for a deposited payment
+ * Our hostname
*/
-struct GNUNET_TIME_Relative edate_delay;
+static char *hostname;
/**
- * To make 'TMH_PARSE_navigate_json ()' compile
+ * The port we are running on
*/
-char *TMH_mint_currency_string;
+static long long unsigned port;
/**
- * Trusted mints
+ * File holding the merchant's private key
+ */
+static char *keyfile;
+
+/**
+ * This value tells the mint by which date this merchant would like
+ * to receive the funds for a deposited payment
*/
-struct MERCHANT_Mint *mints;
+struct GNUNET_TIME_Relative edate_delay;
/**
- * Active auditors
+ * Which currency is supported by this merchant?
*/
-struct MERCHANT_Auditor *auditors;
+char *TMH_merchant_currency_string;
/**
* Shutdown task identifier
@@ -90,31 +95,6 @@ static struct GNUNET_SCHEDULER_Task *shutdown_task;
static struct GNUNET_SCHEDULER_Task *mhd_task;
/**
- * Context "poller" identifier
- */
-struct GNUNET_SCHEDULER_Task *poller_task;
-
-/**
- * Our wireformat
- */
-struct MERCHANT_WIREFORMAT_Sepa *wire;
-
-/**
- * Salt used to hash the wire object
- */
-long long salt;
-
-/**
- * The number of accepted mints
- */
-unsigned int nmints;
-
-/**
- * The number of active auditors
- */
-unsigned int nauditors;
-
-/**
* Should we do a dry run where temporary tables are used for storing the data.
*/
static int dry;
@@ -127,13 +107,14 @@ static int result;
/**
* Connection handle to the our database
*/
-PGconn *db_conn;
+struct TALER_MERCHANTDB_Plugin *db;
/**
* The MHD Daemon
*/
static struct MHD_Daemon *mhd;
+
/**
* A client has requested the given url using the given method
* (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
@@ -190,15 +171,9 @@ url_handler (void *cls,
"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 },
- /* Further test page */
- { "/hello", MHD_HTTP_METHOD_GET, "text/plain",
- "Hello, Customer.\n", 0,
- &TMH_MHD_handler_static_response, MHD_HTTP_OK },
-
{ "/contract", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_contract, MHD_HTTP_OK },
-
{ "/contract", NULL, "text/plain",
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
@@ -206,33 +181,24 @@ url_handler (void *cls,
{ "/pay", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_pay, MHD_HTTP_OK },
-
{ "/pay", NULL, "text/plain",
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
{NULL, NULL, NULL, NULL, 0, 0 }
};
-
static struct TMH_RequestHandler h404 =
{
"", NULL, "text/html",
"<html><title>404: not found</title></html>", 0,
&TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
};
-
- /* Compiler complains about non returning a value in a non-void
- declared function: the FIX is to return what the handler for
- a particular URL returns */
-
struct TMH_RequestHandler *rh;
unsigned int i;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Handling request for URL '%s'\n",
+ "Handling request for URL `%s'\n",
url);
-
for (i=0;NULL != handlers[i].url;i++)
{
rh = &handlers[i];
@@ -252,43 +218,6 @@ url_handler (void *cls,
con_cls,
upload_data,
upload_data_size);
-
-}
-
-/**
- * Function called with information about who is auditing
- * a particular mint and what key the mint is using.
- *
- * @param cls closure, will be 'struct MERCHANT_Mint' so that
- * when this function gets called, it will change the flag 'pending'
- * to 'false'. Note: 'keys' is automatically saved inside the mint's
- * handle, which is contained inside 'struct MERCHANT_Mint', when
- * this callback is called. Thus, once 'pending' turns 'false',
- * it is safe to call 'TALER_MINT_get_keys()' on the mint's handle,
- * in order to get the "good" keys.
- *
- * @param keys information about the various keys used
- * by the mint
- */
-static void
-keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
-{
- /* HOT UPDATE: the merchants need the denomination keys!
- Because it wants to (firstly) verify the deposit confirmation
- sent by the mint, and the signed blob depends (among the
- other things) on the coin's deposit fee. That information
- is never communicated by the wallet to the merchant.
- Again, the merchant needs it because it wants to verify that
- the wallet didn't exceede the limit imposed by the merchant
- on the total deposit fee for a purchase */
-
- if (NULL != keys)
- {
- ((struct MERCHANT_Mint *) cls)->pending = 0;
- }
- else
- printf ("no keys gotten\n");
-
}
@@ -302,19 +231,6 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
static void
do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
- unsigned int cnt;
-
- for (cnt = 0; cnt < nmints; cnt++)
- {
- if (NULL != mints[cnt].conn)
- TALER_MINT_disconnect (mints[cnt].conn);
-
- }
- if (NULL != poller_task)
- {
- GNUNET_SCHEDULER_cancel (poller_task);
- poller_task = NULL;
- }
if (NULL != mhd_task)
{
GNUNET_SCHEDULER_cancel (mhd_task);
@@ -325,77 +241,19 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
MHD_stop_daemon (mhd);
mhd = NULL;
}
- if (NULL != db_conn)
+ if (NULL != db)
{
- MERCHANT_DB_disconnect (db_conn);
- db_conn = NULL;
+ TALER_MERCHANTDB_plugin_unload (db);
+ db = NULL;
}
+ TMH_MINTS_done ();
+ TMH_AUDITORS_done ();
if (NULL != keyfile)
GNUNET_free (privkey);
}
/**
- * Task that runs the context's event loop using the GNUnet scheduler.
- *
- * @param cls mint context
- * @param tc scheduler context (unused)
- */
-void
-context_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- long timeout;
- int max_fd;
- fd_set read_fd_set;
- fd_set write_fd_set;
- fd_set except_fd_set;
- struct GNUNET_NETWORK_FDSet *rs;
- struct GNUNET_NETWORK_FDSet *ws;
- struct GNUNET_TIME_Relative delay;
- struct TALER_MINT_Context *ctx;
-
- ctx = (struct TALER_MINT_Context *) cls;
- poller_task = NULL;
- TALER_MINT_perform (ctx);
- max_fd = -1;
- timeout = -1;
- FD_ZERO (&read_fd_set);
- FD_ZERO (&write_fd_set);
- FD_ZERO (&except_fd_set);
- TALER_MINT_get_select_info (ctx,
- &read_fd_set,
- &write_fd_set,
- &except_fd_set,
- &max_fd,
- &timeout);
- if (timeout >= 0)
- delay =
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- timeout);
- else
- delay = GNUNET_TIME_UNIT_FOREVER_REL;
- rs = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_copy_native (rs,
- &read_fd_set,
- max_fd + 1);
- ws = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_copy_native (ws,
- &write_fd_set,
- max_fd + 1);
- poller_task =
- GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- delay,
- rs,
- ws,
- &context_task,
- cls);
- GNUNET_NETWORK_fdset_destroy (rs);
- GNUNET_NETWORK_fdset_destroy (ws);
-}
-
-
-/**
* Function called whenever MHD is done with a request. If the
* request was a POST, we may have stored a `struct Buffer *` in the
* @a con_cls that might still need to be cleaned up. Call the
@@ -451,9 +309,12 @@ run_daemon (void *cls,
/**
* Kick MHD to run now, to be called after MHD_resume_connection().
+ * Basically, we need to explicitly resume MHD's event loop whenever
+ * we made progress serving a request. This function re-schedules
+ * the task processing MHD's activities to run immediately.
*/
void
-TM_trigger_daemon ()
+TMH_trigger_daemon ()
{
GNUNET_SCHEDULER_cancel (mhd_task);
run_daemon (NULL, NULL);
@@ -461,6 +322,100 @@ TM_trigger_daemon ()
/**
+ * Parse the SEPA information from the configuration. If any of the
+ * required fields is missing return an error.
+ *
+ * @param cfg the configuration
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ unsigned long long salt;
+ char *iban;
+ char *name;
+ char *bic;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "wire-sepa",
+ "SALT",
+ &salt))
+ {
+ salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+ UINT64_MAX);
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "No SALT option given in `wire-sepa`, using %llu\n",
+ (unsigned long long) salt);
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "IBAN",
+ &iban))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "NAME",
+ &name))
+ {
+ GNUNET_free (iban);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "wire-sepa",
+ "BIC",
+ &bic))
+ {
+ GNUNET_free (iban);
+ GNUNET_free (name);
+ GNUNET_free (bic);
+ }
+ j_wire = json_pack ("{s:s, s:s, s:s, s:s, s:o}",
+ "type", "SEPA",
+ "IBAN", iban,
+ "name", name,
+ "bic", bic,
+ "r", json_integer (salt));
+ GNUNET_free (iban);
+ GNUNET_free (name);
+ GNUNET_free (bic);
+ if (NULL == j_wire)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Verify that #j_wire contains a well-formed wire format, and
+ * update #h_wire to match it (if successful).
+ *
+ * @param allowed which wire format is allowed/expected?
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+validate_and_hash_wireformat (const char *allowed)
+{
+ const char *allowed_arr[] = {
+ allowed,
+ NULL
+ };
+
+ if (GNUNET_YES !=
+ TALER_json_validate_wireformat (allowed_arr,
+ j_wire))
+ return GNUNET_SYSERR;
+ if (GNUNET_SYSERR ==
+ TALER_hash_json (j_wire,
+ &h_wire))
+ return MHD_NO;
+ return GNUNET_OK;
+}
+
+
+/**
* Function that queries MHD's select sets and
* starts the task waiting for them.
*
@@ -510,55 +465,49 @@ prepare_daemon ()
}
-
/**
* Main function that will be run by the scheduler.
*
* @param cls closure
* @param args remaining command-line arguments
* @param cfgfile name of the configuration file used (for saving, can be
- * NULL!)
+ * NULL!)
* @param config configuration
*/
void
-run (void *cls, char *const *args, const char *cfgfile,
+run (void *cls,
+ char *const *args,
+ const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *config)
{
-
- unsigned int cnt;
- mints = NULL;
- keyfile = NULL;
result = GNUNET_SYSERR;
shutdown_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown,
- NULL);
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO, "merchant launched\n");
-
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown,
+ NULL);
EXITIF (GNUNET_SYSERR ==
- (nmints =
- TALER_MERCHANT_parse_mints (config,
- &mints)));
+ TMH_MINTS_init (config));
EXITIF (GNUNET_SYSERR ==
- (nauditors =
- TALER_MERCHANT_parse_auditors (config,
- &auditors)));
- EXITIF (NULL ==
- (wire =
- TALER_MERCHANT_parse_wireformat_sepa (config)));
+ TMH_AUDITORS_init (config));
+ /* FIXME: for now, we just support SEPA here: */
+ EXITIF (GNUNET_OK !=
+ parse_wireformat_sepa (config));
+ EXITIF (GNUNET_OK !=
+ validate_and_hash_wireformat ("SEPA"));
EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_filename (config,
- "merchant",
- "KEYFILE",
- &keyfile));
+ GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "KEYFILE",
+ &keyfile));
EXITIF (NULL ==
- (privkey =
- GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ (privkey =
+ GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ GNUNET_CRYPTO_eddsa_key_get_public (privkey,
+ &pubkey.eddsa_pub);
EXITIF (NULL ==
- (db_conn = MERCHANT_DB_connect (config)));
+ (db = TALER_MERCHANTDB_plugin_load (config)));
EXITIF (GNUNET_OK !=
- MERCHANT_DB_initialize (db_conn, dry));
+ db->initialize (db->cls, dry));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_number (config,
"merchant",
@@ -573,30 +522,13 @@ run (void *cls, char *const *args, const char *cfgfile,
GNUNET_CONFIGURATION_get_value_string (config,
"merchant",
"CURRENCY",
- &TMH_mint_currency_string));
+ &TMH_merchant_currency_string));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_time (config,
- "merchant",
- "EDATE",
- &edate_delay));
-
- salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
- UINT64_MAX);
-
- for (cnt = 0; cnt < nmints; cnt++)
- {
- EXITIF (NULL == (mints[cnt].ctx = TALER_MINT_init ()));
- mints[cnt].pending = 1;
- mints[cnt].conn = TALER_MINT_connect (mints[cnt].ctx,
- mints[cnt].hostname,
- &keys_mgmt_cb,
- &mints[cnt],
- TALER_MINT_OPTION_END);
- EXITIF (NULL == mints[cnt].conn);
- poller_task =
- GNUNET_SCHEDULER_add_now (&context_task, mints[cnt].ctx);
- }
+ "merchant",
+ "EDATE",
+ &edate_delay));
mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME,
port,
@@ -609,15 +541,15 @@ run (void *cls, char *const *args, const char *cfgfile,
result = GNUNET_OK;
mhd_task = prepare_daemon ();
- EXITIF_exit:
- if (GNUNET_OK != result)
- GNUNET_SCHEDULER_shutdown ();
+ EXITIF_exit:
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
GNUNET_free_non_null (keyfile);
- if (GNUNET_OK != result)
- GNUNET_SCHEDULER_shutdown ();
-
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
}
+
/**
* The main function of the serve tool
*
@@ -628,21 +560,18 @@ run (void *cls, char *const *args, const char *cfgfile,
int
main (int argc, char *const *argv)
{
-
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
{'t', "temp", NULL,
gettext_noop ("Use temporary database tables"), GNUNET_NO,
&GNUNET_GETOPT_set_one, &dry},
- GNUNET_GETOPT_OPTION_END
- };
-
+ GNUNET_GETOPT_OPTION_END
+ };
if (GNUNET_OK !=
GNUNET_PROGRAM_run (argc, argv,
- "taler-merchant-http",
- "Serve merchant's HTTP interface",
+ "taler-merchant-httpd",
+ "Taler merchant's HTTP backend interface",
options, &run, NULL))
return 3;
return (GNUNET_OK == result) ? 0 : 1;
-
}