commit b0097e0389376fe5b1fc78684c53c2b0de647582
parent 1ca15d8f10a8c4b6860eeccf30899252eb6c3b79
Author: Marcello Stanisci <marcello.stanisci@inria.fr>
Date: Fri, 23 Oct 2015 19:56:07 +0200
Restructuring merchant:
- Calling URLs handlers as the mint does
- Moving URLs handlers into dedicated functions
(moved inside dedicated source files)
- Some file renaming
- Fixing some warnings about 'const' variables used
in test_contract
Diffstat:
14 files changed, 1547 insertions(+), 656 deletions(-)
diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am
@@ -7,11 +7,13 @@ lib_LTLIBRARIES = \
include_HEADERS = \
taler_merchant_lib.h \
taler_merchant_contract_lib.h
+ taler_merchant_deposit_lib.h
libtalermerchant_la_SOURCES = \
- taler-merchant-httpd_contract.c \
- taler-merchant-httpd_deposit.c \
+ merchant_api_contract.c \
+ merchant_api_deposit.c \
taler_merchant_contract_lib.h \
+ taler_merchant_deposit_lib.h \
merchant_db.c merchant_db.h \
merchant.h
diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/merchant_api_contract.c
diff --git a/src/backend-lib/taler-merchant-httpd_deposit.c b/src/backend-lib/merchant_api_deposit.c
diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c
@@ -314,9 +314,6 @@ MERCHANT_DB_contract_create (PGconn *conn,
PQclear (res);
return GNUNET_OK;
- EXITIF_exit:
- PQclear (res);
- return GNUNET_SYSERR;
}
long long
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
@@ -10,6 +10,8 @@ taler_merchant_httpd_SOURCES = \
../backend-lib/merchant_db.c ../backend-lib/merchant_db.h \
taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \
taler-mint-httpd_responses.c taler-mint-httpd_responses.h \
+ taler-mint-httpd.h \
+ taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \
../backend-lib/taler-merchant-httpd_contract.h
taler_merchant_httpd_LDADD = \
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
@@ -25,24 +25,14 @@
#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"
/**
* Our hostname
@@ -65,24 +55,24 @@ struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
char *keyfile;
/**
- * The MHD Daemon
+ * Mint context
*/
-static struct MHD_Daemon *mhd;
+static struct TALER_MINT_Context *mctx;
/**
- * Connection handle to the our database
+ * Mints' URL,port,key triples
*/
-PGconn *db_conn;
+struct MERCHANT_MintInfo *mint_infos;
/**
- * merchant's conf handle
+ * Shutdown task identifier
*/
-struct GNUNET_CONFIGURATION_Handle *cfg;
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
/**
- * Shutdown task identifier
+ * Hashmap to store the mint context information
*/
-static struct GNUNET_SCHEDULER_Task *shutdown_task;
+static struct GNUNET_CONTAINER_MultiPeerMap *mints_map;
/**
* Our wireformat
@@ -90,6 +80,11 @@ static struct GNUNET_SCHEDULER_Task *shutdown_task;
static struct MERCHANT_WIREFORMAT_Sepa *wire;
/**
+ * The number of accepted mints
+ */
+unsigned int nmints;
+
+/**
* Should we do a dry run where temporary tables are used for storing the data.
*/
static int dry;
@@ -100,9 +95,15 @@ static int dry;
static int result;
/**
- * Mint context
+ * Connection handle to the our database
*/
-static struct TALER_MINT_Context *mctx;
+PGconn *db_conn;
+
+/**
+ * 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;
/**
* Context information of the mints we trust
@@ -121,214 +122,9 @@ struct Mint
};
/**
- * 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
+ * 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 +173,100 @@ 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
+ static struct TMH_RequestHandler handlers[] =
{
- status = MHD_HTTP_METHOD_NOT_ALLOWED;
- }
- }
+ /* 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 },
- 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;
+ /* Further test page */
+ { "/hello", MHD_HTTP_METHOD_GET, "text/plain",
+ "Hello, Customer.\n", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_OK },
- }
- 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 */
+ { "/contract", MHD_HTTP_METHOD_GET, "application/json",
+ NULL, 0,
+ &MH_handler_contract, MHD_HTTP_OK },
- /* 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));
+ { "/contract", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
- 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;
+ {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)));
+ 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);
- GNUNET_free (contract_str);
+}
- TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK);
- return MHD_YES;
+/**
+ * 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 */
- }
- end:
+ return;
- 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)
@@ -823,25 +296,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
}
/**
- * 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 */
- return;
-
-}
-
-
-
-/**
* Main function that will be run by the scheduler.
*
* @param cls closure
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
@@ -0,0 +1,74 @@
+/*
+ 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_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"
+
+/**
+ * Manage a contract 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
+ *
+ * @return MHD result code
+ */
+int
+MH_handler_contract (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ /* 1. Generate preferred mint(s) array.
+
+ a. Add the configuration lines to specify the mint's business
+ address.
+ b. Add this address to the 'locations' object, that should be
+ already present in the 'proposition' gotten from the frontend.
+ c. Point the 'address' field's label to the one added to 'locations'
+
+ The 'mint' JSON layout is as follows:
+
+ { "address": "address_label",
+ "url": "mint_base_url",
+ "master_pub": "base32 mint's master public key" }
+
+ */
+
+
+
+
+
+}
diff --git a/src/backend/taler-merchant-httpd_obsolete.c b/src/backend/taler-merchant-httpd_obsolete.c
@@ -0,0 +1,985 @@
+/*
+ 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_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;
+
+}
diff --git a/src/backend/taler-mint-httpd.h b/src/backend/taler-mint-httpd.h
@@ -0,0 +1,85 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 GNUnet e.V.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-mint-httpd.h
+ * @brief Global declarations for the mint
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ *
+ * FIXME: Consider which of these need to really be globals...
+ */
+#ifndef TALER_MINT_HTTPD_H
+#define TALER_MINT_HTTPD_H
+
+#include <microhttpd.h>
+
+/**
+ * @brief Struct describing an URL and the handler for it.
+ */
+struct TMH_RequestHandler
+{
+
+ /**
+ * URL the handler is for.
+ */
+ const char *url;
+
+ /**
+ * Method the handler is for, NULL for "all".
+ */
+ const char *method;
+
+ /**
+ * Mime type to use in reply (hint, can be NULL).
+ */
+ const char *mime_type;
+
+ /**
+ * Raw data for the @e handler
+ */
+ const void *data;
+
+ /**
+ * Number of bytes in @e data, 0 for 0-terminated.
+ */
+ size_t data_size;
+
+ /**
+ * Function to call to handle the request.
+ *
+ * @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
+ * @return MHD result code
+ */
+ int (*handler)(struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+ /**
+ * Default response code.
+ */
+ int response_code;
+};
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_mhd.c b/src/backend/taler-mint-httpd_mhd.c
@@ -0,0 +1,154 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 GNUnet e.V.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd_mhd.c
+ * @brief helpers for MHD interaction; these are TALER_MINT_handler_ functions
+ * that generate simple MHD replies that do not require any real operations
+ * to be performed (error handling, static pages, etc.)
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler-mint-httpd_responses.h"
+#include "taler-mint-httpd.h"
+#include "taler-mint-httpd_mhd.h"
+#include "taler-mint-httpd_responses.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a 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
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ struct MHD_Response *response;
+ int ret;
+
+ if (0 == rh->data_size)
+ rh->data_size = strlen ((const char *) rh->data);
+ response = MHD_create_response_from_buffer (rh->data_size,
+ (void *) rh->data,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ TMH_RESPONSE_add_global_headers (response);
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @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
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ const char *agpl =
+ "This server is licensed under the Affero GPL. You will now be redirected to the source code.";
+ struct MHD_Response *response;
+ int ret;
+
+ response = MHD_create_response_from_buffer (strlen (agpl),
+ (void *) agpl,
+ MHD_RESPMEM_PERSISTENT);
+ if (NULL == response)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ TMH_RESPONSE_add_global_headers (response);
+ if (NULL != rh->mime_type)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ rh->mime_type);
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_LOCATION,
+ "http://www.git.taler.net/?p=mint.git");
+ ret = MHD_queue_response (connection,
+ rh->response_code,
+ response);
+ MHD_destroy_response (response);
+ return ret;
+}
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a 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
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ rh->response_code,
+ "{s:s}",
+ "error",
+ rh->data);
+}
+
+
+/* end of taler-mint-httpd_mhd.c */
diff --git a/src/backend/taler-mint-httpd_mhd.h b/src/backend/taler-mint-httpd_mhd.h
@@ -0,0 +1,111 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014 GNUnet e.V.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-mint-httpd_mhd.h
+ * @brief helpers for MHD interaction, used to generate simple responses
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MINT_HTTPD_MHD_H
+#define TALER_MINT_HTTPD_MHD_H
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+
+/**
+ * Function to call to handle the request by sending
+ * back static data from the @a 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
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by sending
+ * back a redirect to the AGPL source code.
+ *
+ * @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
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply from varargs.
+ *
+ * @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 response_code HTTP response code to use
+ * @param do_cache can the response be cached? (0: no, 1: yes)
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD result code
+ */
+int
+TMH_MHD_helper_send_json_pack (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void *connection_cls,
+ int response_code,
+ int do_cache,
+ const char *fmt,
+ ...);
+
+
+/**
+ * Function to call to handle the request by building a JSON
+ * reply with an error message from @a 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
+ * @return MHD result code
+ */
+int
+TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size);
+
+
+#endif
diff --git a/src/backend/taler-mint-httpd_responses.c b/src/backend/taler-mint-httpd_responses.c
@@ -165,4 +165,25 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection)
"invalid json");
}
+/**
+ * Add headers we want to return in every response.
+ * Useful for testing, like if we want to always close
+ * connections.
+ *
+ * @param response response to modify
+ */
+void
+TMH_RESPONSE_add_global_headers (struct MHD_Response *response)
+{
+ int TMH_mint_connection_close;
+ TMH_mint_connection_close = 0;
+
+ /* this test is taken verbatim from the mint's code,
+ so there is no particular need to do that for a merchant */
+ if (TMH_mint_connection_close)
+ (void) MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONNECTION,
+ "close");
+}
+
/* end of taler-mint-httpd_responses.c */
diff --git a/src/backend/taler-mint-httpd_responses.h b/src/backend/taler-mint-httpd_responses.h
@@ -89,4 +89,14 @@ TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
int
TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection);
+/**
+ * Add headers we want to return in every response.
+ * Useful for testing, like if we want to always close
+ * connections.
+ *
+ * @param response response to modify
+ */
+void
+TMH_RESPONSE_add_global_headers (struct MHD_Response *response);
+
#endif
diff --git a/src/tests/test_contract.c b/src/tests/test_contract.c
@@ -23,7 +23,7 @@
#include "platform.h"
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_json_lib.h>
+#include <taler/taler_util.h>
#include "merchant.h"
#include "merchant_db.h"
#include <taler_merchant_lib.h>
@@ -80,7 +80,6 @@ run (void *cls, char *const *args, const char *cfgfile,
{
json_t *j_fake_contract;
- json_t *j_wire;
json_t *j_details;
json_t *j_mints;
json_t *j_item;
@@ -98,7 +97,6 @@ run (void *cls, char *const *args, const char *cfgfile,
json_t *j_merchant_zipcode;
json_t *j_lnames;
json_t *j_deldate;
- char *contract_tmp_str;
char *desc;
struct TALER_Amount amount;
int64_t t_id;
@@ -113,7 +111,7 @@ run (void *cls, char *const *args, const char *cfgfile,
uint64_t nounce;
struct GNUNET_HashCode h_contract_str;
char *aa;
- char *fancy_time;
+ const char *fancy_time;
uint32_t ret;
db_conn = NULL;
@@ -278,8 +276,6 @@ run (void *cls, char *const *args, const char *cfgfile,
nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
- j_wire = MERCHANT_get_wire_json (wire, nounce, now);
-
ret = MERCHANT_handle_contract (j_fake_contract,
db_conn,
&contract,