summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2015-11-05 17:56:12 +0100
committerMarcello Stanisci <marcello.stanisci@inria.fr>2015-11-05 17:56:12 +0100
commitc1159ff547afffd135cde78c026dd35817d2f927 (patch)
tree9d0126a494b7018d4ad70e2c7d21de75ca6c3e8f
parent45cd6f3e16d8411add89588673cd58873293c2b0 (diff)
parente55860d9139702f39ce2b61df5bb0e33e1f30a08 (diff)
downloadmerchant-c1159ff547afffd135cde78c026dd35817d2f927.tar.gz
merchant-c1159ff547afffd135cde78c026dd35817d2f927.tar.bz2
merchant-c1159ff547afffd135cde78c026dd35817d2f927.zip
Merge branch 'fix4013'
Conflicts: src/frontend/index.html
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/backend-lib/Makefile.am8
-rw-r--r--src/backend-lib/merchant_api_contract.c60
-rw-r--r--src/backend-lib/merchant_api_deposit.c (renamed from src/backend-lib/taler-merchant-httpd_deposit.c)23
-rw-r--r--src/backend-lib/merchant_db.c141
-rw-r--r--src/backend-lib/merchant_db.h17
-rw-r--r--src/backend-lib/taler-merchant-httpd_contract.c121
-rw-r--r--src/backend-lib/taler_merchant_contract_lib.h58
-rw-r--r--src/backend/Makefile.am8
-rw-r--r--src/backend/melted/Makefile.am62
-rw-r--r--src/backend/melted/README9
-rwxr-xr-xsrc/backend/melted/copy.sh17
-rw-r--r--src/backend/melted/merchant.c173
-rw-r--r--src/backend/melted/merchant.conf18
-rw-r--r--src/backend/melted/merchant.h110
-rw-r--r--src/backend/melted/merchant_db.c354
-rw-r--r--src/backend/melted/merchant_db.h101
-rw-r--r--src/backend/melted/myconf.sh3
-rw-r--r--src/backend/melted/taler-merchant-httpd.c821
-rw-r--r--src/backend/merchant.c94
-rw-r--r--src/backend/merchant.conf12
-rw-r--r--src/backend/taler-merchant-httpd.c971
-rw-r--r--src/backend/taler-merchant-httpd_contract.c173
-rw-r--r--src/backend/taler-merchant-httpd_contract.h46
-rw-r--r--src/backend/taler-merchant-httpd_obsolete.c986
-rw-r--r--src/backend/taler-merchant-httpd_pay.c368
-rw-r--r--src/backend/taler-merchant-httpd_pay.h46
-rw-r--r--src/backend/taler-mint-httpd.h85
-rw-r--r--src/backend/taler-mint-httpd_mhd.c154
-rw-r--r--src/backend/taler-mint-httpd_mhd.h111
-rw-r--r--src/backend/taler-mint-httpd_parsing.c819
-rw-r--r--src/backend/taler-mint-httpd_parsing.h21
-rw-r--r--src/backend/taler-mint-httpd_responses.c38
-rw-r--r--src/backend/taler-mint-httpd_responses.h20
-rw-r--r--src/frontend/checkout.php6
-rw-r--r--src/frontend/generate_taler_contract.php35
-rw-r--r--src/frontend/index.html34
-rw-r--r--src/frontend/pay.php99
-rw-r--r--src/include/merchant.h101
-rw-r--r--src/tests/Makefile.am2
-rw-r--r--src/tests/deposit_permission.sample31
-rw-r--r--src/tests/deposit_permission_backend.sample36
-rw-r--r--src/tests/deposit_permission_edate_backend.sample37
-rw-r--r--src/tests/merchant.c173
-rw-r--r--src/tests/test_contract.c12
-rw-r--r--src/tests/test_contract_README10
-rw-r--r--src/tests/test_pay_README26
48 files changed, 3814 insertions, 2839 deletions
diff --git a/configure.ac b/configure.ac
index 55d1e196..67d84f11 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,7 +180,6 @@ AC_CHECK_FUNCS([strdup])
AC_CONFIG_FILES([Makefile
src/Makefile
src/include/Makefile
-src/tests/Makefile
src/backend/Makefile
src/backend-lib/Makefile])
AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index 9a5b5646..665be7d3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,2 +1,2 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/include
-SUBDIRS = include backend-lib backend tests
+SUBDIRS = include backend-lib backend
diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am
index e4cdb7a3..583c4d03 100644
--- a/src/backend-lib/Makefile.am
+++ b/src/backend-lib/Makefile.am
@@ -6,12 +6,14 @@ lib_LTLIBRARIES = \
include_HEADERS = \
taler_merchant_lib.h \
- taler_merchant_contract_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/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c
new file mode 100644
index 00000000..20b69cd3
--- /dev/null
+++ b/src/backend-lib/merchant_api_contract.c
@@ -0,0 +1,60 @@
+/*
+ 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/merchant_db.c
+ * @brief DB work related to contract management
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include <jansson.h>
+#include <taler/taler_signatures.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "merchant.h"
+#include "merchant_db.h"
+#include "taler_merchant_contract_lib.h"
+
+/**
+ * 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 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 salt)
+
+{
+
+ json_t *root;
+ json_t *j_salt;
+
+ j_salt = json_integer (salt);
+
+ 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,
+ "r", json_integer_value (j_salt))))
+ return NULL;
+
+ return root;
+}
diff --git a/src/backend-lib/taler-merchant-httpd_deposit.c b/src/backend-lib/merchant_api_deposit.c
index 41d4ca3d..a3cae20c 100644
--- a/src/backend-lib/taler-merchant-httpd_deposit.c
+++ b/src/backend-lib/merchant_api_deposit.c
@@ -1,3 +1,26 @@
+/*
+ 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/merchant_db.c
+ * @brief DB and crypto work related to deposit management
+ * @author Marcello Stanisci
+ */
+
+
#include "platform.h"
#include <jansson.h>
#include <taler/taler_signatures.h>
diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c
index 035a6646..415ff223 100644
--- a/src/backend-lib/merchant_db.c
+++ b/src/backend-lib/merchant_db.c
@@ -104,7 +104,12 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
"contract_id INT8 REFERENCES contracts(contract_id),"
"amount INT4 NOT NULL,"
"amount_fraction INT4 NOT NULL,"
- "coin_sig BYTEA NOT NULL);",
+ "coin_sig BYTEA NOT NULL);"
+ "CREATE %1$s TABLE IF NOT EXISTS deposits ("
+ "dep_perm TEXT NOT NULL,"
+ "transaction_id INT8 PRIMARY KEY,"
+ "pending INT4 NOT NULL,"
+ "mint_url TEXT NOT NULL);",
tmp_str);
ret = GNUNET_POSTGRES_exec (conn, sql);
(void) GNUNET_POSTGRES_exec (conn,
@@ -165,6 +170,28 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
EXITIF (NULL == (res = PQprepare
(conn,
+ "store_deposit_permission",
+ "INSERT INTO deposits"
+ "(dep_perm, transaction_id, pending, mint_url) "
+ "VALUES ($1, $2, $3, $4);", 4, NULL)));
+
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
+ PQclear (res);
+
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "update_deposit_permission",
+ "UPDATE deposits "
+ "SET pending = $1 "
+ "WHERE transaction_id = $2", 2, NULL)));
+
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
+ PQclear (res);
+
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
"get_contract_product",
"SELECT ("
"product"
@@ -218,6 +245,115 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp)
return GNUNET_SYSERR;
}
+/**
+ * Update the pending column of a deposit permission
+ * @param conn handle to DB
+ * @param transaction_id identification number of the deposit to
+ * update
+ * @param pending true if still pending, false otherwise (i.e. the
+ * mint did respond something)
+ * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
+ */
+uint32_t
+MERCHANT_DB_update_deposit_permission (PGconn *conn,
+ uint64_t transaction_id,
+ unsigned int pending)
+{
+ PGresult *res;
+ ExecStatusType status;
+
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_uint32 (&pending),
+ };
+
+ res = TALER_PQ_exec_prepared (conn, "update_deposit_permission", params);
+ status = PQresultStatus (res);
+
+ if (PGRES_COMMAND_OK != status)
+ {
+
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database commit failure: %s\n",
+ sqlstate);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+}
+
+/**
+ * Store a deposit permission in DB. To be mainly used if /deposit should
+ * be retried; also, the merchant can benefit from this information in case
+ * he needs to later investigate about some transaction_id.
+ * @param conn DB handle
+ * @param transaction_id identification number of this payment (which is the
+ * same id of the related contract)
+ * @param pending if true, this payment got to a persistent state
+ * @param which mint is to get this deposit permission
+ * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
+ */
+uint32_t
+MERCHANT_DB_store_deposit_permission (PGconn *conn,
+ const char *deposit_permission,
+ uint64_t transaction_id,
+ unsigned int pending,
+ const char *mint_url)
+{
+ PGresult *res;
+ ExecStatusType status;
+
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_fixed_size (deposit_permission, strlen (deposit_permission)),
+ TALER_PQ_query_param_uint64 (&transaction_id),
+ TALER_PQ_query_param_uint32 (&pending),
+ TALER_PQ_query_param_fixed_size (mint_url, strlen (mint_url)),
+ TALER_PQ_query_param_end
+ };
+ res = TALER_PQ_exec_prepared (conn, "store_deposit_permission", params);
+ status = PQresultStatus (res);
+
+ if (PGRES_COMMAND_OK != status)
+ {
+ const char *sqlstate;
+
+ sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE);
+ if (NULL == sqlstate)
+ {
+ /* very unexpected... */
+ GNUNET_break (0);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+ /* 40P01: deadlock, 40001: serialization failure */
+ if ( (0 == strcmp (sqlstate,
+ "23505")))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Inserting same transaction id twice\n");
+ /* Primary key violation */
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Database commit failure: %s\n",
+ sqlstate);
+ PQclear (res);
+ return GNUNET_SYSERR;
+ }
+
+ PQclear (res);
+ return GNUNET_OK;
+}
/**
* Insert a contract record into the database and if successfull
@@ -314,9 +450,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-lib/merchant_db.h b/src/backend-lib/merchant_db.h
index 540bddbd..c748002e 100644
--- a/src/backend-lib/merchant_db.h
+++ b/src/backend-lib/merchant_db.h
@@ -164,4 +164,21 @@ MERCHANT_DB_get_contract_handle (PGconn *conn,
const struct GNUNET_HashCode *h_contract,
struct MERCHANT_contract_handle *contract_handle);
+/**
+ * Store a deposit permission in DB. To be mainly used if /deposit should
+ * be retried; also, the merchant can benefit from this information in case
+ * he needs to later investigate about some transaction_id.
+ * @param conn DB handle
+ * @param transaction_id identification number of this payment (which is the
+ * same id of the related contract)
+ * @param pending if true, this payment got to a persistent state
+ * @param which mint is to get this deposit permission
+ * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors
+ */
+uint32_t
+MERCHANT_DB_store_deposit_permission (PGconn *conn,
+ const char *deposit_permission,
+ uint64_t transaction_id,
+ unsigned int pending,
+ const char *mint_url);
/* end of merchant-db.h */
diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c
deleted file mode 100644
index 5f6744aa..00000000
--- a/src/backend-lib/taler-merchant-httpd_contract.c
+++ /dev/null
@@ -1,121 +0,0 @@
-#include "platform.h"
-#include <jansson.h>
-#include <taler/taler_signatures.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant.h"
-#include "merchant_db.h"
-#include "taler_merchant_contract_lib.h"
-
-/**
- * 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
- * @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)
-
-{
-
- json_t *root;
- json_t *j_edate;
- json_t *j_nounce;
-
- j_nounce = json_integer (nounce);
- j_edate = TALER_json_from_abs (edate);
-
- if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:o, s:I}",
- "type", "SEPA",
- "IBAN", wire->iban,
- "name", wire->name,
- "bic", wire->bic,
- "edate", j_edate,
- "r", json_integer_value (j_nounce))))
- return NULL;
-
- return root;
-}
-
-
-
-/**
-* Take from the frontend the (partly) generated contract and fill
-* the missing values in it; for example, the SEPA details.
-* Moreover, it stores the contract in the DB.
-* @param j_contract parsed contract, originated by the frontend. It will be
-* hold the new values.
-* @param db_conn the handle to the local DB
-* @param contract where to store the (subset of the) contract to be (still) signed
-* @param timestamp contract's timestamp (shall be generated by the merchant)
-* @param expiry the time when the contract will expire
-* @param edate when the merchant wants to receive the wire transfer corresponding
-* to this deal (this value is also a field inside the 'wire' JSON format)
-* @param refund deadline until which the merchant can return the paid amount
-* @param nounce the nounce used to hash the wire details
-* @param a will be pointed to the (allocated) stringified 0-terminated contract
-* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the
-* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors.
-*/
-
-/**
-* TODO: inspect reference counting and, accordingly, free those json_t*(s)
-* still allocated */
-
-uint32_t
-MERCHANT_handle_contract (json_t *j_contract,
- PGconn *db_conn,
- struct Contract *contract,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute expiry,
- struct GNUNET_TIME_Absolute edate,
- struct GNUNET_TIME_Absolute refund,
- char **a,
- uint64_t nounce)
-{
- json_t *j_amount;
- json_int_t j_product_id;
- json_int_t j_trans_id;
- char *contract_str;
-
- struct TALER_Amount amount;
-
-
-
- /* Extracting values useful for DB work. Only gettable from the JSON
- since they are generated by the frontend */
- if (-1 == json_unpack (j_contract, "{s:o, s:I, s:{s:I}}",
- "amount", &j_amount,
- "trans_id", &j_trans_id,
- "details", "product_id",
- &j_product_id))
- {
- printf ("no unpacking\n");
- return GNUNET_SYSERR;
- }
-
- TALER_json_to_amount (j_amount, &amount);
- contract_str = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER);
- *a = contract_str;
- GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1,
- &contract->h_contract_details);
- contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
- contract->purpose.size = htonl (sizeof (struct Contract));
-
- // DB mgmt
- return MERCHANT_DB_contract_create (db_conn,
- timestamp,
- expiry,
- edate,
- refund,
- &amount,
- &contract->h_contract_details,
- (uint64_t) j_trans_id, // safe?
- contract_str,
- nounce,
- (uint64_t) j_product_id);
-}
diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h
index ca799ff2..3f95841f 100644
--- a/src/backend-lib/taler_merchant_contract_lib.h
+++ b/src/backend-lib/taler_merchant_contract_lib.h
@@ -1,65 +1,11 @@
/**
- * The contract sent by the merchant to the wallet
- */
-struct Contract
-{
- /**
- * Purpose header for the signature over contract
- */
- struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
-
- /**
- * Hash of the JSON contract in UTF-8 including 0-termination,
- * using JSON_COMPACT encoding with sorted fields.
- */
- struct GNUNET_HashCode h_contract_details;
-
-};
-
-/**
* 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);
-
-/**
-* Take from the frontend the (partly) generated contract and fill
-* the missing values in it; for example, the SEPA details.
-* Moreover, it stores the contract in the DB.
-* @param j_contract parsed contract, originated by the frontend. It will be
-* hold the new values.
-* @param db_conn the handle to the local DB
-* @param contract where to store the (subset of the) contract to be (still) signed
-* @param timestamp contract's timestamp (shall be generated by the merchant)
-* @param expiry the time when the contract will expire
-* @param edate when the merchant wants to receive the wire transfer corresponding
-* to this deal (this value is also a field inside the 'wire' JSON format)
-* @param refund deadline until which the merchant can return the paid amount
-* @param nounce the nounce used to hash the wire details
-* @param a will be pointed to the (allocated) stringified 0-terminated contract
-* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the
-* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors.
-*/
-
-/**
-* TODO: inspect reference counting and, accordingly, free those json_t*(s)
-* still allocated */
-
-uint32_t
-MERCHANT_handle_contract (json_t *j_contract,
- PGconn *db_conn,
- struct Contract *contract,
- struct GNUNET_TIME_Absolute timestamp,
- struct GNUNET_TIME_Absolute expiry,
- struct GNUNET_TIME_Absolute edate,
- struct GNUNET_TIME_Absolute refund,
- char **a,
- uint64_t nounce);
+ uint64_t salt);
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index 6d38a37e..d5b283b8 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -10,7 +10,13 @@ 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 \
- ../backend-lib/taler-merchant-httpd_contract.h
+ taler-mint-httpd.h \
+ taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \
+ taler-merchant-httpd_contract.c \
+ taler-merchant-httpd_contract.h \
+ taler-merchant-httpd_pay.c \
+ taler-merchant-httpd_pay.h
+
taler_merchant_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \
diff --git a/src/backend/melted/Makefile.am b/src/backend/melted/Makefile.am
deleted file mode 100644
index 66d784ec..00000000
--- a/src/backend/melted/Makefile.am
+++ /dev/null
@@ -1,62 +0,0 @@
-# This Makefile.am is in the public domain
-AM_CPPFLAGS = -I$(top_srcdir)/src/include
-
-if USE_COVERAGE
- AM_CFLAGS = --coverage -O0
- XLIB = -lgcov
-endif
-
-bin_PROGRAMS = \
- taler-mint-httpd \
- taler-merchant-httpd
-
-taler_mint_httpd_SOURCES = \
- taler-mint-httpd.c taler-mint-httpd.h \
- taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \
- taler-mint-httpd_db.c taler-mint-httpd_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_mhd.c taler-mint-httpd_mhd.h \
- taler-mint-httpd_admin.c taler-mint-httpd_admin.h \
- taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \
- taler-mint-httpd_withdraw.c taler-mint-httpd_withdraw.h \
- taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h
-taler_mint_httpd_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/mintdb/libtalermintdb.la \
- -lmicrohttpd \
- -ljansson \
- -lgnunetutil \
- -lpthread
-taler_merchant_httpd_SOURCES = \
- taler-merchant-httpd.c \
- merchant.c merchant.h \
- merchant_db.c merchant_db.h \
- taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \
- taler-mint-httpd_db.c taler-mint-httpd_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_mhd.c taler-mint-httpd_mhd.h \
- taler-mint-httpd_admin.c taler-mint-httpd_admin.h \
- taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \
- taler-mint-httpd_withdraw.c taler-mint-httpd_withdraw.h \
- taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h
-
-taler_merchant_httpd_LDADD = \
- $(LIBGCRYPT_LIBS) \
- $(top_builddir)/src/util/libtalerutil.la \
- $(top_builddir)/src/mintdb/libtalermintdb.la \
- -lmicrohttpd \
- -ljansson \
- -lgnunetutil \
- -ltalermint \
- -ltalerpq \
- -lgnunetpostgres \
- -lpq \
- -lpthread
-
-if HAVE_DEVELOPER
-taler_mint_httpd_SOURCES += \
- taler-mint-httpd_test.c taler-mint-httpd_test.h
-endif
diff --git a/src/backend/melted/README b/src/backend/melted/README
deleted file mode 100644
index 45c4fb80..00000000
--- a/src/backend/melted/README
+++ /dev/null
@@ -1,9 +0,0 @@
-Since the merchant's backend makes use of several routines that are native
-to the mint (in particular, those aimed to parse JSON object in HTTP POSTs),
-and since there is no possibility to export some of them in a shared library,
-then the files in this directory need to be moved *inside* the mint/src/mint
-directory in order to be compiled. Use 'copy.sh SRC DST' (STILL NOT TESTED)
-to copy the files in the desired location.
-
-Lastly, passing CFLAGS='-I/usr/include/postgresql' to ./configure seems to be
-mandatory, in order to compile the merchant http daemon.
diff --git a/src/backend/melted/copy.sh b/src/backend/melted/copy.sh
deleted file mode 100755
index 570bf545..00000000
--- a/src/backend/melted/copy.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-# copy all the backend relevant files from the first to the second
-# directory given in the arguments.
-# The intended use is to move back and forth those files
-# (since they are git'ed on the merchant's repository) as long as not
-# configure flag like --enable-merchant will be available from the mint
-
-# STILL NOT TESTED
-
-cp -t $2 \
-$1/Makefile.am \
-$1/merchant.c \
-$1/merchant_db.c \
-$1/merchant_db.h \
-$1/merchant.h \
-$1/taler-merchant-httpd.c \
-$1/merchant.conf
diff --git a/src/backend/melted/merchant.c b/src/backend/melted/merchant.c
deleted file mode 100644
index f124a030..00000000
--- a/src/backend/melted/merchant.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- 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/merchant.c
- * @brief Common utility functions for merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant.h"
-
-
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Parses mints from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of mints upon successful parsing. Will be NULL upon
- * error.
- * @return the number of mints in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_MintInfo **mints)
-{
- char *mints_str;
- char *token_nf; /* do no free (nf) */
- char *mint_section;
- char *mint_hostname;
- char *mint_pubkey_enc;
- struct MERCHANT_MintInfo *r_mints;
- struct MERCHANT_MintInfo mint;
- unsigned long long mint_port;
- unsigned int cnt;
- int OK;
-
- OK = 0;
- mints_str = NULL;
- 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,
- "merchant",
- "TRUSTED_MINTS",
- &mints_str));
- for (token_nf = strtok (mints_str, " ");
- NULL != token_nf;
- token_nf = strtok (NULL, " "))
- {
- GNUNET_assert (0 < GNUNET_asprintf (&mint_section,
- "mint-%s", token_nf));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (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;
- }
- OK = 1;
-
- EXITIF_exit:
- 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);
- return GNUNET_SYSERR;
- }
-
- *mints = r_mints;
- return cnt;
-}
-
-
-/**
- * Parse the SEPA information from the configuration. If any of the required
- * fileds is missing return NULL.
- *
- * @param cfg the configuration
- * @return Sepa details as a structure; NULL upon error
- */
-struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- struct MERCHANT_WIREFORMAT_Sepa *wf;
-
- wf = GNUNET_new (struct MERCHANT_WIREFORMAT_Sepa);
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "IBAN",
- &wf->iban));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "NAME",
- &wf->name));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "BIC",
- &wf->bic));
- return wf;
-
- EXITIF_exit:
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
- return NULL;
-
-}
-
-
-/**
- * Destroy and free resouces occupied by the wireformat structure
- *
- * @param wf the wireformat structure
- */
-void
-TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf)
-{
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
-}
-
-/* end of merchant.c */
diff --git a/src/backend/melted/merchant.conf b/src/backend/melted/merchant.conf
deleted file mode 100644
index 3b637448..00000000
--- a/src/backend/melted/merchant.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-[merchant]
-PORT = 9966
-HOSTNAME = localhost
-TRUSTED_MINTS = taler
-KEYFILE = merchant.priv
-
-[mint-taler]
-HOSTNAME = demo.taler.net
-PORT = 80
-PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0
-
-[merchant-db]
-CONFIG = postgres:///taler
-
-[wire-sepa]
-IBAN = DE67830654080004822650
-NAME = GNUNET E.V
-BIC = GENODEF1SRL
diff --git a/src/backend/melted/merchant.h b/src/backend/melted/merchant.h
deleted file mode 100644
index c66131ed..00000000
--- a/src/backend/melted/merchant.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- 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/merchant.c
- * @brief Common utility functions for merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#ifndef MERCHANT_H
-#define MERCHANT_H
-
-#include <gnunet/gnunet_common.h>
-#include <gnunet/gnunet_crypto_lib.h>
-
-/**
- * A mint
- */
-struct MERCHANT_MintInfo {
- /**
- * Hostname
- */
- char *hostname;
-
- /**
- * The public key of the mint
- */
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
-
- /**
- * The port where the mint's service is running
- */
- uint16_t port;
-
-};
-
-
-/**
- * Parses mints from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of mints upon successful parsing. Will be NULL upon
- * error.
- * @return the number of mints in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_MintInfo **mints);
-
-
-GNUNET_NETWORK_STRUCT_BEGIN
-struct MERCHANT_WIREFORMAT_Sepa
-{
- /**
- * The international bank account number
- */
- char *iban;
-
- /**
- * Name of the bank account holder
- */
- char *name;
-
- /**
- *The bank identification code
- */
- char *bic;
-
- /**
- * The latest payout date when the payment corresponding to this account has
- * to take place. A value of 0 indicates a transfer as soon as possible.
- */
- struct GNUNET_TIME_AbsoluteNBO payout;
-};
-GNUNET_NETWORK_STRUCT_END
-
-/**
- * Parse the SEPA information from the configuration. If any of the required
- * fileds is missing return NULL.
- *
- * @param cfg the configuration
- * @return Sepa details as a structure; NULL upon error
- */
-struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Destroy and free resouces occupied by the wireformat structure
- *
- * @param wf the wireformat structure
- */
-void
-TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf);
-
-#endif /* MERCHANT_H */
diff --git a/src/backend/melted/merchant_db.c b/src/backend/melted/merchant_db.c
deleted file mode 100644
index 274de25a..00000000
--- a/src/backend/melted/merchant_db.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- 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/merchant_db.c
- * @brief database helper functions used by the merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <taler/taler_util.h>
-#include <taler/taler_pq_lib.h>
-#include "merchant_db.h"
-
-
-#define PQSQL_strerror(kind, cmd, res) \
- GNUNET_log_from (kind, "merchant-db", \
- "SQL %s failed at %s:%u with error: %s", \
- cmd, __FILE__, __LINE__, PQresultErrorMessage (res));
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Connect to postgresql database
- *
- * @param cfg the configuration handle
- * @return connection to the postgresql database; NULL upon error
- */
-PGconn *
-MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- return GNUNET_POSTGRES_connect (cfg, "merchant-db");
-}
-
-
-/**
- * Disconnect from the database
- *
- * @param conn database handle to close
- */
-void
-MERCHANT_DB_disconnect (PGconn *conn)
-{
- PQfinish (conn);
-}
-
-
-/**
- * Initialize merchant tables
- *
- * @param conn the connection handle to postgres db.
- * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
- * contents are dropped when the @a conn is closed
- * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
- */
-int
-MERCHANT_DB_initialize (PGconn *conn, int tmp)
-{
- const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
- char *sql;
- PGresult *res;
- ExecStatusType status;
- int ret;
-
- res = NULL;
- (void) GNUNET_asprintf (&sql,
- "BEGIN TRANSACTION;"
- "CREATE %1$s TABLE IF NOT EXISTS contracts ("
- "contract_id INT8 PRIMARY KEY,"
- "amount INT8 NOT NULL,"
- "amount_fraction INT4 NOT NULL,"
- "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL,"
- "description TEXT NOT NULL,"
- "nounce INT8 NOT NULL,"
- "expiry INT8 NOT NULL,"
- "product INT8 NOT NULL);"
- "CREATE %1$s TABLE IF NOT EXISTS checkouts ("
- "coin_pub BYTEA PRIMARY KEY,"
- "contract_id INT8 REFERENCES contracts(contract_id),"
- "amount INT4 NOT NULL,"
- "amount_fraction INT4 NOT NULL,"
- "coin_sig BYTEA NOT NULL);",
- tmp_str);
- ret = GNUNET_POSTGRES_exec (conn, sql);
- (void) GNUNET_POSTGRES_exec (conn,
- (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK;");
- GNUNET_free (sql);
- if (GNUNET_OK != ret)
- return ret;
-
- while (NULL != (res = PQgetResult (conn)))
- {
- status = PQresultStatus (res);
- PQclear (res);
- }
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "contract_create",
- "INSERT INTO contracts"
- "(contract_id, amount, amount_fraction, amount_currency,"
- "description, nounce, expiry, product) VALUES"
- "($1, $2, $3, $4, $5, $6, $7, $8)",
- 8, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_contract_product",
- "SELECT ("
- "product"
- ") FROM contracts "
- "WHERE ("
- "contract_id=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "checkout_create",
- "INSERT INTO checkouts ("
- "coin_pub,"
- "contract_id,"
- "amount,"
- "amount_fraction,"
- "coin_sig"
- ") VALUES ("
- "$1, $2, $3, $4, $5"
- ")",
- 5, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
- PQclear (res);
-
- EXITIF (NULL == (res = PQprepare
- (conn,
- "get_checkout_product",
- "SELECT ("
- "product"
- ") FROM contracts "
- "WHERE "
- "contract_id IN ("
- "SELECT (contract_id) FROM checkouts "
- "WHERE coin_pub=$1"
- ")",
- 1, NULL)));
- EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res)));
- PQclear (res);
-
- return GNUNET_OK;
-
- EXITIF_exit:
- if (NULL != res)
- {
- PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res);
- PQclear (res);
- }
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Inserts a contract record into the database and if successfull returns the
- * serial number of the inserted row.
- *
- * @param conn the database connection
- * @param expiry the time when the contract will expire
- * @param amount the taler amount corresponding to the contract
- * @param c_id contract's id
- * @param desc descripition of the contract
- * @param nounce a random 64-bit nounce
- * @param product description to identify a product
- * @return GNUNET_OK on success, GNUNET_SYSERR upon error
- */
-
-uint32_t
-MERCHANT_DB_contract_create (PGconn *conn,
- const struct GNUNET_TIME_Absolute *expiry,
- const struct TALER_Amount *amount,
- uint64_t c_id,
- const char *desc,
- uint64_t nounce,
- uint64_t product)
-{
- PGresult *res;
- #if 0
- uint64_t expiry_ms_nbo;
- uint64_t value_nbo;
- uint32_t fraction_nbo;
- uint64_t nounce_nbo;
- #endif
- ExecStatusType status;
-
- #if 0
- /*
- NOTE: the conversion to nl(l) happens *inside* the query param helpers; since
- the policy imposes this format for storing values. */
- value_nbo = GNUNET_htonll (amount->value);
- fraction_nbo = GNUNET_htonll (amount->fraction);
- nounce_nbo = GNUNET_htonll (nounce);
- expiry_ms_nbo = GNUNET_htonll (expiry.abs_value_us);
- product = GNUNET_htonll (product);
- #endif
-
- /* ported. To be tested/compiled */
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_uint64 (&c_id),
- TALER_PQ_query_param_amount (amount),
- /* a *string* is being put in the following statement,
- though the API talks about a *blob*. Will this be liked by
- the DB ? */
- // the following inserts a string as a blob. Will Taler provide a param-from-string helper?
- TALER_PQ_query_param_fixed_size (desc, strlen(desc)),
- TALER_PQ_query_param_uint64 (&nounce),
- TALER_PQ_query_param_absolute_time (expiry),
- TALER_PQ_query_param_uint64 (&product),
- TALER_PQ_query_param_end
- };
-
- /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */
- res = TALER_PQ_exec_prepared (conn, "contract_create", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_COMMAND_OK != status);
- PQclear (res);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (res);
- return GNUNET_SYSERR;
-}
-
-long long
-MERCHANT_DB_get_contract_product (PGconn *conn,
- uint64_t contract_id)
-{
- PGresult *res;
- uint64_t product;
- ExecStatusType status;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_uint64 (&contract_id),
- TALER_PQ_query_param_end
- };
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("product", &product),
- TALER_PQ_result_spec_end
- };
-
- contract_id = GNUNET_htonll (contract_id);
- res = TALER_PQ_exec_prepared (conn, "get_contract_product", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_ntohll ((uint64_t) product);
-
- EXITIF_exit:
- PQclear (res);
- return -1;
-}
-
-unsigned int
-MERCHANT_DB_checkout_create (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
- uint64_t transaction_id,
- struct TALER_Amount *amount,
- struct GNUNET_CRYPTO_rsa_Signature *coin_sig)
-{
- PGresult *res;
- ExecStatusType status;
- uint32_t value_nbo;
- uint32_t fraction_nbo;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_rsa_public_key (coin_pub),
- TALER_PQ_query_param_uint64 (&transaction_id),
- TALER_PQ_query_param_uint32 (&value_nbo),
- TALER_PQ_query_param_uint32 (&fraction_nbo),
- TALER_PQ_query_param_rsa_signature (coin_sig),
- TALER_PQ_query_param_end
- };
-
- transaction_id = GNUNET_htonll (transaction_id);
- value_nbo = htonl (amount->value);
- fraction_nbo = htonl (amount->fraction);
- res = TALER_PQ_exec_prepared (conn, "checkout_create", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_COMMAND_OK != status);
- PQclear (res);
- return GNUNET_OK;
-
- EXITIF_exit:
- PQclear (res);
- return GNUNET_SYSERR;
-}
-
-
-long long
-MERCHANT_DB_get_checkout_product (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub)
-{
- PGresult *res;
- ExecStatusType status;
- uint64_t product;
- struct TALER_PQ_QueryParam params[] = {
- TALER_PQ_query_param_rsa_public_key (coin_pub),
- TALER_PQ_query_param_end
- };
- struct TALER_PQ_ResultSpec rs[] = {
- TALER_PQ_result_spec_uint64 ("product", &product),
- TALER_PQ_result_spec_end
- };
-
- product = -1;
- res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params);
- status = PQresultStatus (res);
- EXITIF (PGRES_TUPLES_OK != status);
- if (0 == PQntuples (res))
- {
- TALER_LOG_DEBUG ("Checkout not found for given coin");
- goto EXITIF_exit;
- }
- EXITIF (1 != PQntuples (res));
- EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0));
- PQclear (res);
- return GNUNET_ntohll ((uint64_t) product);
-
- EXITIF_exit:
- PQclear (res);
- return -1;
-}
-/* end of merchant-db.c */
diff --git a/src/backend/melted/merchant_db.h b/src/backend/melted/merchant_db.h
deleted file mode 100644
index a723b229..00000000
--- a/src/backend/melted/merchant_db.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- 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/merchant_db.h
- * @brief database helper functions used by the merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#ifndef MERCHANT_DB_H
-#define MERCHANT_DB_H
-
-#include <gnunet/gnunet_postgres_lib.h>
-#include <taler/taler_util.h>
-
-/**
- * Connect to postgresql database
- *
- * @param cfg the configuration handle
- * @return connection to the postgresql database; NULL upon error
- */
-PGconn *
-MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-
-/**
- * Disconnect from the database
- *
- * @param conn database handle to close
- */
-void
-MERCHANT_DB_disconnect (PGconn *conn);
-
-
-/**
- * Initialize merchant tables
- *
- * @param conn the connection handle to postgres db.
- * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their
- * contents are dropped when the @a conn is closed
- * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure
- */
-int
-MERCHANT_DB_initialize (PGconn *conn, int tmp);
-
-
-/**
- * Inserts a contract record into the database and if successfull returns the
- * serial number of the inserted row.
- *
- * @param conn the database connection
- * @param expiry the time when the contract will expire
- * @param amount the taler amount corresponding to the contract
- * @param c_id this contract's identification number
- * @param desc descripition of the contract
- * @param nounce a random 64-bit nounce
- * @param product description to identify a product
- * @return GNUNET_OK on success, GNUNET_SYSERR upon error
- */
-
-uint32_t
-MERCHANT_DB_contract_create (PGconn *conn,
- const struct GNUNET_TIME_Absolute *expiry,
- const struct TALER_Amount *amount,
- uint64_t c_id,
- const char *desc,
- uint64_t nounce,
- uint64_t product);
-
-long long
-MERCHANT_DB_get_contract_product (PGconn *conn,
- uint64_t contract_id);
-
-unsigned int
-MERCHANT_DB_checkout_create (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub,
- uint64_t transaction_id,
- struct TALER_Amount *amount,
- struct GNUNET_CRYPTO_rsa_Signature *coin_sig);
-
-
-long long
-MERCHANT_DB_get_checkout_product (PGconn *conn,
- struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub);
-
-#endif /* MERCHANT_DB_H */
-
-/* end of merchant-db.h */
diff --git a/src/backend/melted/myconf.sh b/src/backend/melted/myconf.sh
deleted file mode 100644
index 0b7d2594..00000000
--- a/src/backend/melted/myconf.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-./configure CFLAGS='-I/usr/include/postgresql'
diff --git a/src/backend/melted/taler-merchant-httpd.c b/src/backend/melted/taler-merchant-httpd.c
deleted file mode 100644
index 46379809..00000000
--- a/src/backend/melted/taler-merchant-httpd.c
+++ /dev/null
@@ -1,821 +0,0 @@
-/*
- 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 <taler/taler_json_lib.h>
-#include <taler/taler_mint_service.h>
-#include "taler-mint-httpd_parsing.h"
-#include "taler-mint-httpd_mhd.h"
-#include "taler-mint-httpd_admin.h"
-#include "taler-mint-httpd_deposit.h"
-#include "taler-mint-httpd_withdraw.h"
-#include "taler-mint-httpd_refresh.h"
-#include "taler-mint-httpd_keystate.h"
-#include "taler-mint-httpd_responses.h"
-#include "merchant.h"
-#include "merchant_db.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)
-
-/**
- * Macro to round microseconds to seconds in GNUNET_TIME_* structs.
- */
-#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000)
-
-/**
- * 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;
-
-/**
- * The MHD Daemon
- */
-static struct MHD_Daemon *mhd;
-
-/**
- * Connection handle to the our database
- */
-PGconn *db_conn;
-
-/**
- * Which currency is used by this mint?
- * (verbatim copy from mint's code, just to make this
- * merchant's source compile)
- */
-char *TMH_mint_currency_string;
-
-/* As above */
-struct TALER_MINTDB_Plugin *TMH_plugin;
-
-
-/**
- * As above, though the merchant does need some form of
- * configuration
- */
-struct GNUNET_CONFIGURATION_Handle *cfg;
-
-
-/**
- * As above
- */
-int TMH_test_mode;
-
-
-/**
- * As above
- */
-char *TMH_mint_directory;
-
-
-/**
- * As above
- */
-struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
-
-/**
- * As above
- */
-char *TMH_expected_wire_format;
-
-/**
- * Shutdown task identifier
- */
-static struct GNUNET_SCHEDULER_Task *shutdown_task;
-
-/**
- * Our wireformat
- */
-static struct MERCHANT_WIREFORMAT_Sepa *wire;
-
-/**
- * Hash of the wireformat
- */
-static struct GNUNET_HashCode h_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;
-
-GNUNET_NETWORK_STRUCT_BEGIN
-
-struct Contract
-{
- /**
- * The signature of the merchant for this contract
- */
- struct GNUNET_CRYPTO_EddsaSignature sig;
-
- /**
- * Purpose header for the signature over contract
- */
- struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
-
- /**
- * The transaction identifier
- */
- char m[13];
-
- /**
- * Expiry time
- */
- struct GNUNET_TIME_AbsoluteNBO t;
-
- /**
- * The invoice amount
- */
- struct TALER_AmountNBO amount;
-
- /**
- * The hash of the preferred wire format + nounce
- */
- struct GNUNET_HashCode h_wire;
-
- /**
- * The contract data
- */
- char a[];
-};
-
-GNUNET_NETWORK_STRUCT_END
-
-/**
- * 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;
-
-/**
-* 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) // this parameter was preceded by a '_' in its original file. Why?
-{
-
- unsigned int ret;
-
- *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg,
- MHD_RESPMEM_PERSISTENT);
- ret = 200;
- return ret;
-
-
-}
-
-/**
-* Generate the 'hello world' response
-* @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_hello (struct MHD_Response **resp) // this parameter was preceded by a '_' in its original file. Why?
-{
-
- 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;
-
-
-}
-
-/**
- * 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 ();
-}
-
-/**
-* 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_404[]="\
-<!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_500[]="\
-<!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 (400 <= status);
- resp = NULL;
- switch (status)
- {
- case 404:
- PAGE(404);
- break;
- default:
- status = 500;
- case 500:
- PAGE(500);
- }
-#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;
-}
-
-
-/**
-* Generate the hash containing the information (= a nounce + merchant's IBAN) to
-* redeem money from mint in a subsequent /deposit operation
-* @param nounce the nounce
-* @return the hash to be included in the contract's blob
-*
-*/
-
-static struct GNUNET_HashCode
-hash_wireformat (uint64_t nounce)
-{
- struct GNUNET_HashContext *hc;
- struct GNUNET_HashCode hash;
-
- hc = GNUNET_CRYPTO_hash_context_start ();
- GNUNET_CRYPTO_hash_context_read (hc, wire->iban, strlen (wire->iban));
- GNUNET_CRYPTO_hash_context_read (hc, wire->name, strlen (wire->name));
- GNUNET_CRYPTO_hash_context_read (hc, wire->bic, strlen (wire->bic));
- nounce = GNUNET_htonll (nounce);
- GNUNET_CRYPTO_hash_context_read (hc, &nounce, sizeof (nounce));
- GNUNET_CRYPTO_hash_context_finish (hc, &hash);
- return hash;
-}
-
-
-
-/*
-* Make a binary blob representing a contract, store it into the DB, sign it
-* and return a pointer to it.
-* @param a 0-terminated string representing the description of this
-* @param c_id contract id provided by the frontend
-* purchase (it should contain a human readable description of the good
-* in question)
-* @param product some product numerical id. Its indended use is to link the
-* good, or service being sold to some entry in the DB managed by the frontend
-* @price the cost of this good or service
-* @return pointer to the allocated contract (which has a field, 'sig', holding
-* its own signature), NULL upon errors
-*/
-
-struct Contract *
-generate_and_store_contract (const char *a, uint64_t c_id, uint64_t product, struct TALER_Amount *price)
-{
-
- struct Contract *contract;
- struct GNUNET_TIME_Absolute expiry;
- uint64_t nounce;
- uint64_t contract_id_nbo;
-
- expiry = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
- GNUNET_TIME_UNIT_DAYS);
- ROUND_TO_SECS (expiry, abs_value_us);
- nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
- EXITIF (GNUNET_SYSERR == MERCHANT_DB_contract_create (db_conn,
- &expiry,
- price,
- c_id,
- a,
- nounce,
- product));
- contract_id_nbo = GNUNET_htonll ((uint64_t) c_id);
- contract = GNUNET_malloc (sizeof (struct Contract) + strlen (a) + 1);
- contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
- contract->purpose.size = htonl (sizeof (struct Contract)
- - offsetof (struct Contract, purpose)
- + strlen (a) + 1);
- GNUNET_STRINGS_data_to_string (&contract_id_nbo, sizeof (contract_id_nbo),
- contract->m, sizeof (contract->m));
- contract->t = GNUNET_TIME_absolute_hton (expiry);
- (void) strcpy (contract->a, a);
- contract->h_wire = hash_wireformat (nounce);
- TALER_amount_hton (&contract->amount, price);
- GNUNET_CRYPTO_eddsa_sign (privkey, &contract->purpose, &contract->sig);
- return contract;
-
- /* legacy from old merchant */
- EXITIF_exit:
- if (NULL != contract)
- {
- GNUNET_free (contract);
- }
- return NULL;
-}
-
-
-/**
- * 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)
-{
-
- unsigned int status;
- unsigned int no_destroy;
- json_int_t prod_id;
- json_int_t contract_id;
- struct Contract *contract;
- struct MHD_Response *resp;
- struct TALER_Amount price;
- struct GNUNET_CRYPTO_EddsaPublicKey pub;
- json_t *json_price;
- json_t *root;
- json_t *contract_enc;
- json_t *sig_enc;
- json_t *eddsa_pub_enc;
- json_t *response;
-
- int res;
- const char *desc;
-
- #define URL_HELLO "/hello"
- #define URL_CONTRACT "/contract"
- 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); //TBD
- else
- GNUNET_break (0);
- }
-
- // to be called by the frontend passing all the product's information
- // which are relevant for the contract's generation
- if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
- {
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- status = generate_message (&resp, "Sorry, only POST is allowed");
- else
- res = TMH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
-
- if (GNUNET_SYSERR == res)
- {
- status = generate_message (&resp, "unable to parse JSON root");
- return MHD_NO;
-
- }
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- /* The frontend should supply a JSON in the follwoing format:
-
- {
-
- "desc" : string human-readable describing this deal,
- "product" : uint64-like integer referring to the product in some
- DB adminstered by the frontend,
- "cid" : uint64-like integer, this contract's id
- "price" : a 'struct TALER_Amount' in the Taler compliant JSON format }
-
- */
-
- #if 0
- /*res = json_typeof (root); <- seg fault*/
- json_int_t id;
- const char *desc_test;
- const char *cur_test;
- json_t *id_json;
- json_t *desc_json;
- json_t *cur_json;
- id_json = json_object_get (root, "product");
- desc_json = json_object_get (root, "desc");
- id = json_integer_value (id_json);
- desc_test = json_string_value (desc_json);
- json_price = json_object_get (root, "price");
- json_typeof (json_price);
- cur_json = json_object_get (json_price, "currency");
- cur_test = json_string_value (cur_json);
- printf ("id is %" JSON_INTEGER_FORMAT "\n", id);
- printf ("desc is %s\n", desc_test);
- TALER_json_to_amount (json_price, &price);
- printf ("cur_test is %s\n", price.currency);
- json_error_t err;
- if (res = json_unpack_ex (root, &err, JSON_VALIDATE_ONLY, "{s:s, s:I, s:o}",
- "desc",
- //&desc,
- "product",
- //&prod_id,
- "price"//,
- //json_price
- ))
- #else
- if ((res = json_unpack (root, "{s:s, s:I, s:I, s:o}",
- "desc",
- &desc,
- "product",
- &prod_id,
- "cid",
- &contract_id,
- "price",
- &json_price
- )))
- #endif
-
-
- /* still not possible to return a taler-compliant error message
- since this JSON format is not among the taler officials ones */
- {
- status = generate_message (&resp, "unable to parse /contract JSON\n");
- }
- else
- {
-
- if (GNUNET_OK != TALER_json_to_amount (json_price, &price))
- {/* still not possible to return a taler-compliant error message
- since this JSON format is not among the taler officials ones */
- status = generate_message (&resp, "unable to parse `price' field in /contract JSON");}
- else
- {
- /* Let's generate this contract! */
- if (NULL == (contract = generate_and_store_contract (desc, contract_id, prod_id, &price)))
- {
- /* status equals 500, so the user will get a "Internal server error" */
- //failure_resp (connection, status);
- status = generate_message (&resp, "unable to generate and store this contract");
- //return MHD_YES;
-
- }
- else
- {
- json_decref (root);
- json_decref (json_price);
-
- printf ("Good contract\n");
- /* the contract is good and stored in DB, produce now JSON to return.
- As of now, the format is {"contract" : base32contract,
- "sig" : contractSignature,
- "eddsa_pub" : keyToCheckSignatureAgainst
- }
-
- */
-
- sig_enc = TALER_json_from_eddsa_sig (&contract->purpose, &contract->sig);
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
- eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub));
- /* cutting of the signature at the beginning */
- contract_enc = TALER_json_from_data (&contract->purpose, sizeof (*contract)
- - offsetof (struct Contract, purpose)
- + strlen (desc) +1);
- response = json_pack ("{s:o, s:o, s:o}", "contract", contract_enc, "sig", sig_enc,
- "eddsa_pub", eddsa_pub_enc);
- TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK);
- printf ("Got something?\n");
- return MHD_YES;
- /* FRONTIER - CODE ABOVE STILL NOT TESTED */
- }
- }
- }
- }
-
- if (NULL != resp)
- {
- EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
- 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;
- }
-
-
-}
-
-
-
-/**
- * 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
- */
-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?) public key from
- the conf file */
- 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
- */
-static void
-run (void *cls, char *const *args, const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *config)
-{
-
- char *keyfile;
- unsigned int nmints;
- unsigned int cnt;
- struct MERCHANT_MintInfo *mint_infos;
- void *keys_mgmt_cls;
-
- 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_NO));
- 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 ()));
- EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES)));
-
- for (cnt = 0; cnt < nmints; cnt++)
- {
- struct Mint *mint;
-
- 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));
- }
-
-
- 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 ? */
- GNUNET_CRYPTO_hash (wire, sizeof (*wire), &h_wire);
- 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/merchant.c b/src/backend/merchant.c
index f124a030..02b37fb8 100644
--- a/src/backend/merchant.c
+++ b/src/backend/merchant.c
@@ -42,16 +42,14 @@
*/
int
TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_MintInfo **mints)
+ struct MERCHANT_Mint **mints)
{
char *mints_str;
char *token_nf; /* do no free (nf) */
char *mint_section;
char *mint_hostname;
- char *mint_pubkey_enc;
- struct MERCHANT_MintInfo *r_mints;
- struct MERCHANT_MintInfo mint;
- unsigned long long mint_port;
+ struct MERCHANT_Mint *r_mints;
+ struct MERCHANT_Mint mint;
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);
@@ -118,6 +97,73 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
return cnt;
}
+/**
+ * Parses auditors from the configuration.
+ *
+ * @param cfg the configuration
+ * @param mints the array of auditors upon successful parsing. Will be NULL upon
+ * error.
+ * @return the number of auditors in the above array; GNUNET_SYSERR upon error in
+ * parsing.
+ */
+int
+TALER_MERCHANT_parse_auditors (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct MERCHANT_Auditor **auditors)
+{
+ char *auditors_str;
+ char *token_nf; /* do no free (nf) */
+ char *auditor_section;
+ char *auditor_name;
+ struct MERCHANT_Auditor *r_auditors;
+ struct MERCHANT_Auditor auditor;
+ unsigned int cnt;
+ int OK;
+
+ OK = 0;
+ auditors_str = NULL;
+ token_nf = NULL;
+ auditor_section = NULL;
+ auditor_name = NULL;
+ r_auditors = NULL;
+ cnt = 0;
+ EXITIF (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "merchant",
+ "AUDITORS",
+ &auditors_str));
+ for (token_nf = strtok (auditors_str, " ");
+ NULL != token_nf;
+ token_nf = strtok (NULL, " "))
+ {
+ GNUNET_assert (0 < GNUNET_asprintf (&auditor_section,
+ "auditor-%s", token_nf));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ auditor_section,
+ "NAME",
+ &auditor_name));
+ auditor.name = auditor_name;
+ GNUNET_array_append (r_auditors, cnt, auditor);
+ auditor_name = NULL;
+ GNUNET_free (auditor_section);
+ auditor_section = NULL;
+ }
+ OK = 1;
+
+ EXITIF_exit:
+ GNUNET_free_non_null (auditors_str);
+ GNUNET_free_non_null (auditor_section);
+ GNUNET_free_non_null (auditor_name);
+ if (!OK)
+ {
+ GNUNET_free_non_null (r_auditors);
+ return GNUNET_SYSERR;
+ }
+
+ *auditors = r_auditors;
+ return cnt;
+}
+
/**
* Parse the SEPA information from the configuration. If any of the required
diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf
index 3b637448..68aeacfe 100644
--- a/src/backend/merchant.conf
+++ b/src/backend/merchant.conf
@@ -3,14 +3,18 @@ PORT = 9966
HOSTNAME = localhost
TRUSTED_MINTS = taler
KEYFILE = merchant.priv
+CURRENCY = KUDOS
+EDATE = 3 week
+AUDITORS = france
[mint-taler]
-HOSTNAME = demo.taler.net
-PORT = 80
-PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0
+HOSTNAME = localmint
+
+[auditor-france]
+NAME = Charles De Gaulle
[merchant-db]
-CONFIG = postgres:///taler
+CONFIG = postgres:///talerdemo
[wire-sepa]
IBAN = DE67830654080004822650
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 2b489040..ae9a4613 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -16,7 +16,8 @@
/**
* @file merchant/backend/taler-merchant-httpd.c
- * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @brief HTTP serving layer intended to perform crypto-work and
+ * communication with the mint
* @author Marcello Stanisci
*/
@@ -25,24 +26,16 @@
#include <jansson.h>
#include <gnunet/gnunet_util_lib.h>
#include <curl/curl.h>
-#include <taler/taler_json_lib.h>
+#include <taler/taler_util.h>
#include <taler/taler_mint_service.h>
#include "taler-mint-httpd_parsing.h"
#include "taler-mint-httpd_responses.h"
#include "merchant_db.h"
#include "merchant.h"
#include "taler_merchant_lib.h"
-
-extern struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
-
-/**
- * Shorthand for exit jumps.
- */
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
+#include "taler-mint-httpd_mhd.h"
+#include "taler-merchant-httpd_contract.h"
+#include "taler-merchant-httpd_pay.h"
/**
* Our hostname
@@ -65,270 +58,80 @@ struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
char *keyfile;
/**
- * The MHD Daemon
- */
-static struct MHD_Daemon *mhd;
-
-/**
- * Connection handle to the our database
- */
-PGconn *db_conn;
-
-/**
- * merchant's conf handle
+ * Mint context
*/
-struct GNUNET_CONFIGURATION_Handle *cfg;
+static struct TALER_MINT_Context *mctx;
/**
- * Shutdown task identifier
+ * This value tells the mint by which date this merchant would like
+ * to receive the funds for a deposited payment
*/
-static struct GNUNET_SCHEDULER_Task *shutdown_task;
+struct GNUNET_TIME_Relative edate_delay;
/**
- * Our wireformat
+ * To make 'TMH_PARSE_navigate_json ()' compile
*/
-static struct MERCHANT_WIREFORMAT_Sepa *wire;
+char *TMH_mint_currency_string;
/**
- * Should we do a dry run where temporary tables are used for storing the data.
+ * Trusted mints
*/
-static int dry;
+struct MERCHANT_Mint *mints;
/**
- * Global return code
+ * Active auditors
*/
-static int result;
+struct MERCHANT_Auditor *auditors;
/**
- * Mint context
- */
-static struct TALER_MINT_Context *mctx;
-
-/**
- * Context information of the mints we trust
+ * Shutdown task identifier
*/
-struct Mint
-{
- /**
- * Public key of this mint
- */
- struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
-
- /**
- * Connection handle to this mint
- */
- struct TALER_MINT_Handle *conn;
-};
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
/**
- * Hashmap to store the mint context information
+ * Context "poller" identifier
*/
-static struct GNUNET_CONTAINER_MultiPeerMap *mints_map;
+static struct GNUNET_SCHEDULER_Task *poller_task;
/**
- * Hashmap (with 'big entries') to make a mint's base URL
- * to point to some mint-describing structure
+ * Our wireformat
*/
-static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap;
-
-
+struct MERCHANT_WIREFORMAT_Sepa *wire;
/**
- * Mints' URL,port,key triples
+ * Salt used to hash the wire object
*/
-struct MERCHANT_MintInfo *mint_infos;
+long long salt;
/**
* The number of accepted mints
*/
unsigned int nmints;
-struct Mint_Response
-{
- char *ptr;
- size_t size;
-
-};
-
-
/**
- * Generate the 'hello world' response
- * @param connection a MHD connection
- * @param resp where to store the response for the calling function.
- * Note that in its original implementation this parameter was preceeded
- * by a '_'. Still not clear why.
- * @return HTTP status code reflecting the operation outcome
- *
+ * The number of active auditors
*/
-static unsigned int
-generate_hello (struct MHD_Response **resp)
-{
-
- const char *hello = "Hello customer\n";
- unsigned int ret;
-
- *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello,
- MHD_RESPMEM_PERSISTENT);
- ret = 200;
- return ret;
-}
+unsigned int nauditors;
/**
- * Return the given message to the other end of connection
- * @msg (0-terminated) message to show
- * @param connection a MHD connection
- * @param resp where to store the response for the calling function
- * @return HTTP status code reflecting the operation outcome
- *
+ * Should we do a dry run where temporary tables are used for storing the data.
*/
-static unsigned int
-generate_message (struct MHD_Response **resp, const char *msg)
-{
-
- unsigned int ret;
-
- *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg,
- MHD_RESPMEM_MUST_FREE);
- ret = 200;
- return ret;
-}
+static int dry;
/**
- * Callback to pass to curl used to store a HTTP response
- * in a custom memory location.
- * See http://curl.haxx.se/libcurl/c/getinmemory.html for a
- * detailed example
- *
- * @param contents the data gotten so far from the server
- * @param size symbolic (arbitrarily chosen by libcurl) unit
- * of bytes
- * @param nmemb factor to multiply by @a size to get the real
- * size of @a contents
- * @param userdata a pointer to a memory location which remains
- * the same across all the calls to this callback (i.e. it has
- * to be grown at each invocation of this callback)
- * @return number of written bytes
- * See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
- * for official documentation
- *
+ * Global return code
*/
-size_t
-get_response_in_memory (char *contents,
- size_t size,
- size_t nmemb,
- void *userdata)
-{
- struct Mint_Response *mr;
- size_t realsize;
-
- realsize = size * nmemb;
- mr = userdata;
- mr->ptr = realloc (mr->ptr, mr->size + realsize + 1);
-
- if (mr->ptr == NULL) {
- printf ("Out of memory, could not get in memory mint's"
- "response");
- return 0;
- }
- memcpy(&(mr->ptr[mr->size]), contents, realsize);
- mr->size += realsize;
- mr->ptr[mr->size] = 0;
-
- return realsize;
-
-}
+static int result;
-#ifdef PANIC_MGMT
/**
- * Callback for catching serious error conditions from MHD.
- *
- * @param cls user specified value
- * @param file where the error occured
- * @param line where the error occured
- * @param reason error detail, may be NULL
+ * Connection handle to the our database
*/
-static void
-mhd_panic_cb (void *cls,
- const char *file,
- unsigned int line,
- const char *reason)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "MHD panicked at %s:%u: %s",
- file, line, reason);
- result = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
-}
-#endif
+PGconn *db_conn;
/**
- * Manage a non 200 HTTP status. I.e. it shows a 'failure' page to
- * the client
- * @param connection the channel thorugh which send the message
- * @status the HTTP status to examine
- * @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error
+ * The MHD Daemon
*/
-static int
-failure_resp (struct MHD_Connection *connection, unsigned int status)
-{
- static char page_MHD_HTTP_NOT_FOUND[]="\
-<!DOCTYPE html> \
-<html><title>Resource not found</title><body><center> \
-<h3>The resource you are looking for is not found.</h3> \
-</center></body></html>";
- static char page_MHD_HTTP_BAD_REQUEST[]="\
-<!DOCTYPE html> \
-<html><title>Bad request</title><body><center> \
-<h3>Malformed POSTed JSON.</h3> \
-</center></body></html>";
-static char page_MHD_HTTP_METHOD_NOT_ALLOWED[]="\
-<!DOCTYPE html> \
-<html><title>Method NOT allowed</title><body><center> \
-<h3>ONLY POSTs are allowed.</h3> \
-</center></body></html>";
- static char page_MHD_HTTP_INTERNAL_SERVER_ERROR[]="\
-<!DOCTYPE html> <html><title>Internal Server Error</title><body><center> \
-<h3>The server experienced an internal error and hence cannot serve your \
-request</h3></center></body></html>";
- struct MHD_Response *resp;
- char *page;
- size_t size;
-#define PAGE(number) \
- do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0)
-
- GNUNET_assert (MHD_HTTP_BAD_REQUEST <= status);
- resp = NULL;
- switch (status)
- {
- case MHD_HTTP_NOT_FOUND :
- PAGE(MHD_HTTP_NOT_FOUND);
- break;
- case MHD_HTTP_BAD_REQUEST:
- PAGE(MHD_HTTP_BAD_REQUEST);
- break;
- case MHD_HTTP_METHOD_NOT_ALLOWED:
- PAGE(MHD_HTTP_METHOD_NOT_ALLOWED);
- break;
- default:
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- case MHD_HTTP_INTERNAL_SERVER_ERROR:
- PAGE(MHD_HTTP_INTERNAL_SERVER_ERROR);
- }
-#undef PAGE
-
- EXITIF (NULL == (resp = MHD_create_response_from_buffer (size,
- page,
- MHD_RESPMEM_PERSISTENT)));
- EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
- MHD_destroy_response (resp);
- return GNUNET_OK;
-
- EXITIF_exit:
- if (NULL != resp)
- MHD_destroy_response (resp);
- return GNUNET_SYSERR;
-}
-
+static struct MHD_Daemon *mhd;
/**
* A client has requested the given url using the given method
@@ -377,423 +180,116 @@ url_handler (void *cls,
const char *version,
const char *upload_data,
size_t *upload_data_size,
- void **connection_cls)
+ void **con_cls)
{
-
- /*printf ("%s\n", url);*/
- unsigned long status;
- unsigned int no_destroy;
- struct GNUNET_CRYPTO_EddsaSignature c_sig;
- struct GNUNET_CRYPTO_EddsaSignature deposit_confirm_sig;
- struct GNUNET_CRYPTO_EddsaPublicKey pub;
- #ifdef OBSOLETE
- struct ContractNBO contract;
- #else
- struct Contract contract;
- #endif
- struct MHD_Response *resp;
- json_t *root;
- json_t *j_sig_enc;
- json_t *j_h_contract;
- json_t *j_tmp;
- json_t *eddsa_pub_enc;
- json_t *response;
- json_t *j_mints;
- json_t *j_mint;
- json_t *j_wire;
- int cnt; /* loop counter */
- char *deposit_body;
- json_t *j_contract_add;
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Absolute expiry;
- struct GNUNET_TIME_Absolute edate;
- struct GNUNET_TIME_Absolute refund;
- struct GNUNET_HashCode h_json_wire;
- json_t *j_h_json_wire;
- struct curl_slist *slist;
- char *contract_str;
- struct GNUNET_HashCode h_contract_str;
- struct MERCHANT_contract_handle ch;
- struct TALER_MintPublicKeyP mint_pub;
- uint64_t nounce;
-
- CURL *curl;
- CURLcode curl_res;
-
- uint32_t res = GNUNET_SYSERR;
-
- #define URL_HELLO "/hello"
- #define URL_CONTRACT "/contract"
- #define URL_PAY "/pay"
- no_destroy = 0;
- resp = NULL;
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
-
- if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO)))
- {
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- status = generate_hello (&resp);
- else
- {
- status = MHD_HTTP_METHOD_NOT_ALLOWED;
- }
- }
-
- if (0 == strncasecmp (url, URL_PAY, sizeof (URL_PAY)))
- {
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- {
- status = MHD_HTTP_METHOD_NOT_ALLOWED;
- goto end;
-
- }
- else
- res = TMH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- {
- status = MHD_HTTP_BAD_REQUEST;
- goto end;
- }
-
- /* the POST's body has to be further fetched */
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- /* Firstly, check if the wallet is paying against an approved
- mint */
- json_t *j_chosen_mint;
- j_chosen_mint = json_object_get (root, "mint");
- struct GNUNET_HashCode hash_key;
- char *chosen_mint;
-
- chosen_mint = json_string_value (j_chosen_mint);
- GNUNET_CRYPTO_hash (chosen_mint, strlen (chosen_mint), &hash_key);
-
- if (NULL ==
- GNUNET_CONTAINER_multihashmap_get (mints_hashmap, &hash_key))
- {
- printf ("Untrusted mint\n");
- status = MHD_HTTP_FORBIDDEN;
- goto end;
-
- }
-
- /* NOTE: from now on, the mint's base URL is pointed by 'chosen_mint' */
-
- /* The merchant will only add its 'wire' object to the JSON
- it got from the wallet */
-
- /* Get this dep. perm.'s H_contract */
-
- if (NULL == (j_h_contract = json_object_get (root, "H_contract")))
+ static struct TMH_RequestHandler handlers[] =
{
- printf ("H_contract field missing\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
- TALER_json_to_data (j_h_contract, &h_contract_str, sizeof (struct GNUNET_HashCode));
+ /* Landing page, tell humans to go away. */
+ { "/", MHD_HTTP_METHOD_GET, "text/plain",
+ "Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.\n", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_OK },
- nounce = 0;
- edate.abs_value_us = 0;
+ /* Further test page */
+ { "/hello", MHD_HTTP_METHOD_GET, "text/plain",
+ "Hello, Customer.\n", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_OK },
- if (GNUNET_SYSERR ==
- MERCHANT_DB_get_contract_values (db_conn,
- &h_contract_str,
- &nounce,
- &edate))
- {
- printf ("not existing contract\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+ { "/contract", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &MH_handler_contract, MHD_HTTP_OK },
- /* Reproducing the wire object */
- if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
- nounce,
- edate)))
+ { "/contract", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
- {
- printf ("wire object not reproduced\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+ { "/pay", MHD_HTTP_METHOD_POST, "application/json",
+ NULL, 0,
+ &MH_handler_pay, MHD_HTTP_OK },
- if (-1 == json_object_set (root, "wire", j_wire))
- {
- printf ("depperm not augmented\n");
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
+ { "/pay", NULL, "text/plain",
+ "Only POST is allowed", 0,
+ &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
- /* POST to mint's "/deposit" */
- curl = curl_easy_init ();
- struct Mint_Response mr;
- mr.ptr = malloc(1);
- mr.size = 0;
+ {NULL, NULL, NULL, NULL, 0, 0 }
+ };
- if (curl)
+ static struct TMH_RequestHandler h404 =
{
-
- char deposit_url[strlen (chosen_mint) + strlen ("http://") + strlen ("/deposit") + 1];
- sprintf (deposit_url, "http://%s/deposit", chosen_mint);
- slist = curl_slist_append (slist, "Content-type: application/json");
- curl_easy_setopt (curl, CURLOPT_HTTPHEADER, slist);
-
- curl_easy_setopt (curl, CURLOPT_URL, deposit_url);
- curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, get_response_in_memory);
- curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) &mr);
-
- /* NOTE: hopefully, this string won't need any URL-encoding, since as for the
- Jansson specs, any space and-or newline are not in place using JSON_COMPACT
- flag */
- deposit_body = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER);
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, deposit_body);
-
- curl_res = curl_easy_perform (curl);
-
- curl_slist_free_all(slist);
- if(curl_res != CURLE_OK)
- {
- printf ("deposit not sent\n");
- goto end;
- }
- else
- printf ("\ndeposit request issued\n");
-
- curl_easy_cleanup(curl);
-
- curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status);
-
- /* If the request was successful, the deposit confirmation
- has to be verified*/
-
- if (MHD_HTTP_OK != result)
- /* Jump here to error mgmt, see issue #3800 (TODO) */
-
- if (GNUNET_SYSERR ==
- MERCHANT_DB_get_contract_handle (db_conn, &h_contract_str, &ch))
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
-
- root = json_loads (mr.ptr, 0, NULL);
- j_tmp = json_object_get (root, "sig");
- TALER_json_to_data (j_tmp,
- &deposit_confirm_sig,
- sizeof (struct GNUNET_CRYPTO_EddsaSignature));
- j_tmp = json_object_get (root, "pub");
-
- TALER_json_to_data (j_tmp,
- &mint_pub.eddsa_pub,
- sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
-
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
-
-
- /* The following check could be done once the merchant is able to
- retrieve the deposit fee for each coin it gets a payment with.
- That happens because the signature made on a deposit confirmation
- uses, among its values of interest, the subtraction of the deposit
- fee from the used coin. That fee is never communicated by the wallet
- to the merchant, so the only way for the merchant to get this
- information is to fetch all the mint's keys, namely it needs to
- invoke "/keys", and store what gotten in its DB */
-
- #ifdef DENOMMGMT
- if (GNUNET_NO ==
- MERCHANT_verify_confirmation (&h_contract_str,
- &h_json_wire,
- ch.timestamp,
- ch.refund_deadline,
- ch.contract_id,
- amount_minus_fee, /* MISSING */
- coin_pub, /* MISSING */
- &pub,
- &deposit_confirm_sig,
- &mint_pub))
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
-
- }
-
- #endif
-
- /* Place here the successful message to issue to the frontend */
- status = MHD_HTTP_OK;
- generate_message (&resp, "fullfillment page here");
- GNUNET_free (mr.ptr);
-
- }
+ "", NULL, "text/html",
+ "<html><title>404: not found</title></html>", 0,
+ &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
+ };
+ /* Compiler complains about non returning a value in a non-void
+ declared function: the FIX is to return what the handler for
+ a particular URL returns */
- }
+ struct TMH_RequestHandler *rh;
+ unsigned int i;
- /*
- * To be called by the frontend passing the contract with some "holes"
- * which will be completed, stored in DB, signed, and returned
- *
- */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Handling request for URL '%s'\n",
+ url);
- if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
+ for (i=0;NULL != handlers[i].url;i++)
{
- if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
- {
- status = MHD_HTTP_METHOD_NOT_ALLOWED;
- goto end;
-
- }
- else
- res = TMH_PARSE_post_json (connection,
- connection_cls,
- upload_data,
- upload_data_size,
- &root);
- if (GNUNET_SYSERR == res)
- {
- status = MHD_HTTP_BAD_REQUEST;
- goto end;
- }
-
-
- /* the POST's body has to be fetched furthermore */
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- j_mints = json_array ();
- for (cnt = 0; cnt < nmints; cnt++)
- {
- j_mint = json_pack ("{s:s}",
- mint_infos[cnt].hostname,
- GNUNET_CRYPTO_eddsa_public_key_to_string (&mint_infos[cnt].pubkey));
- json_array_append_new (j_mints, j_mint);
-
- }
-
- /* timestamp */
- now = GNUNET_TIME_absolute_get ();
- /* expiry */
- expiry = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
- /* edate, note: this value must be generated now (and not when the
- wallet sends back a deposit permission because the hashed 'wire' object,
- which carries this values in it, has to be included in the signed bundle
- by the wallet) */
- edate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
- refund = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
-
- TALER_round_abs_time (&now);
- TALER_round_abs_time (&expiry);
- TALER_round_abs_time (&edate);
- TALER_round_abs_time (&refund);
-
- /* getting the SEPA-aware JSON */
- /* nounce for hashing the wire object */
- nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
-
- /* get wire object */
-
- if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
- nounce,
- edate)))
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
-
- /* hash wire objcet */
- if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire))
- goto end;
-
- j_h_json_wire = TALER_json_from_data ((void *) &h_json_wire, sizeof (struct GNUNET_HashCode));
-
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
- eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub));
-
- if (NULL == (j_contract_add = json_pack ("{s:s, s:s, s:o, s:o, s:o}",
- "merchant_pub", json_string_value (eddsa_pub_enc),
- "H_wire", json_string_value (j_h_json_wire),
- "timestamp", TALER_json_from_abs (now),
- "refund", TALER_json_from_abs (refund),
- "mints", j_mints)))
- {
- printf ("BAD contract enhancement\n");
- goto end;
- }
-
- /* melt to what received from the wallet */
- if (-1 == json_object_update (root, j_contract_add))
- {
- printf ("depperm response not built\n");
- goto end;
- }
-
- res = MERCHANT_handle_contract (root,
- db_conn,
- &contract,
- now,
- expiry,
- edate,
- refund,
- &contract_str,
- nounce);
- if (GNUNET_SYSERR == res)
- {
- status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- goto end;
- }
- if (GNUNET_NO == res)
- {
- status = MHD_HTTP_METHOD_NOT_ACCEPTABLE;
- goto end;
- }
-
- GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig);
- GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str);
-
- j_sig_enc = TALER_json_from_eddsa_sig (&contract.purpose, &c_sig);
-
- response = json_pack ("{s:o, s:o, s:o}",
- "contract", root,
- "sig", j_sig_enc,
- "h_contract",
- TALER_json_from_data ((void *) &h_contract_str, sizeof (struct GNUNET_HashCode)));
-
- GNUNET_free (contract_str);
-
- TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK);
- return MHD_YES;
-
+ rh = &handlers[i];
+ if ( (0 == strcasecmp (url,
+ rh->url)) &&
+ ( (NULL == rh->method) ||
+ (0 == strcasecmp (method,
+ rh->method)) ) )
+ return rh->handler (rh,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
}
+ return TMH_MHD_handler_static_response (&h404,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size);
- end:
+}
- if (NULL != resp)
+/**
+ * Function called with information about who is auditing
+ * a particular mint and what key the mint is using.
+ *
+ * @param cls closure, will be 'struct MERCHANT_Mint' so that
+ * when this function gets called, it will change the flag 'pending'
+ * to 'false'. Note: 'keys' is automatically saved inside the mint's
+ * handle, which is contained inside 'struct MERCHANT_Mint', when
+ * this callback is called. Thus, once 'pending' turns 'false',
+ * it is safe to call 'TALER_MINT_get_keys()' on the mint's handle,
+ * in order to get the "good" keys.
+ *
+ * @param keys information about the various keys used
+ * by the mint
+ */
+static void
+keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
+{
+ /* HOT UPDATE: the merchants need the denomination keys!
+ Because it wants to (firstly) verify the deposit confirmation
+ sent by the mint, and the signed blob depends (among the
+ other things) on the coin's deposit fee. That information
+ is never communicated by the wallet to the merchant.
+ Again, the merchant needs it because it wants to verify that
+ the wallet didn't exceede the limit imposed by the merchant
+ on the total deposit fee for a purchase */
+
+ if (NULL != keys)
{
- EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
- return MHD_YES;
- if (!no_destroy)
- MHD_destroy_response (resp);
+ ((struct MERCHANT_Mint *) cls)->pending = 0;
}
else
- {
-
- EXITIF (GNUNET_OK != failure_resp (connection, status));
- return MHD_YES;
-
- }
-
- EXITIF_exit:
- result = GNUNET_SYSERR;
- GNUNET_SCHEDULER_shutdown ();
- return MHD_NO;
+ printf ("no keys gotten\n");
}
+
/**
* Shutdown task (magically invoked when the application is being
* quit)
@@ -804,6 +300,19 @@ url_handler (void *cls,
static void
do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
+ unsigned int cnt;
+
+ for (cnt = 0; cnt < nmints; cnt++)
+ {
+ if (NULL != mints[cnt].conn)
+ TALER_MINT_disconnect (mints[cnt].conn);
+
+ }
+ if (NULL != poller_task)
+ {
+ GNUNET_SCHEDULER_cancel (poller_task);
+ poller_task = NULL;
+ }
if (NULL != mhd)
{
@@ -816,37 +325,100 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
MERCHANT_DB_disconnect (db_conn);
db_conn = NULL;
}
- if (keyfile != NULL)
+ if (NULL != keyfile)
GNUNET_free (privkey);
-
-
}
/**
- * Function called with information about who is auditing
- * a particular mint and what key the mint is using.
+ * Task that runs the context's event loop with the GNUnet scheduler.
*
- * @param cls closure
- * @param keys information about the various keys used
- * by the mint
+ * @param cls unused
+ * @param tc scheduler context (unused)
*/
static void
-keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
+context_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
{
- /* which kind of mint's keys a merchant should need? Sign
- keys? It has already the mint's master key from the conf file */
- return;
-
+ long timeout;
+ int max_fd;
+ fd_set read_fd_set;
+ fd_set write_fd_set;
+ fd_set except_fd_set;
+ struct GNUNET_NETWORK_FDSet *rs;
+ struct GNUNET_NETWORK_FDSet *ws;
+ struct GNUNET_TIME_Relative delay;
+
+ poller_task = NULL;
+ TALER_MINT_perform (mctx);
+ max_fd = -1;
+ timeout = -1;
+ FD_ZERO (&read_fd_set);
+ FD_ZERO (&write_fd_set);
+ FD_ZERO (&except_fd_set);
+ TALER_MINT_get_select_info (mctx,
+ &read_fd_set,
+ &write_fd_set,
+ &except_fd_set,
+ &max_fd,
+ &timeout);
+ if (timeout >= 0)
+ delay =
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ timeout);
+ else
+ delay = GNUNET_TIME_UNIT_FOREVER_REL;
+ rs = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (rs,
+ &read_fd_set,
+ max_fd + 1);
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy_native (ws,
+ &write_fd_set,
+ max_fd + 1);
+ poller_task =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ delay,
+ rs,
+ ws,
+ &context_task,
+ cls);
+ GNUNET_NETWORK_fdset_destroy (rs);
+ GNUNET_NETWORK_fdset_destroy (ws);
}
-
+/**
+ * Function called whenever MHD is done with a request. If the
+ * request was a POST, we may have stored a `struct Buffer *` in the
+ * @a con_cls that might still need to be cleaned up. Call the
+ * respective function to free the memory.
+ *
+ * @param cls client-defined closure
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ * the #MHD_AccessHandlerCallback
+ * @param toe reason for request termination
+ * @see #MHD_OPTION_NOTIFY_COMPLETED
+ * @ingroup request
+ */
+static void
+handle_mhd_completion_callback (void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ if (NULL == *con_cls)
+ return;
+ TMH_PARSE_post_cleanup_callback (*con_cls);
+ *con_cls = NULL;
+}
/**
* Main function that will be run by the scheduler.
*
* @param cls closure
* @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfgfile name of the configuration file used (for saving, can be
+ * NULL!)
* @param config configuration
*/
void
@@ -855,88 +427,87 @@ run (void *cls, char *const *args, const char *cfgfile,
{
unsigned int cnt;
- void *keys_mgmt_cls;
-
- keys_mgmt_cls = NULL;
- mint_infos = NULL;
+ mints = NULL;
keyfile = NULL;
result = GNUNET_SYSERR;
- shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
- &do_shutdown, NULL);
- EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config,
- &mint_infos)));
- EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config)));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config,
- "merchant",
- "KEYFILE",
- &keyfile));
- EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
- EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config)));
- EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_YES));
+ shutdown_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown,
+ NULL);
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "merchant launched\n");
+
+ EXITIF (GNUNET_SYSERR ==
+ (nmints =
+ TALER_MERCHANT_parse_mints (config,
+ &mints)));
+ EXITIF (GNUNET_SYSERR ==
+ (nauditors =
+ TALER_MERCHANT_parse_auditors (config,
+ &auditors)));
+ EXITIF (NULL ==
+ (wire =
+ TALER_MERCHANT_parse_wireformat_sepa (config)));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "KEYFILE",
+ &keyfile));
+ EXITIF (NULL ==
+ (privkey =
+ GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ EXITIF (NULL ==
+ (db_conn = MERCHANT_DB_connect (config)));
+ EXITIF (GNUNET_OK !=
+ MERCHANT_DB_initialize (db_conn, GNUNET_YES));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_number (config,
"merchant",
- "port",
+ "PORT",
&port));
EXITIF (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_string (config,
"merchant",
- "hostname",
+ "HOSTNAME",
&hostname));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "merchant",
+ "CURRENCY",
+ &TMH_mint_currency_string));
+
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_time (config,
+ "merchant",
+ "EDATE",
+ &edate_delay));
+
+ salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
+ UINT64_MAX);
EXITIF (NULL == (mctx = TALER_MINT_init ()));
- /* Still not used */
- EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES)));
- /* Used when the wallet points out which mint it want to deal with.
- That indication is made through the mint's base URL, which will be
- the hash-key for this table */
- EXITIF (NULL == (mints_hashmap = GNUNET_CONTAINER_multihashmap_create (nmints, GNUNET_NO)));
-
for (cnt = 0; cnt < nmints; cnt++)
{
- struct Mint *mint;
- struct GNUNET_HashCode mint_key;
-
- mint = GNUNET_new (struct Mint);
- mint->pubkey = mint_infos[cnt].pubkey;
- /* port this to the new API */
- mint->conn = TALER_MINT_connect (mctx,
- mint_infos[cnt].hostname,
- &keys_mgmt_cb,
- keys_mgmt_cls); /*<- safe?segfault friendly?*/
-
- /* NOTE: the keys mgmt callback should roughly do what the following lines do */
- EXITIF (NULL == mint->conn);
-
- EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put
- (mints_map,
- (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey,
- mint,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
-
- /* 1 create hash key
- 2 create big entry
- 3 put
- */
- GNUNET_CRYPTO_hash (mint_infos[cnt].hostname,
- strlen (mint_infos[cnt].hostname),
- &mint_key);
- GNUNET_CONTAINER_multihashmap_put (mints_hashmap,
- &mint_key,
- &mint_infos[cnt],
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ mints[cnt].pending = 1;
+ mints[cnt].conn = TALER_MINT_connect (mctx,
+ mints[cnt].hostname,
+ &keys_mgmt_cb,
+ &mints[cnt]);
+ EXITIF (NULL == mints[cnt].conn);
+ poller_task =
+ GNUNET_SCHEDULER_add_now (&context_task, mctx);
}
mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
port,
NULL, NULL,
&url_handler, NULL,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, NULL,
+
MHD_OPTION_END);
EXITIF (NULL == mhd);
-
- /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that
- mandatory ? */
result = GNUNET_OK;
EXITIF_exit:
@@ -969,7 +540,7 @@ main (int argc, char *const *argv)
if (GNUNET_OK !=
GNUNET_PROGRAM_run (argc, argv,
- "taler-merchant-serve",
+ "taler-merchant-http",
"Serve merchant's HTTP interface",
options, &run, NULL))
return 3;
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
new file mode 100644
index 00000000..e4b556a4
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -0,0 +1,173 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file merchant/backend/taler-merchant-httpd.c
+ * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <curl/curl.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_amount_lib.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_mint_service.h>
+#include "taler-mint-httpd.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_Mint *mints;
+extern struct MERCHANT_Auditor *auditors;
+extern struct GNUNET_CRYPTO_EddsaPrivateKey privkey;
+extern const struct MERCHANT_WIREFORMAT_Sepa *wire;
+extern unsigned int nmints;
+extern unsigned int nauditors;
+extern PGconn *db_conn;
+extern long long salt;
+
+/**
+ * Manage a contract request. In practical terms, it adds the fields 'mints',
+ * 'merchant_pub', and 'H_wire' to the contract 'proposition' gotten from the
+ * frontend. Finally, it adds (outside of the contract) a signature of the
+ * (hashed stringification) of this contract and the hashed stringification
+ * of this contract to the final bundle sent back to the frontend.
+ * @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)
+{
+ json_t *root;
+ json_t *trusted_mints;
+ json_t *j_auditors;
+ json_t *auditor;
+ json_t *mint;
+ json_t *j_wire;
+ const struct TALER_MINT_Keys *keys;
+ int res;
+ int cnt;
+ struct GNUNET_HashCode h_wire;
+ struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
+ struct MERCHANT_Contract contract;
+ char *contract_str;
+ struct GNUNET_CRYPTO_EddsaSignature contract_sig;
+
+ 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;
+
+ /* Generate preferred mint(s) array. */
+
+ 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);
+ }
+ }
+ j_auditors = json_array ();
+ for (cnt = 0; cnt < nauditors; cnt++)
+ {
+ auditor = json_pack ("{s:s}",
+ "name", auditors[cnt].name);
+ json_array_append_new (j_auditors, auditor);
+ }
+
+ /**
+ * Return badly if no mints are trusted (or no call to /keys has still
+ * returned the expected data). 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;
+
+ /**
+ * Hard error, no action can be taken by a wallet
+ */
+ if (!json_array_size (j_auditors))
+ return MHD_NO;
+
+ json_object_set_new (root, "mints", trusted_mints);
+ json_object_set_new (root, "auditors", j_auditors);
+
+ 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)));
+
+ /* Sign */
+ contract_str = json_dumps (root, JSON_COMPACT | JSON_SORT_KEYS);
+ GNUNET_CRYPTO_hash (contract_str, strlen (contract_str), &contract.h_contract);
+ contract.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT);
+ contract.purpose.size = htonl (sizeof (contract));
+ GNUNET_CRYPTO_eddsa_sign (&privkey, &contract.purpose, &contract_sig);
+
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_OK,
+ "{s:o, s:o, s:o}",
+ "contract", root,
+ "sig", TALER_json_from_data
+ (&contract_sig, sizeof (contract_sig)),
+ "H_contract", TALER_json_from_data
+ (&contract.h_contract,
+ sizeof (contract.h_contract)));
+
+}
diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h
new file mode 100644
index 00000000..5e72c514
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_contract.h
@@ -0,0 +1,46 @@
+/*
+ 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_contract.h
+ * @brief headers for /contract handler
+ * @author Marcello Stanisci
+ */
+
+#ifndef TALER_MINT_HTTPD_CONTRACT_H
+#define TALER_MINT_HTTPD_CONTRACT_H
+#include <microhttpd.h>
+#include "taler-mint-httpd.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);
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_obsolete.c b/src/backend/taler-merchant-httpd_obsolete.c
new file mode 100644
index 00000000..a96dad59
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_obsolete.c
@@ -0,0 +1,986 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file merchant/backend/taler-merchant-httpd.c
+ * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <curl/curl.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_mint_service.h>
+#include "taler-mint-httpd_parsing.h"
+#include "taler-mint-httpd_responses.h"
+#include "merchant_db.h"
+#include "merchant.h"
+#include "taler_merchant_lib.h"
+
+extern struct MERCHANT_WIREFORMAT_Sepa *
+TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+/**
+ * Shorthand for exit jumps.
+ */
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+/**
+ * Our hostname
+ */
+static char *hostname;
+
+/**
+ * The port we are running on
+ */
+static long long unsigned port;
+
+/**
+ * Merchant's private key
+ */
+struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
+
+/**
+ * File holding the merchant's private key
+ */
+char *keyfile;
+
+/**
+ * The MHD Daemon
+ */
+static struct MHD_Daemon *mhd;
+
+/**
+ * Connection handle to the our database
+ */
+PGconn *db_conn;
+
+/**
+ * merchant's conf handle
+ */
+struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Shutdown task identifier
+ */
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+/**
+ * Our wireformat
+ */
+static struct MERCHANT_WIREFORMAT_Sepa *wire;
+
+/**
+ * Should we do a dry run where temporary tables are used for storing the data.
+ */
+static int dry;
+
+/**
+ * Global return code
+ */
+static int result;
+
+/**
+ * Mint context
+ */
+static struct TALER_MINT_Context *mctx;
+
+/**
+ * Context information of the mints we trust
+ */
+struct Mint
+{
+ /**
+ * Public key of this mint
+ */
+ struct GNUNET_CRYPTO_EddsaPublicKey pubkey;
+
+ /**
+ * Connection handle to this mint
+ */
+ struct TALER_MINT_Handle *conn;
+};
+
+/**
+ * Hashmap to store the mint context information
+ */
+static struct GNUNET_CONTAINER_MultiPeerMap *mints_map;
+
+/**
+ * Hashmap (with 'big entries') to make a mint's base URL
+ * to point to some mint-describing structure
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap;
+
+/**
+ * Mints' URL,port,key triples
+ */
+struct MERCHANT_MintInfo *mint_infos;
+
+/**
+ * The number of accepted mints
+ */
+unsigned int nmints;
+
+struct Mint_Response
+{
+ char *ptr;
+ size_t size;
+
+};
+
+
+/**
+ * Generate the 'hello world' response
+ * @param connection a MHD connection
+ * @param resp where to store the response for the calling function.
+ * Note that in its original implementation this parameter was preceeded
+ * by a '_'. Still not clear why.
+ * @return HTTP status code reflecting the operation outcome
+ *
+ */
+static unsigned int
+generate_hello (struct MHD_Response **resp)
+{
+
+ const char *hello = "Hello customer\n";
+ unsigned int ret;
+
+ *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello,
+ MHD_RESPMEM_PERSISTENT);
+ ret = 200;
+ return ret;
+}
+
+/**
+ * Return the given message to the other end of connection
+ * @msg (0-terminated) message to show
+ * @param connection a MHD connection
+ * @param resp where to store the response for the calling function
+ * @return HTTP status code reflecting the operation outcome
+ *
+ */
+static unsigned int
+generate_message (struct MHD_Response **resp, const char *msg)
+{
+
+ unsigned int ret;
+
+ *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg,
+ MHD_RESPMEM_MUST_FREE);
+ ret = 200;
+ return ret;
+}
+
+/**
+ * Callback to pass to curl used to store a HTTP response
+ * in a custom memory location.
+ * See http://curl.haxx.se/libcurl/c/getinmemory.html for a
+ * detailed example
+ *
+ * @param contents the data gotten so far from the server
+ * @param size symbolic (arbitrarily chosen by libcurl) unit
+ * of bytes
+ * @param nmemb factor to multiply by @a size to get the real
+ * size of @a contents
+ * @param userdata a pointer to a memory location which remains
+ * the same across all the calls to this callback (i.e. it has
+ * to be grown at each invocation of this callback)
+ * @return number of written bytes
+ * See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html
+ * for official documentation
+ *
+ */
+size_t
+get_response_in_memory (char *contents,
+ size_t size,
+ size_t nmemb,
+ void *userdata)
+{
+ struct Mint_Response *mr;
+ size_t realsize;
+
+ realsize = size * nmemb;
+ mr = userdata;
+ mr->ptr = realloc (mr->ptr, mr->size + realsize + 1);
+
+ if (mr->ptr == NULL) {
+ printf ("Out of memory, could not get in memory mint's"
+ "response");
+ return 0;
+ }
+ memcpy(&(mr->ptr[mr->size]), contents, realsize);
+ mr->size += realsize;
+ mr->ptr[mr->size] = 0;
+
+ return realsize;
+
+}
+
+#ifdef PANIC_MGMT
+/**
+ * Callback for catching serious error conditions from MHD.
+ *
+ * @param cls user specified value
+ * @param file where the error occured
+ * @param line where the error occured
+ * @param reason error detail, may be NULL
+ */
+static void
+mhd_panic_cb (void *cls,
+ const char *file,
+ unsigned int line,
+ const char *reason)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "MHD panicked at %s:%u: %s",
+ file, line, reason);
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+}
+#endif
+
+/**
+ * Manage a non 200 HTTP status. I.e. it shows a 'failure' page to
+ * the client
+ * @param connection the channel thorugh which send the message
+ * @status the HTTP status to examine
+ * @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error
+ */
+static int
+failure_resp (struct MHD_Connection *connection, unsigned int status)
+{
+ static char page_MHD_HTTP_NOT_FOUND[]="\
+<!DOCTYPE html> \
+<html><title>Resource not found</title><body><center> \
+<h3>The resource you are looking for is not found.</h3> \
+</center></body></html>";
+ static char page_MHD_HTTP_BAD_REQUEST[]="\
+<!DOCTYPE html> \
+<html><title>Bad request</title><body><center> \
+<h3>Malformed POSTed JSON.</h3> \
+</center></body></html>";
+static char page_MHD_HTTP_METHOD_NOT_ALLOWED[]="\
+<!DOCTYPE html> \
+<html><title>Method NOT allowed</title><body><center> \
+<h3>ONLY POSTs are allowed.</h3> \
+</center></body></html>";
+ static char page_MHD_HTTP_INTERNAL_SERVER_ERROR[]="\
+<!DOCTYPE html> <html><title>Internal Server Error</title><body><center> \
+<h3>The server experienced an internal error and hence cannot serve your \
+request</h3></center></body></html>";
+ struct MHD_Response *resp;
+ char *page;
+ size_t size;
+#define PAGE(number) \
+ do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0)
+
+ GNUNET_assert (MHD_HTTP_BAD_REQUEST <= status);
+ resp = NULL;
+ switch (status)
+ {
+ case MHD_HTTP_NOT_FOUND :
+ PAGE(MHD_HTTP_NOT_FOUND);
+ break;
+ case MHD_HTTP_BAD_REQUEST:
+ PAGE(MHD_HTTP_BAD_REQUEST);
+ break;
+ case MHD_HTTP_METHOD_NOT_ALLOWED:
+ PAGE(MHD_HTTP_METHOD_NOT_ALLOWED);
+ break;
+ default:
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ case MHD_HTTP_INTERNAL_SERVER_ERROR:
+ PAGE(MHD_HTTP_INTERNAL_SERVER_ERROR);
+ }
+#undef PAGE
+
+ EXITIF (NULL == (resp = MHD_create_response_from_buffer (size,
+ page,
+ MHD_RESPMEM_PERSISTENT)));
+ EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
+ MHD_destroy_response (resp);
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ if (NULL != resp)
+ MHD_destroy_response (resp);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * A client has requested the given url using the given method
+ * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
+ * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback
+ * must call MHD callbacks to provide content to give back to the
+ * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
+ * #MHD_HTTP_NOT_FOUND, etc.).
+ *
+ * @param cls argument given together with the function
+ * pointer when the handler was registered with MHD
+ * @param url the requested url
+ * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
+ * #MHD_HTTP_METHOD_PUT, etc.)
+ * @param version the HTTP version string (i.e.
+ * #MHD_HTTP_VERSION_1_1)
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of #MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * @a upload_data)
+ * @param upload_data_size set initially to the size of the
+ * @a upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param con_cls pointer that the callback can set to some
+ * address and that will be preserved by MHD for future
+ * calls for this request; since the access handler may
+ * be called many times (i.e., for a PUT/POST operation
+ * with plenty of upload data) this allows the application
+ * to easily associate some request-specific state.
+ * If necessary, this state can be cleaned up in the
+ * global #MHD_RequestCompletedCallback (which
+ * can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
+ * Initially, `*con_cls` will be NULL.
+ * @return #MHD_YES if the connection was handled successfully,
+ * #MHD_NO if the socket must be closed due to a serios
+ * error while handling the request
+ */
+static int
+url_handler (void *cls,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *version,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **connection_cls)
+{
+
+ /*printf ("%s\n", url);*/
+ unsigned long status;
+ unsigned int no_destroy;
+ struct GNUNET_CRYPTO_EddsaSignature c_sig;
+ struct GNUNET_CRYPTO_EddsaSignature deposit_confirm_sig;
+ struct GNUNET_CRYPTO_EddsaPublicKey pub;
+ #ifdef OBSOLETE
+ struct ContractNBO contract;
+ #else
+ struct Contract contract;
+ #endif
+ struct MHD_Response *resp;
+ json_t *root;
+ json_t *j_sig_enc;
+ json_t *j_h_contract;
+ json_t *j_tmp;
+ json_t *eddsa_pub_enc;
+ json_t *response;
+ json_t *j_mints;
+ json_t *j_mint;
+ json_t *j_wire;
+ int cnt; /* loop counter */
+ char *deposit_body;
+ json_t *j_contract_add;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Absolute expiry;
+ struct GNUNET_TIME_Absolute edate;
+ struct GNUNET_TIME_Absolute refund;
+ struct GNUNET_HashCode h_json_wire;
+ json_t *j_h_json_wire;
+ struct curl_slist *slist;
+ char *contract_str;
+ struct GNUNET_HashCode h_contract_str;
+ struct MERCHANT_contract_handle ch;
+ struct TALER_MintPublicKeyP mint_pub;
+ uint64_t nounce;
+
+ CURL *curl;
+ CURLcode curl_res;
+
+ uint32_t res = GNUNET_SYSERR;
+
+ #define URL_HELLO "/hello"
+ #define URL_CONTRACT "/contract"
+ #define URL_PAY "/pay"
+ no_destroy = 0;
+ resp = NULL;
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+
+ if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO)))
+ {
+ if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
+ status = generate_hello (&resp);
+ else
+ {
+ status = MHD_HTTP_METHOD_NOT_ALLOWED;
+ }
+ }
+
+ if (0 == strncasecmp (url, URL_PAY, sizeof (URL_PAY)))
+ {
+ if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
+ {
+ status = MHD_HTTP_METHOD_NOT_ALLOWED;
+ goto end;
+
+ }
+ else
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ {
+ status = MHD_HTTP_BAD_REQUEST;
+ goto end;
+ }
+
+ /* the POST's body has to be further fetched */
+ if ((GNUNET_NO == res) || (NULL == root))
+ return MHD_YES;
+
+ /* Firstly, check if the wallet is paying against an approved
+ mint */
+ json_t *j_chosen_mint;
+ j_chosen_mint = json_object_get (root, "mint");
+ struct GNUNET_HashCode hash_key;
+ char *chosen_mint;
+
+ chosen_mint = json_string_value (j_chosen_mint);
+ GNUNET_CRYPTO_hash (chosen_mint, strlen (chosen_mint), &hash_key);
+
+ if (NULL ==
+ GNUNET_CONTAINER_multihashmap_get (mints_hashmap, &hash_key))
+ {
+ printf ("Untrusted mint\n");
+ status = MHD_HTTP_FORBIDDEN;
+ goto end;
+
+ }
+
+ /* NOTE: from now on, the mint's base URL is pointed by 'chosen_mint' */
+
+ /* The merchant will only add its 'wire' object to the JSON
+ it got from the wallet */
+
+ /* Get this dep. perm.'s H_contract */
+
+ if (NULL == (j_h_contract = json_object_get (root, "H_contract")))
+ {
+ printf ("H_contract field missing\n");
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+ TALER_json_to_data (j_h_contract, &h_contract_str, sizeof (struct GNUNET_HashCode));
+
+ nounce = 0;
+ edate.abs_value_us = 0;
+
+ if (GNUNET_SYSERR ==
+ MERCHANT_DB_get_contract_values (db_conn,
+ &h_contract_str,
+ &nounce,
+ &edate))
+ {
+ printf ("not existing contract\n");
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ /* Reproducing the wire object */
+ if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
+ nounce,
+ edate)))
+
+ {
+ printf ("wire object not reproduced\n");
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ if (-1 == json_object_set (root, "wire", j_wire))
+ {
+ printf ("depperm not augmented\n");
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ /* POST to mint's "/deposit" */
+ curl = curl_easy_init ();
+
+ struct Mint_Response mr;
+ mr.ptr = malloc(1);
+ mr.size = 0;
+
+ if (curl)
+ {
+
+ char deposit_url[strlen (chosen_mint) + strlen ("http://") + strlen ("/deposit") + 1];
+ sprintf (deposit_url, "http://%s/deposit", chosen_mint);
+ slist = curl_slist_append (slist, "Content-type: application/json");
+ curl_easy_setopt (curl, CURLOPT_HTTPHEADER, slist);
+
+ curl_easy_setopt (curl, CURLOPT_URL, deposit_url);
+ curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, get_response_in_memory);
+ curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) &mr);
+
+ /* NOTE: hopefully, this string won't need any URL-encoding, since as for the
+ Jansson specs, any space and-or newline are not in place using JSON_COMPACT
+ flag */
+ deposit_body = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, deposit_body);
+
+ curl_res = curl_easy_perform (curl);
+
+ curl_slist_free_all(slist);
+ if(curl_res != CURLE_OK)
+ {
+ printf ("deposit not sent\n");
+ goto end;
+ }
+ else
+ printf ("\ndeposit request issued\n");
+
+ curl_easy_cleanup(curl);
+
+ curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status);
+
+ /* If the request was successful, the deposit confirmation
+ has to be verified*/
+
+ if (MHD_HTTP_OK != result)
+ /* Jump here to error mgmt, see issue #3800 (TODO) */
+
+ if (GNUNET_SYSERR ==
+ MERCHANT_DB_get_contract_handle (db_conn, &h_contract_str, &ch))
+ {
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ root = json_loads (mr.ptr, 0, NULL);
+ j_tmp = json_object_get (root, "sig");
+ TALER_json_to_data (j_tmp,
+ &deposit_confirm_sig,
+ sizeof (struct GNUNET_CRYPTO_EddsaSignature));
+ j_tmp = json_object_get (root, "pub");
+
+ TALER_json_to_data (j_tmp,
+ &mint_pub.eddsa_pub,
+ sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+
+ GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
+
+
+ /* The following check could be done once the merchant is able to
+ retrieve the deposit fee for each coin it gets a payment with.
+ That happens because the signature made on a deposit confirmation
+ uses, among its values of interest, the subtraction of the deposit
+ fee from the used coin. That fee is never communicated by the wallet
+ to the merchant, so the only way for the merchant to get this
+ information is to fetch all the mint's keys, namely it needs to
+ invoke "/keys", and store what gotten in its DB */
+
+ #ifdef DENOMMGMT
+ if (GNUNET_NO ==
+ MERCHANT_verify_confirmation (&h_contract_str,
+ &h_json_wire,
+ ch.timestamp,
+ ch.refund_deadline,
+ ch.contract_id,
+ amount_minus_fee, /* MISSING */
+ coin_pub, /* MISSING */
+ &pub,
+ &deposit_confirm_sig,
+ &mint_pub))
+ {
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+
+ }
+
+ #endif
+
+ /* Place here the successful message to issue to the frontend */
+ status = MHD_HTTP_OK;
+ generate_message (&resp, "fullfillment page here");
+ GNUNET_free (mr.ptr);
+
+ }
+
+
+ }
+
+ /*
+ * To be called by the frontend passing the contract with some "holes"
+ * which will be completed, stored in DB, signed, and returned
+ *
+ */
+
+ if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT)))
+ {
+ if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
+ {
+ status = MHD_HTTP_METHOD_NOT_ALLOWED;
+ goto end;
+
+ }
+ else
+ res = TMH_PARSE_post_json (connection,
+ connection_cls,
+ upload_data,
+ upload_data_size,
+ &root);
+ if (GNUNET_SYSERR == res)
+ {
+ status = MHD_HTTP_BAD_REQUEST;
+ goto end;
+ }
+
+
+ /* the POST's body has to be fetched furthermore */
+ if ((GNUNET_NO == res) || (NULL == root))
+ return MHD_YES;
+
+ j_mints = json_array ();
+ for (cnt = 0; cnt < nmints; cnt++)
+ {
+ j_mint = json_pack ("{s:s}",
+ mint_infos[cnt].hostname,
+ GNUNET_CRYPTO_eddsa_public_key_to_string (&mint_infos[cnt].pubkey));
+ json_array_append_new (j_mints, j_mint);
+
+ }
+
+ /* timestamp */
+ now = GNUNET_TIME_absolute_get ();
+ /* expiry */
+ expiry = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
+ /* edate, note: this value must be generated now (and not when the
+ wallet sends back a deposit permission because the hashed 'wire' object,
+ which carries this values in it, has to be included in the signed bundle
+ by the wallet) */
+ edate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
+ refund = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS);
+
+ TALER_round_abs_time (&now);
+ TALER_round_abs_time (&expiry);
+ TALER_round_abs_time (&edate);
+ TALER_round_abs_time (&refund);
+
+ /* getting the SEPA-aware JSON */
+ /* nounce for hashing the wire object */
+ nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX);
+
+ /* get wire object */
+
+ if (NULL == (j_wire = MERCHANT_get_wire_json (wire,
+ nounce,
+ edate)))
+ {
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+
+ /* hash wire objcet */
+ if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire))
+ goto end;
+
+ j_h_json_wire = TALER_json_from_data ((void *) &h_json_wire, sizeof (struct GNUNET_HashCode));
+
+ GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub);
+ eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub));
+
+ if (NULL == (j_contract_add = json_pack ("{s:s, s:s, s:o, s:o, s:o}",
+ "merchant_pub", json_string_value (eddsa_pub_enc),
+ "H_wire", json_string_value (j_h_json_wire),
+ "timestamp", TALER_json_from_abs (now),
+ "refund", TALER_json_from_abs (refund),
+ "mints", j_mints)))
+ {
+ printf ("BAD contract enhancement\n");
+ goto end;
+ }
+
+ /* melt to what received from the wallet */
+ if (-1 == json_object_update (root, j_contract_add))
+ {
+ printf ("depperm response not built\n");
+ goto end;
+ }
+
+ res = MERCHANT_handle_contract (root,
+ db_conn,
+ &contract,
+ now,
+ expiry,
+ edate,
+ refund,
+ &contract_str,
+ nounce);
+ if (GNUNET_SYSERR == res)
+ {
+ status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ goto end;
+ }
+ if (GNUNET_NO == res)
+ {
+ status = MHD_HTTP_METHOD_NOT_ACCEPTABLE;
+ goto end;
+ }
+
+ GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig);
+ GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str);
+
+ j_sig_enc = TALER_json_from_eddsa_sig (&contract.purpose, &c_sig);
+
+ response = json_pack ("{s:o, s:o, s:o}",
+ "contract", root,
+ "sig", j_sig_enc,
+ "h_contract",
+ TALER_json_from_data ((void *) &h_contract_str, sizeof (struct GNUNET_HashCode)));
+
+ GNUNET_free (contract_str);
+
+ TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK);
+ return MHD_YES;
+
+ }
+
+ end:
+
+ if (NULL != resp)
+ {
+ EXITIF (MHD_YES != MHD_queue_response (connection, status, resp));
+ return MHD_YES;
+ if (!no_destroy)
+ MHD_destroy_response (resp);
+ }
+ else
+ {
+
+ EXITIF (GNUNET_OK != failure_resp (connection, status));
+ return MHD_YES;
+
+ }
+
+ EXITIF_exit:
+ result = GNUNET_SYSERR;
+ GNUNET_SCHEDULER_shutdown ();
+ return MHD_NO;
+}
+
+/**
+ * Shutdown task (magically invoked when the application is being
+ * quit)
+ *
+ * @param cls NULL
+ * @param tc scheduler task context
+ */
+static void
+do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+
+ if (NULL != mhd)
+ {
+ MHD_stop_daemon (mhd);
+ mhd = NULL;
+ }
+
+ if (NULL != db_conn)
+ {
+ MERCHANT_DB_disconnect (db_conn);
+ db_conn = NULL;
+ }
+ if (keyfile != NULL)
+ GNUNET_free (privkey);
+
+
+}
+
+/**
+ * Function called with information about who is auditing
+ * a particular mint and what key the mint is using.
+ *
+ * @param cls closure
+ * @param keys information about the various keys used
+ * by the mint
+ */
+static void
+keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
+{
+ /* which kind of mint's keys a merchant should need? Sign
+ keys? It has already the mint's master key from the conf file */
+
+ /* HOT UPDATE: the merchants needs the denomination keys!
+ Because it wants to (firstly) verify the deposit confirmation
+ sent by the mint, and the signed blob depends (among the
+ other things) on the coin's deposit fee. That information
+ is never communicated by the wallet to the merchant.
+ Again, the merchant needs it because it wants to verify that
+ the wallet didn't exceede the limit imposed by the merchant
+ on the total deposit fee for a purchase */
+
+
+ return;
+
+}
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param config configuration
+ */
+void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
+{
+
+ unsigned int cnt;
+ void *keys_mgmt_cls;
+
+ keys_mgmt_cls = NULL;
+ mint_infos = NULL;
+ keyfile = NULL;
+ result = GNUNET_SYSERR;
+ shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown, NULL);
+ EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config,
+ &mint_infos)));
+ EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config)));
+ EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config,
+ "merchant",
+ "KEYFILE",
+ &keyfile));
+ EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config)));
+ EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_YES));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_number (config,
+ "merchant",
+ "port",
+ &port));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CONFIGURATION_get_value_string (config,
+ "merchant",
+ "hostname",
+ &hostname));
+
+ EXITIF (NULL == (mctx = TALER_MINT_init ()));
+ /* Still not used */
+ EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES)));
+ /* Used when the wallet points out which mint it want to deal with.
+ That indication is made through the mint's base URL, which will be
+ the hash-key for this table */
+ EXITIF (NULL == (mints_hashmap = GNUNET_CONTAINER_multihashmap_create (nmints, GNUNET_NO)));
+
+ for (cnt = 0; cnt < nmints; cnt++)
+ {
+ struct Mint *mint;
+ struct GNUNET_HashCode mint_key;
+
+ mint = GNUNET_new (struct Mint);
+ mint->pubkey = mint_infos[cnt].pubkey;
+ /* port this to the new API */
+ mint->conn = TALER_MINT_connect (mctx,
+ mint_infos[cnt].hostname,
+ &keys_mgmt_cb,
+ keys_mgmt_cls); /*<- safe?segfault friendly?*/
+
+ /* NOTE: the keys mgmt callback should roughly do what the following lines do */
+ EXITIF (NULL == mint->conn);
+
+ EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put
+ (mints_map,
+ (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey,
+ mint,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
+
+ /* 1 create hash key
+ 2 create big entry
+ 3 put
+ */
+ GNUNET_CRYPTO_hash (mint_infos[cnt].hostname,
+ strlen (mint_infos[cnt].hostname),
+ &mint_key);
+ GNUNET_CONTAINER_multihashmap_put (mints_hashmap,
+ &mint_key,
+ &mint_infos[cnt],
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ }
+
+ mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
+ port,
+ NULL, NULL,
+ &url_handler, NULL,
+ MHD_OPTION_END);
+
+ EXITIF (NULL == mhd);
+
+ /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that
+ mandatory ? */
+ result = GNUNET_OK;
+
+ EXITIF_exit:
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free_non_null (keyfile);
+ if (GNUNET_OK != result)
+ GNUNET_SCHEDULER_shutdown ();
+
+}
+
+/**
+ * The main function of the serve tool
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ {'t', "temp", NULL,
+ gettext_noop ("Use temporary database tables"), GNUNET_NO,
+ &GNUNET_GETOPT_set_one, &dry},
+ GNUNET_GETOPT_OPTION_END
+ };
+
+
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv,
+ "taler-merchant-serve",
+ "Serve merchant's HTTP interface",
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+
+}
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
new file mode 100644
index 00000000..c96476ac
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -0,0 +1,368 @@
+/*
+ This file is part of TALER
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file merchant/backend/taler-merchant-httpd.c
+ * @brief HTTP serving layer mainly intended to communicate with the frontend
+ * @author Marcello Stanisci
+ */
+
+#include "platform.h"
+#include <microhttpd.h>
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <curl/curl.h>
+#include <taler/taler_signatures.h>
+#include <taler/taler_amount_lib.h>
+#include <taler/taler_json_lib.h>
+#include <taler/taler_mint_service.h>
+#include "taler-mint-httpd.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_Mint *mints;
+extern const struct MERCHANT_WIREFORMAT_Sepa *wire;
+extern PGconn *db_conn;
+extern long long salt;
+extern unsigned int nmints;
+extern struct GNUNET_TIME_Relative edate_delay;
+extern struct GNUNET_CRYPTO_EddsaPrivateKey privkey;
+
+
+
+/**
+ * Fetch the deposit fee related to the given coin aggregate.
+ * @param connection the connection to send an error response to
+ * @param coin_aggregate a coin "aggregate" is the JSON set of
+ * values contained in a single cell of the 'coins' array sent
+ * in a payment
+ * @param deposit_fee where to store the resulting deposit fee
+ * @param mint_index the index which points the chosen mint within
+ * the global 'mints' array
+ * @return GNUNET_OK if successful, GNUNET_NO if the data supplied
+ * is invalid (including the case when the key is not found),
+ * GNUNET_SYSERR upon internal errors
+ */
+int
+deposit_fee_from_coin_aggregate (struct MHD_Connection *connection,
+ json_t *coin_aggregate,
+ struct TALER_Amount *deposit_fee,
+ unsigned int mint_index)
+{
+ int res;
+ const struct TALER_MINT_Keys *keys;
+ const struct TALER_MINT_DenomPublicKey *denom_details;
+ struct TALER_DenominationPublicKey denom;
+
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &denom),
+ TMH_PARSE_MEMBER_END
+ };
+
+ res = TMH_PARSE_json_data (connection,
+ coin_aggregate,
+ spec);
+ if (GNUNET_OK != res)
+ return res; /* may return GNUNET_NO */
+
+ /*printf ("mint %s (%d), pends: %d\n",
+ mints[mint_index].hostname,
+ mint_index,
+ mints[mint_index].pending);*/
+
+ if (1 == mints[mint_index].pending)
+ return GNUNET_SYSERR;
+ keys = TALER_MINT_get_keys (mints[mint_index].conn);
+ denom_details = TALER_MINT_get_denomination_key (keys, &denom);
+ if (NULL == denom_details)
+ {
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:o}",
+ "hint", "unknown denom to mint",
+ "denom_pub", TALER_json_from_rsa_public_key (denom.rsa_public_key));
+ return GNUNET_NO;
+ }
+ *deposit_fee = denom_details->fee_deposit;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Callback to handle a deposit permission's response. How does this behave if the mint
+ * goes offline during a call?
+ * @param cls closure (in our invocation it will be the transaction_id, so the cb can refer
+ * to the right DB row for this deposit permission)
+ * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
+ * 0 if the mint's reply is bogus (fails to follow the protocol)
+ * @param proof the received JSON reply, should be kept as proof (and, in case of errors,
+ * be forwarded to the customer)
+ */
+void
+deposit_cb (void *cls, unsigned int http_status, json_t *proof)
+{
+ if (MHD_HTTP_OK == http_status)
+ ; /* set pending to false in DB and notify the frontend with "OK" */
+ else
+ ; /* set a timeout to retry */
+}
+
+/**
+ * Accomplish this payment.
+ * @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_pay (struct TMH_RequestHandler *rh,
+ struct MHD_Connection *connection,
+ void **connection_cls,
+ const char *upload_data,
+ size_t *upload_data_size)
+{
+
+ json_t *root;
+ json_t *coins;
+ char *chosen_mint;
+ json_t *coin_aggregate;
+ json_t *wire_details;
+ unsigned int mint_index; /*a cell in the global array*/
+ unsigned int coins_index; /*a cell in the global array*/
+ unsigned int coins_cnt; /*a cell in the global array*/
+ uint64_t transaction_id;
+ int res;
+
+ struct TALER_MINT_DepositHandle *dh;
+ struct TALER_Amount max_fee;
+ struct TALER_Amount acc_fee;
+ struct TALER_Amount coin_fee;
+ struct TALER_Amount amount;
+ struct GNUNET_TIME_Absolute edate;
+ struct GNUNET_TIME_Absolute timestamp;
+ struct GNUNET_TIME_Absolute refund_deadline;
+ struct TALER_MerchantPublicKeyP pubkey;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_DenominationPublicKey denom_pub;
+ struct TALER_DenominationSignature ub_sig;
+ struct TALER_CoinSpendSignatureP coin_sig;
+ struct GNUNET_HashCode h_contract;
+
+ struct MERCHANT_DepositConfirmation *dc;
+ struct MERCHANT_DepositConfirmationCls *dccls;
+
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_array ("coins", &coins),
+ TMH_PARSE_member_string ("mint", &chosen_mint),
+ TMH_PARSE_member_amount ("max_fee", &max_fee),
+ TMH_PARSE_member_time_abs ("timestamp", &timestamp),
+ TMH_PARSE_member_time_abs ("refund_deadline", &refund_deadline),
+ TMH_PARSE_member_uint64 ("transaction_id", &transaction_id),
+ TMH_PARSE_member_fixed ("H_contract", &h_contract),
+ TMH_PARSE_MEMBER_END
+ };
+
+ struct TMH_PARSE_FieldSpecification coin_aggregate_spec[] = {
+ TMH_PARSE_member_amount ("f", &amount),
+ TMH_PARSE_member_fixed ("coin_pub", &coin_pub.eddsa_pub),
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &denom_pub),
+ TMH_PARSE_member_denomination_signature ("ub_sig", &ub_sig),
+ TMH_PARSE_member_fixed ("coin_sig", &coin_sig.eddsa_signature),
+ 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;
+
+ res = TMH_PARSE_json_data (connection,
+ root,
+ spec);
+
+ if (GNUNET_YES != res)
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+
+ /* 1 Check if the chosen mint is among the merchant's preferred.
+
+ An error in this case could be due to:
+
+ * the wallet indicated a non existent mint
+ * the wallet indicated a non trusted mint
+
+ NOTE: by preventively checking this, the merchant
+ avoids getting HTTP response codes from random
+ websites that may mislead the wallet in the way
+ of managing the error. Of course, that protect the
+ merchant from POSTing coins to untrusted mints.
+
+ */
+
+ for (mint_index = 0; mint_index <= nmints; mint_index++)
+ {
+ /* no mint found in array */
+ if (mint_index == nmints)
+ {
+ mint_index = -1;
+ break;
+ }
+
+ /* test it by checking public key */
+ if (0 == strcmp (mints[mint_index].hostname,
+ chosen_mint))
+ break;
+
+ }
+
+ if (-1 == mint_index)
+ return TMH_RESPONSE_reply_external_error (connection, "unknown mint");
+
+ /* no 'edate' from frontend. Generate it here; it will be timestamp
+ + a edate delay supplied in config file */
+ if (NULL == json_object_get (root, "edate"))
+ {
+ edate = GNUNET_TIME_absolute_add (timestamp, edate_delay);
+ if (-1 == json_object_set (root, "edate", TALER_json_from_abs (edate)))
+ return MHD_NO;
+ }
+
+ coins_cnt = json_array_size (coins);
+
+ if (0 == coins_cnt)
+ return TMH_RESPONSE_reply_external_error (connection, "no coins given");
+
+ json_array_foreach (coins, coins_index, coin_aggregate)
+ {
+ res = deposit_fee_from_coin_aggregate (connection,
+ coin_aggregate,
+ &coin_fee,
+ mint_index);
+ if (GNUNET_NO == res)
+ return MHD_YES;
+ if (GNUNET_SYSERR == res)
+ return MHD_NO;
+
+ if (0 == coins_index)
+ acc_fee = coin_fee;
+ else
+ TALER_amount_add (&acc_fee,
+ &acc_fee,
+ &coin_fee);
+ }
+
+
+ if (-1 == TALER_amount_cmp (&max_fee, &acc_fee))
+ return MHD_HTTP_NOT_ACCEPTABLE;
+
+ /* cutting off unneeded fields from deposit permission as
+ gotten from the wallet */
+ if (-1 == json_object_del (root, "mint"))
+ return TMH_RESPONSE_reply_external_error (connection,
+ "malformed/non-existent 'mint' field");
+ if (-1 == json_object_del (root, "coins"))
+ return TMH_RESPONSE_reply_external_error (connection,
+ "malformed/non-existent 'coins' field");
+
+ /* adding our public key to deposit permission */
+ GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey.eddsa_pub);
+ json_object_set_new (root,
+ "merchant_pub",
+ TALER_json_from_data (&pubkey, sizeof (pubkey)));
+
+ wire_details = MERCHANT_get_wire_json (wire, salt);
+ /* since memory is zero'd out by GNUNET_malloc, any 'ackd' field will be
+ (implicitly) set to false */
+ dc = GNUNET_malloc (coins_cnt * sizeof (struct MERCHANT_DepositConfirmation));
+ if (NULL == dc)
+ return TMH_RESPONSE_reply_internal_error (connection, "memory failure");
+ json_array_foreach (coins, coins_index, coin_aggregate)
+ {
+
+ /* 3 For each coin in DB
+
+ a. Generate a deposit permission
+ b. store it and its tid in DB
+ c. POST to the mint (see mint-lib for this)
+ (retry until getting a persisten state)
+ */
+
+ /* a */
+ if (-1 == json_object_update (root, coin_aggregate))
+ return TMH_RESPONSE_reply_internal_error (connection,
+ "deposit permission not generated for storing");
+
+ /* b */
+ char *deposit_permission_str = json_dumps (root, JSON_COMPACT);
+ if (GNUNET_OK != MERCHANT_DB_store_deposit_permission (db_conn,
+ deposit_permission_str,
+ transaction_id,
+ 1,
+ mints[mint_index].hostname))
+ return TMH_RESPONSE_reply_internal_error (connection, "internal DB failure");
+ res = TMH_PARSE_json_data (connection,
+ coin_aggregate,
+ coin_aggregate_spec);
+ if (GNUNET_OK != res)
+ return res; /* may return GNUNET_NO */
+
+ dccls = GNUNET_malloc (sizeof (struct MERCHANT_DepositConfirmationCls));
+ dccls->index = coins_index;
+ dccls->dc = dc;
+
+ dh = TALER_MINT_deposit (mints[mint_index].conn,
+ &amount,
+ edate,
+ wire_details,
+ &h_contract,
+ &coin_pub,
+ &ub_sig,
+ &denom_pub,
+ timestamp,
+ transaction_id,
+ &pubkey,
+ refund_deadline,
+ &coin_sig,
+ deposit_cb,
+ dccls); /*may be destroyed by the time the cb gets called..*/
+ if (NULL == dh)
+ TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_SERVICE_UNAVAILABLE,
+ "{s:s, s:i}",
+ "mint", mints[mint_index].hostname,
+ "transaction_id", transaction_id);
+ }
+
+ /* suspend connection until the last coin has been ack'd to the cb.
+ That last cb will finally resume the connection and send back a response */
+ MHD_suspend_connection (connection);
+ return MHD_YES;
+
+ /* 4 Return response code: success, or whatever data the
+ mint sent back regarding some bad coin */
+}
diff --git a/src/backend/taler-merchant-httpd_pay.h b/src/backend/taler-merchant-httpd_pay.h
new file mode 100644
index 00000000..6a796e06
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_pay.h
@@ -0,0 +1,46 @@
+/*
+ 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_contract.h
+ * @brief headers for /pay handler
+ * @author Marcello Stanisci
+ */
+
+#ifndef TALER_MINT_HTTPD_PAY_H
+#define TALER_MINT_HTTPD_PAY_H
+#include <microhttpd.h>
+#include "taler-mint-httpd.h"
+
+/**
+ * Manage a payment
+ *
+ * @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_pay (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.h b/src/backend/taler-mint-httpd.h
new file mode 100644
index 00000000..ad8702f0
--- /dev/null
+++ 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
new file mode 100644
index 00000000..419c4fb0
--- /dev/null
+++ 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
new file mode 100644
index 00000000..a9f575df
--- /dev/null
+++ 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_parsing.c b/src/backend/taler-mint-httpd_parsing.c
index 8d9e3f9f..9efd6c23 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,110 @@ 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_RET_STRING:
+ 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 +350,712 @@ TMH_PARSE_post_json (struct MHD_Connection *connection,
return GNUNET_YES;
}
+/**
+ * Generate line in parser specification for string. The returned
+ * string is already nul-terminated internally by JSON, so no length
+ * information is provided. The string will live as long as the containg
+ * JSON will, and must not be freed by the user
+ * @param field name of the field
+ * @param[out] pointer to the string
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_string (const char *field,
+ char **out)
+{
+ struct TMH_PARSE_FieldSpecification ret =
+ {field, (void **) out, 0, NULL, TMH_PARSE_JNC_RET_STRING, 0};
+ return ret;
+}
+
+/**
+ * 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_STRING:
+ {
+ void **where = va_arg (argp, void **);
+ *where = (void*) json_string_value (root);
+ 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_STRING:
+ ret = TMH_PARSE_navigate_json (connection,
+ root,
+ TMH_PARSE_JNC_FIELD,
+ spec[i].field_name,
+ TMH_PARSE_JNC_RET_STRING,
+ spec[i].destination);
+ 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/backend/taler-mint-httpd_parsing.h b/src/backend/taler-mint-httpd_parsing.h
index d6a2b4ea..dae65092 100644
--- a/src/backend/taler-mint-httpd_parsing.h
+++ b/src/backend/taler-mint-httpd_parsing.h
@@ -139,7 +139,13 @@ enum TMH_PARSE_JsonNavigationCommand
* encoded as a JSON integer.
* Param: uint64_t *
*/
- TMH_PARSE_JNC_RET_UINT64
+ TMH_PARSE_JNC_RET_UINT64,
+ /**
+ * Return a 'char *' as returned from 'json_string_value ()'.
+ * So it will live as long as the containg JSON is not freed,
+ * and must not be freed by the user
+ */
+ TMH_PARSE_JNC_RET_STRING
};
@@ -265,7 +271,18 @@ TMH_PARSE_member_variable (const char *field,
void **ptr,
size_t *ptr_size);
-
+/**
+ * Generate line in parser specification for string. The returned
+ * string is already nul-terminated internally by JSON, so no length
+ * information is provided. The string will live as long as the containg
+ * JSON will, and must not be freed by the user
+ * @param field name of the field
+ * @param[out] pointer to the string
+ * @return corresponding field spec
+ */
+struct TMH_PARSE_FieldSpecification
+TMH_PARSE_member_string (const char *field,
+ char **out);
/**
* Generate line in parser specification for 64-bit integer
* given as an integer in JSON.
diff --git a/src/backend/taler-mint-httpd_responses.c b/src/backend/taler-mint-httpd_responses.c
index aa8f0bf8..00a4d25f 100644
--- a/src/backend/taler-mint-httpd_responses.c
+++ b/src/backend/taler-mint-httpd_responses.c
@@ -165,4 +165,42 @@ 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");
+}
+
+/**
+ * Send a response indicating an external error.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about the error's nature
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
+ const char *hint)
+{
+ return TMH_RESPONSE_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:s}",
+ "error", "client error",
+ "hint", hint);
+}
/* 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
index b1a49d42..f947bd57 100644
--- a/src/backend/taler-mint-httpd_responses.h
+++ b/src/backend/taler-mint-httpd_responses.h
@@ -81,6 +81,16 @@ int
TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
const char *hint);
/**
+ * Send a response indicating an external error.
+ *
+ * @param connection the MHD connection to use
+ * @param hint hint about the error's nature
+ * @return a MHD result code
+ */
+int
+TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
+ const char *hint);
+/**
* Send a response indicating that the request was too big.
*
* @param connection the MHD connection to use
@@ -89,4 +99,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/frontend/checkout.php b/src/frontend/checkout.php
index 72e78944..ab1c2c28 100644
--- a/src/frontend/checkout.php
+++ b/src/frontend/checkout.php
@@ -76,9 +76,7 @@
pass it to the extension */
function handle_contract(json_contract)
{
- var cEvent = new CustomEvent('taler-contract',
- { detail: json_contract,
- target: "/taler/pay"});
+ var cEvent = new CustomEvent('taler-contract', { detail: json_contract });
document.body.dispatchEvent(cEvent);
};
@@ -89,7 +87,7 @@ function handle_contract(json_contract)
function taler_pay(form)
{
var contract_request = new XMLHttpRequest();
- contract_request.open("POST", "/generate_taler_contract.php", true);
+ contract_request.open("GET", "/generate_taler_contract.php", true);
contract_request.onload = function (e)
{
if (contract_request.readyState == 4)
diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php
index 33cb2a47..9849dc82 100644
--- a/src/frontend/generate_taler_contract.php
+++ b/src/frontend/generate_taler_contract.php
@@ -23,8 +23,26 @@
2. generate the JSON to forward to the backend
3. forward the response with the contract from the backend to
to the wallet
-*/
+
+ To test this feature from the command line, issue:
+
+ - $ curl http://merchant_url/generate_taler_contract.php?cli_debug=yes
+ if the whole "journey" to the backend is begin tested
+ - $ curl http://merchant_url/generate_taler_contract.php?backend_test=no
+ if just the frontend job is being tested
+*/
+
$cli_debug = false;
+$backend_test = true;
+
+if ($_GET['cli_debug'] == 'yes')
+ $cli_debug = true;
+
+if ($_GET['backend_test'] == 'no')
+{
+ $cli_debug = true;
+ $backend_test = false;
+}
// 1) recover the session information
session_start();
@@ -48,6 +66,7 @@ else
{
$receiver = "Test Receiver";
$amount = 5;
+ $currency = "KUDOS";
}
@@ -71,6 +90,9 @@ $teatax = array ('value' => 1,
'fraction' => 0,
'currency' => $currency);
+// Take a timestamp
+$now = new DateTime('now');
+
// pack the JSON for the contract
// --- FIXME: exact format needs review!
$json = json_encode (array ('amount' => array ('value' => $value,
@@ -79,7 +101,7 @@ $json = json_encode (array ('amount' => array ('value' => $value,
'max_fee' => array ('value' => 3,
'fraction' => 01010,
'currency' => $currency),
- 'trans_id' => $transaction_id,
+ 'transaction_id' => $transaction_id,
'products' => array (
array ('description' => $desc,
'quantity' => 1,
@@ -90,6 +112,10 @@ $json = json_encode (array ('amount' => array ('value' => $value,
'taxes' => array (array ('teatax' => $teatax)),
'delivery_date' => "Some Date Format",
'delivery_location' => 'LNAME1')),
+ 'timestamp' => "/Date(" . $now->getTimestamp() . ")/",
+ 'pay_url' => "/taler/pay",
+ 'expiry' => "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/",
+ 'refund_deadline' => "/Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")/",
'merchant' => array ('address' => 'LNAME2',
'name' => 'test merchant',
'jurisdiction' => 'LNAME3'),
@@ -115,9 +141,8 @@ $json = json_encode (array ('amount' => array ('value' => $value,
'state' => 'Test State',
'region' => 'Test Region',
'province' => 'Test Province',
- 'ZIP code' => 4908)))); //, JSON_PRETTY_PRINT);
-
-if ($cli_debug && TRUE)
+ 'ZIP code' => 4908))), JSON_PRETTY_PRINT);
+if ($cli_debug && !$backend_test)
{
echo $json . "\n";
exit;
diff --git a/src/frontend/index.html b/src/frontend/index.html
index 8f0c57f9..19b7a21c 100644
--- a/src/frontend/index.html
+++ b/src/frontend/index.html
@@ -70,39 +70,9 @@
obtain Taler coins. This is typically done using a
wire transfer. However, as this is just a demonstrator,
we will allow you to send the mint KUDOS coins using a simple
- form on this website instead.</p>
- <p>You begin by clicking on the Taler icon and selecting
- "Create reserve". The extension will then display some
- hexadecimal reserve public key, which you should copy to the
- clipboard. After that, you can paste it into the form
- below. When dealing with real currency, you would do
- the same, except that you would have to copy the string
- into the subject area for your wire transfer instead of
- into this form.
+ form on the <a href="http://demo.taler.net/transfer" target="_blank">mint's website</a>
+ (new tab).</p>
</p>
- <form id="reserve-form" name="tform" action="/fake_wire_transfer.php" method="POST">
- <div class="participation" id="fake-wire">
- <br>
- Paste your reserve public key here (right-click, "paste"):
- <input type="text" name="reserve_pk" />
- <select id="mint" name="mint_url">
- <option value="demo.taler.net">mint @taler.net</option>
- <option value="localmint">localmint (**)</option>
- </select>
- <br>
- Amount to credit to your reserve:
- <select id="amount" name="kudos_amount">
- <option value="1">1 KUDOS</option>
- <option value="2">2 KUDOS</option>
- <option value="5">5 KUDOS</option>
- <option value="10">10 KUDOS</option>
- <option value="1000">1000 KUDOS</option>
- </select>
- <br>
- <input type="submit" value="Submit"/>
- <br>
- </div>
- </form>
</div>
<div class="explanation" id="payment" role="article" style="display:none;">
<h2>Step 3: Select project to donate KUDOS to!</h2>
diff --git a/src/frontend/pay.php b/src/frontend/pay.php
index c09f83a0..33f3b712 100644
--- a/src/frontend/pay.php
+++ b/src/frontend/pay.php
@@ -1,13 +1,6 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Fullfillment page</title>
-</head>
-<body>
-
<?php
/*
- This file is part of TALER
+ This file is part of GNU TALER.
Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
@@ -23,21 +16,83 @@
*/
- /*
-// recover the session
- session_start();
- if(!isset($_SESSION['contract'])){
-// http_response_code(404);
- echo "Sorry..";
- }
- else echo "Paid";
+/*
+ This serving module adds the 'max_fee' field to the object which
+ sends to the backend, and optionally the field 'edate' (indicating
+ to the mint the tollerated deadline to receive funds for this payment)
+ NOTE: 'max_fee' must be consistent with the same value indicated within
+ the contract; thus, a "real" merchant must implement such a mapping
- session_destroy();
+*/
+$cli_debug = false;
+$backend_test = true;
-*/
-?>
-Payment successful, thanks!
+if ($_GET['cli_debug'] == 'yes')
+ $cli_debug = true;
+
+if ($_GET['backend_test'] == 'no')
+{
+ $cli_debug = true;
+ $backend_test = false;
+}
+
+$post_body = file_get_contents('php://input');
+
+$now = new DateTime('now');
+$edate = array ('edate' =>
+ "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/");
+
+$deposit_permission = json_decode ($post_body, true);
+$max_fee = array ('max_fee' => array ('value' => 3,
+ 'fraction' => 8,
+ 'currency' => "KUDOS"));
+
+$new_deposit_permission = array_merge ($deposit_permission, $max_fee);
+$new_deposit_permission_edate = array_merge ($new_deposit_permission, $edate);
-</body>
-</html>
+/* Craft the HTTP request, note that the backend
+ could be on an entirely different machine if
+ desired. */
+
+if ($cli_debug && !$backend_test)
+{
+
+ /* DO NOTE the newline at the end of 'echo's argument */
+ //echo json_encode ($new_deposit_permission_edate, JSON_PRETTY_PRINT)
+ echo json_encode ($new_deposit_permission, JSON_PRETTY_PRINT)
+ . "\n";
+ exit;
+}
+
+$req = new http\Client\Request ("POST",
+ "http://" . $_SERVER["SERVER_NAME"] . "/backend/pay",
+ array ("Content-Type" => "application/json"));
+$req->getBody()->append (json_encode ($new_deposit_permission));
+
+// Execute the HTTP request
+$client = new http\Client;
+$client->enqueue($req)->send ();
+
+// Fetch the response
+$resp = $client->getResponse ();
+$status_code = $resp->getResponseCode ();
+
+// Our response code is the same we got from the backend:
+http_response_code ($status_code);
+
+// Now generate our body
+if ($status_code != 200)
+{
+ /* error: just forwarding to the wallet what
+ gotten from the backend (which is forwarding 'as is'
+ the error gotten from the mint) */
+ echo $resp->body->toString ();
+
+}
+else
+{
+ echo "Payment succedeed!\n";
+}
+
+?>
diff --git a/src/include/merchant.h b/src/include/merchant.h
index a5273507..fd7e0e20 100644
--- a/src/include/merchant.h
+++ b/src/include/merchant.h
@@ -25,6 +25,8 @@
#include <gnunet/gnunet_common.h>
#include <gnunet/gnunet_crypto_lib.h>
+#include <taler/taler_mint_service.h>
+#include "merchant.h"
/**
* Macro to round microseconds to seconds in GNUNET_TIME_* structs.
@@ -40,26 +42,99 @@
} while (0)
/**
- * A mint
+ * Outcome of a /deposit request for a coin
*/
-struct MERCHANT_MintInfo {
+struct MERCHANT_DepositConfirmation
+{
+ /**
+ * How many coins this request is made of
+ */
+ unsigned int coins_cnt;
+ /**
+ * True if this coin's outcome has been read from
+ * its cb
+ */
+ unsigned int ackd;
+
+ /**
+ * The mint's response to this /deposit
+ */
+ unsigned int exit_status;
+
+ /**
+ * The mint's response body (JSON). Mainly useful in case
+ * some callback needs to send back to the to the wallet the
+ * outcome of an erroneous coin
+ */
+ json_t *proof;
+
+};
+
+struct MERCHANT_DepositConfirmationCls
+{
+ /**
+ * Offset of this coin into the array of all coins outcomes
+ */
+ unsigned int index;
+
+ /**
+ * Pointer to the global (malloc'd) array of all coins outcomes
+ */
+ struct MERCHANT_DepositConfirmation *dc;
+
+};
+
+/**
+ * Mint
+ */
+struct MERCHANT_Mint
+{
/**
* Hostname
*/
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;
+ int pending;
/**
- * The port where the mint's service is running
+ * A connection to this mint
*/
- uint16_t port;
+ struct TALER_MINT_Handle *conn;
};
+struct MERCHANT_Auditor
+{
+ /**
+ * Auditor's legal name
+ */
+ char *name;
+
+};
+
+/**
+ * The contract sent by the merchant to the wallet
+ */
+struct MERCHANT_Contract
+{
+ /**
+ * Purpose header for the signature over contract
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * Hash of the JSON contract in UTF-8 including 0-termination,
+ * using JSON_COMPACT | JSON_SORT_KEYS
+ */
+ struct GNUNET_HashCode h_contract;
+
+};
+
+
/**
* Parses mints from the configuration.
@@ -72,8 +147,20 @@ struct MERCHANT_MintInfo {
*/
int
TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_MintInfo **mints);
+ struct MERCHANT_Mint **mints);
+/**
+ * Parses auditors from the configuration.
+ *
+ * @param cfg the configuration
+ * @param mints the array of auditors upon successful parsing. Will be NULL upon
+ * error.
+ * @return the number of auditors in the above array; GNUNET_SYSERR upon error in
+ * parsing.
+ */
+int
+TALER_MERCHANT_parse_auditors (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct MERCHANT_Auditor **auditors);
GNUNET_NETWORK_STRUCT_BEGIN
struct MERCHANT_WIREFORMAT_Sepa
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index e73ff6ef..30b1e04b 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -9,7 +9,7 @@ bin_PROGRAMS = \
test_contract_SOURCES = \
test_contract.c \
- merchant.c \
+ ../backend/merchant.c \
../backend-lib/merchant_db.c ../backend-lib/merchant_db.h
test_contract_LDADD = \
diff --git a/src/tests/deposit_permission.sample b/src/tests/deposit_permission.sample
new file mode 100644
index 00000000..c1f2d704
--- /dev/null
+++ b/src/tests/deposit_permission.sample
@@ -0,0 +1,31 @@
+{
+ "transaction_id" : 1,
+ "timestamp": "\/Date(1447334003)\/",
+ "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR",
+ "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70",
+ "mint": "localmint",
+ "coins": [
+ {
+ "f": {
+ "value": 0,
+ "fraction": 100000,
+ "currency": "KUDOS"
+ },
+ "coin_pub": "AH86D5WV9G6RT0A3CHYJW598BNMH8848GAKYTEH1JX6X2BKZWTX0",
+ "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0",
+ "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C9J8D1MCHHJ84S34H1K8H1M6HHN60V3GH9S84R3GH9K6MVM6GSM70RKCDT188VM4CHM74RM8GSN6GSKEHHS6CT44H9Q8CWKCD1P68SK2G9S8GR34D1P60T4CCJ67523EDJ28S1KJD9N8H330CT58RS38CSM8H346GT58N13CGJ16GS34CHQ6GT42CSS6CTM4C9H8N1KCG9G74T36C1K8GT44E1N8RRK0D218GV46G9R6CT4CHA4890M6H1H8GV34E1Q8N244DHM8CRK2H1R8CV3ADA37113JDHR88W36E9R61242GT5752KEE9R84WK8H9G8GT34GT26S2MAE1P70RMAGSK74W30C1J60S32GSK64WKAC218MWM6GHM8S332DSN70SM2DHR6H146E228CTM8D135452081918G2J2G0",
+ "coin_sig": "MFTHC54GFYHA3CGKHC8SSDSTYX8YMEJDNQA7AEY5M7JBK3WPDQ9NCJ54ZSPQRZ4QCJC0CPREP0XRWH9JQ509ENSEXWKNNM5FEVRG238"
+ },
+ {
+ "f": {
+ "value": 0,
+ "fraction": 100000,
+ "currency": "KUDOS"
+ },
+ "coin_pub": "MQY067A8AGG90TSH11C8JNRW8P1R669JAAQX4V2HGFHGJ2WY4ZA0",
+ "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0",
+ "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H1J60R4AHHG6N1KEC268N23ECT664VKAGSJ8GRK4CA56H2KAH1H8CS4AHA188SKJE266RR3EDA58N148DSS74TM6CA58N24AC9K6X1M4C9Q6RW48CT674SMCE2264V42CSH8D336GSQ84SM6CT564TKECSR8MRMAGHG8GW3JEA56WT32D9S8H334DJ564SK8GSS60WM6E1P8S346H238GRM8DSN6MR36C256GVM8H9M6WV30C246X0MCH9R84TK2D1M70V44CA260VKJGHN6RR4ACT488R3GCA46MW3GDHK6GWMAHHP6S242H9G6D134D9N8RV32DSJ8GSKGC1J6RTK2GHQ8H0K2DHH6WT38GSH74WM4EA574RKEE1M6D0M4DHK8MW4ACT68CT4CE236H236GH35452081918G2J2G0",
+ "coin_sig": "K9S5273GT4QKF5Y9FYJ62BV710WGEFE1DSXV75A37X272ADWBCV0ERZV9TF2VYTCSH1837R3F7A39R5QEPCC0NYW3JQ5S70X8MJG008"
+ }
+ ]
+}
diff --git a/src/tests/deposit_permission_backend.sample b/src/tests/deposit_permission_backend.sample
new file mode 100644
index 00000000..3cd920ed
--- /dev/null
+++ b/src/tests/deposit_permission_backend.sample
@@ -0,0 +1,36 @@
+{
+ "transaction_id" : 1,
+ "timestamp": "\/Date(1447334003)\/",
+ "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR",
+ "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70",
+ "mint": "demo.taler.net",
+ "coins": [
+ {
+ "f": {
+ "value": 0,
+ "fraction": 100000,
+ "currency": "KUDOS"
+ },
+ "coin_pub": "AH86D5WV9G6RT0A3CHYJW598BNMH8848GAKYTEH1JX6X2BKZWTX0",
+ "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0",
+ "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C9J8D1MCHHJ84S34H1K8H1M6HHN60V3GH9S84R3GH9K6MVM6GSM70RKCDT188VM4CHM74RM8GSN6GSKEHHS6CT44H9Q8CWKCD1P68SK2G9S8GR34D1P60T4CCJ67523EDJ28S1KJD9N8H330CT58RS38CSM8H346GT58N13CGJ16GS34CHQ6GT42CSS6CTM4C9H8N1KCG9G74T36C1K8GT44E1N8RRK0D218GV46G9R6CT4CHA4890M6H1H8GV34E1Q8N244DHM8CRK2H1R8CV3ADA37113JDHR88W36E9R61242GT5752KEE9R84WK8H9G8GT34GT26S2MAE1P70RMAGSK74W30C1J60S32GSK64WKAC218MWM6GHM8S332DSN70SM2DHR6H146E228CTM8D135452081918G2J2G0",
+ "coin_sig": "MFTHC54GFYHA3CGKHC8SSDSTYX8YMEJDNQA7AEY5M7JBK3WPDQ9NCJ54ZSPQRZ4QCJC0CPREP0XRWH9JQ509ENSEXWKNNM5FEVRG238"
+ },
+ {
+ "f": {
+ "value": 0,
+ "fraction": 100000,
+ "currency": "KUDOS"
+ },
+ "coin_pub": "MQY067A8AGG90TSH11C8JNRW8P1R669JAAQX4V2HGFHGJ2WY4ZA0",
+ "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0",
+ "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H1J60R4AHHG6N1KEC268N23ECT664VKAGSJ8GRK4CA56H2KAH1H8CS4AHA188SKJE266RR3EDA58N148DSS74TM6CA58N24AC9K6X1M4C9Q6RW48CT674SMCE2264V42CSH8D336GSQ84SM6CT564TKECSR8MRMAGHG8GW3JEA56WT32D9S8H334DJ564SK8GSS60WM6E1P8S346H238GRM8DSN6MR36C256GVM8H9M6WV30C246X0MCH9R84TK2D1M70V44CA260VKJGHN6RR4ACT488R3GCA46MW3GDHK6GWMAHHP6S242H9G6D134D9N8RV32DSJ8GSKGC1J6RTK2GHQ8H0K2DHH6WT38GSH74WM4EA574RKEE1M6D0M4DHK8MW4ACT68CT4CE236H236GH35452081918G2J2G0",
+ "coin_sig": "K9S5273GT4QKF5Y9FYJ62BV710WGEFE1DSXV75A37X272ADWBCV0ERZV9TF2VYTCSH1837R3F7A39R5QEPCC0NYW3JQ5S70X8MJG008"
+ }
+ ],
+ "max_fee": {
+ "value": 3,
+ "fraction": 8,
+ "currency": "KUDOS"
+ }
+}
diff --git a/src/tests/deposit_permission_edate_backend.sample b/src/tests/deposit_permission_edate_backend.sample
new file mode 100644
index 00000000..259a62bc
--- /dev/null
+++ b/src/tests/deposit_permission_edate_backend.sample
@@ -0,0 +1,37 @@
+{
+ "transaction_id" : 1,
+ "timestamp": "\/Date(1447334003)\/",
+ "edate": "\/Date(1447334003)\/",
+ "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR",
+ "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70",
+ "mint": "demo.taler.net",
+ "coins": [
+ {
+ "f": {
+ "value": 0,
+ "fraction": 100000,
+ "currency": "KUDOS"
+ },
+ "coin_pub": "AH86D5WV9G6RT0A3CHYJW598BNMH8848GAKYTEH1JX6X2BKZWTX0",
+ "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0",
+ "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C9J8D1MCHHJ84S34H1K8H1M6HHN60V3GH9S84R3GH9K6MVM6GSM70RKCDT188VM4CHM74RM8GSN6GSKEHHS6CT44H9Q8CWKCD1P68SK2G9S8GR34D1P60T4CCJ67523EDJ28S1KJD9N8H330CT58RS38CSM8H346GT58N13CGJ16GS34CHQ6GT42CSS6CTM4C9H8N1KCG9G74T36C1K8GT44E1N8RRK0D218GV46G9R6CT4CHA4890M6H1H8GV34E1Q8N244DHM8CRK2H1R8CV3ADA37113JDHR88W36E9R61242GT5752KEE9R84WK8H9G8GT34GT26S2MAE1P70RMAGSK74W30C1J60S32GSK64WKAC218MWM6GHM8S332DSN70SM2DHR6H146E228CTM8D135452081918G2J2G0",
+ "coin_sig": "MFTHC54GFYHA3CGKHC8SSDSTYX8YMEJDNQA7AEY5M7JBK3WPDQ9NCJ54ZSPQRZ4QCJC0CPREP0XRWH9JQ509ENSEXWKNNM5FEVRG238"
+ },
+ {
+ "f": {
+ "value": 0,
+ "fraction": 100000,
+ "currency": "KUDOS"
+ },
+ "coin_pub": "MQY067A8AGG90TSH11C8JNRW8P1R669JAAQX4V2HGFHGJ2WY4ZA0",
+ "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0",
+ "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H1J60R4AHHG6N1KEC268N23ECT664VKAGSJ8GRK4CA56H2KAH1H8CS4AHA188SKJE266RR3EDA58N148DSS74TM6CA58N24AC9K6X1M4C9Q6RW48CT674SMCE2264V42CSH8D336GSQ84SM6CT564TKECSR8MRMAGHG8GW3JEA56WT32D9S8H334DJ564SK8GSS60WM6E1P8S346H238GRM8DSN6MR36C256GVM8H9M6WV30C246X0MCH9R84TK2D1M70V44CA260VKJGHN6RR4ACT488R3GCA46MW3GDHK6GWMAHHP6S242H9G6D134D9N8RV32DSJ8GSKGC1J6RTK2GHQ8H0K2DHH6WT38GSH74WM4EA574RKEE1M6D0M4DHK8MW4ACT68CT4CE236H236GH35452081918G2J2G0",
+ "coin_sig": "K9S5273GT4QKF5Y9FYJ62BV710WGEFE1DSXV75A37X272ADWBCV0ERZV9TF2VYTCSH1837R3F7A39R5QEPCC0NYW3JQ5S70X8MJG008"
+ }
+ ],
+ "max_fee": {
+ "value": 3,
+ "fraction": 8,
+ "currency": "KUDOS"
+ }
+}
diff --git a/src/tests/merchant.c b/src/tests/merchant.c
deleted file mode 100644
index f124a030..00000000
--- a/src/tests/merchant.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- 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/merchant.c
- * @brief Common utility functions for merchant
- * @author Sree Harsha Totakura <sreeharsha@totakura.in>
- */
-
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include "merchant.h"
-
-
-#define EXITIF(cond) \
- do { \
- if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
- } while (0)
-
-
-/**
- * Parses mints from the configuration.
- *
- * @param cfg the configuration
- * @param mints the array of mints upon successful parsing. Will be NULL upon
- * error.
- * @return the number of mints in the above array; GNUNET_SYSERR upon error in
- * parsing.
- */
-int
-TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg,
- struct MERCHANT_MintInfo **mints)
-{
- char *mints_str;
- char *token_nf; /* do no free (nf) */
- char *mint_section;
- char *mint_hostname;
- char *mint_pubkey_enc;
- struct MERCHANT_MintInfo *r_mints;
- struct MERCHANT_MintInfo mint;
- unsigned long long mint_port;
- unsigned int cnt;
- int OK;
-
- OK = 0;
- mints_str = NULL;
- 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,
- "merchant",
- "TRUSTED_MINTS",
- &mints_str));
- for (token_nf = strtok (mints_str, " ");
- NULL != token_nf;
- token_nf = strtok (NULL, " "))
- {
- GNUNET_assert (0 < GNUNET_asprintf (&mint_section,
- "mint-%s", token_nf));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (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;
- }
- OK = 1;
-
- EXITIF_exit:
- 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);
- return GNUNET_SYSERR;
- }
-
- *mints = r_mints;
- return cnt;
-}
-
-
-/**
- * Parse the SEPA information from the configuration. If any of the required
- * fileds is missing return NULL.
- *
- * @param cfg the configuration
- * @return Sepa details as a structure; NULL upon error
- */
-struct MERCHANT_WIREFORMAT_Sepa *
-TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- struct MERCHANT_WIREFORMAT_Sepa *wf;
-
- wf = GNUNET_new (struct MERCHANT_WIREFORMAT_Sepa);
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "IBAN",
- &wf->iban));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "NAME",
- &wf->name));
- EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
- "wire-sepa",
- "BIC",
- &wf->bic));
- return wf;
-
- EXITIF_exit:
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
- return NULL;
-
-}
-
-
-/**
- * Destroy and free resouces occupied by the wireformat structure
- *
- * @param wf the wireformat structure
- */
-void
-TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf)
-{
- GNUNET_free_non_null (wf->iban);
- GNUNET_free_non_null (wf->name);
- GNUNET_free_non_null (wf->bic);
- GNUNET_free (wf);
-}
-
-/* end of merchant.c */
diff --git a/src/tests/test_contract.c b/src/tests/test_contract.c
index 47b3b0e6..147ea4f3 100644
--- 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,22 +97,17 @@ 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;
int64_t p_id;
- #ifdef OBSOLETE
- struct ContractNBO contract;
- #else
struct Contract contract;
- #endif
struct GNUNET_TIME_Absolute edate;
struct GNUNET_TIME_Absolute now;
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 +272,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,
diff --git a/src/tests/test_contract_README b/src/tests/test_contract_README
new file mode 100644
index 00000000..9fd337cf
--- /dev/null
+++ b/src/tests/test_contract_README
@@ -0,0 +1,10 @@
+- In order to test the "/contract" facility of the merchant,
+ just issue
+
+ - curl http://merchant-url/generate_taler_contract.php?cli_debug=yes
+ (this form tests the whole communication between frontend and backend)
+
+ - curl http://merchant-url/generate_taler_contract.php?backend_test=no
+ (this form test only the contract proposition generation of the frontend,
+ so it doesn't further connect to the backend to get the proposition JSON
+ enhanced)
diff --git a/src/tests/test_pay_README b/src/tests/test_pay_README
new file mode 100644
index 00000000..1704d6b7
--- /dev/null
+++ b/src/tests/test_pay_README
@@ -0,0 +1,26 @@
+The merchant's "/pay" facility can be tested either directly by querying the
+backend or by querying the frontend (which in turn will query the backend).
+Each file *.sample is sample JSON to POST to both components (with the '_backend'
+named version being dedicated to just the backend).
+
+Note: the sample 'deposit_permission_edate_backend.sample' contains the optional
+'edate' field to the deposit permission (that field indicates the tollerated delay
+by this merchant to receive funds relative to this payment)
+
+The frontend test admits two versions of queries, as in the following examples:
+
+ * curl -H 'Content-type: application/json' -d@deposit_permission.sample \
+ http://merchant-url/pay.php?cli_debug=yes
+
+ (this form tests the whole communication between frontend and backend)
+
+ * curl -d@deposit_permission.sample http://merchant-url/pay.php?backend_test=no
+
+ (this form tests only the deposit permission enhancement made by the
+ frontend, so it doesn't further connect to the backend to actually POST
+ that data. By default, the frontend doesn't add the 'edate' field, so it
+ is necessary to uncomment the related line of code to test this feature)
+
+Finally, to POST to the backend, issue
+ * curl -H 'Content-type: application/json' -d@*_backend.sample http://backend-url/pay
+