diff options
Diffstat (limited to 'src/backend/melted')
-rw-r--r-- | src/backend/melted/Makefile.am | 61 | ||||
-rw-r--r-- | src/backend/melted/README | 9 | ||||
-rw-r--r-- | src/backend/melted/copy.sh | 15 | ||||
-rw-r--r-- | src/backend/melted/merchant.c | 173 | ||||
-rw-r--r-- | src/backend/melted/merchant.h | 110 | ||||
-rw-r--r-- | src/backend/melted/merchant_db.c | 348 | ||||
-rw-r--r-- | src/backend/melted/merchant_db.h | 98 | ||||
-rw-r--r-- | src/backend/melted/taler-merchant-httpd.c | 506 |
8 files changed, 1320 insertions, 0 deletions
diff --git a/src/backend/melted/Makefile.am b/src/backend/melted/Makefile.am new file mode 100644 index 00000000..14b36e8a --- /dev/null +++ b/src/backend/melted/Makefile.am @@ -0,0 +1,61 @@ +# 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 \ + -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 new file mode 100644 index 00000000..45c4fb80 --- /dev/null +++ b/src/backend/melted/README @@ -0,0 +1,9 @@ +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 new file mode 100644 index 00000000..6b2fd098 --- /dev/null +++ b/src/backend/melted/copy.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# copy all the backend relevant files into the directory given +# as argument. 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 diff --git a/src/backend/melted/merchant.c b/src/backend/melted/merchant.c new file mode 100644 index 00000000..f124a030 --- /dev/null +++ b/src/backend/melted/merchant.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/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.h b/src/backend/melted/merchant.h new file mode 100644 index 00000000..c66131ed --- /dev/null +++ b/src/backend/melted/merchant.h @@ -0,0 +1,110 @@ +/* + 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 new file mode 100644 index 00000000..66ab5bcf --- /dev/null +++ b/src/backend/melted/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/melted/merchant_db.h b/src/backend/melted/merchant_db.h new file mode 100644 index 00000000..bf334989 --- /dev/null +++ b/src/backend/melted/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/melted/taler-merchant-httpd.c b/src/backend/melted/taler-merchant-httpd.c new file mode 100644 index 00000000..336f8915 --- /dev/null +++ b/src/backend/melted/taler-merchant-httpd.c @@ -0,0 +1,506 @@ +/* + 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-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 "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) + +// task 1. Just implement a hello world server launched a` la GNUNET + +/** + * The port we are running on + */ +unsigned short port; + +/** + * 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; + +/** + * Should we do a dry run where temporary tables are used for storing the data. + */ +static int dry; + +/** + * Global return code + */ +static int result; + +/** +* 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"; + unsigned int ret; + + *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello, + MHD_RESPMEM_PERSISTENT); + ret = 200; + return ret; + + +} + + +/** +* 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) +{ + printf ("called failure mgmt\n"); + 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; +} + + +/** + * 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; + struct MHD_Response *resp; + struct TALER_Amount price; + json_t json_price; + json_t *root; + int res; + char *desc; + + #define URL_HELLO "/hello" + #define URL_CONTRACT "/contract" + no_destroy = 0; + resp = NULL; + status = 500; + 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 + + /* + 1. parse the json + 2. generate the contract + 3. pack the contract's json + 4. return it + */ + + res = TMH_PARSE_post_json (connection, + connection_cls, + upload_data, + upload_data_size, + &root); + + if (GNUNET_SYSERR == res) + return MHD_NO; + if ( (GNUNET_NO == res) || (NULL == root) ) + return MHD_YES; + + /* not really needed for getting just a string. Though it'd be very handy + to enhace the mint's JSON-parsing capabilities with the merchant's needs. + + struct TMH_PARSE_FieldSpecification spec[] = { + TMH_PARSE_member_variable ("desc", (void **) &desc, &desc_len), + TMH_PARSE_member_amount ("price", &price), + TMH_PARSE_MEMBER_END + }; + res = TMH_PARSE_json_data (connection, + root, + spec); */ + /* + + The expected JSON : + + { + "desc" : "some description", + "price" : a JSON compliant TALER_Amount objet + } + + */ + + if (!json_unpack (root, "{s:s, s:o}", "desc", &desc, "price", &json_price)) + status = generate_message (&resp, "unable to parse /contract JSON"); + else + { + if (GNUNET_OK != TALER_json_to_amount (&json_price, &price)) + status = generate_message (&resp, "unable to parse `price' field in /contract JSON"); + else + { + /* Let's generate this contract! */ + /* First, initialize the DB, since it'll be stored there */ + + + + + + } + + } + + + + + + + } + + 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 (); to a later stage, maybe + 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; + } + +} + + +/** + * 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) +{ + + port = 9966; + + + EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); + EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, dry)); + + 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); + + + 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[] = { + {'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; + + + +} |