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.c971
1 files changed, 271 insertions, 700 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 2b489040..ae9a4613 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -16,7 +16,8 @@
/**
* @file merchant/backend/taler-merchant-httpd.c
- * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @brief HTTP serving layer intended to perform crypto-work and
+ * communication with the mint
* @author Marcello Stanisci
*/
@@ -25,24 +26,16 @@
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
#include <curl/curl.h>
-#include <taler/taler_json_lib.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"
-
-extern struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
+#include "taler-mint-httpd_mhd.h"
+#include "taler-merchant-httpd_contract.h"
+#include "taler-merchant-httpd_pay.h"
/**
* Our hostname
@@ -65,270 +58,80 @@ struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
char *keyfile;
/**
- * The MHD Daemon
- */
-static struct MHD_Daemon *mhd;
-
-/**
- * Connection handle to the our database
- */
-PGconn *db_conn;
-
-/**
- * merchant's conf handle
+ * Mint context
*/
-struct GNUNET_CONFIGURATION_Handle *cfg;
+static struct TALER_MINT_Context *mctx;
/**
- * Shutdown task identifier
+ * This value tells the mint by which date this merchant would like
+ * to receive the funds for a deposited payment
*/
-static struct GNUNET_SCHEDULER_Task *shutdown_task;
+struct GNUNET_TIME_Relative edate_delay;
/**
- * Our wireformat
+ * To make 'TMH_PARSE_navigate_json ()' compile
*/
-static struct MERCHANT_WIREFORMAT_Sepa *wire;
+char *TMH_mint_currency_string;
/**
- * Should we do a dry run where temporary tables are used for storing the data.
+ * Trusted mints
*/
-static int dry;
+struct MERCHANT_Mint *mints;
/**
- * Global return code
+ * Active auditors
*/
-static int result;
+struct MERCHANT_Auditor *auditors;
/**
- * Mint context
- */
-static struct TALER_MINT_Context *mctx;
-
-/**
- * Context information of the mints we trust
+ * Shutdown task identifier
*/
-struct Mint
-{
- /**
- * Public key of this mint
- */
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
-
- /**
- * Connection handle to this mint
- */
- struct TALER_MINT_Handle *conn;
-};
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
/**
- * Hashmap to store the mint context information
+ * Context "poller" identifier
*/
-static struct GNUNET_CONTAINER_MultiPeerMap *mints_map;
+static struct GNUNET_SCHEDULER_Task *poller_task;
/**
- * Hashmap (with 'big entries') to make a mint's base URL
- * to point to some mint-describing structure
+ * Our wireformat
*/
-static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap;
-
-
+struct MERCHANT_WIREFORMAT_Sepa *wire;
/**
- * Mints' URL,port,key triples
+ * Salt used to hash the wire object
*/
-struct MERCHANT_MintInfo *mint_infos;
+long long salt;
/**
* The number of accepted mints
*/
unsigned int nmints;
-struct Mint_Response
-{
- char *ptr;
- size_t size;
-
-};
-
-
/**
- * Generate the 'hello world' response
- * @param connection a MHD connection
- * @param resp where to store the response for the calling function.
- * Note that in its original implementation this parameter was preceeded
- * by a '_'. Still not clear why.
- * @return HTTP status code reflecting the operation outcome
- *
+ * The number of active auditors
*/
-static unsigned int
-generate_hello (struct MHD_Response **resp)
-{
-
- const char *hello = "Hello customer\n";
- unsigned int ret;
-
- *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello,
- MHD_RESPMEM_PERSISTENT);
- ret = 200;
- return ret;
-}
+unsigned int nauditors;
/**
- * Return the given message to the other end of connection
- * @msg (0-terminated) message to show
- * @param connection a MHD connection
- * @param resp where to store the response for the calling function
- * @return HTTP status code reflecting the operation outcome
- *
+ * Should we do a dry run where temporary tables are used for storing the data.
*/
-static unsigned int
-generate_message (struct MHD_Response **resp, const char *msg)
-{
-
- unsigned int ret;
-
- *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg,
- MHD_RESPMEM_MUST_FREE);
- ret = 200;
- return ret;
-}
+static int dry;
/**
- * Callback to pass to curl used to store a HTTP response
- * in a custom memory location.
- * See http://curl.haxx.se/libcurl/c/getinmemory.html for a
- * detailed example
- *
- * @param contents the data gotten so far from the server
- * @param size symbolic (arbitrarily chosen by libcurl) unit
- * of bytes
- * @param nmemb factor to multiply by @a size to get the real
- * size of @a contents
- * @param userdata a pointer to a memory location which remains
- * the same across all the calls to this callback (i.e. it has
- * to be grown at each invocation of this callback)
- * @return number of written bytes
- * See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
- * for official documentation
- *
+ * Global return code
*/
-size_t
-get_response_in_memory (char *contents,
- size_t size,
- size_t nmemb,
- void *userdata)
-{
- struct Mint_Response *mr;
- size_t realsize;
-
- realsize = size * nmemb;
- mr = userdata;
- mr->ptr = realloc (mr->ptr, mr->size + realsize + 1);
-
- if (mr->ptr == NULL) {
- printf ("Out of memory, could not get in memory mint's"
- "response");
- return 0;
- }
- memcpy(&(mr->ptr[mr->size]), contents, realsize);
- mr->size += realsize;
- mr->ptr[mr->size] = 0;
-
- return realsize;
-
-}
+static int result;
-#ifdef PANIC_MGMT
/**
- * Callback for catching serious error conditions from MHD.
- *
- * @param cls user specified value
- * @param file where the error occured
- * @param line where the error occured
- * @param reason error detail, may be NULL
+ * Connection handle to the our database
*/
-static void
-mhd_panic_cb (void *cls,
- const char *file,
- unsigned int line,
- const char *reason)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "MHD panicked at %s:%u: %s",
- file, line, reason);
- result = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
-}
-#endif
+PGconn *db_conn;
/**
- * Manage a non 200 HTTP status. I.e. it shows a 'failure' page to
- * the client
- * @param connection the channel thorugh which send the message
- * @status the HTTP status to examine
- * @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error
+ * The MHD Daemon
*/
-static int
-failure_resp (struct MHD_Connection *connection, unsigned int status)
-{
- static char page_MHD_HTTP_NOT_FOUND[]="\
-<!DOCTYPE html> \
-<html><title>Resource not found</title><body><center> \
-<h3>The resource you are looking for is not found.</h3> \
-</center></body></html>";
- static char page_MHD_HTTP_BAD_REQUEST[]="\
-<!DOCTYPE html> \
-<html><title>Bad request</title><body><center> \
-<h3>Malformed POSTed JSON.</h3> \
-</center></body></html>";
-static char page_MHD_HTTP_METHOD_NOT_ALLOWED[]="\
-<!DOCTYPE html> \
-<html><title>Method NOT allowed</title><body><center> \
-<h3>ONLY POSTs are allowed.</h3> \
-</center></body></html>";
- static char page_MHD_HTTP_INTERNAL_SERVER_ERROR[]="\
-<!DOCTYPE html> <html><title>Internal Server Error</title><body><center> \
-<h3>The server experienced an internal error and hence cannot serve your \
-request</h3></center></body></html>";
- struct MHD_Response *resp;
- char *page;
- size_t size;
-#define PAGE(number) \
- do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0)
-
- GNUNET_assert (MHD_HTTP_BAD_REQUEST <= status);
- resp = NULL;
- switch (status)
- {
- case MHD_HTTP_NOT_FOUND :
- PAGE(MHD_HTTP_NOT_FOUND);
- break;
- case MHD_HTTP_BAD_REQUEST:
- PAGE(MHD_HTTP_BAD_REQUEST);
- break;
- case MHD_HTTP_METHOD_NOT_ALLOWED:
- PAGE(MHD_HTTP_METHOD_NOT_ALLOWED);
- break;
- default:
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- PAGE(MHD_HTTP_INTERNAL_SERVER_ERROR);
- }
-#undef PAGE
-
- EXITIF (NULL == (resp = MHD_create_response_from_buffer (size,
- page,
- MHD_RESPMEM_PERSISTENT)));
- EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
- MHD_destroy_response (resp);
- return GNUNET_OK;
-
- EXITIF_exit:
- if (NULL != resp)
- MHD_destroy_response (resp);
- return GNUNET_SYSERR;
-}
-
+static struct MHD_Daemon *mhd;
/**
* A client has requested the given url using the given method
@@ -377,423 +180,116 @@ url_handler (void *cls,
const char *version,
const char *upload_data,
size_t *upload_data_size,
- void **connection_cls)
+ void **con_cls)
{
-
- /*printf ("%s\n", url);*/
- unsigned long status;
- unsigned int no_destroy;
- struct GNUNET_CRYPTO_EddsaSignature c_sig;
- struct GNUNET_CRYPTO_EddsaSignature deposit_confirm_sig;
- struct GNUNET_CRYPTO_EddsaPublicKey pub;
- #ifdef OBSOLETE
- struct ContractNBO contract;
- #else
- struct Contract contract;
- #endif
- struct MHD_Response *resp;
- json_t *root;
- json_t *j_sig_enc;
- json_t *j_h_contract;
- json_t *j_tmp;
- json_t *eddsa_pub_enc;
- json_t *response;
- json_t *j_mints;
- json_t *j_mint;
- json_t *j_wire;
- int cnt; /* loop counter */
- char *deposit_body;
- json_t *j_contract_add;
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Absolute expiry;
- struct GNUNET_TIME_Absolute edate;
- struct GNUNET_TIME_Absolute refund;
- struct GNUNET_HashCode h_json_wire;
- json_t *j_h_json_wire;
- struct curl_slist *slist;
- char *contract_str;
- struct GNUNET_HashCode h_contract_str;
- struct MERCHANT_contract_handle ch;
- struct TALER_MintPublicKeyP mint_pub;
- uint64_t nounce;
-
- CURL *curl;
- CURLcode curl_res;
-
- uint32_t res = GNUNET_SYSERR;
-
- #define URL_HELLO "/hello"
- #define URL_CONTRACT "/contract"
- #define URL_PAY "/pay"
- no_destroy = 0;
- resp = NULL;
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
-
- if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO)))
- {
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- status = generate_hello (&resp);
- else
- {
- status = MHD_HTTP_METHOD_NOT_ALLOWED;
- }
- }
-
- if (0 == strncasecmp (url, URL_PAY, sizeof (URL_PAY)))
- {
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- {
- status = MHD_HTTP_METHOD_NOT_ALLOWED;
- goto end;
-
- }
- else
- res = TMH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- {
- status = MHD_HTTP_BAD_REQUEST;
- goto end;
- }
-
- /* the POST's body has to be further fetched */
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- /* Firstly, check if the wallet is paying against an approved
- mint */
- json_t *j_chosen_mint;
- j_chosen_mint = json_object_get (root, "mint");
- struct GNUNET_HashCode hash_key;
- char *chosen_mint;
-
- chosen_mint = json_string_value (j_chosen_mint);
- GNUNET_CRYPTO_hash (chosen_mint, strlen (chosen_mint), &hash_key);
-
- if (NULL ==
- GNUNET_CONTAINER_multihashmap_get (mints_hashmap, &hash_key))
- {
- printf ("Untrusted mint\n");
- status = MHD_HTTP_FORBIDDEN;
- goto end;
-
- }
-
- /* NOTE: from now on, the mint's base URL is pointed by 'chosen_mint' */
-
- /* The merchant will only add its 'wire' object to the JSON
- it got from the wallet */
-
- /* Get this dep. perm.'s H_contract */
-
- if (NULL == (j_h_contract = json_object_get (root, "H_contract")))
+ static struct TMH_RequestHandler handlers[] =
{
- printf ("H_contract field missing\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
- TALER_json_to_data (j_h_contract, &h_contract_str, sizeof (struct GNUNET_HashCode));
+ /* 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 },
- nounce = 0;
- edate.abs_value_us = 0;
+ /* Further test page */
+ { "/hello", MHD_HTTP_METHOD_GET, "text/plain",
+ "Hello, Customer.\n", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_OK },
- if (GNUNET_SYSERR ==
- MERCHANT_DB_get_contract_values (db_conn,
- &h_contract_str,
- &nounce,
- &edate))
- {
- printf ("not existing contract\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+ { "/contract", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &MH_handler_contract, MHD_HTTP_OK },
- /* Reproducing the wire object */
- if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
- nounce,
- edate)))
+ { "/contract", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
- {
- printf ("wire object not reproduced\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+ { "/pay", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &MH_handler_pay, MHD_HTTP_OK },
- if (-1 == json_object_set (root, "wire", j_wire))
- {
- printf ("depperm not augmented\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+ { "/pay", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
- /* POST to mint's "/deposit" */
- curl = curl_easy_init ();
- struct Mint_Response mr;
- mr.ptr = malloc(1);
- mr.size = 0;
+ {NULL, NULL, NULL, NULL, 0, 0 }
+ };
- if (curl)
+ static struct TMH_RequestHandler h404 =
{
-
- char deposit_url[strlen (chosen_mint) + strlen ("http://") + strlen ("/deposit") + 1];
- sprintf (deposit_url, "http://%s/deposit", chosen_mint);
- slist = curl_slist_append (slist, "Content-type: application/json");
- curl_easy_setopt (curl, CURLOPT_HTTPHEADER, slist);
-
- curl_easy_setopt (curl, CURLOPT_URL, deposit_url);
- curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, get_response_in_memory);
- curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) &mr);
-
- /* NOTE: hopefully, this string won't need any URL-encoding, since as for the
- Jansson specs, any space and-or newline are not in place using JSON_COMPACT
- flag */
- deposit_body = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, deposit_body);
-
- curl_res = curl_easy_perform (curl);
-
- curl_slist_free_all(slist);
- if(curl_res != CURLE_OK)
- {
- printf ("deposit not sent\n");
- goto end;
- }
- else
- printf ("\ndeposit request issued\n");
-
- curl_easy_cleanup(curl);
-
- curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status);
-
- /* If the request was successful, the deposit confirmation
- has to be verified*/
-
- if (MHD_HTTP_OK != result)
- /* Jump here to error mgmt, see issue #3800 (TODO) */
-
- if (GNUNET_SYSERR ==
- MERCHANT_DB_get_contract_handle (db_conn, &h_contract_str, &ch))
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
-
- root = json_loads (mr.ptr, 0, NULL);
- j_tmp = json_object_get (root, "sig");
- TALER_json_to_data (j_tmp,
- &deposit_confirm_sig,
- sizeof (struct GNUNET_CRYPTO_EddsaSignature));
- j_tmp = json_object_get (root, "pub");
-
- TALER_json_to_data (j_tmp,
- &mint_pub.eddsa_pub,
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
-
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
-
-
- /* The following check could be done once the merchant is able to
- retrieve the deposit fee for each coin it gets a payment with.
- That happens because the signature made on a deposit confirmation
- uses, among its values of interest, the subtraction of the deposit
- fee from the used coin. That fee is never communicated by the wallet
- to the merchant, so the only way for the merchant to get this
- information is to fetch all the mint's keys, namely it needs to
- invoke "/keys", and store what gotten in its DB */
-
- #ifdef DENOMMGMT
- if (GNUNET_NO ==
- MERCHANT_verify_confirmation (&h_contract_str,
- &h_json_wire,
- ch.timestamp,
- ch.refund_deadline,
- ch.contract_id,
- amount_minus_fee, /* MISSING */
- coin_pub, /* MISSING */
- &pub,
- &deposit_confirm_sig,
- &mint_pub))
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
-
- }
-
- #endif
-
- /* Place here the successful message to issue to the frontend */
- status = MHD_HTTP_OK;
- generate_message (&resp, "fullfillment page here");
- GNUNET_free (mr.ptr);
-
- }
+ "", 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;
- /*
- * To be called by the frontend passing the contract with some "holes"
- * which will be completed, stored in DB, signed, and returned
- *
- */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Handling request for URL '%s'\n",
+ url);
- if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
+ for (i=0;NULL != handlers[i].url;i++)
{
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- {
- status = MHD_HTTP_METHOD_NOT_ALLOWED;
- goto end;
-
- }
- else
- res = TMH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- {
- status = MHD_HTTP_BAD_REQUEST;
- goto end;
- }
-
-
- /* the POST's body has to be fetched furthermore */
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- j_mints = json_array ();
- for (cnt = 0; cnt < nmints; cnt++)
- {
- j_mint = json_pack ("{s:s}",
- mint_infos[cnt].hostname,
- GNUNET_CRYPTO_eddsa_public_key_to_string (&mint_infos[cnt].pubkey));
- json_array_append_new (j_mints, j_mint);
-
- }
-
- /* timestamp */
- now = GNUNET_TIME_absolute_get ();
- /* expiry */
- expiry = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
- /* edate, note: this value must be generated now (and not when the
- wallet sends back a deposit permission because the hashed 'wire' object,
- which carries this values in it, has to be included in the signed bundle
- by the wallet) */
- edate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
- refund = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
-
- TALER_round_abs_time (&now);
- TALER_round_abs_time (&expiry);
- TALER_round_abs_time (&edate);
- TALER_round_abs_time (&refund);
-
- /* getting the SEPA-aware JSON */
- /* nounce for hashing the wire object */
- nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
-
- /* get wire object */
-
- if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
- nounce,
- edate)))
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
-
- /* hash wire objcet */
- if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire))
- goto end;
-
- j_h_json_wire = TALER_json_from_data ((void *) &h_json_wire, sizeof (struct GNUNET_HashCode));
-
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
- eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub));
-
- if (NULL == (j_contract_add = json_pack ("{s:s, s:s, s:o, s:o, s:o}",
- "merchant_pub", json_string_value (eddsa_pub_enc),
- "H_wire", json_string_value (j_h_json_wire),
- "timestamp", TALER_json_from_abs (now),
- "refund", TALER_json_from_abs (refund),
- "mints", j_mints)))
- {
- printf ("BAD contract enhancement\n");
- goto end;
- }
-
- /* melt to what received from the wallet */
- if (-1 == json_object_update (root, j_contract_add))
- {
- printf ("depperm response not built\n");
- goto end;
- }
-
- res = MERCHANT_handle_contract (root,
- db_conn,
- &contract,
- now,
- expiry,
- edate,
- refund,
- &contract_str,
- nounce);
- if (GNUNET_SYSERR == res)
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
- if (GNUNET_NO == res)
- {
- status = MHD_HTTP_METHOD_NOT_ACCEPTABLE;
- goto end;
- }
-
- GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig);
- GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str);
-
- j_sig_enc = TALER_json_from_eddsa_sig (&contract.purpose, &c_sig);
-
- response = json_pack ("{s:o, s:o, s:o}",
- "contract", root,
- "sig", j_sig_enc,
- "h_contract",
- TALER_json_from_data ((void *) &h_contract_str, sizeof (struct GNUNET_HashCode)));
-
- GNUNET_free (contract_str);
-
- TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK);
- return MHD_YES;
-
+ rh = &handlers[i];
+ if ( (0 == strcasecmp (url,
+ rh->url)) &&
+ ( (NULL == rh->method) ||
+ (0 == strcasecmp (method,
+ rh->method)) ) )
+ return rh->handler (rh,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
}
+ return TMH_MHD_handler_static_response (&h404,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
- end:
+}
- if (NULL != resp)
+/**
+ * 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)
{
- EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
- return MHD_YES;
- if (!no_destroy)
- MHD_destroy_response (resp);
+ ((struct MERCHANT_Mint *) cls)->pending = 0;
}
else
- {
-
- EXITIF (GNUNET_OK != failure_resp (connection, status));
- return MHD_YES;
-
- }
-
- EXITIF_exit:
- result = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
- return MHD_NO;
+ printf ("no keys gotten\n");
}
+
/**
* Shutdown task (magically invoked when the application is being
* quit)
@@ -804,6 +300,19 @@ url_handler (void *cls,
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)
{
@@ -816,37 +325,100 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
MERCHANT_DB_disconnect (db_conn);
db_conn = NULL;
}
- if (keyfile != NULL)
+ if (NULL != keyfile)
GNUNET_free (privkey);
-
-
}
/**
- * Function called with information about who is auditing
- * a particular mint and what key the mint is using.
+ * Task that runs the context's event loop with the GNUnet scheduler.
*
- * @param cls closure
- * @param keys information about the various keys used
- * by the mint
+ * @param cls unused
+ * @param tc scheduler context (unused)
*/
static void
-keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
{
- /* which kind of mint's keys a merchant should need? Sign
- keys? It has already the mint's master key from the conf file */
- return;
-
+ 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;
+
+ poller_task = NULL;
+ TALER_MINT_perform (mctx);
+ 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 (mctx,
+ &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
+ * respective function to free the memory.
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ * the #MHD_AccessHandlerCallback
+ * @param toe reason for request termination
+ * @see #MHD_OPTION_NOTIFY_COMPLETED
+ * @ingroup request
+ */
+static void
+handle_mhd_completion_callback (void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ if (NULL == *con_cls)
+ return;
+ TMH_PARSE_post_cleanup_callback (*con_cls);
+ *con_cls = NULL;
+}
/**
* 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!)
+ * @param cfgfile name of the configuration file used (for saving, can be
+ * NULL!)
* @param config configuration
*/
void
@@ -855,88 +427,87 @@ run (void *cls, char *const *args, const char *cfgfile,
{
unsigned int cnt;
- void *keys_mgmt_cls;
-
- keys_mgmt_cls = NULL;
- mint_infos = NULL;
+ mints = NULL;
keyfile = NULL;
result = GNUNET_SYSERR;
- shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown, NULL);
- EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config,
- &mint_infos)));
- EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config)));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config,
- "merchant",
- "KEYFILE",
- &keyfile));
- EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
- EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config)));
- EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_YES));
+ shutdown_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown,
+ NULL);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "merchant launched\n");
+
+ EXITIF (GNUNET_SYSERR ==
+ (nmints =
+ TALER_MERCHANT_parse_mints (config,
+ &mints)));
+ EXITIF (GNUNET_SYSERR ==
+ (nauditors =
+ TALER_MERCHANT_parse_auditors (config,
+ &auditors)));
+ EXITIF (NULL ==
+ (wire =
+ TALER_MERCHANT_parse_wireformat_sepa (config)));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "KEYFILE",
+ &keyfile));
+ EXITIF (NULL ==
+ (privkey =
+ GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ EXITIF (NULL ==
+ (db_conn = MERCHANT_DB_connect (config)));
+ EXITIF (GNUNET_OK !=
+ MERCHANT_DB_initialize (db_conn, GNUNET_YES));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_number (config,
"merchant",
- "port",
+ "PORT",
&port));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_string (config,
"merchant",
- "hostname",
+ "HOSTNAME",
&hostname));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "merchant",
+ "CURRENCY",
+ &TMH_mint_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);
EXITIF (NULL == (mctx = TALER_MINT_init ()));
- /* Still not used */
- EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES)));
- /* Used when the wallet points out which mint it want to deal with.
- That indication is made through the mint's base URL, which will be
- the hash-key for this table */
- EXITIF (NULL == (mints_hashmap = GNUNET_CONTAINER_multihashmap_create (nmints, GNUNET_NO)));
-
for (cnt = 0; cnt < nmints; cnt++)
{
- struct Mint *mint;
- struct GNUNET_HashCode mint_key;
-
- mint = GNUNET_new (struct Mint);
- mint->pubkey = mint_infos[cnt].pubkey;
- /* port this to the new API */
- mint->conn = TALER_MINT_connect (mctx,
- mint_infos[cnt].hostname,
- &keys_mgmt_cb,
- keys_mgmt_cls); /*<- safe?segfault friendly?*/
-
- /* NOTE: the keys mgmt callback should roughly do what the following lines do */
- EXITIF (NULL == mint->conn);
-
- EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put
- (mints_map,
- (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey,
- mint,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
-
- /* 1 create hash key
- 2 create big entry
- 3 put
- */
- GNUNET_CRYPTO_hash (mint_infos[cnt].hostname,
- strlen (mint_infos[cnt].hostname),
- &mint_key);
- GNUNET_CONTAINER_multihashmap_put (mints_hashmap,
- &mint_key,
- &mint_infos[cnt],
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ mints[cnt].pending = 1;
+ mints[cnt].conn = TALER_MINT_connect (mctx,
+ mints[cnt].hostname,
+ &keys_mgmt_cb,
+ &mints[cnt]);
+ EXITIF (NULL == mints[cnt].conn);
+ poller_task =
+ GNUNET_SCHEDULER_add_now (&context_task, mctx);
}
mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
port,
NULL, NULL,
&url_handler, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, NULL,
+
MHD_OPTION_END);
EXITIF (NULL == mhd);
-
- /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that
- mandatory ? */
result = GNUNET_OK;
EXITIF_exit:
@@ -969,7 +540,7 @@ main (int argc, char *const *argv)
if (GNUNET_OK !=
GNUNET_PROGRAM_run (argc, argv,
- "taler-merchant-serve",
+ "taler-merchant-http",
"Serve merchant's HTTP interface",
options, &run, NULL))
return 3;