summaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd_obsolete.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/taler-merchant-httpd_obsolete.c')
-rw-r--r--src/backend/taler-merchant-httpd_obsolete.c986
1 files changed, 986 insertions, 0 deletions
diff --git a/src/backend/taler-merchant-httpd_obsolete.c b/src/backend/taler-merchant-httpd_obsolete.c
new file mode 100644
index 00000000..a96dad59
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_obsolete.c
@@ -0,0 +1,986 @@
+/*
+ This file is part of TALER
+ (C) 2014 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
+ 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, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file merchant/backend/taler-merchant-httpd.c
+ * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <curl/curl.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_json_lib.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)
+
+/**
+ * Our hostname
+ */
+static char *hostname;
+
+/**
+ * The port we are running on
+ */
+static long long unsigned port;
+
+/**
+ * Merchant's private key
+ */
+struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
+
+/**
+ * File holding the merchant's private key
+ */
+char *keyfile;
+
+/**
+ * The MHD Daemon
+ */
+static struct MHD_Daemon *mhd;
+
+/**
+ * Connection handle to the our database
+ */
+PGconn *db_conn;
+
+/**
+ * merchant's conf handle
+ */
+struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Shutdown task identifier
+ */
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+/**
+ * Our wireformat
+ */
+static struct MERCHANT_WIREFORMAT_Sepa *wire;
+
+/**
+ * Should we do a dry run where temporary tables are used for storing the data.
+ */
+static int dry;
+
+/**
+ * Global return code
+ */
+static int result;
+
+/**
+ * Mint context
+ */
+static struct TALER_MINT_Context *mctx;
+
+/**
+ * Context information of the mints we trust
+ */
+struct Mint
+{
+ /**
+ * Public key of this mint
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
+
+ /**
+ * Connection handle to this mint
+ */
+ struct TALER_MINT_Handle *conn;
+};
+
+/**
+ * Hashmap to store the mint context information
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *mints_map;
+
+/**
+ * Hashmap (with 'big entries') to make a mint's base URL
+ * to point to some mint-describing structure
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap;
+
+/**
+ * Mints' URL,port,key triples
+ */
+struct MERCHANT_MintInfo *mint_infos;
+
+/**
+ * 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
+ *
+ */
+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;
+}
+
+/**
+ * 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
+ *
+ */
+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;
+}
+
+/**
+ * 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
+ *
+ */
+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;
+
+}
+
+#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
+ */
+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
+
+/**
+ * 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
+ */
+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;
+}
+
+
+/**
+ * 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
+ * must call MHD callbacks to provide content to give back to the
+ * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
+ * #MHD_HTTP_NOT_FOUND, etc.).
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param url the requested url
+ * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
+ * #MHD_HTTP_METHOD_PUT, etc.)
+ * @param version the HTTP version string (i.e.
+ * #MHD_HTTP_VERSION_1_1)
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of #MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * @a upload_data)
+ * @param upload_data_size set initially to the size of the
+ * @a upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param con_cls pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global #MHD_RequestCompletedCallback (which
+ * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, `*con_cls` will be NULL.
+ * @return #MHD_YES if the connection was handled successfully,
+ * #MHD_NO if the socket must be closed due to a serios
+ * error while handling the request
+ */
+static int
+url_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **connection_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")))
+ {
+ 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));
+
+ nounce = 0;
+ edate.abs_value_us = 0;
+
+ 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;
+ }
+
+ /* Reproducing the wire object */
+ if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
+ nounce,
+ edate)))
+
+ {
+ printf ("wire object not reproduced\n");
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ if (-1 == json_object_set (root, "wire", j_wire))
+ {
+ printf ("depperm not augmented\n");
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ /* POST to mint's "/deposit" */
+ curl = curl_easy_init ();
+
+ struct Mint_Response mr;
+ mr.ptr = malloc(1);
+ mr.size = 0;
+
+ if (curl)
+ {
+
+ 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);
+
+ }
+
+
+ }
+
+ /*
+ * To be called by the frontend passing the contract with some "holes"
+ * which will be completed, stored in DB, signed, and returned
+ *
+ */
+
+ if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
+ {
+ 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;
+
+ }
+
+ end:
+
+ if (NULL != resp)
+ {
+ EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
+ return MHD_YES;
+ if (!no_destroy)
+ MHD_destroy_response (resp);
+ }
+ else
+ {
+
+ EXITIF (GNUNET_OK != failure_resp (connection, status));
+ return MHD_YES;
+
+ }
+
+ EXITIF_exit:
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ return MHD_NO;
+}
+
+/**
+ * Shutdown task (magically invoked when the application is being
+ * quit)
+ *
+ * @param cls NULL
+ * @param tc scheduler task context
+ */
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+
+ if (NULL != mhd)
+ {
+ MHD_stop_daemon (mhd);
+ mhd = NULL;
+ }
+
+ if (NULL != db_conn)
+ {
+ MERCHANT_DB_disconnect (db_conn);
+ db_conn = NULL;
+ }
+ if (keyfile != NULL)
+ GNUNET_free (privkey);
+
+
+}
+
+/**
+ * Function called with information about who is auditing
+ * a particular mint and what key the mint is using.
+ *
+ * @param cls closure
+ * @param keys information about the various keys used
+ * by the mint
+ */
+static void
+keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
+{
+ /* which kind of mint's keys a merchant should need? Sign
+ keys? It has already the mint's master key from the conf file */
+
+ /* HOT UPDATE: the merchants needs 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 */
+
+
+ return;
+
+}
+
+/**
+ * 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 config configuration
+ */
+void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
+{
+
+ unsigned int cnt;
+ void *keys_mgmt_cls;
+
+ keys_mgmt_cls = NULL;
+ mint_infos = 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));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_number (config,
+ "merchant",
+ "port",
+ &port));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "merchant",
+ "hostname",
+ &hostname));
+
+ 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);
+ }
+
+ mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
+ port,
+ NULL, NULL,
+ &url_handler, 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:
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free_non_null (keyfile);
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
+
+}
+
+/**
+ * The main function of the serve tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+
+ 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
+ };
+
+
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv,
+ "taler-merchant-serve",
+ "Serve merchant's HTTP interface",
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+
+}