aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcello Stanisci <marcello.stanisci@inria.fr>2015-07-31 18:41:56 +0200
committerMarcello Stanisci <marcello.stanisci@inria.fr>2015-07-31 18:41:56 +0200
commit4e8112a444a4b93efe8f439a40d1f78faea49d2f (patch)
tree1d2cf7f6b99b2f6fd1a942927ccfcd7c381b5db5
parent403e50afd96f2f3caf84c6d2c55ce4adcf026313 (diff)
downloadmerchant-4e8112a444a4b93efe8f439a40d1f78faea49d2f.tar.gz
merchant-4e8112a444a4b93efe8f439a40d1f78faea49d2f.tar.bz2
merchant-4e8112a444a4b93efe8f439a40d1f78faea49d2f.zip
merchant DB initializable
-rw-r--r--src/backend/Makefile.am14
-rw-r--r--src/backend/merchant_db.c348
-rw-r--r--src/backend/merchant_db.h98
-rwxr-xr-xsrc/backend/taler-merchant-dbinitbin0 -> 35040 bytes
-rw-r--r--src/backend/taler-merchant-dbinit.c79
-rw-r--r--src/backend/taler-merchant-httpd.c83
6 files changed, 611 insertions, 11 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index d16859a7..f7afde8e 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -2,11 +2,23 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include $(POSTGRESQL_CPPFLAGS)
MERCHANT_DB = merchant_db.c merchant_db.h
bin_PROGRAMS = \
- taler-merchant-httpd
+ taler-merchant-httpd \
+ taler-merchant-dbinit
taler_merchant_httpd_SOURCES = \
taler-merchant-httpd.c
+taler_merchant_dbinit_SOURCES = \
+ taler-merchant-dbinit.c \
+ $(MERCHANT_DB)
+
taler_merchant_httpd_LDADD = \
-lmicrohttpd \
-lgnunetutil
+
+
+taler_merchant_dbinit_LDADD = \
+ -ltalerpq \
+ -lgnunetutil \
+ -lgnunetpostgres \
+ -lpq
diff --git a/src/backend/merchant_db.c b/src/backend/merchant_db.c
new file mode 100644
index 00000000..66ab5bcf
--- /dev/null
+++ b/src/backend/merchant_db.c
@@ -0,0 +1,348 @@
+/*
+ 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 ("
+ "transaction_id SERIAL8 PRIMARY KEY,"
+ "amount INT4 NOT NULL,"
+ "amount_fraction INT4 NOT NULL,"
+ "description TEXT NOT NULL,"
+ "nounce BYTEA NOT NULL,"
+ "expiry INT8 NOT NULL,"
+ "product INT8 NOT NULL);"
+ "CREATE %1$s TABLE IF NOT EXISTS checkouts ("
+ "coin_pub BYTEA PRIMARY KEY,"
+ "transaction_id INT8 REFERENCES contracts(transaction_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"
+ "(amount, amount_fraction, description,"
+ "nounce, expiry, product) VALUES"
+ "($1, $2, $3, $4, $5, $6)"
+ "RETURNING transaction_id",
+ 6, NULL)));
+ EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res)));
+ PQclear (res);
+
+ EXITIF (NULL == (res = PQprepare
+ (conn,
+ "get_contract_product",
+ "SELECT ("
+ "product"
+ ") FROM contracts "
+ "WHERE ("
+ "transaction_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,"
+ "transaction_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 "
+ "transaction_id IN ("
+ "SELECT (transaction_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 desc descripition of the contract
+ * @param nounce a random 64-bit nounce
+ * @param product description to identify a product
+ * @return -1 upon error; the serial id of the inserted contract upon success
+ */
+long long
+MERCHANT_DB_contract_create (PGconn *conn,
+ struct GNUNET_TIME_Absolute expiry,
+ struct TALER_Amount *amount,
+ const char *desc,
+ uint64_t nounce,
+ uint64_t product)
+{
+ PGresult *res;
+ uint64_t expiry_ms_nbo;
+ uint32_t value_nbo;
+ uint32_t fraction_nbo;
+ uint64_t nounce_nbo;
+ ExecStatusType status;
+ uint64_t id;
+ /* ported. To be tested/compiled */
+ struct TALER_PQ_QueryParam params[] = {
+ TALER_PQ_query_param_uint32 (&value_nbo),
+ TALER_PQ_query_param_uint32 (&fraction_nbo),
+ /* a *string* is being put in the following statement,
+ though the API talks about a *blob*. Will this be liked by
+ the DB ? */
+ TALER_PQ_query_param_fixed_size (desc, strlen(desc)),
+ TALER_PQ_query_param_uint64 (&nounce_nbo),
+ TALER_PQ_query_param_uint64 (&expiry_ms_nbo),
+ TALER_PQ_query_param_uint64 (&product),
+ TALER_PQ_query_param_end
+ };
+ struct TALER_PQ_ResultSpec rs[] = {
+ TALER_PQ_result_spec_uint64 ("transaction_id", &id),
+ TALER_PQ_result_spec_end
+ };
+
+ expiry_ms_nbo = GNUNET_htonll (expiry.abs_value_us);
+ value_nbo = htonl (amount->value);
+ fraction_nbo = htonl (amount->fraction);
+ nounce_nbo = GNUNET_htonll (nounce);
+ product = GNUNET_htonll (product);
+ /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */
+ res = TALER_PQ_exec_prepared (conn, "contract_create", 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) id);
+
+ EXITIF_exit:
+ PQclear (res);
+ return -1;
+}
+
+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/merchant_db.h b/src/backend/merchant_db.h
new file mode 100644
index 00000000..bf334989
--- /dev/null
+++ b/src/backend/merchant_db.h
@@ -0,0 +1,98 @@
+/*
+ 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 desc descripition of the contract
+ * @param nounce a random 64-bit nounce
+ * @param product description to identify a product
+ * @return -1 upon error; the serial id of the inserted contract upon success
+ */
+long long
+MERCHANT_DB_contract_create (PGconn *conn,
+ struct GNUNET_TIME_Absolute expiry,
+ struct TALER_Amount *amount,
+ 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/taler-merchant-dbinit b/src/backend/taler-merchant-dbinit
new file mode 100755
index 00000000..850b77d0
--- /dev/null
+++ b/src/backend/taler-merchant-dbinit
Binary files differ
diff --git a/src/backend/taler-merchant-dbinit.c b/src/backend/taler-merchant-dbinit.c
new file mode 100644
index 00000000..07731b47
--- /dev/null
+++ b/src/backend/taler-merchant-dbinit.c
@@ -0,0 +1,79 @@
+/*
+ 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/taler_merchant_dbinit.c
+ * @brief Program to initialise merchant database
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ */
+
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "merchant_db.h"
+
+
+/**
+ * Global execution result
+ */
+static int result;
+
+
+/**
+ * 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)
+{
+ PGconn *conn;
+
+ conn = MERCHANT_DB_connect (config);
+ if (NULL == conn)
+ return;
+ if (GNUNET_OK == MERCHANT_DB_initialize (conn, GNUNET_NO))
+ result = GNUNET_OK;
+ MERCHANT_DB_disconnect (conn);
+}
+
+
+/**
+ * The main function
+ *
+ * @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[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ result = GNUNET_SYSERR;
+ if (GNUNET_OK !=
+ GNUNET_PROGRAM_run (argc, argv, "taler-merchant-dbinit",
+ gettext_noop
+ ("Initialise Taler Merchant's database"),
+ options, &run, NULL))
+ return 3;
+ return (GNUNET_OK == result) ? 0 : 1;
+}
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index eafef40b..515cefc2 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -45,6 +45,16 @@ unsigned short port;
static struct MHD_Daemon *mhd;
/**
+ * Shutdown task identifier
+ */
+static struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+/**
+ * Should we do a dry run where temporary tables are used for storing the data.
+ */
+static int dry;
+
+/**
* Global return code
*/
static int result;
@@ -216,32 +226,85 @@ url_handler (void *cls,
}
+/**
+ * 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;
+ }
+
+}
/**
- * The main function of the serve tool
+ * Main function that will be run by the scheduler.
*
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
+ * @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
*/
-int
-main (int argc, char *const *argv)
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *config)
{
-
+
port = 9966;
+
+ shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
+ &do_shutdown, NULL);
+
mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY,
port,
NULL, NULL,
&url_handler, NULL,
MHD_OPTION_END);
- getchar ();
- MHD_stop_daemon (mhd);
- return 0;
+ EXITIF (NULL == mhd);
+ result = GNUNET_OK;
+ EXITIF_exit:
+ 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[] = {
+ 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;
+
+
+
}