diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/Makefile.am | 3 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 7 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 2 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_contract.c | 10 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_map.c | 246 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_map.h | 66 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 142 | ||||
-rw-r--r-- | src/backenddb/test_merchantdb.c | 19 | ||||
-rw-r--r-- | src/include/taler_merchant_service.h | 54 | ||||
-rw-r--r-- | src/include/taler_merchantdb_plugin.h | 28 | ||||
-rw-r--r-- | src/lib/Makefile.am | 3 | ||||
-rw-r--r-- | src/lib/merchant_api_contract.c | 64 | ||||
-rw-r--r-- | src/lib/merchant_api_history.c | 3 | ||||
-rw-r--r-- | src/lib/merchant_api_map.c | 230 | ||||
-rw-r--r-- | src/lib/merchant_api_pay.c | 1 | ||||
-rw-r--r-- | src/lib/merchant_api_track_transaction.c | 2 | ||||
-rw-r--r-- | src/lib/merchant_api_track_transfer.c | 2 | ||||
-rw-r--r-- | src/lib/test_merchant_api.c | 230 | ||||
-rw-r--r-- | src/merchant-tools/Makefile.am | 16 | ||||
-rwxr-xr-x | src/merchant-tools/taler-merchant-dbinit | 228 | ||||
-rw-r--r-- | src/merchant-tools/taler-merchant-dbinit.c | 115 |
21 files changed, 1374 insertions, 97 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index a1aaf540..f7fc4c88 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -23,7 +23,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h \ taler-merchant-httpd_history.c taler-merchant-httpd_history.h \ taler-merchant-httpd_track-transaction.c taler-merchant-httpd_track-transaction.h \ - taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h + taler-merchant-httpd_track-transfer.c taler-merchant-httpd_track-transfer.h \ + taler-merchant-httpd_map.c taler-merchant-httpd_map.h taler_merchant_httpd_LDADD = \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index c5ebe21e..95ac4b32 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -40,6 +40,7 @@ #include "taler-merchant-httpd_track-transaction.h" #include "taler-merchant-httpd_track-transfer.h" #include "taler-merchant-httpd_history.h" +#include "taler-merchant-httpd_map.h" /** * Backlog for listen operation on unix-domain sockets. @@ -191,6 +192,12 @@ url_handler (void *cls, { "/history", MHD_HTTP_METHOD_GET, "text/plain", "Only GET is allowed", 0, &MH_handler_history, MHD_HTTP_OK}, + { "/map/in", MHD_HTTP_METHOD_POST, NULL, + "Only POST is allowed", 0, + &MH_handler_map_in, MHD_HTTP_OK}, + { "/map/out", MHD_HTTP_METHOD_GET, "text/plain", + "Only GET is allowed", 0, + &MH_handler_map_out, MHD_HTTP_OK}, {NULL, NULL, NULL, NULL, 0, 0 } }; static struct TMH_RequestHandler h404 = diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h index 9d45ab67..f5adeac9 100644 --- a/src/backend/taler-merchant-httpd.h +++ b/src/backend/taler-merchant-httpd.h @@ -190,7 +190,7 @@ typedef void * Each MHD response handler that sets the "connection_cls" to a * non-NULL value must use a struct that has this struct as its first * member. This struct contains a single callback, which will be - * invoked to clean up the memory when the contection is completed. + * invoked to clean up the memory when the connection is completed. */ struct TM_HandlerContext { diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index f47e5ba9..e9ebda49 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -81,7 +81,7 @@ check_products (json_t *products) /** - * Information we keep for an individual calls + * Information we keep for individual calls * to requests that parse JSON, but keep no other state. */ struct TMH_JsonParseContext @@ -169,7 +169,7 @@ MH_handler_contract (struct TMH_RequestHandler *rh, GNUNET_JSON_spec_absolute_time ("timestamp", ×tamp), GNUNET_JSON_spec_absolute_time ("refund_deadline", &refund_deadline), GNUNET_JSON_spec_absolute_time ("pay_deadline", &pay_deadline), - GNUNET_JSON_spec_end() + GNUNET_JSON_spec_end () }; if (NULL == *connection_cls) @@ -215,9 +215,9 @@ MH_handler_contract (struct TMH_RequestHandler *rh, if (GNUNET_SYSERR == res) { json_decref (root); - return TMH_RESPONSE_reply_arg_invalid (connection, - TALER_EC_PARAMETER_MALFORMED, - "contract"); + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_NONE, + "Impossible to parse contract"); } /* check contract is well-formed */ if (GNUNET_OK != check_products (products)) diff --git a/src/backend/taler-merchant-httpd_map.c b/src/backend/taler-merchant-httpd_map.c new file mode 100644 index 00000000..a41968a0 --- /dev/null +++ b/src/backend/taler-merchant-httpd_map.c @@ -0,0 +1,246 @@ +/* + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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 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, see <http://www.gnu.org/licenses/> +*/ +/** + * @file backend/taler-merchant-httpd_map.c + * @brief Provides the frontend the mean to store plain contracts in database + * @author Marcello Stanisci + */ +#include "platform.h" +#include <jansson.h> +#include <taler/taler_signatures.h> +#include <taler/taler_json_lib.h> +#include "taler-merchant-httpd.h" +#include "taler-merchant-httpd_responses.h" +#include "taler-merchant-httpd_parsing.h" + +/** + * Information we keep for individual calls + * to requests that parse JSON, but keep no other state. + */ +struct TMH_JsonParseContext +{ + + /** + * This field MUST be first. + * FIXME: Explain why! + */ + struct TM_HandlerContext hc; + + /** + * Placeholder for #TMH_PARSE_post_json() to keep its internal state. + */ + void *json_parse_context; +}; + +/** + * Custom cleanup routine for a `struct TMH_JsonParseContext`. + * + * @param hc the `struct TMH_JsonParseContext` to clean up. + */ +static void +json_parse_cleanup (struct TM_HandlerContext *hc) +{ + struct TMH_JsonParseContext *jpc = (struct TMH_JsonParseContext *) hc; + + TMH_PARSE_post_cleanup_callback (jpc->json_parse_context); + GNUNET_free (jpc); +} + + +/** + * Manage a /map/in request. Store in db a plain text contract + * and its hashcode. + * + * @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_map_in (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + + int res; + json_t *root; + json_t *contract; + struct GNUNET_HashCode h_contract; + struct GNUNET_HashCode tmp; + struct TMH_JsonParseContext *ctx; + +/* Fetch body */ + + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("contract", &contract), + GNUNET_JSON_spec_fixed_auto ("h_contract", &h_contract), + GNUNET_JSON_spec_end () + }; + + if (NULL == *connection_cls) + { + ctx = GNUNET_new (struct TMH_JsonParseContext); + ctx->hc.cc = &json_parse_cleanup; + *connection_cls = ctx; + } + else + { + ctx = *connection_cls; + } + + res = TMH_PARSE_post_json (connection, + &ctx->json_parse_context, + 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_NO == res) + { + json_decref (root); + return MHD_YES; + } + if (GNUNET_SYSERR == res) + { + json_decref (root); + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_NONE, + "Impossible to parse JSON"); + } + + /* Sanity checks */ + if (GNUNET_SYSERR == + TALER_JSON_hash (contract, + &tmp)) + return TMH_RESPONSE_reply_invalid_json (connection); + + /** + * Check hashes match. This check does NOT detect invalid + * contracts though. + */ + + if (0 != memcmp (&tmp, + &h_contract, + sizeof (struct GNUNET_HashCode))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "'h_contract' does not match 'contract'\n"); + return TMH_RESPONSE_reply_json_pack + (connection, + MHD_HTTP_UNPROCESSABLE_ENTITY, + "{s:I, s:s}", + "code", (json_int_t) TALER_EC_MAP_IN_UNMATCHED_HASH, + "error", "field 'h_contract' is not hash of 'contract'"); + } + + /* Store body */ + if (GNUNET_OK != db->store_map (db->cls, + &h_contract, + contract)) + { + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_MAP_IN_STORE_DB_ERROR, + "Could not store data into db"); + } + + /* Test */ + json_t *hello; + + hello = json_pack ("{s:s}", "ok", "computer"); + return TMH_RESPONSE_reply_json (connection, + hello, + MHD_HTTP_OK); + + +} + + +/** + * Manage a /map/out request. Query the db and returns a plain + * text contract associated with the hashcode given as input + * + * @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_map_out (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + const char *h_contract_enc; + struct GNUNET_HashCode h_contract; + int res; + json_t *contract; + + h_contract_enc = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "h_contract"); + if (NULL == h_contract_enc) + return TMH_RESPONSE_reply_arg_missing (connection, + TALER_EC_PARAMETER_MISSING, + "h_contract"); + + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (h_contract_enc, + strlen (h_contract_enc), + &h_contract, + sizeof (h_contract))) + { + GNUNET_break_op (0); + return TMH_RESPONSE_reply_bad_request (connection, + TALER_EC_PARAMETER_MALFORMED, + "Could not decode hashcode into binary form"); + } + + res = db->find_contract (db->cls, + &contract, + &h_contract); + + if (GNUNET_SYSERR == res) + { + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_MAP_OUT_GET_FROM_DB_ERROR, + "Could not retrieve data from db"); + } + + if (GNUNET_NO == res) + { + return TMH_RESPONSE_reply_not_found (connection, + TALER_EC_MAP_OUT_CONTRACT_UNKNOWN, + "contract"); + } + + return TMH_RESPONSE_reply_json (connection, + contract, + MHD_HTTP_OK); +} +/* end of taler-merchant-httpd_history.c */ diff --git a/src/backend/taler-merchant-httpd_map.h b/src/backend/taler-merchant-httpd_map.h new file mode 100644 index 00000000..de9150a3 --- /dev/null +++ b/src/backend/taler-merchant-httpd_map.h @@ -0,0 +1,66 @@ +/* + This file is part of TALER + (C) 2014, 2015, 2016 INRIA + + 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 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, see <http://www.gnu.org/licenses/> +*/ +/** + * @file backend/taler-merchant-httpd_contract.c + * @brief HTTP serving layer mainly intended to communicate with the frontend + * @author Marcello Stanisci + */ + +#ifndef TALER_MERCHANT_HTTPD_MAP_H +#define TALER_MERCHANT_HTTPD_MAP_H +#include <microhttpd.h> +#include "taler-merchant-httpd.h" + + +/** + * Manage a /map/in request. Store in db a plain text contract + * and its hashcode. + * + * @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_map_in (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +/** + * Manage a /map/out request. Query the db and returns a plain + * text contract associated with the hashcode given as input + * + * @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_map_out (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + +/* end of taler-merchant-httpd_history.c */ +#endif diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index c6d8ab95..8fbed465 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -41,6 +41,14 @@ struct PostgresClosure }; +/** + * Extract error code. + * + * @param res postgres result object with error details + */ +#define EXTRACT_DB_ERROR(res) \ + PQresultErrorField(res, PG_DIAG_SQLSTATE) + /** * Log error from PostGres. @@ -137,6 +145,7 @@ postgres_drop_tables (void *cls) PG_EXEC_INDEX (pg, "DROP TABLE merchant_deposits;"); PG_EXEC_INDEX (pg, "DROP TABLE merchant_transactions;"); PG_EXEC_INDEX (pg, "DROP TABLE merchant_proofs;"); + PG_EXEC_INDEX (pg, "DROP TABLE merchant_contract_maps;"); return GNUNET_OK; } @@ -154,6 +163,13 @@ postgres_initialize (void *cls) /* Setup tables */ PG_EXEC (pg, + "CREATE TABLE IF NOT EXISTS merchant_contract_maps (" + "h_contract BYTEA NOT NULL CHECK (LENGTH(h_contract)=64)" + ",plain_contract BYTEA NOT NULL" + ",PRIMARY KEY (h_contract)" + ");"); + + PG_EXEC (pg, "CREATE TABLE IF NOT EXISTS merchant_transactions (" " transaction_id INT8" ",exchange_uri VARCHAR NOT NULL" @@ -262,6 +278,22 @@ postgres_initialize (void *cls) 5); PG_PREPARE (pg, + "insert_map", + "INSERT INTO merchant_contract_maps" + "(h_contract" + ",plain_contract)" + " VALUES " + "($1, $2)", + 2); + + PG_PREPARE (pg, + "find_contract", + "SELECT plain_contract FROM merchant_contract_maps" + " WHERE" + " h_contract=$1", + 1); + + PG_PREPARE (pg, "find_transactions_by_date", "SELECT" " transaction_id" @@ -365,6 +397,114 @@ postgres_initialize (void *cls) return GNUNET_OK; } +/** + * Retrieve plain contract given its hashcode + * + * @param cls closure + * @param h_contract hashcode of the contract to retrieve + * @param contract where to store the retrieved contract + * @return #GNUNET_OK on success, #GNUNET_NO if no contract is + * found, #GNUNET_SYSERR upon error + */ +static int +postgres_find_contract (void *cls, + json_t **contract, + struct GNUNET_HashCode *h_contract) +{ + struct PostgresClosure *pg = cls; + PGresult *result; + unsigned int i; + + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (h_contract), + GNUNET_PQ_query_param_end + }; + + result = GNUNET_PQ_exec_prepared (pg->conn, + "find_contract", + params); + i = PQntuples (result); + if (1 < i) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Mupltiple contracts share the same hashcode.\n"); + return GNUNET_SYSERR; + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "i, %d\n", i); + + if (0 == i) + return GNUNET_NO; + + /* FIXME, figure out how to pass back json_t's */ + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_json ("plain_contract", + contract), + GNUNET_PQ_result_spec_end + }; + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + 0)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * Insert a hash to contract map into the database + * + * @param cls closure + * @param h_contract hashcode of @a contract + * @param contract contract to store + * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + */ +static int +postgres_store_map (void *cls, + struct GNUNET_HashCode *h_contract, + const json_t *contract) +{ + struct PostgresClosure *pg = cls; + PGresult *result; + int ret; + + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (h_contract), + TALER_PQ_query_param_json (contract), + GNUNET_PQ_query_param_end + }; + + result = GNUNET_PQ_exec_prepared (pg->conn, + "insert_map", + params); + + /** + * We don't treat a unique_violation (code '23505') error as + * an actual error, since there is no problem if a frontend tries + * to store twice the same contract. That is especially needed + * when DB-less frontends perform replayed payments. + */ + if (PGRES_COMMAND_OK != PQresultStatus (result) + && (0 != memcmp ("23505", + EXTRACT_DB_ERROR (result), + 5))) + { + ret = GNUNET_SYSERR; + BREAK_DB_ERR (result); + } + else + { + ret = GNUNET_OK; + } + PQclear (result); + return ret; +} /** * Insert transaction data into the database. @@ -1229,6 +1369,8 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) plugin->find_transfers_by_id = &postgres_find_transfers_by_id; plugin->find_deposits_by_wtid = &postgres_find_deposits_by_wtid; plugin->find_proof_by_wtid = &postgres_find_proof_by_wtid; + plugin->store_map = &postgres_store_map; + plugin->find_contract = &postgres_find_contract; return plugin; } diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c index f67b0f7d..d30b7f5e 100644 --- a/src/backenddb/test_merchantdb.c +++ b/src/backenddb/test_merchantdb.c @@ -130,6 +130,11 @@ static json_t *deposit_proof; */ static json_t *transfer_proof; +/** + * A mock contract, not need to be well-formed + */ +static json_t *contract; + /** * Function called with information about a transaction. @@ -347,6 +352,20 @@ run (void *cls) json_object_set_new (transfer_proof, "test", json_string ("backenddb test B"))); + contract = json_object (); + + FAILIF (GNUNET_OK != + plugin->store_map (plugin->cls, + &h_contract, + contract)); + + json_t *out; + + FAILIF (GNUNET_OK != + plugin->find_contract (plugin->cls, + &out, + &h_contract)); + FAILIF (GNUNET_OK != plugin->store_transaction (plugin->cls, transaction_id, diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index eaf85caf..0ed6856e 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -28,6 +28,60 @@ #include <jansson.h> +/* ********************* /map/{in,out} *********************** */ + +struct TALER_MERCHANT_MapOutOperation; + +typedef void +(*TALER_MERCHANT_MapOperationCallback) (void *cls, + unsigned int http_status, + const json_t *body); + +/** + * Issue a /map/out request to the backend. + * + * @param ctx execution context + * @param backend_uri base URL of the merchant backend + * @param h_contract hashcode of `contract` + * @param map_in_cb callback which will work the response gotten from the backend + * @param map_in_cb_cls closure to pass to @a history_cb + * @return handle for this operation, NULL upon errors + */ +struct TALER_MERCHANT_MapOperation * +TALER_MERCHANT_map_out (struct GNUNET_CURL_Context *ctx, + const char *backend_uri, + const struct GNUNET_HashCode *h_contract, + TALER_MERCHANT_MapOperationCallback map_cb, + void *map_cb_cls); + +/** + * Issue a /map/in request to the backend. + * + * @param ctx execution context + * @param backend_uri base URL of the merchant backend + * @param contract contract to store + * @param h_contract hashcode of `contract` + * @param map_in_cb callback which will work the response gotten from the backend + * @param map_in_cb_cls closure to pass to @a history_cb + * @return handle for this operation, NULL upon errors + */ +struct TALER_MERCHANT_MapOperation * +TALER_MERCHANT_map_in (struct GNUNET_CURL_Context *ctx, + const char *backend_uri, + const json_t *contract, + const struct GNUNET_HashCode *h_contract, + TALER_MERCHANT_MapOperationCallback map_cb, + void *map_cb_cls); + +/** + * Cancel a /map/in request. + * + * @param mio handle to the request to be canceled + */ +void +TALER_MERCHANT_map_cancel (struct TALER_MERCHANT_MapOperation *mo); + + /* ********************* /contract *********************** */ diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 92375fb2..3923fde1 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -150,6 +150,34 @@ struct TALER_MERCHANTDB_Plugin /** + * Insert a hash to contract map into the database + * + * @param cls closure + * @param h_contract hashcode of @a contract + * @param contract contract to store + * @return #GNUNET_OK on success, #GNUNET_SYSERR upon error + */ + int + (*store_map) (void *cls, + struct GNUNET_HashCode *h_contract, + const json_t *contract); + + + /** + * Retrieve plain contract given its hashcode + * + * @param cls closure + * @param h_contract hashcode of the contract to retrieve + * @param contract where to store the retrieved contract + * @return #GNUNET_OK on success, #GNUNET_NO if no contract is + * found, #GNUNET_SYSERR upon error + */ + int + (*find_contract) (void *cls, + json_t **contract, + struct GNUNET_HashCode *h_contract); + + /** * Insert transaction data into the database. * * @param cls closure diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index d36eec22..d862365e 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -18,7 +18,8 @@ libtalermerchant_la_SOURCES = \ merchant_api_pay.c \ merchant_api_track_transaction.c \ merchant_api_track_transfer.c \ - merchant_api_history.c + merchant_api_history.c \ + merchant_api_map.c libtalermerchant_la_LIBADD = \ -ltalerexchange \ diff --git a/src/lib/merchant_api_contract.c b/src/lib/merchant_api_contract.c index 469539bf..c5db8d3b 100644 --- a/src/lib/merchant_api_contract.c +++ b/src/lib/merchant_api_contract.c @@ -94,9 +94,9 @@ handle_contract_finished (void *cls, h_contractp = NULL; switch (response_code) { - case 0: - break; - case MHD_HTTP_OK: + case 0: + break; + case MHD_HTTP_OK: { struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_json ("contract", &contract), @@ -104,7 +104,7 @@ handle_contract_finished (void *cls, GNUNET_JSON_spec_fixed_auto ("H_contract", &h_contract), GNUNET_JSON_spec_end() }; - + if (GNUNET_OK != GNUNET_JSON_parse (json, spec, @@ -117,34 +117,33 @@ handle_contract_finished (void *cls, h_contractp = &h_contract; sigp = &sig; } - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the merchant is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_FORBIDDEN: - break; - case MHD_HTTP_UNAUTHORIZED: - /* Nothing really to verify, merchant says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u\n", - (unsigned int) response_code); - GNUNET_break (0); - response_code = 0; - break; + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the merchant is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_FORBIDDEN: + break; + case MHD_HTTP_UNAUTHORIZED: + /* Nothing really to verify, merchant says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + (unsigned int) response_code); + GNUNET_break (0); + response_code = 0; } co->cb (co->cb_cls, response_code, @@ -155,7 +154,6 @@ handle_contract_finished (void *cls, h_contractp); if (NULL != contract) json_decref (contract); - TALER_MERCHANT_contract_sign_cancel (co); } diff --git a/src/lib/merchant_api_history.c b/src/lib/merchant_api_history.c index bf7505f4..39f9bd82 100644 --- a/src/lib/merchant_api_history.c +++ b/src/lib/merchant_api_history.c @@ -128,7 +128,6 @@ history_raw_cb (void *cls, response_code, TALER_JSON_get_error_code (json), json); - TALER_MERCHANT_history_cancel (ho); } @@ -171,8 +170,6 @@ TALER_MERCHANT_history (struct GNUNET_CURL_Context *ctx, return NULL; } - - if (NULL == (ho->job = GNUNET_CURL_job_add (ctx, eh, GNUNET_YES, diff --git a/src/lib/merchant_api_map.c b/src/lib/merchant_api_map.c new file mode 100644 index 00000000..d1aaf5a1 --- /dev/null +++ b/src/lib/merchant_api_map.c @@ -0,0 +1,230 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING.LGPL. If not, see + <http://www.gnu.org/licenses/> +*/ +/** + * @file lib/merchant_api_map.c + * @brief Implementation of the /map/{in,out} request of the merchant's HTTP API + * @author Marcello Stanisci + */ +#include "platform.h" +#include <curl/curl.h> +#include <jansson.h> +#include <microhttpd.h> /* just for HTTP status codes */ +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_curl_lib.h> +#include "taler_merchant_service.h" +#include <taler/taler_json_lib.h> + + +/** + * This structure acts like a "handle" for both /map/in and + * /map/out operations, as they only differ about the 'json_enc' + * field (which is just left NULL when not needed). + */ +struct TALER_MERCHANT_MapOperation +{ + /** + * Full URI, includes "/map/in". + */ + char *url; + + /** + * Request's body. Left NULL in case of /map/out. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct GNUNET_CURL_Job *job; + + /** + * Function to call with the result. + */ + TALER_MERCHANT_MapOperationCallback cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Reference to the execution context. + */ + struct GNUNET_CURL_Context *ctx; + +}; + + +/** + * Cancel a /map/{in,out} request. + * + * @param mio handle to the request to be canceled + */ +void +TALER_MERCHANT_map_cancel (struct TALER_MERCHANT_MapOperation *mo) +{ + if (NULL != mo->job) + { + GNUNET_CURL_job_cancel (mo->job); + mo->job = NULL; + } + GNUNET_free (mo->url); + GNUNET_free_non_null (mo->json_enc); + GNUNET_free (mo); +} + + +/** + * Function called when we're done processing the HTTP /map/{in,out} request. + * + * @param cls the `struct TALER_MERCHANT_MapInOperation` + * @param response_code HTTP response code, 0 on error + * @param json response body, should be NULL + */ +static void +handle_map_finished (void *cls, + long response_code, + const json_t *json) +{ + struct TALER_MERCHANT_MapOperation *mo = cls; + + /** + * As no data is supposed to be extracted from this + * call, we just invoke the provided callback from here. + */ + mo->cb (mo->cb_cls, + response_code, + json); +} + +/** + * Issue a /map/out request to the backend. + * + * @param ctx execution context + * @param backend_uri base URL of the merchant backend + * @param h_contract hashcode of `contract` + * @param map_in_cb callback which will work the response gotten from the backend + * @param map_in_cb_cls closure to pass to @a history_cb + * @return handle for this operation, NULL upon errors + */ +struct TALER_MERCHANT_MapOperation * +TALER_MERCHANT_map_out (struct GNUNET_CURL_Context *ctx, + const char *backend_uri, + const struct GNUNET_HashCode *h_contract, + TALER_MERCHANT_MapOperationCallback map_cb, + void *map_cb_cls) +{ + struct TALER_MERCHANT_MapOperation *mo; + CURL *eh; + char *hash_enc; + + mo = GNUNET_new (struct TALER_MERCHANT_MapOperation); + mo->ctx = ctx; + mo->cb = map_cb; + mo->cb_cls = map_cb_cls; + + hash_enc = GNUNET_STRINGS_data_to_string_alloc (h_contract, + sizeof (struct GNUNET_HashCode)); + GNUNET_asprintf (&mo->url, + "%s/map/out?h_contract=%s", + backend_uri, + hash_enc); + eh = curl_easy_init (); + if (CURLE_OK != curl_easy_setopt (eh, + CURLOPT_URL, + mo->url)) + { + GNUNET_break (0); + return NULL; + } + + if (NULL == (mo->job = GNUNET_CURL_job_add (ctx, + eh, + GNUNET_YES, + &handle_map_finished, + mo))) + { + GNUNET_break (0); + return NULL; + } + return mo; +} + +/** + * Issue a /map/in request to the backend. + * + * @param ctx execution context + * @param backend_uri base URL of the merchant backend + * @param contract contract to store + * @param h_contract hashcode of `contract` + * @param map_in_cb callback which will work the response gotten from the backend + * @param map_in_cb_cls closure to pass to @a history_cb + * @return handle for this operation, NULL upon errors + */ +struct TALER_MERCHANT_MapOperation * +TALER_MERCHANT_map_in (struct GNUNET_CURL_Context *ctx, + const char *backend_uri, + const json_t *contract, + const struct GNUNET_HashCode *h_contract, + TALER_MERCHANT_MapOperationCallback map_cb, + void *map_cb_cls) +{ + struct TALER_MERCHANT_MapOperation *mo; + CURL *eh; + json_t *req; + + mo = GNUNET_new (struct TALER_MERCHANT_MapOperation); + mo->ctx = ctx; + mo->cb = map_cb; + mo->cb_cls = map_cb_cls; + + GNUNET_asprintf (&mo->url, + "%s%s", + backend_uri, + "/map/in"); + + // build final json + req = json_pack ("{s:o, s:o}", + "contract", contract, + "h_contract", GNUNET_JSON_from_data_auto (h_contract)); + + GNUNET_assert (NULL != + (mo->json_enc = json_dumps (req, JSON_COMPACT)) + ); + + json_decref (req); + eh = curl_easy_init (); + + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + mo->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + mo->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (mo->json_enc))); + mo->job = GNUNET_CURL_job_add (ctx, + eh, + GNUNET_YES, + &handle_map_finished, + mo); + return mo; +} diff --git a/src/lib/merchant_api_pay.c b/src/lib/merchant_api_pay.c index 0c06326a..5475ec70 100644 --- a/src/lib/merchant_api_pay.c +++ b/src/lib/merchant_api_pay.c @@ -243,7 +243,6 @@ handle_pay_finished (void *cls, response_code, TALER_JSON_get_error_code (json), json); - TALER_MERCHANT_pay_cancel (ph); } diff --git a/src/lib/merchant_api_track_transaction.c b/src/lib/merchant_api_track_transaction.c index 4aed3c68..cb864410 100644 --- a/src/lib/merchant_api_track_transaction.c +++ b/src/lib/merchant_api_track_transaction.c @@ -161,7 +161,6 @@ parse_track_transaction_ok (struct TALER_MERCHANT_TrackTransactionHandle *tdo, transfers); free_transfers (num_transfers, transfers); - TALER_MERCHANT_track_transaction_cancel (tdo); return GNUNET_OK; } @@ -223,7 +222,6 @@ handle_track_transaction_finished (void *cls, json, 0, NULL); - TALER_MERCHANT_track_transaction_cancel (tdo); } diff --git a/src/lib/merchant_api_track_transfer.c b/src/lib/merchant_api_track_transfer.c index e7a1fdc3..ee6c979c 100644 --- a/src/lib/merchant_api_track_transfer.c +++ b/src/lib/merchant_api_track_transfer.c @@ -147,7 +147,6 @@ check_track_transfer_response_ok (struct TALER_MERCHANT_TrackTransferHandle *wdh details); } GNUNET_JSON_parse_free (inner_spec); - TALER_MERCHANT_track_transfer_cancel (wdh); return GNUNET_OK; } @@ -206,7 +205,6 @@ handle_track_transfer_finished (void *cls, NULL, 0, NULL); - TALER_MERCHANT_track_transfer_cancel (tdo); } diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c index 45f17a64..f2081c2d 100644 --- a/src/lib/test_merchant_api.c +++ b/src/lib/test_merchant_api.c @@ -55,6 +55,20 @@ */ #define CONTRACT_MAX_SIZE 1000 +#define RND_BLK(ptr) \ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) + + +/** + * Handle to database. + */ +struct TALER_MERCHANTDB_Plugin *db; + +/** + * Configuration handle. + */ +struct GNUNET_CONFIGURATION_Handle *cfg; + /** * Handle to access the exchange. */ @@ -118,6 +132,18 @@ enum OpCode OC_END = 0, /** + * Ask the backend to store a contract and its hashcode into + * the database. + */ + OC_MAP_IN, + + /** + * Ask the backend to retrieve a contract from the database, according + * to its hashcode. + */ + OC_MAP_OUT, + + /** * Add funds to a reserve by (faking) incoming wire transfer. */ OC_ADMIN_ADD_INCOMING, @@ -292,6 +318,21 @@ struct Command } admin_add_incoming; /** + * Information for both #OC_MAP_{IN,OUT} command. + */ + struct + { + + /** + * Reference to a contract we need to hash and store. + */ + const char *contract_reference; + + struct TALER_MERCHANT_MapOperation *mo; + + } map; + + /** * Information for a #OC_WITHDRAW_STATUS command. */ struct @@ -611,47 +652,6 @@ fail (struct InterpreterState *is) GNUNET_SCHEDULER_shutdown (); } -/** - * Get a new contract proposal for each OC_CONTRACT - * in the list of command. It's used when we run multiple - * instances beacuse we can't have the same transaction_id - * for two instances. - * - * @param cmds the list of commands - */ -void -get_new_contracts (struct Command *cmds) -{ - unsigned int i; - unsigned int d = 0; - struct Command *cmd; - #define DELTA 1000 - - for (i=0;OC_END != (cmd = &cmds[i])->oc;i++) - if ( (NULL != cmd->label) && - (OC_CONTRACT == cmd->oc) ) - { - - if (0 == strcmp (cmd->label, "create-contract-2")) - d = DELTA; - - snprintf (cmd->details.contract.proposal, - CONTRACT_MAX_SIZE, - "{\ - \"max_fee\":\ - {\"currency\":\"EUR\", \"value\":0, \"fraction\":50000000},\ - \"transaction_id\":%d,\ - \"timestamp\":\"\\/Date(42)\\/\",\ - \"refund_deadline\":\"\\/Date(0)\\/\",\ - \"expiry\":\"\\/Date(999999999)\\/\",\ - \"amount\":{\"currency\":\"EUR\", \"value\":5, \"fraction\":0},\ - \"products\":\ - [ {\"description\":\"ice cream\", \"value\":\"{EUR:5}\"} ]\ - }", - instance_idx + d); - } -} - /** * Find a command by label. @@ -1273,6 +1273,28 @@ track_transfer_cb (void *cls, next_command (is); } +/** + * Callback for /map/in issued at backend. Just check + * whether response code is as expected. + * + * @param cls closure + * @param http_status HTTP status code we got + */ +static void +map_cb (void *cls, + unsigned int http_status, + const json_t *json) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + cmd->details.map.mo = NULL; + + if (cmd->expected_response_code != http_status) + fail (is); + + next_command (is); +} /** * Function called with detailed wire transfer data. @@ -1472,10 +1494,60 @@ interpreter_run (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Switching instance: '%s'\n", instance); - /*get_new_contracts(is->commands);*/ + is->task = GNUNET_SCHEDULER_add_now (interpreter_run, is); return; + + case OC_MAP_IN: + case OC_MAP_OUT: + { + struct GNUNET_HashCode h_proposal; + json_error_t error; + json_t *proposal; + + GNUNET_assert (NULL != cmd->details.map.contract_reference); + ref = find_command (is, cmd->details.map.contract_reference); + GNUNET_assert (NULL != ref); + + /** + * WARNING, make sure what is hashed here, is exactly the same + * contract hashed then by /map/in handler. + */ + proposal = json_loads (ref->details.contract.proposal, + JSON_REJECT_DUPLICATES, + &error); + + GNUNET_assert (GNUNET_SYSERR != + TALER_JSON_hash (proposal, &h_proposal)); + + if (OC_MAP_IN == cmd->oc) + { + + if (MHD_HTTP_UNPROCESSABLE_ENTITY == cmd->expected_response_code) + RND_BLK (&h_proposal); + + GNUNET_assert (NULL != + (cmd->details.map.mo + = TALER_MERCHANT_map_in (ctx, + MERCHANT_URI, + proposal, + &h_proposal, + map_cb, + is))); + } + else + GNUNET_assert (NULL != + (cmd->details.map.mo + = TALER_MERCHANT_map_out (ctx, + MERCHANT_URI, + &h_proposal, + map_cb, + is))); + + } + return; + case OC_ADMIN_ADD_INCOMING: if (NULL != cmd->details.admin_add_incoming.reserve_reference) @@ -1961,6 +2033,18 @@ do_shutdown (void *cls) case OC_END: GNUNET_assert (0); break; + case OC_MAP_IN: + case OC_MAP_OUT: + if (NULL != cmd->details.map.mo) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + i, + cmd->label); + TALER_MERCHANT_map_cancel (cmd->details.map.mo); + } + break; + case OC_ADMIN_ADD_INCOMING: if (NULL != cmd->details.admin_add_incoming.aih) { @@ -2102,6 +2186,10 @@ do_shutdown (void *cls) } TALER_FAKEBANK_stop (fakebank); fakebank = NULL; + + db->drop_tables (db->cls); + TALER_MERCHANTDB_plugin_unload (db); + GNUNET_CONFIGURATION_destroy (cfg); } @@ -2212,8 +2300,15 @@ run (void *cls) .details.pay.coin_ref = "withdraw-coin-1", .details.pay.amount_with_fee = "EUR:5", .details.pay.amount_without_fee = "EUR:4.99" }, - /* Create another contract */ + /* Store contract-1 */ + { + .oc = OC_MAP_IN, + .label = "store-contract-1", + .expected_response_code = MHD_HTTP_OK, + .details.map.contract_reference = "create-contract-1" }, + + /* Create another contract */ { .oc = OC_CONTRACT, .label = "create-contract-2", .expected_response_code = MHD_HTTP_OK, @@ -2227,6 +2322,7 @@ run (void *cls) \"amount\":{\"currency\":\"EUR\", \"value\":5, \"fraction\":0},\ \"products\":\ [ {\"description\":\"ice cream\", \"value\":\"{EUR:5}\"} ] }" }, + /* Try to double-spend the 5 EUR coin at the same merchant (but different transaction ID) */ { .oc = OC_PAY, @@ -2245,6 +2341,14 @@ run (void *cls) .details.admin_add_incoming.transfer_details = "{ \"uuid\": 2}", .details.admin_add_incoming.amount = "EUR:1" }, + /* Store contract-1 */ + { + .oc = OC_MAP_IN, + .label = "store-contract-2", + .expected_response_code = MHD_HTTP_OK, + .details.map.contract_reference = "create-contract-2", + }, + /* Add another 4.01 EUR to reserve #2 */ { .oc = OC_ADMIN_ADD_INCOMING, .label = "create-reserve-2b", @@ -2261,6 +2365,13 @@ run (void *cls) .details.reserve_withdraw.reserve_reference = "create-reserve-2", .details.reserve_withdraw.amount = "EUR:5" }, + /* Fetch contract-1 */ + { + .oc = OC_MAP_OUT, + .label = "fetch-contract-2", + .expected_response_code = MHD_HTTP_OK, + .details.map.contract_reference = "create-contract-2" }, + /* Check nothing happened on the bank side so far */ { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY, .label = "check_bank_empty" }, @@ -2390,6 +2501,34 @@ run (void *cls) .details.history.date.abs_value_us = 43 * 1000LL * 1000LL, .details.history.nresult = 0 }, + /* Retrieve via /map/out a contract NOT stored previously. */ + { + .oc = OC_MAP_OUT, + .label = "fetch-contract-not-found", + .expected_response_code = MHD_HTTP_NOT_FOUND, + .details.map.contract_reference = "create-contract-3" }, + + /* Create another contract, NOT to be stored. */ + { .oc = OC_CONTRACT, + .label = "create-contract-3", + .expected_response_code = MHD_HTTP_OK, + .details.contract.proposal = "{\ + \"max_fee\":\ + {\"currency\":\"EUR\", \"value\":0, \"fraction\":10000},\ + \"transaction_id\":3,\ + \"timestamp\":\"\\/Date(42)\\/\",\ + \"refund_deadline\":\"\\/Date(0)\\/\",\ + \"pay_deadline\":\"\\/Date(0)\\/\",\ + \"amount\":{\"currency\":\"EUR\", \"value\":1, \"fraction\":0},\ + \"products\":\ + [ {\"description\":\"bogus\", \"value\":\"{EUR:1}\"} ] }" }, + + /* Try to store a contract passing a bogus hashcode. */ + { + .oc = OC_MAP_IN, + .label = "store-contract-bogus", + .expected_response_code = MHD_HTTP_UNPROCESSABLE_ENTITY, + .details.map.contract_reference = "create-contract-3" }, /* end of testcase */ { .oc = OC_END } }; @@ -2440,8 +2579,6 @@ main (int argc, struct GNUNET_OS_Process *proc; struct GNUNET_OS_Process *exchanged; struct GNUNET_OS_Process *merchantd; - struct TALER_MERCHANTDB_Plugin *db; - struct GNUNET_CONFIGURATION_Handle *cfg; unsigned int cnt; struct GNUNET_SIGNAL_Context *shc_chld; @@ -2478,9 +2615,6 @@ main (int argc, GNUNET_CONFIGURATION_destroy (cfg); return 77; } - TALER_MERCHANTDB_plugin_unload (db); - GNUNET_CONFIGURATION_destroy (cfg); - proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, NULL, diff --git a/src/merchant-tools/Makefile.am b/src/merchant-tools/Makefile.am new file mode 100644 index 00000000..6b8162e9 --- /dev/null +++ b/src/merchant-tools/Makefile.am @@ -0,0 +1,16 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +bin_PROGRAMS = \ + taler-merchant-dbinit + +taler_merchant_dbinit_SOURCES = \ + taler-merchant-dbinit.c + + +taler_merchant_dbinit_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/backenddb/libtalermerchantdb.la \ + -lgnunetutil \ + -ltalerutil \ + -ltalerpq diff --git a/src/merchant-tools/taler-merchant-dbinit b/src/merchant-tools/taler-merchant-dbinit new file mode 100755 index 00000000..99ac8fbd --- /dev/null +++ b/src/merchant-tools/taler-merchant-dbinit @@ -0,0 +1,228 @@ +#! /bin/bash + +# taler-merchant-dbinit - temporary wrapper script for .libs/taler-merchant-dbinit +# Generated by libtool (GNU libtool) 2.4.2 Debian-2.4.2-1.11 +# +# The taler-merchant-dbinit program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command="(cd /home/marcello/merchant/src/merchant-tools; { test -z \"\${LIBRARY_PATH+set}\" || unset LIBRARY_PATH || { LIBRARY_PATH=; export LIBRARY_PATH; }; }; { test -z \"\${COMPILER_PATH+set}\" || unset COMPILER_PATH || { COMPILER_PATH=; export COMPILER_PATH; }; }; { test -z \"\${GCC_EXEC_PREFIX+set}\" || unset GCC_EXEC_PREFIX || { GCC_EXEC_PREFIX=; export GCC_EXEC_PREFIX; }; }; { test -z \"\${LD_RUN_PATH+set}\" || unset LD_RUN_PATH || { LD_RUN_PATH=; export LD_RUN_PATH; }; }; LD_LIBRARY_PATH=/home/marcello/tmp/lib/; export LD_LIBRARY_PATH; PATH=/home/marcello/local/bin:/home/marcello/.local/bin:/home/marcello/local/bin:/home/marcello/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games; export PATH; gcc -std=gnu99 -Wall -Wall -O0 -g -o \$progdir/\$file taler-merchant-dbinit.o -L/home/marcello/local/lib -L/usr/local/lib /home/marcello/local/lib/libgcrypt.so -L/usr/lib/i386-linux-gnu /home/marcello/local/lib/libgpg-error.so ../../src/backenddb/.libs/libtalermerchantdb.so /home/marcello/local/lib/libgnunetutil.so /home/marcello/local/lib/libtalerutil.so /home/marcello/local/lib/libtalerpq.so -Wl,-rpath -Wl,/home/marcello/local/lib -Wl,-rpath -Wl,/home/marcello/merchant/src/backenddb/.libs -Wl,-rpath -Wl,/home/marcello/local/lib)" + +# This environment variable determines our operation mode. +if test "$libtool_install_magic" = "%%%MAGIC variable%%%"; then + # install mode needs the following variables: + generated_by_libtool_version='2.4.2' + notinst_deplibs=' ../../src/backenddb/libtalermerchantdb.la' +else + # When we are sourced in execute mode, $file and $ECHO are already set. + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + file="$0" + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + ECHO="printf %s\\n" + fi + +# Very basic option parsing. These options are (a) specific to +# the libtool wrapper, (b) are identical between the wrapper +# /script/ and the wrapper /executable/ which is used only on +# windows platforms, and (c) all begin with the string --lt- +# (application programs are unlikely to have options which match +# this pattern). +# +# There are only two supported options: --lt-debug and +# --lt-dump-script. There is, deliberately, no --lt-help. +# +# The first argument to this parsing function should be the +# script's ../../libtool value, followed by no. +lt_option_debug= +func_parse_lt_options () +{ + lt_script_arg0=$0 + shift + for lt_opt + do + case "$lt_opt" in + --lt-debug) lt_option_debug=1 ;; + --lt-dump-script) + lt_dump_D=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%/[^/]*$%%'` + test "X$lt_dump_D" = "X$lt_script_arg0" && lt_dump_D=. + lt_dump_F=`$ECHO "X$lt_script_arg0" | /bin/sed -e 's/^X//' -e 's%^.*/%%'` + cat "$lt_dump_D/$lt_dump_F" + exit 0 + ;; + --lt-*) + $ECHO "Unrecognized --lt- option: '$lt_opt'" 1>&2 + exit 1 + ;; + esac + done + + # Print the debug banner immediately: + if test -n "$lt_option_debug"; then + echo "taler-merchant-dbinit:taler-merchant-dbinit:${LINENO}: libtool wrapper (GNU libtool) 2.4.2 Debian-2.4.2-1.11" 1>&2 + fi +} + +# Used when --lt-debug. Prints its arguments to stdout +# (redirection is the responsibility of the caller) +func_lt_dump_args () +{ + lt_dump_args_N=1; + for lt_arg + do + $ECHO "taler-merchant-dbinit:taler-merchant-dbinit:${LINENO}: newargv[$lt_dump_args_N]: $lt_arg" + lt_dump_args_N=`expr $lt_dump_args_N + 1` + done +} + +# Core function for launching the target application +func_exec_program_core () +{ + + if test -n "$lt_option_debug"; then + $ECHO "taler-merchant-dbinit:taler-merchant-dbinit:${LINENO}: newargv[0]: $progdir/$program" 1>&2 + func_lt_dump_args ${1+"$@"} 1>&2 + fi + exec "$progdir/$program" ${1+"$@"} + + $ECHO "$0: cannot exec $program $*" 1>&2 + exit 1 +} + +# A function to encapsulate launching the target application +# Strips options in the --lt-* namespace from $@ and +# launches target application with the remaining arguments. +func_exec_program () +{ + case " $* " in + *\ --lt-*) + for lt_wr_arg + do + case $lt_wr_arg in + --lt-*) ;; + *) set x "$@" "$lt_wr_arg"; shift;; + esac + shift + done ;; + esac + func_exec_program_core ${1+"$@"} +} + + # Parse options + func_parse_lt_options "$0" ${1+"$@"} + + # Find the directory that this script lives in. + thisdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + test "x$thisdir" = "x$file" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=`ls -ld "$file" | /bin/sed -n 's/.*-> //p'` + while test -n "$file"; do + destdir=`$ECHO "$file" | /bin/sed 's%/[^/]*$%%'` + + # If there was a directory component, then change thisdir. + if test "x$destdir" != "x$file"; then + case "$destdir" in + [\\/]* | [A-Za-z]:[\\/]*) thisdir="$destdir" ;; + *) thisdir="$thisdir/$destdir" ;; + esac + fi + + file=`$ECHO "$file" | /bin/sed 's%^.*/%%'` + file=`ls -ld "$thisdir/$file" | /bin/sed -n 's/.*-> //p'` + done + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no + if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then + # special case for '.' + if test "$thisdir" = "."; then + thisdir=`pwd` + fi + # remove .libs from thisdir + case "$thisdir" in + *[\\/].libs ) thisdir=`$ECHO "$thisdir" | /bin/sed 's%[\\/][^\\/]*$%%'` ;; + .libs ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=`cd "$thisdir" && pwd` + test -n "$absdir" && thisdir="$absdir" + + program=lt-'taler-merchant-dbinit' + progdir="$thisdir/.libs" + + if test ! -f "$progdir/$program" || + { file=`ls -1dt "$progdir/$program" "$progdir/../$program" 2>/dev/null | /bin/sed 1q`; \ + test "X$file" != "X$progdir/$program"; }; then + + file="$$-$program" + + if test ! -d "$progdir"; then + mkdir "$progdir" + else + rm -f "$progdir/$file" + fi + + # relink executable if necessary + if test -n "$relink_command"; then + if relink_command_output=`eval $relink_command 2>&1`; then : + else + printf %s\n "$relink_command_output" >&2 + rm -f "$progdir/$file" + exit 1 + fi + fi + + mv -f "$progdir/$file" "$progdir/$program" 2>/dev/null || + { rm -f "$progdir/$program"; + mv -f "$progdir/$file" "$progdir/$program"; } + rm -f "$progdir/$file" + fi + + if test -f "$progdir/$program"; then + if test "$libtool_execute_magic" != "%%%MAGIC variable%%%"; then + # Run the actual program with our arguments. + func_exec_program ${1+"$@"} + fi + else + # The program doesn't exist. + $ECHO "$0: error: \`$progdir/$program' does not exist" 1>&2 + $ECHO "This script is just a wrapper for $program." 1>&2 + $ECHO "See the libtool documentation for more information." 1>&2 + exit 1 + fi +fi diff --git a/src/merchant-tools/taler-merchant-dbinit.c b/src/merchant-tools/taler-merchant-dbinit.c new file mode 100644 index 00000000..5bffe9d1 --- /dev/null +++ b/src/merchant-tools/taler-merchant-dbinit.c @@ -0,0 +1,115 @@ +/* + 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 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, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant-tools/taler-merchant-dbinit.c + * @brief Create tables for the merchant database. + * @author Florian Dold + * @author Marcello Stanisci + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_merchantdb_plugin.h" + + +/** + * Return value from main(). + */ +static int global_ret; + +/** + * -r option: do full DB reset + */ +static int reset_db; + +/** + * Main function that will be run. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct TALER_MERCHANTDB_Plugin *plugin; + + if (NULL == + (plugin = TALER_MERCHANTDB_plugin_load (cfg))) + { + fprintf (stderr, + "Failed to initialize database plugin.\n"); + global_ret = 1; + return; + } + if (reset_db) + (void) plugin->drop_tables (plugin->cls); + if (GNUNET_OK != + plugin->initialize (plugin->cls)) + { + fprintf (stderr, + "Failed to initialize database.\n"); + TALER_MERCHANTDB_plugin_unload (plugin); + global_ret = 1; + return; + } + + TALER_MERCHANTDB_plugin_unload (plugin); +} + + +/** + * The main function of the database initialization tool. + * Used to initialize the Taler Exchange's database. + * + * @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) +{ + const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'r', "reset", NULL, + "reset database (DANGEROUS: all existing data is lost!)", 0, + &GNUNET_GETOPT_set_one, &reset_db}, + GNUNET_GETOPT_OPTION_END + }; + + /* force linker to link against libtalerutil; if we do + not do this, the linker may "optimize" libtalerutil + away and skip #TALER_OS_init(), which we do need */ + (void) TALER_project_data_default (); + GNUNET_assert (GNUNET_OK == + GNUNET_log_setup ("taler-merchant-dbinit", + "INFO", + NULL)); + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, + "taler-merchant-dbinit", + "Initialize Taler merchant database", + options, + &run, NULL)) + return 1; + return global_ret; +} + + +/* end of taler-exchange-dbinit.c */ |