summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2015-10-28 17:40:30 +0100
committerMarcello Stanisci <marcello.stanisci@inria.fr>2015-10-28 17:40:30 +0100
commit9cec564ddc9bec9ea34ed7b0ca308800651cabd6 (patch)
tree01cfe65dd9f454d9b6b92abc93a070d17d7b1103
parent2fbdccc8dc18157e2df7486e4ecedee343c81d8d (diff)
downloadmerchant-9cec564ddc9bec9ea34ed7b0ca308800651cabd6.tar.gz
merchant-9cec564ddc9bec9ea34ed7b0ca308800651cabd6.tar.bz2
merchant-9cec564ddc9bec9ea34ed7b0ca308800651cabd6.zip
Adding main behaviour to contract mgmt, plus importing additional
features from the mint code base
-rw-r--r--src/backend-lib/merchant_api_contract.c16
-rw-r--r--src/backend-lib/taler_merchant_contract_lib.h6
-rw-r--r--src/backend/merchant.c21
-rw-r--r--src/backend/merchant.conf12
-rw-r--r--src/backend/taler-merchant-httpd.c92
-rw-r--r--src/backend/taler-merchant-httpd_contract.c103
-rw-r--r--src/backend/taler-mint-httpd_parsing.c783
-rw-r--r--src/frontend/generate_taler_contract.php1
-rw-r--r--src/include/merchant.h11
9 files changed, 963 insertions, 82 deletions
diff --git a/src/backend-lib/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c
index 42e1e064..239e29fb 100644
--- a/src/backend-lib/merchant_api_contract.c
+++ b/src/backend-lib/merchant_api_contract.c
@@ -32,32 +32,28 @@
* Take the global wire details and return a JSON containing them,
* compliantly with the Taler's API.
* @param wire the merchant's wire details
- * @param nounce the nounce for hashing the wire details with
+ * @param salt the nounce for hashing the wire details with
* @param edate when the beneficiary wants this transfer to take place
* @return JSON representation of the wire details, NULL upon errors
*/
json_t *
MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire,
- uint64_t nounce,
- const struct GNUNET_TIME_Absolute edate)
+ uint64_t salt)
{
json_t *root;
- json_t *j_edate;
- json_t *j_nounce;
+ json_t *j_salt;
- j_nounce = json_integer (nounce);
- j_edate = TALER_json_from_abs (edate);
+ j_nounce = json_integer (salt);
- if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:o, s:I}",
+ if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:I}",
"type", "SEPA",
"IBAN", wire->iban,
"name", wire->name,
"bic", wire->bic,
- "edate", j_edate,
- "r", json_integer_value (j_nounce))))
+ "r", json_integer_value (j_salt))))
return NULL;
return root;
diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h
index ca799ff2..1d2d0eba 100644
--- a/src/backend-lib/taler_merchant_contract_lib.h
+++ b/src/backend-lib/taler_merchant_contract_lib.h
@@ -20,15 +20,13 @@ struct Contract
* Take the global wire details and return a JSON containing them,
* compliantly with the Taler's API.
* @param wire the merchant's wire details
- * @param nounce the nounce for hashing the wire details with
- * @param edate when the beneficiary wants this transfer to take place
+ * @param salt the nounce for hashing the wire details with
* @return JSON representation of the wire details, NULL upon errors
*/
json_t *
MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire,
- uint64_t nounce,
- struct GNUNET_TIME_Absolute edate);
+ uint64_t salt);
/**
* Take from the frontend the (partly) generated contract and fill
diff --git a/src/backend/merchant.c b/src/backend/merchant.c
index 58005394..4fa66f16 100644
--- a/src/backend/merchant.c
+++ b/src/backend/merchant.c
@@ -48,10 +48,8 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
char *token_nf; /* do no free (nf) */
char *mint_section;
char *mint_hostname;
- char *mint_pubkey_enc;
struct MERCHANT_Mint *r_mints;
struct MERCHANT_Mint mint;
- unsigned long long mint_port;
unsigned int cnt;
int OK;
@@ -60,7 +58,6 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
token_nf = NULL;
mint_section = NULL;
mint_hostname = NULL;
- mint_pubkey_enc = NULL;
r_mints = NULL;
cnt = 0;
EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
@@ -78,26 +75,9 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
mint_section,
"HOSTNAME",
&mint_hostname));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (cfg,
- mint_section,
- "PORT",
- &mint_port));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- mint_section,
- "PUBKEY",
- &mint_pubkey_enc));
- EXITIF (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc,
- strlen (mint_pubkey_enc),
- &mint.pubkey));
mint.hostname = mint_hostname;
- mint.port = (uint16_t) mint_port;
GNUNET_array_append (r_mints, cnt, mint);
mint_hostname = NULL;
- GNUNET_free (mint_pubkey_enc);
- mint_pubkey_enc = NULL;
GNUNET_free (mint_section);
mint_section = NULL;
}
@@ -107,7 +87,6 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
GNUNET_free_non_null (mints_str);
GNUNET_free_non_null (mint_section);
GNUNET_free_non_null (mint_hostname);
- GNUNET_free_non_null (mint_pubkey_enc);
if (!OK)
{
GNUNET_free_non_null (r_mints);
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf
index 851a6062..f24611a9 100644
--- a/src/backend/merchant.conf
+++ b/src/backend/merchant.conf
@@ -6,15 +6,9 @@ KEYFILE = merchant.priv
[mint-taler]
HOSTNAME = demo.taler.net
-PORT = 80
-PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0
-COUNTRY = France
-CITY = Rennes
-REGION = Bretagne
-PROVINCE = Ile-et-Vilaine
-ZIP_CODE = 35510
-STREET = Avenue du General Leclerc
-STREET_NUMBER = 21
+
+[mint-localmint]
+HOSTNAME = localmint
[merchant-db]
CONFIG = postgres:///talerdemo
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 91a6fdf8..8da6ce21 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -62,6 +62,11 @@ char *keyfile;
static struct TALER_MINT_Context *mctx;
/**
+ * To make 'TMH_PARSE_navigate_json ()' compile
+ */
+char *TMH_mint_currency_string;
+
+/**
* Mints' URL,port,key triples
*/
struct MERCHANT_Mint *mints;
@@ -79,7 +84,12 @@ static struct GNUNET_SCHEDULER_Task *poller_task;
/**
* Our wireformat
*/
-static struct MERCHANT_WIREFORMAT_Sepa *wire;
+struct MERCHANT_WIREFORMAT_Sepa *wire;
+
+/**
+ * Salt used to hash the wire object
+ */
+long long salt;
/**
* The number of accepted mints
@@ -102,22 +112,6 @@ static int result;
PGconn *db_conn;
/**
- * 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;
-};
-
-/**
* The MHD Daemon
*/
static struct MHD_Daemon *mhd;
@@ -238,16 +232,20 @@ url_handler (void *cls,
* Function called with information about who is auditing
* a particular mint and what key the mint is using.
*
- * @param cls closure
+ * @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)
{
- /* 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 need the denomination keys!
Because it wants to (firstly) verify the deposit confirmation
sent by the mint, and the signed blob depends (among the
@@ -256,7 +254,8 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
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 */
- printf ("Unregistering connection currently at %p\n", cls);
+
+ ((struct MERCHANT_Mint *) cls)->pending = 0;
}
@@ -270,7 +269,14 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
static void
do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
+ unsigned int cnt;
+ for (cnt = 0; cnt < nmints; cnt++)
+ {
+ if (NULL != mints[cnt].conn)
+ TALER_MINT_disconnect (mints[cnt].conn);
+
+ }
if (NULL != poller_task)
{
GNUNET_SCHEDULER_cancel (poller_task);
@@ -290,8 +296,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
}
if (NULL != keyfile)
GNUNET_free (privkey);
-
-
}
/**
@@ -351,6 +355,31 @@ context_task (void *cls,
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.
@@ -367,9 +396,7 @@ run (void *cls, char *const *args, const char *cfgfile,
{
unsigned int cnt;
- void *keys_mgmt_cls;
- keys_mgmt_cls = NULL;
mints = NULL;
keyfile = NULL;
result = GNUNET_SYSERR;
@@ -406,14 +433,22 @@ run (void *cls, char *const *args, const char *cfgfile,
"merchant",
"hostname",
&hostname));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "merchant",
+ "currency",
+ &TMH_mint_currency_string));
+
+ salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+ UINT64_MAX);
EXITIF (NULL == (mctx = TALER_MINT_init ()));
for (cnt = 0; cnt < nmints; cnt++)
{
+ mints[cnt].pending = 1;
mints[cnt].conn = TALER_MINT_connect (mctx,
mints[cnt].hostname,
&keys_mgmt_cb,
&mints[cnt]);
- printf ("trying to, %p\n", &mints[cnt]);
EXITIF (NULL == mints[cnt].conn);
poller_task =
GNUNET_SCHEDULER_add_now (&context_task, mctx);
@@ -423,6 +458,9 @@ run (void *cls, char *const *args, const char *cfgfile,
port,
NULL, NULL,
&url_handler, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, NULL,
+
MHD_OPTION_END);
EXITIF (NULL == mhd);
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
index 143746b6..04726839 100644
--- a/src/backend/taler-merchant-httpd_contract.c
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -35,6 +35,13 @@
#include "merchant.h"
#include "taler_merchant_lib.h"
+extern struct MERCHANT_Mint *mints;
+extern struct GNUNET_CRYPTO_EddsaPrivateKey privkey;
+extern const struct MERCHANT_WIREFORMAT_Sepa *wire;
+extern unsigned int nmints;
+extern PGconn *db_conn;
+extern long long salt;
+
/**
* Manage a contract request
*
@@ -53,18 +60,108 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
const char *upload_data,
size_t *upload_data_size)
{
+ json_t *root;
+ json_t *trusted_mints;
+ json_t *mint;
+ json_t *j_wire;
+ const struct TALER_MINT_Keys *keys;
+ int res;
+ int cnt;
+ uint64_t trans_id;
+ struct TALER_Amount amount;
+ struct GNUNET_TIME_Absolute timestamp;
+ struct GNUNET_TIME_Absolute refund;
+ struct GNUNET_TIME_Absolute expiry;
+ struct GNUNET_TIME_Absolute edate;
+ struct GNUNET_HashCode h_wire;
+ struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
+ struct TMH_PARSE_FieldSpecification spec[] =
+ {
+ TMH_PARSE_member_time_abs ("timestamp", &timestamp),
+ TMH_PARSE_member_time_abs ("refund_deadline", &refund),
+ TMH_PARSE_member_time_abs ("expiry", &expiry),
+ TMH_PARSE_member_amount ("amount", &amount),
+ TMH_PARSE_member_uint64 ("trans_id", &trans_id),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+ /* the POST's body has to be further fetched */
+ if ((GNUNET_NO == res) || (NULL == root))
+ return MHD_YES;
+
/* 1. Generate preferred mint(s) array.
The 'mint' JSON layout is as follows:
{ "url": "mint_base_url",
"master_pub": "base32 mint's master public key" }
-
*/
+ trusted_mints = json_array ();
+ for (cnt = 0; cnt < nmints; cnt++)
+ {
+ if (!mints[cnt].pending)
+ {
+ keys = TALER_MINT_get_keys (mints[cnt].conn);
+ mint = json_pack ("{s:s, s:o}",
+ "url", mints[cnt].hostname,
+ "master_pub",
+ TALER_json_from_data
+ (&keys->master_pub.eddsa_pub,
+ sizeof (keys->master_pub.eddsa_pub)));
+ json_array_append_new (trusted_mints, mint);
+ }
+ }
+
+ /* Return badly if no mints are trusted (or ready). WARNING: it
+ may be possible that a mint trusted by the wallet is good, but
+ still pending; that case must be handled with some "polling-style"
+ routine, simply ignored, or ended with an invitation to the wallet
+ to just retry later */
+ if (!json_array_size (trusted_mints))
+ return MHD_NO;
+
+ json_object_set_new (root, "mints", trusted_mints);
- /* To suppress compilation warning */
- return 0;
+ res = TMH_PARSE_json_data (connection, root, spec);
+ if (GNUNET_SYSERR == res)
+ return MHD_NO; /* hard failure */
+ if (GNUNET_NO == res)
+ return MHD_YES; /* failure */
+
+ /* (indicative) deadline which the merchant wants its wire tranfer
+ before */
+ edate = GNUNET_TIME_absolute_add (timestamp, GNUNET_TIME_UNIT_WEEKS);
+ TALER_round_abs_time (&edate);
+ if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
+ salt)))
+ return MHD_NO;
+
+ /* hash wire objcet */
+ if (GNUNET_SYSERR ==
+ TALER_hash_json (j_wire, &h_wire))
+ return MHD_NO;
+
+ json_object_set_new (root,
+ "H_wire",
+ TALER_json_from_data (&h_wire, sizeof (h_wire)));
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey);
+ json_object_set_new (root,
+ "merchant_pub",
+ TALER_json_from_data (&pubkey, sizeof (pubkey)));
+
+ /* store relevant values for this contract, in DB */
+ //res = MERCHANT_handle_contract ();
+
}
diff --git a/src/backend/taler-mint-httpd_parsing.c b/src/backend/taler-mint-httpd_parsing.c
index 8d9e3f9f..a4dc43ab 100644
--- a/src/backend/taler-mint-httpd_parsing.c
+++ b/src/backend/taler-mint-httpd_parsing.c
@@ -27,6 +27,13 @@
#include "taler-mint-httpd_parsing.h"
#include "taler-mint-httpd_responses.h"
+/* Although the following declaration isn't in any case useful
+ to a merchant's activity, it's needed here to make the function
+ 'TMH_PARSE_nagivate_json ()' compile fine; so its value will be
+ kept on some merchant's accepted currency. For multi currencies
+ merchants, that of course would require a patch */
+
+extern char *TMH_mint_currency_string;
/**
* Initial size for POST request buffer.
*/
@@ -139,6 +146,107 @@ buffer_append (struct Buffer *buf,
}
/**
+ * Function called whenever we are done with a request
+ * to clean up our state.
+ *
+ * @param con_cls value as it was left by
+ * #TMH_PARSE_post_json(), to be cleaned up
+ */
+void
+TMH_PARSE_post_cleanup_callback (void *con_cls)
+{
+ struct Buffer *r = con_cls;
+
+ if (NULL != r)
+ {
+ buffer_deinit (r);
+ GNUNET_free (r);
+ }
+}
+
+/**
+ * Release all memory allocated for the variable-size fields in
+ * the parser specification.
+ *
+ * @param spec specification to free
+ * @param spec_len number of items in @a spec to look at
+ */
+static void
+release_data (struct TMH_PARSE_FieldSpecification *spec,
+ unsigned int spec_len)
+{
+ unsigned int i;
+
+ for (i=0; i < spec_len; i++)
+ {
+ switch (spec[i].command)
+ {
+ case TMH_PARSE_JNC_FIELD:
+ GNUNET_break (0);
+ return;
+ case TMH_PARSE_JNC_INDEX:
+ GNUNET_break (0);
+ return;
+ case TMH_PARSE_JNC_RET_DATA:
+ break;
+ case TMH_PARSE_JNC_RET_DATA_VAR:
+ if (NULL != spec[i].destination)
+ {
+ GNUNET_free (* (void**) spec[i].destination);
+ *(void**) spec[i].destination = NULL;
+ *spec[i].destination_size_out = 0;
+ }
+ break;
+ case TMH_PARSE_JNC_RET_TYPED_JSON:
+ {
+ json_t *json;
+
+ json = *(json_t **) spec[i].destination;
+ if (NULL != json)
+ {
+ json_decref (json);
+ *(json_t**) spec[i].destination = NULL;
+ }
+ }
+ break;
+ case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY:
+ {
+ struct TALER_DenominationPublicKey *pk;
+
+ pk = spec[i].destination;
+ if (NULL != pk->rsa_public_key)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free (pk->rsa_public_key);
+ pk->rsa_public_key = NULL;
+ }
+ }
+ break;
+ case TMH_PARSE_JNC_RET_RSA_SIGNATURE:
+ {
+ struct TALER_DenominationSignature *sig;
+
+ sig = spec[i].destination;
+ if (NULL != sig->rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free (sig->rsa_signature);
+ sig->rsa_signature = NULL;
+ }
+ }
+ break;
+ case TMH_PARSE_JNC_RET_AMOUNT:
+ memset (spec[i].destination,
+ 0,
+ sizeof (struct TALER_Amount));
+ break;
+ case TMH_PARSE_JNC_RET_TIME_ABSOLUTE:
+ break;
+ case TMH_PARSE_JNC_RET_UINT64:
+ break;
+ }
+ }
+}
+
+/**
* Process a POST request containing a JSON object. This function
* realizes an MHD POST processor that will (incrementally) process
* JSON data uploaded to the HTTP server. It will store the required
@@ -239,4 +347,679 @@ TMH_PARSE_post_json (struct MHD_Connection *connection,
return GNUNET_YES;
}
+
+/**
+ * Generate line in parser specification for 64-bit integer
+ * given as an integer in JSON.
+ *
+ * @param field name of the field
+ * @param[out] u64 integer to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_uint64 (const char *field,
+ uint64_t *u64)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, (void *) u64, sizeof (uint64_t), NULL, TMH_PARSE_JNC_RET_UINT64, 0 };
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for JSON object value.
+ *
+ * @param field name of the field
+ * @param[out] jsonp address of pointer to JSON to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_object (const char *field,
+ json_t **jsonp)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_OBJECT };
+ *jsonp = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for JSON array value.
+ *
+ * @param field name of the field
+ * @param[out] jsonp address of JSON pointer to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_array (const char *field,
+ json_t **jsonp)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_ARRAY };
+ *jsonp = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for an absolute time.
+ *
+ * @param field name of the field
+ * @param[out] atime time to initialize
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_time_abs (const char *field,
+ struct GNUNET_TIME_Absolute *atime)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, atime, sizeof(struct GNUNET_TIME_Absolute), NULL, TMH_PARSE_JNC_RET_TIME_ABSOLUTE, 0 };
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for RSA public key.
+ *
+ * @param field name of the field
+ * @param[out] pk key to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_denomination_public_key (const char *field,
+ struct TALER_DenominationPublicKey *pk)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, pk, 0, NULL, TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, 0 };
+ pk->rsa_public_key = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for RSA public key.
+ *
+ * @param field name of the field
+ * @param sig the signature to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_denomination_signature (const char *field,
+ struct TALER_DenominationSignature *sig)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, sig, 0, NULL, TMH_PARSE_JNC_RET_RSA_SIGNATURE, 0 };
+ sig->rsa_signature = NULL;
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for an amount.
+ *
+ * @param field name of the field
+ * @param amount a `struct TALER_Amount *` to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_amount (const char *field,
+ struct TALER_Amount *amount)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, amount, sizeof(struct TALER_Amount), NULL, TMH_PARSE_JNC_RET_AMOUNT, 0 };
+ memset (amount, 0, sizeof (struct TALER_Amount));
+ return ret;
+}
+
+
+/**
+ * Generate line in parser specification for variable-size value.
+ *
+ * @param field name of the field
+ * @param[out] ptr pointer to initialize
+ * @param[out] ptr_size size to initialize
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_variable (const char *field,
+ void **ptr,
+ size_t *ptr_size)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ { field, ptr, 0, ptr_size, TMH_PARSE_JNC_RET_DATA_VAR, 0 };
+ *ptr = NULL;
+ return ret;
+}
+
+/**
+ * Navigate through a JSON tree.
+ *
+ * Sends an error response if navigation is impossible (i.e.
+ * the JSON object is invalid)
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param ... navigation specification (see `enum TMH_PARSE_JsonNavigationCommand`)
+ * @return
+ * #GNUNET_YES if navigation was successful
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error (no response was generated,
+ * connection must be closed)
+ */
+int
+TMH_PARSE_navigate_json (struct MHD_Connection *connection,
+ const json_t *root,
+ ...)
+{
+ va_list argp;
+ int ret;
+ json_t *path; /* what's our current path from 'root'? */
+
+ path = json_array ();
+ va_start (argp, root);
+ ret = 2; /* just not any of the valid return values */
+ while (2 == ret)
+ {
+ enum TMH_PARSE_JsonNavigationCommand command
+ = va_arg (argp,
+ enum TMH_PARSE_JsonNavigationCommand);
+
+ switch (command)
+ {
+ case TMH_PARSE_JNC_FIELD:
+ {
+ const char *fname = va_arg(argp, const char *);
+
+ json_array_append_new (path,
+ json_string (fname));
+ root = json_object_get (root,
+ fname);
+ if (NULL == root)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s, s:O}",
+ "error", "missing field in JSON",
+ "field", fname,
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ break;
+
+ case TMH_PARSE_JNC_INDEX:
+ {
+ int fnum = va_arg(argp, int);
+
+ json_array_append_new (path,
+ json_integer (fnum));
+ root = json_array_get (root,
+ fnum);
+ if (NULL == root)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "missing index in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_DATA:
+ {
+ void *where = va_arg (argp, void *);
+ size_t len = va_arg (argp, size_t);
+ const char *str;
+ int res;
+
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "string expected",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ res = GNUNET_STRINGS_string_to_data (str, strlen (str),
+ where, len);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_DATA_VAR:
+ {
+ void **where = va_arg (argp, void **);
+ size_t *len = va_arg (argp, size_t *);
+ const char *str;
+ int res;
+
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_internal_error (connection,
+ "json_string_value() failed"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ *len = (strlen (str) * 5) / 8;
+ if (NULL != where)
+ {
+ *where = GNUNET_malloc (*len);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ *where,
+ *len);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (*where);
+ *where = NULL;
+ *len = 0;
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_TYPED_JSON:
+ {
+ int typ = va_arg (argp, int);
+ const json_t **r_json = va_arg (argp, const json_t **);
+
+ if ( (NULL == root) ||
+ ( (-1 != typ) &&
+ (json_typeof (root) != typ)) )
+ {
+ GNUNET_break_op (0);
+ *r_json = NULL;
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:i, s:i, s:O}",
+ "error", "wrong JSON field type",
+ "type_expected", typ,
+ "type_actual", json_typeof (root),
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ *r_json = root;
+ json_incref ((json_t *) root);
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_UINT64:
+ {
+ uint64_t *r_u64 = va_arg (argp, uint64_t *);
+
+ if (json_typeof (root) != JSON_INTEGER)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s, s:i, s:O}",
+ "error", "wrong JSON field type",
+ "type_expected", "integer",
+ "type_actual", json_typeof (root),
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ *r_u64 = (uint64_t) json_integer_value (root);
+ ret = GNUNET_OK;
+ }
+ break;
+
+ case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY:
+ {
+ struct TALER_DenominationPublicKey *where;
+ size_t len;
+ const char *str;
+ int res;
+ void *buf;
+
+ where = va_arg (argp,
+ struct TALER_DenominationPublicKey *);
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "string expected",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ len = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (len);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ len);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (buf);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ where->rsa_public_key = GNUNET_CRYPTO_rsa_public_key_decode (buf,
+ len);
+ GNUNET_free (buf);
+ if (NULL == where->rsa_public_key)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed RSA public key in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ case TMH_PARSE_JNC_RET_RSA_SIGNATURE:
+ {
+ struct TALER_DenominationSignature *where;
+ size_t len;
+ const char *str;
+ int res;
+ void *buf;
+
+ where = va_arg (argp,
+ struct TALER_DenominationSignature *);
+ str = json_string_value (root);
+ if (NULL == str)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "string expected",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ len = (strlen (str) * 5) / 8;
+ buf = GNUNET_malloc (len);
+ res = GNUNET_STRINGS_string_to_data (str,
+ strlen (str),
+ buf,
+ len);
+ if (GNUNET_OK != res)
+ {
+ GNUNET_break_op (0);
+ GNUNET_free (buf);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed binary data in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ where->rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (buf,
+ len);
+ GNUNET_free (buf);
+ if (NULL == where->rsa_signature)
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "malformed RSA signature in JSON",
+ "path", path))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ case TMH_PARSE_JNC_RET_AMOUNT:
+ {
+ struct TALER_Amount *where = va_arg (argp, void *);
+
+ if (GNUNET_OK !=
+ TALER_json_to_amount ((json_t *) root,
+ where))
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES !=
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O}",
+ "error", "Bad format",
+ "path", path))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ break;
+ }
+ if (0 != strcmp (where->currency,
+ TMH_mint_currency_string))
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES !=
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:O, s:s}",
+ "error", "Currency not supported",
+ "path", path,
+ "currency", where->currency))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ memset (where, 0, sizeof (struct TALER_Amount));
+ break;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ case TMH_PARSE_JNC_RET_TIME_ABSOLUTE:
+ {
+ struct GNUNET_TIME_Absolute *where = va_arg (argp, void *);
+
+ if (GNUNET_OK !=
+ TALER_json_to_abs ((json_t *) root,
+ where))
+ {
+ GNUNET_break_op (0);
+ ret = (MHD_YES !=
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s, s:O}",
+ "error", "Bad format",
+ "hint", "expected absolute time",
+ "path", path))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ break;
+ }
+ ret = GNUNET_OK;
+ break;
+ }
+
+ default:
+ GNUNET_break (0);
+ ret = (MHD_YES ==
+ TMH_RESPONSE_reply_internal_error (connection,
+ "unhandled value in switch"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ break;
+ }
+ }
+ va_end (argp);
+ json_decref (path);
+ return ret;
+}
+
+
+
+/**
+ * Parse JSON object into components based on the given field
+ * specification.
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param spec field specification for the parser
+ * @return
+ * #GNUNET_YES if navigation was successful (caller is responsible
+ * for freeing allocated variable-size data using
+ * #TMH_PARSE_release_data() when done)
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+int
+TMH_PARSE_json_data (struct MHD_Connection *connection,
+ const json_t *root,
+ struct TMH_PARSE_FieldSpecification *spec)
+{
+ unsigned int i;
+ int ret;
+
+ ret = GNUNET_YES;
+ for (i=0; NULL != spec[i].field_name; i++)
+ {
+ if (GNUNET_YES != ret)
+ break;
+ switch (spec[i].command)
+ {
+ case TMH_PARSE_JNC_FIELD:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ case TMH_PARSE_JNC_INDEX:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ case TMH_PARSE_JNC_RET_DATA:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_DATA,
+ spec[i].destination,
+ spec[i].destination_size_in);
+ break;
+ case TMH_PARSE_JNC_RET_DATA_VAR:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_DATA_VAR,
+ (void **) spec[i].destination,
+ spec[i].destination_size_out);
+ break;
+ case TMH_PARSE_JNC_RET_TYPED_JSON:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_TYPED_JSON,
+ spec[i].type,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_RSA_SIGNATURE:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_RSA_SIGNATURE,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_AMOUNT:
+ GNUNET_assert (sizeof (struct TALER_Amount) ==
+ spec[i].destination_size_in);
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_AMOUNT,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_TIME_ABSOLUTE:
+ GNUNET_assert (sizeof (struct GNUNET_TIME_Absolute) ==
+ spec[i].destination_size_in);
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_TIME_ABSOLUTE,
+ spec[i].destination);
+ break;
+ case TMH_PARSE_JNC_RET_UINT64:
+ GNUNET_assert (sizeof (uint64_t) ==
+ spec[i].destination_size_in);
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_UINT64,
+ spec[i].destination);
+ break;
+ }
+ }
+ if (GNUNET_YES != ret)
+ release_data (spec,
+ i - 1);
+ return ret;
+}
+
+
/* end of taler-mint-httpd_parsing.c */
diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php
index bcfeaeb9..52db0d22 100644
--- a/src/frontend/generate_taler_contract.php
+++ b/src/frontend/generate_taler_contract.php
@@ -95,6 +95,7 @@ $json = json_encode (array ('amount' => array ('value' => $value,
'delivery_date' => "Some Date Format",
'delivery_location' => 'LNAME1')),
'timestamp' => "Date(" . $now->getTimestamp() . ")",
+ 'expiry' => "Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")",
'refund_deadline' => "Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")",
'merchant' => array ('address' => 'LNAME2',
'name' => 'test merchant',
diff --git a/src/include/merchant.h b/src/include/merchant.h
index e207b4e3..1368c575 100644
--- a/src/include/merchant.h
+++ b/src/include/merchant.h
@@ -51,14 +51,10 @@ struct MERCHANT_Mint
char *hostname;
/**
- * The public key of the mint
+ * Flag which indicates whether some HTTP transfer between
+ * this merchant and the mint is still ongoing
*/
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
-
- /**
- * The port where the mint's service is running
- */
- uint16_t port;
+ int pending;
/**
* A connection to this mint
@@ -67,7 +63,6 @@ struct MERCHANT_Mint
};
-
/**
* Parses mints from the configuration.
*