diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd.c')
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 421 |
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; - } |