diff --git a/INSTALL b/INSTALL index a1e89e18a..209984075 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1,7 @@ Installation Instructions ************************* -Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, @@ -12,8 +12,8 @@ without warranty of any kind. Basic Installation ================== - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following + Briefly, the shell command `./configure && make && make install' +should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented @@ -309,9 +309,10 @@ causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== @@ -367,4 +368,3 @@ operates. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. - diff --git a/configure.ac b/configure.ac index 5835cb635..f69bcefd2 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,13 @@ AS_IF([test $libgnunetutil != 1], *** ]])]) +TALER_LIB_LDFLAGS="-export-dynamic -no-undefined" +TALER_PLUGIN_LDFLAGS="-export-dynamic -avoid-version -module -no-undefined" + +AC_SUBST(TALER_LIB_LDFLAGS) +AC_SUBST(TALER_PLUGIN_LDFLAGS) + + # check for libmicrohttpd microhttpd=0 AC_MSG_CHECKING([for microhttpd]) diff --git a/src/include/taler_util.h b/src/include/taler_util.h index 3569bd782..e46583989 100644 --- a/src/include/taler_util.h +++ b/src/include/taler_util.h @@ -99,4 +99,16 @@ TALER_config_get_denom (struct GNUNET_CONFIGURATION_Handle *cfg, struct TALER_Amount *denom); +/** + * Get the path to a specific Taler installation directory or, with + * #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation + * directory. + * + * @param dirkind what kind of directory is desired? + * @return a pointer to the dir path (to be freed by the caller) + */ +char * +TALER_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind); + + #endif diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index 2436d7f37..c4f99af63 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -1,18 +1,31 @@ # This Makefile.am is in the public domain AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/pq/ $(POSTGRESQL_CPPFLAGS) +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ + libtaler_plugin_mintdb_postgres.la + +libtaler_plugin_mintdb_postgres_la_SOURCES = \ + plugin_mintdb_postgres.c +libtaler_plugin_mintdb_postgres_la_LIBADD = \ + $(LTLIBINTL) +libtaler_plugin_mintdb_postgres_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ + -lpq \ + -lgnunetutil + lib_LTLIBRARIES = \ libtalermint_common.la -# Note: mint_db.c should become a plugin soon! (#3608) libtalermint_common_la_SOURCES = \ key_io.c key_io.h \ - mint_db.c + plugin.c plugin.h \ + taler_mintdb_plugin.h libtalermint_common_la_LIBADD = \ $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetutil \ - -lpq + -lgnunetutil libtalermint_common_la_LDFLAGS = \ $(POSTGRESQL_LDFLAGS) \ diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h deleted file mode 100644 index d11ee8fe9..000000000 --- a/src/mint/mint_db.h +++ /dev/null @@ -1,976 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 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 -*/ -/** - * @file mint/mint_db.h - * @brief Low-level (statement-level) database access for the mint - * @author Florian Dold - * @author Christian Grothoff - */ -#ifndef MINT_DB_H -#define MINT_DB_H - -#include -#include -#include -#include "taler_util.h" - -#define TALER_TEMP_SCHEMA_NAME "taler_temporary" - -/** - * Initialize database subsystem. - * - * @param connection_cfg configuration for the DB - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_init (const char *connection_cfg); - - -/** - * Get the thread-local database-handle. - * Connect to the db if the connection does not exist yet. - * - * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the - * database default one - * @param the database connection, or NULL on error - */ -PGconn * -TALER_MINT_DB_get_connection (int temporary); - - -/** - * Drop the temporary taler schema. This is only useful for testcases - * - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_DB_drop_temporary (PGconn *db); - - -/** - * Create the necessary tables if they are not present - * - * @param temporary should we use a temporary schema - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_DB_create_tables (int temporary); - -/** - * Setup prepared statements. FIXME: should this be part of the API, - * or just internal to "TALER_MINT_DB_get_connection()"? - * - * @param db_conn connection handle to initialize - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure - */ -int -TALER_MINT_DB_prepare (PGconn *db_conn); - - -/** - * Start a transaction. - * - * @param db_conn connection to use - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_transaction (PGconn *db_conn); - - -/** - * Commit a transaction. - * - * @param db_conn connection to use - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_commit (PGconn *db_conn); - - -/** - * Abort/rollback a transaction. - * - * @param db_conn connection to use - */ -void -TALER_MINT_DB_rollback (PGconn *db_conn); - - -/** - * Information we keep on bank transfer(s) that established a reserve. - */ -struct BankTransfer -{ - - /** - * Public key of the reserve that was filled. - */ - struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - - /** - * Amount that was transferred to the mint. - */ - struct TALER_Amount amount; - - /** - * Detailed wire information about the transaction. - */ - const json_t *wire; - -}; - - -/* FIXME: add functions to add bank transfers to our DB - (and to test if we already did add one) (#3633/#3717) */ - -/** - * A summary of a Reserve - */ -struct Reserve -{ - /** - * The reserve's public key. This uniquely identifies the reserve - */ - struct GNUNET_CRYPTO_EddsaPublicKey *pub; - - /** - * The balance amount existing in the reserve - */ - struct TALER_Amount balance; - - /** - * The expiration date of this reserve - */ - struct GNUNET_TIME_Absolute expiry; -}; - - -/** - * Information we keep for a withdrawn coin to reproduce - * the /withdraw operation if needed, and to have proof - * that a reserve was drained by this amount. - */ -struct CollectableBlindcoin -{ - - /** - * Our signature over the (blinded) coin. - */ - struct GNUNET_CRYPTO_rsa_Signature *sig; - - /** - * Denomination key (which coin was generated). - */ - struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; - - /** - * Public key of the reserve that was drained. - */ - struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - - /** - * Hash over the blinded message, needed to verify - * the @e reserve_sig. - */ - struct GNUNET_HashCode h_coin_envelope; - - /** - * Signature confirming the withdrawl, matching @e reserve_pub, - * @e denom_pub and @e h_coin_envelope. - */ - struct GNUNET_CRYPTO_EddsaSignature reserve_sig; -}; - - -/** - * Get the summary of a reserve. - * - * @param db the database connection handle - * @param reserve the reserve data. The public key of the reserve should be set - * in this structure; it is used to query the database. The balance - * and expiration are then filled accordingly. - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -int -TALER_MINT_DB_reserve_get (PGconn *db, - struct Reserve *reserve); - - -/** - * Insert a incoming transaction into reserves. New reserves are also created - * through this function. - * - * @param db the database connection handle - * @param reserve the reserve structure. The public key of the reserve should - * be set here. Upon successful execution of this function, the - * balance and expiration of the reserve will be updated. - * @param balance the amount that has to be added to the reserve - * @param expiry the new expiration time for the reserve - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures - */ -int -TALER_MINT_DB_reserves_in_insert (PGconn *db, - struct Reserve *reserve, - const struct TALER_Amount *balance, - const struct GNUNET_TIME_Absolute expiry); - - -/** - * Locate the response for a /withdraw request under the - * key of the hash of the blinded message. - * - * @param db_conn database connection to use - * @param h_blind hash of the blinded message - * @param collectable corresponding collectable coin (blind signature) - * if a coin is found - * @return #GNUNET_SYSERR on internal error - * #GNUNET_NO if the collectable was not found - * #GNUNET_YES on success - */ -int -TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, - const struct GNUNET_HashCode *h_blind, - struct CollectableBlindcoin *collectable); - - -/** - * Store collectable bit coin under the corresponding - * hash of the blinded message. - * - * @param db_conn database connection to use - * @param h_blind hash of the blinded message - * @param withdraw amount by which the reserve will be withdrawn with this - * transaction - * @param collectable corresponding collectable coin (blind signature) - * if a coin is found - * @return #GNUNET_SYSERR on internal error - * #GNUNET_NO if the collectable was not found - * #GNUNET_YES on success - */ -int -TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, - const struct GNUNET_HashCode *h_blind, - struct TALER_Amount withdraw, - const struct CollectableBlindcoin *collectable); - - - -/** - * Types of operations on a reserved. - */ -enum TALER_MINT_DB_ReserveOperation -{ - /** - * Money was deposited into the reserve via a bank transfer. - */ - TALER_MINT_DB_RO_BANK_TO_MINT = 0, - - /** - * A Coin was withdrawn from the reserve using /withdraw. - */ - TALER_MINT_DB_RO_WITHDRAW_COIN = 1 -}; - - -/** - * Reserve history as a linked list. Lists all of the transactions - * associated with this reserve (such as the bank transfers that - * established the reserve and all /withdraw operations we have done - * since). - */ -struct ReserveHistory -{ - - /** - * Next entry in the reserve history. - */ - struct ReserveHistory *next; - - /** - * Type of the event, determins @e details. - */ - enum TALER_MINT_DB_ReserveOperation type; - - /** - * Details of the operation, depending on @e type. - */ - union - { - - /** - * Details about a bank transfer to the mint. - */ - struct BankTransfer *bank; - - /** - * Details about a /withdraw operation. - */ - struct CollectableBlindcoin *withdraw; - - } details; - -}; - - -/** - * Get all of the transaction history associated with the specified - * reserve. - * - * @param db_conn connection to use - * @param reserve_pub public key of the reserve - * @return known transaction history (NULL if reserve is unknown) - */ -struct ReserveHistory * -TALER_MINT_DB_get_reserve_history (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub); - - -/** - * Free memory associated with the given reserve history. - * - * @param rh history to free. - */ -void -TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh); - - -/** - * Specification for a /deposit operation. - */ -struct Deposit -{ - /** - * Information about the coin that is being deposited. - */ - struct TALER_CoinPublicInfo coin; - - /** - * ECDSA signature affirming that the customer intends - * this coin to be deposited at the merchant identified - * by @e h_wire in relation to the contract identified - * by @e h_contract. - */ - struct GNUNET_CRYPTO_EcdsaSignature csig; - - /** - * Public key of the merchant. Enables later identification - * of the merchant in case of a need to rollback transactions. - */ - struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub; - - /** - * Hash over the contract between merchant and customer - * (remains unknown to the Mint). - */ - struct GNUNET_HashCode h_contract; - - /** - * Hash of the (canonical) representation of @e wire, used - * to check the signature on the request. Generated by - * the mint from the detailed wire data provided by the - * merchant. - */ - struct GNUNET_HashCode h_wire; - - /** - * Detailed wire information for executing the transaction. - */ - const json_t *wire; - - /** - * Merchant-generated transaction ID to detect duplicate - * transactions. - */ - uint64_t transaction_id; - - /** - * Fraction of the coin's remaining value to be deposited. - * The coin is identified by @e coin_pub. - */ - struct TALER_Amount amount; - -}; - - -/** - * Check if we have the specified deposit already in the database. - * - * @param db_conn database connection - * @param deposit deposit to search for - * @return #GNUNET_YES if we know this operation, - * #GNUNET_NO if this deposit is unknown to us, - * #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_have_deposit (PGconn *db_conn, - const struct Deposit *deposit); - - -/** - * Insert information about deposited coin into the - * database. - * - * @param db_conn connection to the database - * @param deposit deposit information to store - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -int -TALER_MINT_DB_insert_deposit (PGconn *db_conn, - const struct Deposit *deposit); - - - -/** - * Global information for a refreshing session. Includes - * dimensions of the operation, security parameters and - * client signatures from "/refresh/melt" and "/refresh/commit". - */ -struct RefreshSession -{ - /** - * Signature over the commitments by the client, - * only valid if @e has_commit_sig is set. - */ - struct GNUNET_CRYPTO_EddsaSignature commit_sig; - - /** - * Hash over coins to melt and coins to create of the - * refresh session. - */ - struct GNUNET_HashCode session_hash; - - /** - * Signature over the melt by the client. - */ - struct GNUNET_CRYPTO_EddsaSignature melt_sig; - - /** - * Number of coins we are melting. - */ - uint16_t num_oldcoins; - - /** - * Number of new coins we are creating. - */ - uint16_t num_newcoins; - - /** - * Number of parallel operations we perform for the cut and choose. - * (must be greater or equal to three for security). 0 if not yet - * known. - */ - uint16_t kappa; - - /** - * Index (smaller @e kappa) which the mint has chosen to not - * have revealed during cut and choose. - */ - uint16_t noreveal_index; - -}; - - -/** - * Lookup refresh session data under the given public key. - * - * @param db_conn database handle to use - * @param refresh_session_pub public key to use for the lookup - * @param session[OUT] where to store the result - * @return #GNUNET_YES on success, - * #GNUNET_NO if not found, - * #GNUNET_SYSERR on DB failure - */ -int -TALER_MINT_DB_get_refresh_session (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - struct RefreshSession *session); - - -/** - * Store new refresh session data under the given public key. - * - * @param db_conn database handle to use - * @param refresh_session_pub public key to use to locate the session - * @param session session data to store - * @return #GNUNET_YES on success, - * #GNUNET_SYSERR on DB failure - */ -int -TALER_MINT_DB_create_refresh_session (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - const struct RefreshSession *session); - - -/** - * Specification for coin in a /refresh/melt operation. - */ -struct RefreshMelt -{ - /** - * Information about the coin that is being melted. - */ - struct TALER_CoinPublicInfo coin; - - /** - * Signature over the melting operation. - */ - struct GNUNET_CRYPTO_EcdsaSignature coin_sig; - - /** - * Which melting operation should the coin become a part of. - */ - struct GNUNET_HashCode melt_hash; - - /** - * How much value is being melted? - * This amount includes the fees, so the final amount contributed - * to the melt is this value minus the fee for melting the coin. - */ - struct TALER_Amount amount; - -}; - - -/** - * Store the given /refresh/melt request in the database. - * - * @param db_conn database connection - * @param session session key of the melt operation - * @param oldcoin_index index of the coin to store - * @param melt coin melt operation details to store - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session, - uint16_t oldcoin_index, - const struct RefreshMelt *melt); - - - -/** - * Get information about melted coin details from the database. - * - * @param db_conn database connection - * @param session session key of the melt operation - * @param oldcoin_index index of the coin to retrieve - * @param melt melt data to fill in - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_get_refresh_melt (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session, - uint16_t oldcoin_index, - struct RefreshMelt *melt); - - -/** - * Store in the database which coin(s) we want to create - * in a given refresh operation. - * - * @param db_conn database connection - * @param session_pub refresh session key - * @param newcoin_index index of the coin to generate - * @param denom_pub denomination of the coin to create - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - uint16_t newcoin_index, - const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub); - - -/** - * Lookup in the database the @a newcoin_index coin that we want to - * create in the given refresh operation. - * - * @param db_conn database connection - * @param session_pub refresh session key - * @param newcoin_index index of the coin to generate - * @param denom_pub denomination of the coin to create - * @return NULL on error (not found or internal error) - */ -struct GNUNET_CRYPTO_rsa_PublicKey * -TALER_MINT_DB_get_refresh_order (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - uint16_t newcoin_index); - - -/** - * We have as many `struct RefreshCommitCoin` as there are new - * coins being created by the refresh (for each of the kappa - * sets). These are the coins we ask the mint to sign if the - * respective set is selected. - */ -struct RefreshCommitCoin -{ - - /** - * Encrypted data allowing those able to decrypt it to derive - * the private keys of the new coins created by the refresh. - */ - struct TALER_RefreshLinkEncrypted *refresh_link; - - /** - * Blinded message to be signed (in envelope), with @e coin_env_size bytes. - */ - char *coin_ev; - - /** - * Number of bytes in @e coin_ev. - */ - size_t coin_ev_size; - -}; - - -/** - * Store information about the commitment of the - * given coin for the given refresh session in the database. - * - * @param db_conn database connection to use - * @param refresh_session_pub refresh session this commitment belongs to - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to refreshed (new) coins - * @param commit_coin coin commitment to store - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on error - */ -int -TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int i, - unsigned int j, - const struct RefreshCommitCoin *commit_coin); - - -/** - * Obtain information about the commitment of the - * given coin of the given refresh session from the database. - * - * @param db_conn database connection to use - * @param refresh_session_pub refresh session the commitment belongs to - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to refreshed (new) coins - * @param commit_coin[OUT] coin commitment to return - * @return #GNUNET_OK on success - * #GNUNET_NO if not found - * #GNUNET_SYSERR on error - */ -int -TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int i, - unsigned int j, - struct RefreshCommitCoin *commit_coin); - - -/** - * For each (old) coin being melted, we have a `struct - * RefreshCommitLink` that allows the user to find the shared secret - * to decrypt the respective refresh links for the new coins in the - * `struct RefreshCommitCoin`. - */ -struct RefreshCommitLink -{ - /** - * Transfer public key (FIXME: explain!) - */ - struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub; - - /** - * Encrypted shared secret to decrypt the link. - */ - struct TALER_EncryptedLinkSecret shared_secret_enc; -}; - - -/** - * Store the commitment to the given (encrypted) refresh link data - * for the given refresh session. - * - * @param db_conn database connection to use - * @param refresh_session_pub public key of the refresh session this - * commitment belongs with - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to melted (old) coins - * @param commit_link link information to store - * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success - */ -int -TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int i, - unsigned int j, - const struct RefreshCommitLink *commit_link); - -/** - * Obtain the commited (encrypted) refresh link data - * for the given refresh session. - * - * @param db_conn database connection to use - * @param refresh_session_pub public key of the refresh session this - * commitment belongs with - * @param i set index (1st dimension) - * @param j coin index (2nd dimension), corresponds to melted (old) coins - * @param cc[OUT] link information to return - * @return #GNUNET_SYSERR on internal error, - * #GNUNET_NO if commitment was not found - * #GNUNET_OK on success - */ -int -TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int i, - unsigned int j, - struct RefreshCommitLink *cc); - - -/** - * Insert signature of a new coin generated during refresh into - * the database indexed by the refresh session and the index - * of the coin. This data is later used should an old coin - * be used to try to obtain the private keys during "/refresh/link". - * - * @param db_conn database connection - * @param session_pub refresh session - * @param newcoin_index coin index - * @param ev_sig coin signature - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - uint16_t newcoin_index, - const struct GNUNET_CRYPTO_rsa_Signature *ev_sig); - - -/** - * Linked list of refresh information linked to a coin. - */ -struct LinkDataList -{ - /** - * Information is stored in a NULL-terminated linked list. - */ - struct LinkDataList *next; - - /** - * Link data, used to recover the private key of the coin - * by the owner of the old coin. - */ - struct TALER_RefreshLinkEncrypted *link_data_enc; - - /** - * Denomination public key, determines the value of the coin. - */ - struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; - - /** - * Signature over the blinded envelope. - */ - struct GNUNET_CRYPTO_rsa_Signature *ev_sig; -}; - - -/** - * Obtain the link data of a coin, that is the encrypted link - * information, the denomination keys and the signatures. - * - * @param db_conn database connection - * @param coin_pub public key to use to retrieve linkage data - * @return all known link data for the coin - */ -struct LinkDataList * -TALER_db_get_link (PGconn *db_conn, - const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub); - - -/** - * Free memory of the link data list. - * - * @param ldl link data list to release - */ -void -TALER_db_link_data_list_free (struct LinkDataList *ldl); - - -/** - * Obtain shared secret and transfer public key from the public key of - * the coin. This information and the link information returned by - * #TALER_db_get_link() enable the owner of an old coin to determine - * the private keys of the new coins after the melt. - * - * - * @param db_conn database connection - * @param coin_pub public key of the coin - * @param transfer_pub[OUT] public transfer key - * @param shared_secret_enc[OUT] set to shared secret - * @return #GNUNET_OK on success, - * #GNUNET_NO on failure (not found) - * #GNUNET_SYSERR on internal failure (database issue) - */ -int -TALER_db_get_transfer (PGconn *db_conn, - const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, - struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, - struct TALER_EncryptedLinkSecret *shared_secret_enc); - - -/** - * Specification for a /lock operation. - */ -struct Lock -{ - /** - * Information about the coin that is being melted. - */ - struct TALER_CoinPublicInfo coin; - - /** - * Signature over the melting operation. - */ - const struct GNUNET_CRYPTO_EcdsaSignature coin_sig; - - /** - * How much value is being melted? - */ - struct TALER_Amount amount; - - // FIXME: more needed... -}; - - -/** - * Test if the given /lock request is known to us. - * - * @param db_conn database connection - * @param lock lock operation - * @return #GNUNET_YES if known, - * #GNUENT_NO if not, - * #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_have_lock (PGconn *db_conn, - const struct Lock *lock); - - -/** - * Store the given /lock request in the database. - * - * @param db_conn database connection - * @param lock lock operation - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ -int -TALER_MINT_DB_insert_lock (PGconn *db_conn, - const struct Lock *lock); - - -/** - * Enumeration to classify the different types of transactions - * that can be done with a coin. - */ -enum TALER_MINT_DB_TransactionType -{ - /** - * /deposit operation. - */ - TALER_MINT_DB_TT_DEPOSIT = 0, - - /** - * /refresh/melt operation. - */ - TALER_MINT_DB_TT_REFRESH_MELT = 1, - - /** - * /lock operation. - */ - TALER_MINT_DB_TT_LOCK = 2 -}; - - -/** - * List of transactions we performed for a particular coin. - */ -struct TALER_MINT_DB_TransactionList -{ - - /** - * Next pointer in the NULL-terminated linked list. - */ - struct TALER_MINT_DB_TransactionList *next; - - /** - * Type of the transaction, determines what is stored in @e details. - */ - enum TALER_MINT_DB_TransactionType type; - - /** - * Details about the transaction, depending on @e type. - */ - union - { - - /** - * Details if transaction was a /deposit operation. - */ - struct Deposit *deposit; - - /** - * Details if transaction was a /refresh/melt operation. - */ - struct RefreshMelt *melt; - - /** - * Details if transaction was a /lock operation. - */ - struct Lock *lock; - - } details; - -}; - - -/** - * Compile a list of all (historic) transactions performed - * with the given coin (/refresh/melt and /deposit operations). - * - * @param db_conn database connection - * @param coin_pub coin to investigate - * @return list of transactions, NULL if coin is fresh - */ -struct TALER_MINT_DB_TransactionList * -TALER_MINT_DB_get_coin_transactions (PGconn *db_conn, - const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub); - - -/** - * Free linked list of transactions. - * - * @param list list to free - */ -void -TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list); - - - -#endif /* _NEURO_MINT_DB_H */ diff --git a/src/mint/plugin.c b/src/mint/plugin.c new file mode 100644 index 000000000..91cd3f406 --- /dev/null +++ b/src/mint/plugin.c @@ -0,0 +1,183 @@ +/* + This file is part of TALER + Copyright (C) 2015 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 +*/ +/** + * @file mint/plugin.c + * @brief Logic to load database plugin + * @author Christian Grothoff + */ +#include "platform.h" +#include "plugin.h" +#include + + +/** + * Global variable with the plugin (once loaded). + */ +struct TALER_MINTDB_Plugin *plugin; + +/** + * Libtool search path before we started. + */ +static char *old_dlsearchpath; + + +/** + * Initialize the plugin. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +int +TALER_MINT_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg) +{ + return GNUNET_SYSERR; +} + + +/** + * Shutdown the plugin. + */ +void +TALER_MINT_plugin_unload () +{ + if (NULL == plugin) + return; +} + + +/** + * Setup libtool paths. + */ +void __attribute__ ((constructor)) +plugin_init () +{ + int err; + const char *opath; + char *path; + char *cpath; + + err = lt_dlinit (); + if (err > 0) + { + FPRINTF (stderr, + _("Initialization of plugin mechanism failed: %s!\n"), + lt_dlerror ()); + return; + } + opath = lt_dlgetsearchpath (); + if (NULL != opath) + old_dlsearchpath = GNUNET_strdup (opath); + path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); + if (NULL != path) + { + if (NULL != opath) + { + GNUNET_asprintf (&cpath, "%s:%s", opath, path); + lt_dlsetsearchpath (cpath); + GNUNET_free (path); + GNUNET_free (cpath); + } + else + { + lt_dlsetsearchpath (path); + GNUNET_free (path); + } + } +} + + +/** + * Shutdown libtool. + */ +void __attribute__ ((destructor)) +plugin_fini () +{ + lt_dlsetsearchpath (old_dlsearchpath); + if (NULL != old_dlsearchpath) + { + GNUNET_free (old_dlsearchpath); + old_dlsearchpath = NULL; + } + lt_dlexit (); +} + + +// FIXME: decide if we should keep these in each plugin, here +// or yet again somewhere else entirely (plugin_common.c?) + +/** + * Free memory associated with the given reserve history. + * + * @param rh history to free. + */ +void +TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh) +{ + struct BankTransfer *bt; + struct CollectableBlindcoin *cbc; + struct ReserveHistory *backref; + + while (NULL != rh) + { + switch(rh->type) + { + case TALER_MINT_DB_RO_BANK_TO_MINT: + bt = rh->details.bank; + if (NULL != bt->wire) + json_decref ((json_t *) bt->wire); /* FIXME: avoid cast? */ + GNUNET_free (bt); + break; + case TALER_MINT_DB_RO_WITHDRAW_COIN: + cbc = rh->details.withdraw; + GNUNET_CRYPTO_rsa_signature_free (cbc->sig); + GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub); + GNUNET_free (cbc); + break; + } + backref = rh; + rh = rh->next; + GNUNET_free (backref); + } +} + + +/** + * Free memory of the link data list. + * + * @param ldl link data list to release + */ +void +TALER_db_link_data_list_free (struct LinkDataList *ldl) +{ + GNUNET_break (0); // FIXME +} + + +/** + * Free linked list of transactions. + * + * @param list list to free + */ +void +TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list) +{ + // FIXME: check logic! + GNUNET_break (0); +} + + + +/* end of plugin.c */ diff --git a/src/mint/plugin.h b/src/mint/plugin.h new file mode 100644 index 000000000..01b99ebc3 --- /dev/null +++ b/src/mint/plugin.h @@ -0,0 +1,77 @@ +/* + This file is part of TALER + Copyright (C) 2015 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 +*/ +/** + * @file mint/plugin.h + * @brief Logic to load database plugins + * @author Christian Grothoff + */ +#ifndef PLUGIN_H +#define PLUGIN_H + +#include +#include "taler_mintdb_plugin.h" + +/** + * Global variable with the plugin (once loaded). + */ +extern struct TALER_MINTDB_Plugin *plugin; + + +/** + * Initialize the plugin. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +int +TALER_MINT_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Shutdown the plugin. + */ +void +TALER_MINT_plugin_unload (void); + + +/** + * Free memory associated with the given reserve history. + * + * @param rh history to free. + */ +void +TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh); + + +/** + * Free memory of the link data list. + * + * @param ldl link data list to release + */ +void +TALER_db_link_data_list_free (struct LinkDataList *ldl); + + +/** + * Free linked list of transactions. + * + * @param list list to free + */ +void +TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list); + + +#endif diff --git a/src/mint/mint_db.c b/src/mint/plugin_mintdb_postgres.c similarity index 78% rename from src/mint/mint_db.c rename to src/mint/plugin_mintdb_postgres.c index 6832cdac9..8935fe039 100644 --- a/src/mint/mint_db.c +++ b/src/mint/plugin_mintdb_postgres.c @@ -15,41 +15,25 @@ */ /** - * @file mint_db.c - * @brief Low-level (statement-level) database access for the mint + * @file plugin_mintdb_postgres.c + * @brief Low-level (statement-level) Postgres database access for the mint * @author Florian Dold * @author Christian Grothoff * @author Sree Harsha Totakura - * - * TODO: - * - The mint_db.h-API should ideally be what we need to port - * when using other databases; so here we should enable - * alternative implementations by returning - * a more opaque DB handle. */ #include "platform.h" #include "db_pq.h" #include "taler_signatures.h" -#include "taler-mint-httpd_responses.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h" #include +#include -/** - * Thread-local database connection. - * Contains a pointer to PGconn or NULL. - */ -static pthread_key_t db_conn_threadlocal; - +#define TALER_TEMP_SCHEMA_NAME "taler_temporary" #define QUERY_ERR(result) \ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s\n", __FILE__, __LINE__, PQresultErrorMessage (result)) -/** - * Database connection string, as read from - * the configuration. - */ -static char *TALER_MINT_db_connection_cfg_str; #define BREAK_DB_ERR(result) do { \ GNUNET_break(0); \ @@ -84,6 +68,41 @@ static char *TALER_MINT_db_connection_cfg_str; */ #define TALER_DB_CURRENCY_LEN 3 + +/** + * Handle for a database session (per-thread, for transactions). + */ +struct TALER_MINTDB_Session +{ + /** + * Postgres connection handle. + */ + PGconn *conn; +}; + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct PostgresClosure +{ + + /** + * Thread-local database connection. + * Contains a pointer to PGconn or NULL. + */ + pthread_key_t db_conn_threadlocal; + + /** + * Database connection string, as read from + * the configuration. + */ + char *TALER_MINT_db_connection_cfg_str; +}; + + + /** * Set the given connection to use a temporary schema * @@ -108,14 +127,16 @@ set_temporary_schema (PGconn *db) /** * Drop the temporary taler schema. This is only useful for testcases * + * @param cls the `struct PostgresClosure` with the plugin-specific state * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ -int -TALER_MINT_DB_drop_temporary (PGconn *db) +static int +postgres_drop_temporary (void *cls, + struct TALER_MINTDB_Session *session) { PGresult *result; - SQLEXEC_ (db, + SQLEXEC_ (session->conn, "DROP SCHEMA " TALER_TEMP_SCHEMA_NAME " CASCADE;", result); return GNUNET_OK; @@ -127,17 +148,19 @@ TALER_MINT_DB_drop_temporary (PGconn *db) /** * Create the necessary tables if they are not present * + * @param pc our overall context * @param temporary should we use a temporary schema * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ -int -TALER_MINT_DB_create_tables (int temporary) +static int +postgres_create_tables (struct PostgresClosure *pc, + int temporary) { PGresult *result; PGconn *conn; result = NULL; - conn = PQconnectdb (TALER_MINT_db_connection_cfg_str); + conn = PQconnectdb (pc->TALER_MINT_db_connection_cfg_str); if (CONNECTION_OK != PQstatus (conn)) { LOG_ERROR ("Database connection failed: %s\n", @@ -279,8 +302,8 @@ TALER_MINT_DB_create_tables (int temporary) * @param db_conn connection handle to initialize * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ -int -TALER_MINT_DB_prepare (PGconn *db_conn) +static int +postgres_prepare (PGconn *db_conn) { PGresult *result; @@ -581,42 +604,26 @@ db_conn_destroy (void *cls) } -/** - * Initialize database subsystem. - * - * @param connection_cfg configuration to use to talk to DB - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_init (const char *connection_cfg) -{ - if (0 != pthread_key_create (&db_conn_threadlocal, - &db_conn_destroy)) - { - LOG_ERROR ("Cannnot create pthread key.\n"); - return GNUNET_SYSERR; - } - TALER_MINT_db_connection_cfg_str = GNUNET_strdup (connection_cfg); - return GNUNET_OK; -} - - /** * Get the thread-local database-handle. * Connect to the db if the connection does not exist yet. * + * @param cls the `struct PostgresClosure` with the plugin-specific state * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the * database default one * @return the database connection, or NULL on error */ -PGconn * -TALER_MINT_DB_get_connection (int temporary) +static struct TALER_MINTDB_Session * +postgres_get_connection (void *cls, + int temporary) { + struct PostgresClosure *pc = cls; PGconn *db_conn; + struct TALER_MINTDB_Session *session; - if (NULL != (db_conn = pthread_getspecific (db_conn_threadlocal))) - return db_conn; - db_conn = PQconnectdb (TALER_MINT_db_connection_cfg_str); + if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal))) + return session; + db_conn = PQconnectdb (pc->TALER_MINT_db_connection_cfg_str); if (CONNECTION_OK != PQstatus (db_conn)) { @@ -632,33 +639,39 @@ TALER_MINT_DB_get_connection (int temporary) return NULL; } if (GNUNET_OK != - TALER_MINT_DB_prepare (db_conn)) + postgres_prepare (db_conn)) { GNUNET_break (0); return NULL; } - if (0 != pthread_setspecific (db_conn_threadlocal, - db_conn)) + session = GNUNET_new (struct TALER_MINTDB_Session); + session->conn = db_conn; + if (0 != pthread_setspecific (pc->db_conn_threadlocal, + session)) { GNUNET_break (0); + // FIXME: close db_conn! + GNUNET_free (session); return NULL; } - return db_conn; + return session; } /** * Start a transaction. * - * @param db_conn the database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection * @return #GNUNET_OK on success */ -int -TALER_MINT_DB_transaction (PGconn *db_conn) +static int +postgres_start (void *cls, + struct TALER_MINTDB_Session *session) { PGresult *result; - result = PQexec (db_conn, + result = PQexec (session->conn, "BEGIN"); if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -678,15 +691,17 @@ TALER_MINT_DB_transaction (PGconn *db_conn) /** * Roll back the current transaction of a database connection. * - * @param db_conn the database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection * @return #GNUNET_OK on success */ -void -TALER_MINT_DB_rollback (PGconn *db_conn) +static void +postgres_rollback (void *cls, + struct TALER_MINTDB_Session *session) { PGresult *result; - result = PQexec (db_conn, + result = PQexec (session->conn, "ROLLBACK"); GNUNET_break (PGRES_COMMAND_OK == PQresultStatus (result)); @@ -697,15 +712,17 @@ TALER_MINT_DB_rollback (PGconn *db_conn) /** * Commit the current transaction of a database connection. * - * @param db_conn the database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection * @return #GNUNET_OK on success */ -int -TALER_MINT_DB_commit (PGconn *db_conn) +static int +postgres_commit (void *cls, + struct TALER_MINTDB_Session *session) { PGresult *result; - result = PQexec (db_conn, + result = PQexec (session->conn, "COMMIT"); if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -723,15 +740,17 @@ TALER_MINT_DB_commit (PGconn *db_conn) /** * Get the summary of a reserve. * - * @param db the database connection handle + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection handle * @param reserve the reserve data. The public key of the reserve should be set * in this structure; it is used to query the database. The balance * and expiration are then filled accordingly. * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure */ -int -TALER_MINT_DB_reserve_get (PGconn *db, - struct Reserve *reserve) +static int +postgres_reserve_get (void *cls, + struct TALER_MINTDB_Session *session, + struct Reserve *reserve) { PGresult *result; uint64_t expiration_date_nbo; @@ -745,7 +764,7 @@ TALER_MINT_DB_reserve_get (PGconn *db, GNUNET_break (0); return GNUNET_SYSERR; } - result = TALER_DB_exec_prepared (db, + result = TALER_DB_exec_prepared (session->conn, "get_reserve", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -783,14 +802,16 @@ TALER_MINT_DB_reserve_get (PGconn *db, /** * Updates a reserve with the data from the given reserve structure. * - * @param db the database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection * @param reserve the reserve structure whose data will be used to update the * corresponding record in the database. * @return #GNUNET_OK upon successful update; #GNUNET_SYSERR upon any error */ -int -reserves_update (PGconn *db, - struct Reserve *reserve) +static int +postgres_reserves_update (void *cls, + struct TALER_MINTDB_Session *session, + struct Reserve *reserve) { PGresult *result; struct TALER_AmountNBO balance_nbo; @@ -810,7 +831,7 @@ reserves_update (PGconn *db, TALER_amount_hton (&balance_nbo, &reserve->balance); expiry_nbo = GNUNET_TIME_absolute_hton (reserve->expiry); - result = TALER_DB_exec_prepared (db, + result = TALER_DB_exec_prepared (session->conn, "update_reserve", params); if (PGRES_COMMAND_OK != PQresultStatus(result)) @@ -827,7 +848,8 @@ reserves_update (PGconn *db, * Insert a incoming transaction into reserves. New reserves are also created * through this function. * - * @param db the database connection handle + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session the database connection handle * @param reserve the reserve structure. The public key of the reserve should * be set here. Upon successful execution of this function, the * balance and expiration of the reserve will be updated. @@ -835,11 +857,12 @@ reserves_update (PGconn *db, * @param expiry the new expiration time for the reserve * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures */ -int -TALER_MINT_DB_reserves_in_insert (PGconn *db, - struct Reserve *reserve, - const struct TALER_Amount *balance, - const struct GNUNET_TIME_Absolute expiry) +static int +postgres_reserves_in_insert (void *cls, + struct TALER_MINTDB_Session *session, + struct Reserve *reserve, + const struct TALER_Amount *balance, + const struct GNUNET_TIME_Absolute expiry) { struct TALER_AmountNBO balance_nbo; struct GNUNET_TIME_AbsoluteNBO expiry_nbo; @@ -852,15 +875,19 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, GNUNET_break (0); return GNUNET_SYSERR; } - if (GNUNET_OK != TALER_MINT_DB_transaction (db)) + if (GNUNET_OK != postgres_start (cls, + session)) { GNUNET_break (0); return GNUNET_SYSERR; } - reserve_exists = TALER_MINT_DB_reserve_get (db, reserve); + reserve_exists = postgres_reserve_get (cls, + session, + reserve); if (GNUNET_SYSERR == reserve_exists) { - TALER_MINT_DB_rollback (db); + postgres_rollback (cls, + session); return GNUNET_SYSERR; } TALER_amount_hton (&balance_nbo, @@ -879,7 +906,7 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, TALER_DB_QUERY_PARAM_PTR (&expiry_nbo), TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db, + result = TALER_DB_exec_prepared (session->conn, "create_reserve", params); if (PGRES_COMMAND_OK != PQresultStatus(result)) @@ -901,7 +928,7 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, TALER_DB_QUERY_PARAM_PTR (&expiry_nbo), TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db, + result = TALER_DB_exec_prepared (session->conn, "create_reserves_in_transaction", params); if (PGRES_COMMAND_OK != PQresultStatus(result)) @@ -913,7 +940,8 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, result = NULL; if (GNUNET_NO == reserve_exists) { - if (GNUNET_OK != TALER_MINT_DB_commit (db)) + if (GNUNET_OK != postgres_commit (cls, + session)) return GNUNET_SYSERR; reserve->balance = *balance; reserve->expiry = expiry; @@ -931,9 +959,12 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, return GNUNET_SYSERR; } updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry, reserve->expiry); - if (GNUNET_OK != reserves_update (db, &updated_reserve)) + if (GNUNET_OK != postgres_reserves_update (cls, + session, + &updated_reserve)) goto rollback; - if (GNUNET_OK != TALER_MINT_DB_commit (db)) + if (GNUNET_OK != postgres_commit (cls, + session)) return GNUNET_SYSERR; reserve->balance = updated_reserve.balance; reserve->expiry = updated_reserve.expiry; @@ -941,7 +972,8 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, rollback: PQclear (result); - TALER_MINT_DB_rollback (db); + postgres_rollback (cls, + session); return GNUNET_SYSERR; } @@ -950,7 +982,8 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, * Locate the response for a /withdraw request under the * key of the hash of the blinded message. * - * @param db_conn database connection to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use * @param h_blind hash of the blinded message * @param collectable corresponding collectable coin (blind signature) * if a coin is found @@ -958,10 +991,11 @@ TALER_MINT_DB_reserves_in_insert (PGconn *db, * #GNUNET_NO if the collectable was not found * #GNUNET_YES on success */ -int -TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, - const struct GNUNET_HashCode *h_blind, - struct CollectableBlindcoin *collectable) +static int +postgres_get_collectable_blindcoin (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *h_blind, + struct CollectableBlindcoin *collectable) { PGresult *result; struct TALER_DB_QueryParam params[] = { @@ -980,7 +1014,7 @@ TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, denom_pub = NULL; denom_pub_enc = NULL; denom_sig_enc = NULL; - result = TALER_DB_exec_prepared (db_conn, + result = TALER_DB_exec_prepared (session->conn, "get_collectable_blindcoin", params); @@ -1038,7 +1072,8 @@ TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, * Store collectable bit coin under the corresponding * hash of the blinded message. * - * @param db_conn database connection to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use * @param h_blind hash of the blinded message * @param withdraw amount by which the reserve will be withdrawn with this * transaction @@ -1048,11 +1083,12 @@ TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, * #GNUNET_NO if the collectable was not found * #GNUNET_YES on success */ -int -TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, - const struct GNUNET_HashCode *h_blind, - struct TALER_Amount withdraw, - const struct CollectableBlindcoin *collectable) +static int +postgres_insert_collectable_blindcoin (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *h_blind, + struct TALER_Amount withdraw, + const struct CollectableBlindcoin *collectable) { PGresult *result; struct Reserve reserve; @@ -1077,9 +1113,10 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, TALER_DB_QUERY_PARAM_PTR (&collectable->reserve_sig), TALER_DB_QUERY_PARAM_END }; - if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) + if (GNUNET_OK != postgres_start (cls, + session)) goto cleanup; - result = TALER_DB_exec_prepared (db_conn, + result = TALER_DB_exec_prepared (session->conn, "insert_collectable_blindcoin", params); if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -1089,24 +1126,29 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, } reserve.pub = (struct GNUNET_CRYPTO_EddsaPublicKey *) &collectable->reserve_pub; - if (GNUNET_OK != TALER_MINT_DB_reserve_get (db_conn, - &reserve)) + if (GNUNET_OK != postgres_reserve_get (cls, + session, + &reserve)) goto rollback; if (GNUNET_SYSERR == TALER_amount_subtract (&reserve.balance, &reserve.balance, &withdraw)) goto rollback; - if (GNUNET_OK != reserves_update (db_conn, &reserve)) + if (GNUNET_OK != postgres_reserves_update (cls, + session, + &reserve)) goto rollback; - if (GNUNET_OK == TALER_MINT_DB_commit (db_conn)) + if (GNUNET_OK == postgres_commit (cls, + session)) { ret = GNUNET_OK; goto cleanup; } rollback: - TALER_MINT_DB_rollback(db_conn); + postgres_rollback (cls, + session); cleanup: PQclear (result); GNUNET_free_non_null (denom_pub_enc); @@ -1119,13 +1161,15 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, * Get all of the transaction history associated with the specified * reserve. * - * @param db_conn connection to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session connection to use * @param reserve_pub public key of the reserve * @return known transaction history (NULL if reserve is unknown) */ -struct ReserveHistory * -TALER_MINT_DB_get_reserve_history (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) +static struct ReserveHistory * +postgres_get_reserve_history (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) { PGresult *result; struct ReserveHistory *rh; @@ -1144,7 +1188,7 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn, TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db_conn, + result = TALER_DB_exec_prepared (session->conn, "get_reserves_in_transactions", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -1202,7 +1246,7 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn, TALER_DB_QUERY_PARAM_PTR (reserve_pub), TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db_conn, + result = TALER_DB_exec_prepared (session->conn, "get_reserves_blindcoins", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -1274,53 +1318,19 @@ TALER_MINT_DB_get_reserve_history (PGconn *db_conn, } -/** - * Free memory associated with the given reserve history. - * - * @param rh history to free. - */ -void -TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh) -{ - struct BankTransfer *bt; - struct CollectableBlindcoin *cbc; - struct ReserveHistory *backref; - - while (NULL != rh) - { - switch(rh->type) - { - case TALER_MINT_DB_RO_BANK_TO_MINT: - bt = rh->details.bank; - if (NULL != bt->wire) - json_decref ((json_t *) bt->wire); /* FIXME: avoid cast? */ - GNUNET_free (bt); - break; - case TALER_MINT_DB_RO_WITHDRAW_COIN: - cbc = rh->details.withdraw; - GNUNET_CRYPTO_rsa_signature_free (cbc->sig); - GNUNET_CRYPTO_rsa_public_key_free (cbc->denom_pub); - GNUNET_free (cbc); - break; - } - backref = rh; - rh = rh->next; - GNUNET_free (backref); - } -} - - /** * Check if we have the specified deposit already in the database. * - * @param db_conn database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection * @param deposit deposit to search for * @return #GNUNET_YES if we know this operation, * #GNUNET_NO if this deposit is unknown to us */ -int -TALER_MINT_DB_have_deposit (PGconn *db_conn, - const struct Deposit *deposit) +static int +postgres_have_deposit (void *cls, + struct TALER_MINTDB_Session *session, + const struct Deposit *deposit) { struct TALER_DB_QueryParam params[] = { TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), @@ -1332,7 +1342,7 @@ TALER_MINT_DB_have_deposit (PGconn *db_conn, int ret; ret = GNUNET_SYSERR; - result = TALER_DB_exec_prepared (db_conn, + result = TALER_DB_exec_prepared (session->conn, "get_deposit", params); if (PGRES_TUPLES_OK != @@ -1359,13 +1369,15 @@ TALER_MINT_DB_have_deposit (PGconn *db_conn, * Insert information about deposited coin into the * database. * - * @param db_conn connection to the database + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session connection to the database * @param deposit deposit information to store * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ -int -TALER_MINT_DB_insert_deposit (PGconn *db_conn, - const struct Deposit *deposit) +static int +postgres_insert_deposit (void *cls, + struct TALER_MINTDB_Session *session, + const struct Deposit *deposit) { char *denom_pub_enc; char *denom_sig_enc; @@ -1403,7 +1415,7 @@ TALER_MINT_DB_insert_deposit (PGconn *db_conn, strlen (json_wire_enc)), TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db_conn, "insert_deposit", params); + result = TALER_DB_exec_prepared (session->conn, "insert_deposit", params); if (PGRES_COMMAND_OK != PQresultStatus (result)) { BREAK_DB_ERR (result); @@ -1423,17 +1435,19 @@ TALER_MINT_DB_insert_deposit (PGconn *db_conn, /** * Lookup refresh session data under the given public key. * - * @param db_conn database handle to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database handle to use * @param refresh_session_pub public key to use for the lookup - * @param session[OUT] where to store the result + * @param refresh_session[OUT] where to store the result * @return #GNUNET_YES on success, * #GNUNET_NO if not found, * #GNUNET_SYSERR on DB failure */ -int -TALER_MINT_DB_get_refresh_session (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - struct RefreshSession *session) +static int +postgres_get_refresh_session (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + struct RefreshSession *refresh_session) { // FIXME: check logic! int res; @@ -1442,7 +1456,9 @@ TALER_MINT_DB_get_refresh_session (PGconn *db_conn, TALER_DB_QUERY_PARAM_END }; - PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_session", params); + PGresult *result = TALER_DB_exec_prepared (session->conn, + "get_refresh_session", + params); if (PGRES_TUPLES_OK != PQresultStatus (result)) { @@ -1461,16 +1477,16 @@ TALER_MINT_DB_get_refresh_session (PGconn *db_conn, /* We're done if the caller is only interested in * whether the session exists or not */ - if (NULL == session) + if (NULL == refresh_session) return GNUNET_YES; memset (session, 0, sizeof (struct RefreshSession)); struct TALER_DB_ResultSpec rs[] = { - TALER_DB_RESULT_SPEC("num_oldcoins", &session->num_oldcoins), - TALER_DB_RESULT_SPEC("num_newcoins", &session->num_newcoins), - TALER_DB_RESULT_SPEC("kappa", &session->kappa), - TALER_DB_RESULT_SPEC("noreveal_index", &session->noreveal_index), + TALER_DB_RESULT_SPEC("num_oldcoins", &refresh_session->num_oldcoins), + TALER_DB_RESULT_SPEC("num_newcoins", &refresh_session->num_newcoins), + TALER_DB_RESULT_SPEC("kappa", &refresh_session->kappa), + TALER_DB_RESULT_SPEC("noreveal_index", &refresh_session->noreveal_index), TALER_DB_RESULT_SPEC_END }; @@ -1483,10 +1499,10 @@ TALER_MINT_DB_get_refresh_session (PGconn *db_conn, return GNUNET_SYSERR; } - session->num_oldcoins = ntohs (session->num_oldcoins); - session->num_newcoins = ntohs (session->num_newcoins); - session->kappa = ntohs (session->kappa); - session->noreveal_index = ntohs (session->noreveal_index); + refresh_session->num_oldcoins = ntohs (refresh_session->num_oldcoins); + refresh_session->num_newcoins = ntohs (refresh_session->num_newcoins); + refresh_session->kappa = ntohs (refresh_session->kappa); + refresh_session->noreveal_index = ntohs (refresh_session->noreveal_index); PQclear (result); return GNUNET_YES; @@ -1496,16 +1512,18 @@ TALER_MINT_DB_get_refresh_session (PGconn *db_conn, /** * Store new refresh session data under the given public key. * - * @param db_conn database handle to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database handle to use * @param refresh_session_pub public key to use to locate the session - * @param session session data to store + * @param refresh_session session data to store * @return #GNUNET_YES on success, * #GNUNET_SYSERR on DB failure */ -int -TALER_MINT_DB_create_refresh_session (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - const struct RefreshSession *session) +static int +postgres_create_refresh_session (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + const struct RefreshSession *refresh_session) { // FIXME: actually store session data! uint16_t noreveal_index; @@ -1518,7 +1536,9 @@ TALER_MINT_DB_create_refresh_session (PGconn *db_conn, noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 1<<15); noreveal_index = htonl (noreveal_index); - PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_session", params); + PGresult *result = TALER_DB_exec_prepared (session->conn, + "insert_refresh_session", + params); if (PGRES_COMMAND_OK != PQresultStatus (result)) { @@ -1535,18 +1555,20 @@ TALER_MINT_DB_create_refresh_session (PGconn *db_conn, /** * Store the given /refresh/melt request in the database. * - * @param db_conn database connection - * @param session session key of the melt operation + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param refresh_session session key of the melt operation * @param oldcoin_index index of the coin to store * @param melt melt operation * @return #GNUNET_OK on success * #GNUNET_SYSERR on internal error */ -int -TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session, - uint16_t oldcoin_index, - const struct RefreshMelt *melt) +static int +postgres_insert_refresh_melt (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, + uint16_t oldcoin_index, + const struct RefreshMelt *melt) { // FIXME: check logic! uint16_t oldcoin_index_nbo = htons (oldcoin_index); @@ -1558,13 +1580,15 @@ TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, &buf); { struct TALER_DB_QueryParam params[] = { - TALER_DB_QUERY_PARAM_PTR(session), + TALER_DB_QUERY_PARAM_PTR(refresh_session), TALER_DB_QUERY_PARAM_PTR(&oldcoin_index_nbo), TALER_DB_QUERY_PARAM_PTR(&melt->coin.coin_pub), TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size), TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db_conn, "insert_refresh_melt", params); + result = TALER_DB_exec_prepared (session->conn, + "insert_refresh_melt", + params); } GNUNET_free (buf); if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -1581,18 +1605,20 @@ TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, /** * Get information about melted coin details from the database. * - * @param db_conn database connection - * @param session session key of the melt operation + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection + * @param refresh_session session key of the melt operation * @param oldcoin_index index of the coin to retrieve * @param melt melt data to fill in * @return #GNUNET_OK on success * #GNUNET_SYSERR on internal error */ -int -TALER_MINT_DB_get_refresh_melt (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session, - uint16_t oldcoin_index, - struct RefreshMelt *melt) +static int +postgres_get_refresh_melt (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, + uint16_t oldcoin_index, + struct RefreshMelt *melt) { // FIXME: check logic! GNUNET_break (0); @@ -1604,18 +1630,20 @@ TALER_MINT_DB_get_refresh_melt (PGconn *db_conn, * Store in the database which coin(s) we want to create * in a given refresh operation. * - * @param db_conn database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection * @param session_pub refresh session key * @param newcoin_index index of the coin to generate * @param denom_pub denomination of the coin to create * @return #GNUNET_OK on success * #GNUNET_SYSERR on internal error */ -int -TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - uint16_t newcoin_index, - const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub) +static int +postgres_insert_refresh_order (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub) { // FIXME: check logic uint16_t newcoin_index_nbo = htons (newcoin_index); @@ -1633,7 +1661,7 @@ TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, TALER_DB_QUERY_PARAM_PTR_SIZED (buf, buf_size), TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db_conn, + result = TALER_DB_exec_prepared (session->conn, "insert_refresh_order", params); } @@ -1658,16 +1686,18 @@ TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, * Lookup in the database the @a newcoin_index coin that we want to * create in the given refresh operation. * - * @param db_conn database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection * @param session_pub refresh session key * @param newcoin_index index of the coin to generate * @param denom_pub denomination of the coin to create * @return NULL on error (not found or internal error) */ -struct GNUNET_CRYPTO_rsa_PublicKey * -TALER_MINT_DB_get_refresh_order (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - uint16_t newcoin_index) +static struct GNUNET_CRYPTO_rsa_PublicKey * +postgres_get_refresh_order (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t newcoin_index) { // FIXME: check logic char *buf; @@ -1681,7 +1711,7 @@ TALER_MINT_DB_get_refresh_order (PGconn *db_conn, TALER_DB_QUERY_PARAM_END }; - PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_order", params); + PGresult *result = TALER_DB_exec_prepared (session->conn, "get_refresh_order", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) { @@ -1719,7 +1749,8 @@ TALER_MINT_DB_get_refresh_order (PGconn *db_conn, * Store information about the commitment of the * given coin for the given refresh session in the database. * - * @param db_conn database connection to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use * @param refresh_session_pub refresh session this commitment belongs to * @param i set index (1st dimension) * @param j coin index (2nd dimension), corresponds to refreshed (new) coins @@ -1727,12 +1758,13 @@ TALER_MINT_DB_get_refresh_order (PGconn *db_conn, * @return #GNUNET_OK on success * #GNUNET_SYSERR on error */ -int -TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int i, - unsigned int j, - const struct RefreshCommitCoin *commit_coin) +static int +postgres_insert_refresh_commit_coin (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int i, + unsigned int j, + const struct RefreshCommitCoin *commit_coin) { // FIXME: check logic! uint16_t cnc_index_nbo = htons (i); @@ -1748,7 +1780,7 @@ TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, TALER_DB_QUERY_PARAM_END }; - PGresult *result = TALER_DB_exec_prepared (db_conn, "insert_refresh_commit_coin", params); + PGresult *result = TALER_DB_exec_prepared (session->conn, "insert_refresh_commit_coin", params); if (PGRES_COMMAND_OK != PQresultStatus (result)) { @@ -1772,7 +1804,8 @@ TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, * Obtain information about the commitment of the * given coin of the given refresh session from the database. * - * @param db_conn database connection to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use * @param refresh_session_pub refresh session the commitment belongs to * @param i set index (1st dimension) * @param j coin index (2nd dimension), corresponds to refreshed (new) coins @@ -1781,12 +1814,13 @@ TALER_MINT_DB_insert_refresh_commit_coin (PGconn *db_conn, * #GNUNET_NO if not found * #GNUNET_SYSERR on error */ -int -TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int cnc_index, - unsigned int newcoin_index, - struct RefreshCommitCoin *cc) +static int +postgres_get_refresh_commit_coin (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int cnc_index, + unsigned int newcoin_index, + struct RefreshCommitCoin *cc) { // FIXME: check logic! uint16_t cnc_index_nbo = htons (cnc_index); @@ -1803,7 +1837,7 @@ TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, size_t rl_buf_size; struct TALER_RefreshLinkEncrypted *rl; - PGresult *result = TALER_DB_exec_prepared (db_conn, "get_refresh_commit_coin", params); + PGresult *result = TALER_DB_exec_prepared (session->conn, "get_refresh_commit_coin", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) { @@ -1849,7 +1883,8 @@ TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, * Store the commitment to the given (encrypted) refresh link data * for the given refresh session. * - * @param db_conn database connection to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use * @param refresh_session_pub public key of the refresh session this * commitment belongs with * @param i set index (1st dimension) @@ -1857,12 +1892,13 @@ TALER_MINT_DB_get_refresh_commit_coin (PGconn *db_conn, * @param commit_link link information to store * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success */ -int -TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int i, - unsigned int j, - const struct RefreshCommitLink *commit_link) +static int +postgres_insert_refresh_commit_link (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int i, + unsigned int j, + const struct RefreshCommitLink *commit_link) { // FIXME: check logic! uint16_t cnc_index_nbo = htons (i); @@ -1876,7 +1912,7 @@ TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, TALER_DB_QUERY_PARAM_END }; - PGresult *result = TALER_DB_exec_prepared (db_conn, + PGresult *result = TALER_DB_exec_prepared (session->conn, "insert_refresh_commit_link", params); if (PGRES_COMMAND_OK != PQresultStatus (result)) @@ -1901,7 +1937,8 @@ TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, * Obtain the commited (encrypted) refresh link data * for the given refresh session. * - * @param db_conn database connection to use + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection to use * @param refresh_session_pub public key of the refresh session this * commitment belongs with * @param i set index (1st dimension) @@ -1911,12 +1948,13 @@ TALER_MINT_DB_insert_refresh_commit_link (PGconn *db_conn, * #GNUNET_NO if commitment was not found * #GNUNET_OK on success */ -int -TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, - unsigned int cnc_index, - unsigned int oldcoin_index, - struct RefreshCommitLink *cc) +static int +postgres_get_refresh_commit_link (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int cnc_index, + unsigned int oldcoin_index, + struct RefreshCommitLink *cc) { // FIXME: check logic! uint16_t cnc_index_nbo = htons (cnc_index); @@ -1929,7 +1967,7 @@ TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn, TALER_DB_QUERY_PARAM_END }; - PGresult *result = TALER_DB_exec_prepared (db_conn, + PGresult *result = TALER_DB_exec_prepared (session->conn, "get_refresh_commit_link", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -1969,17 +2007,19 @@ TALER_MINT_DB_get_refresh_commit_link (PGconn *db_conn, * of the coin. This data is later used should an old coin * be used to try to obtain the private keys during "/refresh/link". * - * @param db_conn database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection * @param session_pub refresh session * @param newcoin_index coin index * @param ev_sig coin signature * @return #GNUNET_OK on success */ -int -TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, - uint16_t newcoin_index, - const struct GNUNET_CRYPTO_rsa_Signature *ev_sig) +static int +postgres_insert_refresh_collectable (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_rsa_Signature *ev_sig) { // FIXME: check logic! uint16_t newcoin_index_nbo = htons (newcoin_index); @@ -1996,7 +2036,7 @@ TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn, TALER_DB_QUERY_PARAM_PTR_SIZED(buf, buf_size), TALER_DB_QUERY_PARAM_END }; - result = TALER_DB_exec_prepared (db_conn, + result = TALER_DB_exec_prepared (session->conn, "insert_refresh_collectable", params); } @@ -2016,12 +2056,14 @@ TALER_MINT_DB_insert_refresh_collectable (PGconn *db_conn, * Obtain the link data of a coin, that is the encrypted link * information, the denomination keys and the signatures. * - * @param db_conn database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection * @param coin_pub public key to use to retrieve linkage data * @return all known link data for the coin */ -struct LinkDataList * -TALER_db_get_link (PGconn *db_conn, +static struct LinkDataList * +postgres_get_link (void *cls, + struct TALER_MINTDB_Session *session, const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) { // FIXME: check logic! @@ -2031,7 +2073,7 @@ TALER_db_get_link (PGconn *db_conn, TALER_DB_QUERY_PARAM_PTR(coin_pub), TALER_DB_QUERY_PARAM_END }; - PGresult *result = TALER_DB_exec_prepared (db_conn, "get_link", params); + PGresult *result = TALER_DB_exec_prepared (session->conn, "get_link", params); ldl = NULL; if (PGRES_TUPLES_OK != PQresultStatus (result)) @@ -2124,26 +2166,14 @@ TALER_db_get_link (PGconn *db_conn, } -/** - * Free memory of the link data list. - * - * @param ldl link data list to release - */ -void -TALER_db_link_data_list_free (struct LinkDataList *ldl) -{ - GNUNET_break (0); // FIXME -} - - /** * Obtain shared secret and transfer public key from the public key of * the coin. This information and the link information returned by * #TALER_db_get_link() enable the owner of an old coin to determine * the private keys of the new coins after the melt. * - * - * @param db_conn database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection * @param coin_pub public key of the coin * @param transfer_pub[OUT] public transfer key * @param shared_secret_enc[OUT] set to shared secret @@ -2151,8 +2181,9 @@ TALER_db_link_data_list_free (struct LinkDataList *ldl) * #GNUNET_NO on failure (not found) * #GNUNET_SYSERR on internal failure (database issue) */ -int -TALER_db_get_transfer (PGconn *db_conn, +static int +postgres_get_transfer (void *cls, + struct TALER_MINTDB_Session *session, const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, struct TALER_EncryptedLinkSecret *shared_secret_enc) @@ -2163,7 +2194,7 @@ TALER_db_get_transfer (PGconn *db_conn, TALER_DB_QUERY_PARAM_END }; - PGresult *result = TALER_DB_exec_prepared (db_conn, "get_transfer", params); + PGresult *result = TALER_DB_exec_prepared (session->conn, "get_transfer", params); if (PGRES_TUPLES_OK != PQresultStatus (result)) { @@ -2205,19 +2236,19 @@ TALER_db_get_transfer (PGconn *db_conn, } - - /** * Compile a list of all (historic) transactions performed * with the given coin (/refresh/melt and /deposit operations). * - * @param db_conn database connection + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session database connection * @param coin_pub coin to investigate * @return list of transactions, NULL if coin is fresh */ -struct TALER_MINT_DB_TransactionList * -TALER_MINT_DB_get_coin_transactions (PGconn *db_conn, - const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) +static struct TALER_MINT_DB_TransactionList * +postgres_get_coin_transactions (void *cls, + struct TALER_MINTDB_Session *session, + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) { // FIXME: check logic! GNUNET_break (0); // FIXME: implement! @@ -2225,17 +2256,61 @@ TALER_MINT_DB_get_coin_transactions (PGconn *db_conn, } + /** - * Free linked list of transactions. + * Initialize Postgres database subsystem. * - * @param list list to free + * @param cls a configuration instance + * @return NULL on error, otherwise a `struct TALER_MINTDB_Plugin` */ -void -TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list) +void * +libtaler_plugin_mintdb_postgres_init (void *cls) { - // FIXME: check logic! - GNUNET_break (0); + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct PostgresClosure *pg; + struct TALER_MINTDB_Plugin *plugin; + + pg = GNUNET_new (struct PostgresClosure); + + if (0 != pthread_key_create (&pg->db_conn_threadlocal, + &db_conn_destroy)) + { + LOG_ERROR ("Cannnot create pthread key.\n"); + return NULL; + } + /* FIXME: use configuration section with "postgres" in its name... */ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "mint", "db", + &pg->TALER_MINT_db_connection_cfg_str)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "mint", + "db"); + return NULL; + } + plugin = GNUNET_new (struct TALER_MINTDB_Plugin); + plugin->cls = pg; + + return plugin; } -/* end of mint_db.c */ +/** + * Shutdown Postgres database subsystem. + * + * @param cls a `struct TALER_MINTDB_Plugin` + * @return NULL (always) + */ +void * +libtaler_plugin_mintdb_postgres_done (void *cls) +{ + struct TALER_MINTDB_Plugin *plugin = cls; + struct PostgresClosure *pg = plugin->cls; + + GNUNET_free (pg); + GNUNET_free (plugin); + return NULL; +} + +/* end of plugin_mintdb_postgres.c */ diff --git a/src/mint/taler-mint-dbinit.c b/src/mint/taler-mint-dbinit.c index 838f98e25..060eabc7b 100644 --- a/src/mint/taler-mint-dbinit.c +++ b/src/mint/taler-mint-dbinit.c @@ -22,7 +22,7 @@ #include #include #include "taler_util.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h" /** diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index 7903d5c1c..9adf26dfd 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -35,7 +35,7 @@ #include "taler-mint-httpd_withdraw.h" #include "taler-mint-httpd_refresh.h" #include "taler-mint-httpd_keystate.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h" /** @@ -260,16 +260,7 @@ mint_serve_process_config (const char *mint_directory) GNUNET_free (master_pub_str); if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "mint", "db", - &db_cfg)) - { - fprintf (stderr, - "invalid configuration: mint.db\n"); - return GNUNET_NO; - } - if (GNUNET_OK != - TALER_MINT_DB_init (db_cfg)) + TALER_MINT_plugin_load (cfg)) { fprintf (stderr, "failed to initialize DB subsystem\n"); diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 0c3675d38..292ef68fa 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -29,9 +29,9 @@ #include "taler-mint-httpd_db.h" #include "taler_signatures.h" #include "taler-mint-httpd_responses.h" -#include "mint_db.h" #include "taler_util.h" #include "taler-mint-httpd_keystate.h" +#include "plugin.h" /** @@ -48,7 +48,7 @@ int TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, const struct Deposit *deposit) { - PGconn *db_conn; + struct TALER_MINTDB_Session *session; struct TALER_MINT_DB_TransactionList *tl; struct TALER_MINT_DB_TransactionList *pos; struct TALER_Amount spent; @@ -59,14 +59,16 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, struct TALER_MINT_DenomKeyIssuePriv *dki; int ret; - if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) + if (NULL == (session = plugin->get_session (plugin->cls, + GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } if (GNUNET_YES == - TALER_MINT_DB_have_deposit (db_conn, - deposit)) + plugin->have_deposit (plugin->cls, + session, + deposit)) { return TALER_MINT_reply_deposit_success (connection, &deposit->coin.coin_pub, @@ -88,13 +90,15 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, TALER_MINT_key_state_release (mks); if (GNUNET_OK != - TALER_MINT_DB_transaction (db_conn)) + plugin->start (plugin->cls, + session)) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } - tl = TALER_MINT_DB_get_coin_transactions (db_conn, - &deposit->coin.coin_pub); + tl = plugin->get_coin_transactions (plugin->cls, + session, + &deposit->coin.coin_pub); spent = fee_deposit; /* fee for THIS transaction */ if (GNUNET_OK != TALER_amount_add (&spent, @@ -155,7 +159,8 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, if (0 < TALER_amount_cmp (&spent, &value)) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); ret = TALER_MINT_reply_deposit_insufficient_funds (connection, tl); TALER_MINT_DB_free_coin_transaction_list (tl); @@ -164,16 +169,19 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, TALER_MINT_DB_free_coin_transaction_list (tl); if (GNUNET_OK != - TALER_MINT_DB_insert_deposit (db_conn, - deposit)) + plugin->insert_deposit (plugin->cls, + session, + deposit)) { LOG_WARNING ("Failed to store /deposit information in database\n"); - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_db_error (connection); } if (GNUNET_OK != - TALER_MINT_DB_commit (db_conn)) + plugin->commit (plugin->cls, + session)) { LOG_WARNING ("/deposit transaction commit failed\n"); return TALER_MINT_reply_commit_error (connection); @@ -200,17 +208,19 @@ int TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection, const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) { - PGconn *db_conn; + struct TALER_MINTDB_Session *session; struct ReserveHistory *rh; int res; - if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) + if (NULL == (session = plugin->get_session (plugin->cls, + GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } - rh = TALER_MINT_DB_get_reserve_history (db_conn, - reserve_pub); + rh = plugin->get_reserve_history (plugin->cls, + session, + reserve_pub); if (NULL == rh) return TALER_MINT_reply_json_pack (connection, MHD_HTTP_NOT_FOUND, @@ -245,7 +255,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, size_t blinded_msg_len, const struct GNUNET_CRYPTO_EddsaSignature *signature) { - PGconn *db_conn; + struct TALER_MINTDB_Session *session; struct ReserveHistory *rh; const struct ReserveHistory *pos; struct MintKeyState *key_state; @@ -266,14 +276,16 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, blinded_msg_len, &h_blind); - if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) + if (NULL == (session = plugin->get_session (plugin->cls, + GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } - res = TALER_MINT_DB_get_collectable_blindcoin (db_conn, - &h_blind, - &collectable); + res = plugin->get_collectable_blindcoin (plugin->cls, + session, + &h_blind, + &collectable); if (GNUNET_SYSERR == res) { GNUNET_break (0); @@ -305,18 +317,21 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, "Denomination not found"); } if (GNUNET_OK != - TALER_MINT_DB_transaction (db_conn)) + plugin->start (plugin->cls, + session)) { GNUNET_break (0); TALER_MINT_key_state_release (key_state); return TALER_MINT_reply_internal_db_error (connection); } - rh = TALER_MINT_DB_get_reserve_history (db_conn, - reserve); + rh = plugin->get_reserve_history (plugin->cls, + session, + reserve); if (NULL == rh) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); TALER_MINT_key_state_release (key_state); return TALER_MINT_reply_json_pack (connection, MHD_HTTP_NOT_FOUND, @@ -336,7 +351,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, &value, &fee_withdraw)) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); TALER_MINT_key_state_release (key_state); return TALER_MINT_reply_internal_db_error (connection); } @@ -356,7 +372,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, &deposit_total, &pos->details.bank->amount)) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); TALER_MINT_key_state_release (key_state); return TALER_MINT_reply_internal_db_error (connection); } @@ -375,7 +392,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, &withdraw_total, &value)) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); TALER_MINT_key_state_release (key_state); return TALER_MINT_reply_internal_db_error (connection); } @@ -392,7 +410,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, &balance)) { TALER_MINT_key_state_release (key_state); - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); res = TALER_MINT_reply_withdraw_sign_insufficient_funds (connection, rh); TALER_MINT_DB_free_reserve_history (rh); @@ -408,7 +427,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, if (NULL == sig) { GNUNET_break (0); - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_error (connection, "Internal error"); } @@ -420,18 +440,21 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, &collectable.h_coin_envelope); collectable.reserve_sig = *signature; if (GNUNET_OK != - TALER_MINT_DB_insert_collectable_blindcoin (db_conn, - &h_blind, - amount_required, - &collectable)) + plugin->insert_collectable_blindcoin (plugin->cls, + session, + &h_blind, + amount_required, + &collectable)) { GNUNET_break (0); GNUNET_CRYPTO_rsa_signature_free (sig); - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_db_error (connection); } if (GNUNET_OK != - TALER_MINT_DB_commit (db_conn)) + plugin->commit (plugin->cls, + session)) { LOG_WARNING ("/withdraw/sign transaction commit failed\n"); return TALER_MINT_reply_commit_error (connection); @@ -448,7 +471,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, * the database. * * @param connection the connection to send errors to - * @param db_conn the database connection + * @param session the database connection * @param key_state the mint's key state * @param session_pub the refresh session's public key * @param coin_public_info the coin to melt @@ -460,7 +483,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, */ static int refresh_accept_melts (struct MHD_Connection *connection, - PGconn *db_conn, + struct TALER_MINTDB_Session *session, const struct MintKeyState *key_state, const struct GNUNET_HashCode *melt_hash, const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, @@ -489,8 +512,9 @@ refresh_accept_melts (struct MHD_Connection *connection, TALER_amount_ntoh (&coin_value, &dki->value); - tl = TALER_MINT_DB_get_coin_transactions (db_conn, - &coin_public_info->coin_pub); + tl = plugin->get_coin_transactions (plugin->cls, + session, + &coin_public_info->coin_pub); /* FIXME: #3636: compute how much value is left with this coin and compare to `expected_value`! (subtract from "coin_value") */ coin_residual = coin_value; @@ -518,10 +542,11 @@ refresh_accept_melts (struct MHD_Connection *connection, melt.melt_hash = *melt_hash; melt.amount = coin_details->melt_amount; if (GNUNET_OK != - TALER_MINT_DB_insert_refresh_melt (db_conn, - session_pub, - oldcoin_index, - &melt)) + plugin->insert_refresh_melt (plugin->cls, + session, + session_pub, + oldcoin_index, + &melt)) { GNUNET_break (0); return GNUNET_SYSERR; @@ -570,37 +595,42 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, struct RefreshCommitLink *const* commit_link) { struct MintKeyState *key_state; - struct RefreshSession session; - PGconn *db_conn; + struct RefreshSession refresh_session; + struct TALER_MINTDB_Session *session; int res; unsigned int i; unsigned int j; - if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) + if (NULL == (session = plugin->get_session (plugin->cls, + GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } if (GNUNET_OK != - TALER_MINT_DB_transaction (db_conn)) + plugin->start (plugin->cls, + session)) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } - res = TALER_MINT_DB_get_refresh_session (db_conn, - refresh_session_pub, - &session); + res = plugin->get_refresh_session (plugin->cls, + session, + refresh_session_pub, + &refresh_session); if (GNUNET_YES == res) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); res = TALER_MINT_reply_refresh_melt_success (connection, - &session.session_hash, - session.noreveal_index); + &refresh_session.session_hash, + refresh_session.noreveal_index); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } if (GNUNET_SYSERR == res) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_db_error (connection); } @@ -610,7 +640,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, { if (GNUNET_OK != (res = refresh_accept_melts (connection, - db_conn, + session, key_state, melt_hash, refresh_session_pub, @@ -619,7 +649,8 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, i))) { TALER_MINT_key_state_release (key_state); - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } } @@ -629,12 +660,14 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, for (i=0;iinsert_refresh_order (plugin->cls, + session, + refresh_session_pub, + i, + denom_pubs[i])) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_db_error (connection); } } @@ -644,13 +677,15 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, for (j = 0; j < num_new_denoms; j++) { if (GNUNET_OK != - TALER_MINT_DB_insert_refresh_commit_coin (db_conn, - refresh_session_pub, - i, - j, - &commit_coin[i][j])) + plugin->insert_refresh_commit_coin (plugin->cls, + session, + refresh_session_pub, + i, + j, + &commit_coin[i][j])) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_db_error (connection); } } @@ -660,13 +695,15 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, for (j = 0; j < coin_count; j++) { if (GNUNET_OK != - TALER_MINT_DB_insert_refresh_commit_link (db_conn, - refresh_session_pub, - i, - j, - &commit_link[i][j])) + plugin->insert_refresh_commit_link (plugin->cls, + session, + refresh_session_pub, + i, + j, + &commit_link[i][j])) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_db_error (connection); } } @@ -674,34 +711,37 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, /* store 'global' session data */ - session.melt_sig = *client_signature; - session.session_hash = *melt_hash; - session.num_oldcoins = coin_count; - session.num_newcoins = num_new_denoms; - session.kappa = KAPPA; // FIXME... - session.noreveal_index + refresh_session.melt_sig = *client_signature; + refresh_session.session_hash = *melt_hash; + refresh_session.num_oldcoins = coin_count; + refresh_session.num_newcoins = num_new_denoms; + refresh_session.kappa = KAPPA; // FIXME... + refresh_session.noreveal_index = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, - session.kappa); + refresh_session.kappa); if (GNUNET_OK != - (res = TALER_MINT_DB_create_refresh_session (db_conn, - refresh_session_pub, - &session))) + (res = plugin->create_refresh_session (plugin->cls, + session, + refresh_session_pub, + &refresh_session))) { - TALER_MINT_DB_rollback (db_conn); + plugin->rollback (plugin->cls, + session); return TALER_MINT_reply_internal_db_error (connection); } if (GNUNET_OK != - TALER_MINT_DB_commit (db_conn)) + plugin->commit (plugin->cls, + session)) { LOG_WARNING ("/refresh/melt transaction commit failed\n"); return TALER_MINT_reply_commit_error (connection); } return TALER_MINT_reply_refresh_melt_success (connection, - &session.session_hash, - session.noreveal_index); + &refresh_session.session_hash, + refresh_session.noreveal_index); } @@ -712,7 +752,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, * Then derives the shared secret for each kappa, and check that they match. * * @param connection the MHD connection to handle - * @param db_conn database connection to use + * @param session database connection to use * @param refresh_session session to query * @param off commitment offset to check * @param num_oldcoins size of the @a transfer_privs and @a melts arrays @@ -726,7 +766,7 @@ TALER_MINT_db_execute_refresh_melt (struct MHD_Connection *connection, */ static int check_commitment (struct MHD_Connection *connection, - PGconn *db_conn, + struct TALER_MINTDB_Session *session, const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, unsigned int off, unsigned int num_oldcoins, @@ -749,11 +789,12 @@ check_commitment (struct MHD_Connection *connection, struct TALER_LinkSecret shared_secret; struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub_check; - res = TALER_MINT_DB_get_refresh_commit_link (db_conn, - refresh_session, - off, - j, - &commit_link); + res = plugin->get_refresh_commit_link (plugin->cls, + session, + refresh_session, + off, + j, + &commit_link); if (GNUNET_OK != res) { GNUNET_break (0); @@ -842,11 +883,12 @@ check_commitment (struct MHD_Connection *connection, char *buf; size_t buf_len; - res = TALER_MINT_DB_get_refresh_commit_coin (db_conn, - refresh_session, - off, - j, - &commit_coin); + res = plugin->get_refresh_commit_coin (plugin->cls, + session, + refresh_session, + off, + j, + &commit_coin); if (GNUNET_OK != res) { GNUNET_break (0); @@ -914,7 +956,7 @@ check_commitment (struct MHD_Connection *connection, * envelope from the database and performs the signing operation. * * @param connection the MHD connection to handle - * @param db_conn database connection to use + * @param session database connection to use * @param refresh_session session to query * @param key_state key state to lookup denomination pubs * @param denom_pub denomination key for the coin to create @@ -925,7 +967,7 @@ check_commitment (struct MHD_Connection *connection, */ static struct GNUNET_CRYPTO_rsa_Signature * refresh_mint_coin (struct MHD_Connection *connection, - PGconn *db_conn, + struct TALER_MINTDB_Session *session, const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, struct MintKeyState *key_state, const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub, @@ -937,11 +979,12 @@ refresh_mint_coin (struct MHD_Connection *connection, struct GNUNET_CRYPTO_rsa_Signature *ev_sig; int res; - res = TALER_MINT_DB_get_refresh_commit_coin (db_conn, - refresh_session, - noreveal_index, - coin_off, - &commit_coin); + res = plugin->get_refresh_commit_coin (plugin->cls, + session, + refresh_session, + noreveal_index, + coin_off, + &commit_coin); if (GNUNET_OK != res) { GNUNET_break (0); @@ -962,10 +1005,11 @@ refresh_mint_coin (struct MHD_Connection *connection, return NULL; } if (GNUNET_OK != - TALER_MINT_DB_insert_refresh_collectable (db_conn, - refresh_session, - coin_off, - ev_sig)) + plugin->insert_refresh_collectable (plugin->cls, + session, + refresh_session, + coin_off, + ev_sig)) { GNUNET_break (0); GNUNET_CRYPTO_rsa_signature_free (ev_sig); @@ -997,7 +1041,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, struct GNUNET_CRYPTO_EcdsaPrivateKey *const*transfer_privs) { int res; - PGconn *db_conn; + struct TALER_MINTDB_Session *session; struct RefreshSession refresh_session; struct MintKeyState *key_state; struct RefreshMelt *melts; @@ -1007,15 +1051,17 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, unsigned int j; unsigned int off; - if (NULL == (db_conn = TALER_MINT_DB_get_connection (GNUNET_NO))) + if (NULL == (session = plugin->get_session (plugin->cls, + GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } - res = TALER_MINT_DB_get_refresh_session (db_conn, - refresh_session_pub, - &refresh_session); + res = plugin->get_refresh_session (plugin->cls, + session, + refresh_session_pub, + &refresh_session); if (GNUNET_NO == res) return TALER_MINT_reply_arg_invalid (connection, "session_pub"); @@ -1032,10 +1078,11 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, for (j=0;jget_refresh_melt (plugin->cls, + session, + refresh_session_pub, + j, + &melts[j])) { GNUNET_break (0); GNUNET_free (melts); @@ -1046,9 +1093,10 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, sizeof (struct GNUNET_CRYPTO_rsa_PublicKey *)); for (j=0;jget_refresh_order (plugin->cls, + session, + refresh_session_pub, + j); if (NULL == denom_pubs[j]) { GNUNET_break (0); @@ -1069,7 +1117,7 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, off = 1; if (GNUNET_OK != (res = check_commitment (connection, - db_conn, + session, refresh_session_pub, i + off, refresh_session.num_oldcoins, @@ -1089,7 +1137,8 @@ TALER_MINT_db_execute_refresh_reveal (struct MHD_Connection *connection, /* Client request OK, start transaction */ if (GNUNET_OK != - TALER_MINT_DB_transaction (db_conn)) + plugin->start (plugin->cls, + session)) { GNUNET_break (0); for (j=0;jcommit (plugin->cls, + session)) { LOG_WARNING ("/refresh/reveal transaction commit failed\n"); for (i=0;iget_session (plugin->cls, + GNUNET_NO))) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } - res = TALER_db_get_transfer (db_conn, - coin_pub, - &transfer_pub, - &shared_secret_enc); + res = plugin->get_transfer (plugin->cls, + session, + coin_pub, + &transfer_pub, + &shared_secret_enc); if (GNUNET_SYSERR == res) { GNUNET_break (0); @@ -1190,7 +1242,9 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection, } GNUNET_assert (GNUNET_OK == res); - ldl = TALER_db_get_link (db_conn, coin_pub); + ldl = plugin->get_link (plugin->cls, + session, + coin_pub); if (NULL == ldl) { return TALER_MINT_reply_json_pack (connection, diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index a40e3ae22..dbfecccd1 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -25,7 +25,7 @@ #include #include #include "taler_util.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h" /** diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index 37d6d23d5..e411e0d8e 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -32,7 +32,7 @@ #include #include #include -#include "mint_db.h" +#include "taler_mintdb_plugin.h" #include "taler_signatures.h" #include "taler_util.h" #include "taler-mint-httpd_parsing.h" diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index e62521ef4..d4979288d 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -24,7 +24,7 @@ #include #include #include -#include "mint_db.h" +#include "taler_mintdb_plugin.h" #include "taler_signatures.h" #include "taler_util.h" #include "taler-mint-httpd_parsing.h" diff --git a/src/mint/taler-mint-reservemod.c b/src/mint/taler-mint-reservemod.c index df3f0cd5d..958d234b1 100644 --- a/src/mint/taler-mint-reservemod.c +++ b/src/mint/taler-mint-reservemod.c @@ -24,7 +24,7 @@ #include #include "taler_util.h" #include "taler_signatures.h" -#include "mint_db.h" +#include "taler_mintdb_plugin.h" #include "db_pq.h" diff --git a/src/mint/taler_mintdb_plugin.h b/src/mint/taler_mintdb_plugin.h new file mode 100644 index 000000000..d330b817b --- /dev/null +++ b/src/mint/taler_mintdb_plugin.h @@ -0,0 +1,1002 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 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 +*/ +/** + * @file mint/mint_db.h + * @brief Low-level (statement-level) database access for the mint + * @author Florian Dold + * @author Christian Grothoff + */ +#ifndef MINT_DB_H +#define MINT_DB_H + +#include +#include "taler_util.h" + + +/** + * Information we keep on bank transfer(s) that established a reserve. + */ +struct BankTransfer +{ + + /** + * Public key of the reserve that was filled. + */ + struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; + + /** + * Amount that was transferred to the mint. + */ + struct TALER_Amount amount; + + /** + * Detailed wire information about the transaction. + */ + const json_t *wire; + +}; + + +/** + * A summary of a Reserve + */ +struct Reserve +{ + /** + * The reserve's public key. This uniquely identifies the reserve + */ + struct GNUNET_CRYPTO_EddsaPublicKey *pub; + + /** + * The balance amount existing in the reserve + */ + struct TALER_Amount balance; + + /** + * The expiration date of this reserve + */ + struct GNUNET_TIME_Absolute expiry; +}; + + +/** + * Information we keep for a withdrawn coin to reproduce + * the /withdraw operation if needed, and to have proof + * that a reserve was drained by this amount. + */ +struct CollectableBlindcoin +{ + + /** + * Our signature over the (blinded) coin. + */ + struct GNUNET_CRYPTO_rsa_Signature *sig; + + /** + * Denomination key (which coin was generated). + */ + struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; + + /** + * Public key of the reserve that was drained. + */ + struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; + + /** + * Hash over the blinded message, needed to verify + * the @e reserve_sig. + */ + struct GNUNET_HashCode h_coin_envelope; + + /** + * Signature confirming the withdrawl, matching @e reserve_pub, + * @e denom_pub and @e h_coin_envelope. + */ + struct GNUNET_CRYPTO_EddsaSignature reserve_sig; +}; + + + +/** + * Types of operations on a reserved. + */ +enum TALER_MINT_DB_ReserveOperation +{ + /** + * Money was deposited into the reserve via a bank transfer. + */ + TALER_MINT_DB_RO_BANK_TO_MINT = 0, + + /** + * A Coin was withdrawn from the reserve using /withdraw. + */ + TALER_MINT_DB_RO_WITHDRAW_COIN = 1 +}; + + +/** + * Reserve history as a linked list. Lists all of the transactions + * associated with this reserve (such as the bank transfers that + * established the reserve and all /withdraw operations we have done + * since). + */ +struct ReserveHistory +{ + + /** + * Next entry in the reserve history. + */ + struct ReserveHistory *next; + + /** + * Type of the event, determins @e details. + */ + enum TALER_MINT_DB_ReserveOperation type; + + /** + * Details of the operation, depending on @e type. + */ + union + { + + /** + * Details about a bank transfer to the mint. + */ + struct BankTransfer *bank; + + /** + * Details about a /withdraw operation. + */ + struct CollectableBlindcoin *withdraw; + + } details; + +}; + + +/** + * Specification for a /deposit operation. + */ +struct Deposit +{ + /** + * Information about the coin that is being deposited. + */ + struct TALER_CoinPublicInfo coin; + + /** + * ECDSA signature affirming that the customer intends + * this coin to be deposited at the merchant identified + * by @e h_wire in relation to the contract identified + * by @e h_contract. + */ + struct GNUNET_CRYPTO_EcdsaSignature csig; + + /** + * Public key of the merchant. Enables later identification + * of the merchant in case of a need to rollback transactions. + */ + struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub; + + /** + * Hash over the contract between merchant and customer + * (remains unknown to the Mint). + */ + struct GNUNET_HashCode h_contract; + + /** + * Hash of the (canonical) representation of @e wire, used + * to check the signature on the request. Generated by + * the mint from the detailed wire data provided by the + * merchant. + */ + struct GNUNET_HashCode h_wire; + + /** + * Detailed wire information for executing the transaction. + */ + const json_t *wire; + + /** + * Merchant-generated transaction ID to detect duplicate + * transactions. + */ + uint64_t transaction_id; + + /** + * Fraction of the coin's remaining value to be deposited. + * The coin is identified by @e coin_pub. + */ + struct TALER_Amount amount; + +}; + + +/** + * Global information for a refreshing session. Includes + * dimensions of the operation, security parameters and + * client signatures from "/refresh/melt" and "/refresh/commit". + */ +struct RefreshSession +{ + /** + * Signature over the commitments by the client, + * only valid if @e has_commit_sig is set. + */ + struct GNUNET_CRYPTO_EddsaSignature commit_sig; + + /** + * Hash over coins to melt and coins to create of the + * refresh session. + */ + struct GNUNET_HashCode session_hash; + + /** + * Signature over the melt by the client. + */ + struct GNUNET_CRYPTO_EddsaSignature melt_sig; + + /** + * Number of coins we are melting. + */ + uint16_t num_oldcoins; + + /** + * Number of new coins we are creating. + */ + uint16_t num_newcoins; + + /** + * Number of parallel operations we perform for the cut and choose. + * (must be greater or equal to three for security). 0 if not yet + * known. + */ + uint16_t kappa; + + /** + * Index (smaller @e kappa) which the mint has chosen to not + * have revealed during cut and choose. + */ + uint16_t noreveal_index; + +}; + + +/** + * Specification for coin in a /refresh/melt operation. + */ +struct RefreshMelt +{ + /** + * Information about the coin that is being melted. + */ + struct TALER_CoinPublicInfo coin; + + /** + * Signature over the melting operation. + */ + struct GNUNET_CRYPTO_EcdsaSignature coin_sig; + + /** + * Which melting operation should the coin become a part of. + */ + struct GNUNET_HashCode melt_hash; + + /** + * How much value is being melted? + * This amount includes the fees, so the final amount contributed + * to the melt is this value minus the fee for melting the coin. + */ + struct TALER_Amount amount; + +}; + + +/** + * We have as many `struct RefreshCommitCoin` as there are new + * coins being created by the refresh (for each of the kappa + * sets). These are the coins we ask the mint to sign if the + * respective set is selected. + */ +struct RefreshCommitCoin +{ + + /** + * Encrypted data allowing those able to decrypt it to derive + * the private keys of the new coins created by the refresh. + */ + struct TALER_RefreshLinkEncrypted *refresh_link; + + /** + * Blinded message to be signed (in envelope), with @e coin_env_size bytes. + */ + char *coin_ev; + + /** + * Number of bytes in @e coin_ev. + */ + size_t coin_ev_size; + +}; + + +/** + * For each (old) coin being melted, we have a `struct + * RefreshCommitLink` that allows the user to find the shared secret + * to decrypt the respective refresh links for the new coins in the + * `struct RefreshCommitCoin`. + */ +struct RefreshCommitLink +{ + /** + * Transfer public key (FIXME: explain!) + */ + struct GNUNET_CRYPTO_EcdsaPublicKey transfer_pub; + + /** + * Encrypted shared secret to decrypt the link. + */ + struct TALER_EncryptedLinkSecret shared_secret_enc; +}; + + +/** + * Linked list of refresh information linked to a coin. + */ +struct LinkDataList +{ + /** + * Information is stored in a NULL-terminated linked list. + */ + struct LinkDataList *next; + + /** + * Link data, used to recover the private key of the coin + * by the owner of the old coin. + */ + struct TALER_RefreshLinkEncrypted *link_data_enc; + + /** + * Denomination public key, determines the value of the coin. + */ + struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub; + + /** + * Signature over the blinded envelope. + */ + struct GNUNET_CRYPTO_rsa_Signature *ev_sig; +}; + + +/** + * Specification for a /lock operation. + */ +struct Lock +{ + /** + * Information about the coin that is being melted. + */ + struct TALER_CoinPublicInfo coin; + + /** + * Signature over the melting operation. + */ + const struct GNUNET_CRYPTO_EcdsaSignature coin_sig; + + /** + * How much value is being melted? + */ + struct TALER_Amount amount; + + // FIXME: more needed... +}; + + +/** + * Enumeration to classify the different types of transactions + * that can be done with a coin. + */ +enum TALER_MINT_DB_TransactionType +{ + /** + * /deposit operation. + */ + TALER_MINT_DB_TT_DEPOSIT = 0, + + /** + * /refresh/melt operation. + */ + TALER_MINT_DB_TT_REFRESH_MELT = 1, + + /** + * /lock operation. + */ + TALER_MINT_DB_TT_LOCK = 2 +}; + + +/** + * List of transactions we performed for a particular coin. + */ +struct TALER_MINT_DB_TransactionList +{ + + /** + * Next pointer in the NULL-terminated linked list. + */ + struct TALER_MINT_DB_TransactionList *next; + + /** + * Type of the transaction, determines what is stored in @e details. + */ + enum TALER_MINT_DB_TransactionType type; + + /** + * Details about the transaction, depending on @e type. + */ + union + { + + /** + * Details if transaction was a /deposit operation. + */ + struct Deposit *deposit; + + /** + * Details if transaction was a /refresh/melt operation. + */ + struct RefreshMelt *melt; + + /** + * Details if transaction was a /lock operation. + */ + struct Lock *lock; + + } details; + +}; + + +/** + * Handle for a database session (per-thread, for transactions). + */ +struct TALER_MINTDB_Session; + + +/** + * The plugin API, returned from the plugin's "init" function. + * The argument given to "init" is simply a configuration handle. + */ +struct TALER_MINTDB_Plugin +{ + + /** + * Closure for all callbacks. + */ + void *cls; + + /** + * Get the thread-local database-handle. + * Connect to the db if the connection does not exist yet. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param temporary #GNUNET_YES to use a temporary schema; #GNUNET_NO to use the + * database default one + * @param the database connection, or NULL on error + */ + struct TALER_MINTDB_Session * + (*get_session) (void *cls, + int temporary); + + + /** + * Drop the temporary taler schema. This is only useful for testcases. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ + int + (*drop_temporary) (void *cls, + struct TALER_MINTDB_Session *db); + + + /** + * Create the necessary tables if they are not present + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param temporary should we use a temporary schema + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ + int + (*create_tables) (void *cls, + int temporary); + + + /** + * Start a transaction. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn connection to use + * @return #GNUNET_OK on success + */ + int + (*start) (void *cls, + struct TALER_MINTDB_Session *db_conn); + + + /** + * Commit a transaction. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn connection to use + * @return #GNUNET_OK on success + */ + int + (*commit) (void *cls, + struct TALER_MINTDB_Session *db_conn); + + + /** + * Abort/rollback a transaction. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn connection to use + */ + void + (*rollback) (void *cls, + struct TALER_MINTDB_Session *db_conn); + + + /** + * Get the summary of a reserve. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db the database connection handle + * @param reserve the reserve data. The public key of the reserve should be set + * in this structure; it is used to query the database. The balance + * and expiration are then filled accordingly. + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ + int + (*reserve_get) (void *cls, + struct TALER_MINTDB_Session *db, + struct Reserve *reserve); + + /* FIXME: add functions to add bank transfers to our DB + (and to test if we already did add one) (#3633/#3717) */ + + + /** + * Insert a incoming transaction into reserves. New reserves are also created + * through this function. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db the database connection handle + * @param reserve the reserve structure. The public key of the reserve should + * be set here. Upon successful execution of this function, the + * balance and expiration of the reserve will be updated. + * @param balance the amount that has to be added to the reserve + * @param expiry the new expiration time for the reserve + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failures + */ + int + (*reserves_in_insert) (void *cls, + struct TALER_MINTDB_Session *db, + struct Reserve *reserve, + const struct TALER_Amount *balance, + const struct GNUNET_TIME_Absolute expiry); + + + /** + * Locate the response for a /withdraw request under the + * key of the hash of the blinded message. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection to use + * @param h_blind hash of the blinded message + * @param collectable corresponding collectable coin (blind signature) + * if a coin is found + * @return #GNUNET_SYSERR on internal error + * #GNUNET_NO if the collectable was not found + * #GNUNET_YES on success + */ + int + (*get_collectable_blindcoin) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_HashCode *h_blind, + struct CollectableBlindcoin *collectable); + + + /** + * Store collectable bit coin under the corresponding + * hash of the blinded message. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection to use + * @param h_blind hash of the blinded message + * @param withdraw amount by which the reserve will be withdrawn with this + * transaction + * @param collectable corresponding collectable coin (blind signature) + * if a coin is found + * @return #GNUNET_SYSERR on internal error + * #GNUNET_NO if the collectable was not found + * #GNUNET_YES on success + */ + int + (*insert_collectable_blindcoin) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_HashCode *h_blind, + struct TALER_Amount withdraw, + const struct CollectableBlindcoin *collectable); + + + /** + * Get all of the transaction history associated with the specified + * reserve. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn connection to use + * @param reserve_pub public key of the reserve + * @return known transaction history (NULL if reserve is unknown) + */ + struct ReserveHistory * + (*get_reserve_history) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub); + + + /** + * Check if we have the specified deposit already in the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param deposit deposit to search for + * @return #GNUNET_YES if we know this operation, + * #GNUNET_NO if this deposit is unknown to us, + * #GNUNET_SYSERR on internal error + */ + int + (*have_deposit) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct Deposit *deposit); + + + /** + * Insert information about deposited coin into the + * database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn connection to the database + * @param deposit deposit information to store + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ + int + (*insert_deposit) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct Deposit *deposit); + + + /** + * Lookup refresh session data under the given public key. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database handle to use + * @param refresh_session_pub public key to use for the lookup + * @param refresh_session[OUT] where to store the result + * @return #GNUNET_YES on success, + * #GNUNET_NO if not found, + * #GNUNET_SYSERR on DB failure + */ + int + (*get_refresh_session) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + struct RefreshSession *refresh_session); + + + /** + * Store new refresh session data under the given public key. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database handle to use + * @param refresh_session_pub public key to use to locate the session + * @param refresh_session session data to store + * @return #GNUNET_YES on success, + * #GNUNET_SYSERR on DB failure + */ + int + (*create_refresh_session) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + const struct RefreshSession *refresh_session); + + + + /** + * Store the given /refresh/melt request in the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param refresh_session session key of the melt operation + * @param oldcoin_index index of the coin to store + * @param melt coin melt operation details to store + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ + int + (*insert_refresh_melt) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, + uint16_t oldcoin_index, + const struct RefreshMelt *melt); + + + /** + * Get information about melted coin details from the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param refresh_session session key of the melt operation + * @param oldcoin_index index of the coin to retrieve + * @param melt melt data to fill in + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ + int + (*get_refresh_melt) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session, + uint16_t oldcoin_index, + struct RefreshMelt *melt); + + + /** + * Store in the database which coin(s) we want to create + * in a given refresh operation. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param session_pub refresh session key + * @param newcoin_index index of the coin to generate + * @param denom_pub denomination of the coin to create + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ + int + (*insert_refresh_order) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_rsa_PublicKey *denom_pub); + + + /** + * Lookup in the database the @a newcoin_index coin that we want to + * create in the given refresh operation. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param session_pub refresh session key + * @param newcoin_index index of the coin to generate + * @param denom_pub denomination of the coin to create + * @return NULL on error (not found or internal error) + */ + struct GNUNET_CRYPTO_rsa_PublicKey * + (*get_refresh_order) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t newcoin_index); + + + /** + * Store information about the commitment of the + * given coin for the given refresh session in the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection to use + * @param refresh_session_pub refresh session this commitment belongs to + * @param i set index (1st dimension) + * @param j coin index (2nd dimension), corresponds to refreshed (new) coins + * @param commit_coin coin commitment to store + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on error + */ + int + (*insert_refresh_commit_coin) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int i, + unsigned int j, + const struct RefreshCommitCoin *commit_coin); + + + /** + * Obtain information about the commitment of the + * given coin of the given refresh session from the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection to use + * @param refresh_session_pub refresh session the commitment belongs to + * @param i set index (1st dimension) + * @param j coin index (2nd dimension), corresponds to refreshed (new) coins + * @param commit_coin[OUT] coin commitment to return + * @return #GNUNET_OK on success + * #GNUNET_NO if not found + * #GNUNET_SYSERR on error + */ + int + (*get_refresh_commit_coin) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int i, + unsigned int j, + struct RefreshCommitCoin *commit_coin); + + + /** + * Store the commitment to the given (encrypted) refresh link data + * for the given refresh session. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection to use + * @param refresh_session_pub public key of the refresh session this + * commitment belongs with + * @param i set index (1st dimension) + * @param j coin index (2nd dimension), corresponds to melted (old) coins + * @param commit_link link information to store + * @return #GNUNET_SYSERR on internal error, #GNUNET_OK on success + */ + int + (*insert_refresh_commit_link) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int i, + unsigned int j, + const struct RefreshCommitLink *commit_link); + + /** + * Obtain the commited (encrypted) refresh link data + * for the given refresh session. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection to use + * @param refresh_session_pub public key of the refresh session this + * commitment belongs with + * @param i set index (1st dimension) + * @param j coin index (2nd dimension), corresponds to melted (old) coins + * @param cc[OUT] link information to return + * @return #GNUNET_SYSERR on internal error, + * #GNUNET_NO if commitment was not found + * #GNUNET_OK on success + */ + int + (*get_refresh_commit_link) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *refresh_session_pub, + unsigned int i, + unsigned int j, + struct RefreshCommitLink *cc); + + + /** + * Insert signature of a new coin generated during refresh into + * the database indexed by the refresh session and the index + * of the coin. This data is later used should an old coin + * be used to try to obtain the private keys during "/refresh/link". + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param session_pub refresh session + * @param newcoin_index coin index + * @param ev_sig coin signature + * @return #GNUNET_OK on success + */ + int + (*insert_refresh_collectable) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *session_pub, + uint16_t newcoin_index, + const struct GNUNET_CRYPTO_rsa_Signature *ev_sig); + + + /** + * Obtain the link data of a coin, that is the encrypted link + * information, the denomination keys and the signatures. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param coin_pub public key to use to retrieve linkage data + * @return all known link data for the coin + */ + struct LinkDataList * + (*get_link) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub); + + + /** + * Obtain shared secret and transfer public key from the public key of + * the coin. This information and the link information returned by + * #TALER_db_get_link() enable the owner of an old coin to determine + * the private keys of the new coins after the melt. + * + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param coin_pub public key of the coin + * @param transfer_pub[OUT] public transfer key + * @param shared_secret_enc[OUT] set to shared secret + * @return #GNUNET_OK on success, + * #GNUNET_NO on failure (not found) + * #GNUNET_SYSERR on internal failure (database issue) + */ + int + (*get_transfer) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub, + struct GNUNET_CRYPTO_EcdsaPublicKey *transfer_pub, + struct TALER_EncryptedLinkSecret *shared_secret_enc); + + + /** + * Test if the given /lock request is known to us. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param lock lock operation + * @return #GNUNET_YES if known, + * #GNUENT_NO if not, + * #GNUNET_SYSERR on internal error + */ + int + (*have_lock) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct Lock *lock); + + + /** + * Store the given /lock request in the database. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param lock lock operation + * @return #GNUNET_OK on success + * #GNUNET_SYSERR on internal error + */ + int + (*insert_lock) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct Lock *lock); + + + /** + * Compile a list of all (historic) transactions performed + * with the given coin (/refresh/melt and /deposit operations). + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param db_conn database connection + * @param coin_pub coin to investigate + * @return list of transactions, NULL if coin is fresh + */ + struct TALER_MINT_DB_TransactionList * + (*get_coin_transactions) (void *cls, + struct TALER_MINTDB_Session *db_conn, + const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub); + +}; + + +#endif /* _NEURO_MINT_DB_H */ diff --git a/src/util/Makefile.am b/src/util/Makefile.am index a15d42ad8..07c120c50 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -8,7 +8,8 @@ libtalerutil_la_SOURCES = \ amount.c \ crypto.c \ util.c \ - json.c + json.c \ + os_installation.c libtalerutil_la_LIBADD = \ -lgnunetutil \ diff --git a/src/util/os_installation.c b/src/util/os_installation.c new file mode 100644 index 000000000..82dc49180 --- /dev/null +++ b/src/util/os_installation.c @@ -0,0 +1,701 @@ +/* + This file is part of GNUnet. + Copyright (C) 2006-2014 Christian Grothoff (and other contributing authors) + + GNUnet 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. + + GNUnet 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 GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file os_installation.c + * @brief get paths used by the program; based heavily on the + * corresponding GNUnet file, just adapted for Taler. + * @author Milan + */ +#include "platform.h" +#include +#if DARWIN +#include +#include +#elif WINDOWS +#include +#endif + + +#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) + +#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) + + +#if LINUX +/** + * Try to determine path by reading /proc/PID/exe + * + * @return NULL on error + */ +static char * +get_path_from_proc_maps () +{ + char fn[64]; + char line[1024]; + char dir[1024]; + FILE *f; + char *lgu; + + GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/maps", getpid ()); + if (NULL == (f = FOPEN (fn, "r"))) + return NULL; + while (NULL != fgets (line, sizeof (line), f)) + { + if ((1 == + SSCANF (line, "%*x-%*x %*c%*c%*c%*c %*x %*2x:%*2x %*u%*[ ]%1023s", dir)) && + (NULL != (lgu = strstr (dir, "libtalerutil")))) + { + lgu[0] = '\0'; + FCLOSE (f); + return GNUNET_strdup (dir); + } + } + FCLOSE (f); + return NULL; +} + + +/** + * Try to determine path by reading /proc/PID/exe + * + * @return NULL on error + */ +static char * +get_path_from_proc_exe () +{ + char fn[64]; + char lnk[1024]; + ssize_t size; + + GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/exe", getpid ()); + size = readlink (fn, lnk, sizeof (lnk) - 1); + if (size <= 0) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn); + return NULL; + } + GNUNET_assert (size < sizeof (lnk)); + lnk[size] = '\0'; + while ((lnk[size] != '/') && (size > 0)) + size--; + /* test for being in lib/taler/libexec/ or lib/MULTIARCH/taler/libexec */ + if ( (size > strlen ("/taler/libexec/")) && + (0 == strcmp ("/taler/libexec/", + &lnk[size - strlen ("/taler/libexec/")])) ) + size -= strlen ("taler/libexec/"); + if ((size < 4) || (lnk[size - 4] != '/')) + { + /* not installed in "/bin/" -- binary path probably useless */ + return NULL; + } + lnk[size] = '\0'; + return GNUNET_strdup (lnk); +} +#endif + + +#if WINDOWS +static HINSTANCE dll_instance; + + +/** + * GNUNET_util_cl_init() in common_logging.c is preferred. + * This function is only for thread-local storage (not used in GNUnet) + * and hInstance saving. + */ +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + dll_instance = hinstDLL; + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + + +/** + * Try to determine path with win32-specific function + * + * @return NULL on error + */ +static char * +get_path_from_module_filename () +{ + size_t pathlen = 512; + DWORD real_pathlen; + wchar_t *idx; + wchar_t *modulepath = NULL; + char *upath; + uint8_t *u8_string; + size_t u8_string_length; + + /* This braindead function won't tell us how much space it needs, so + * we start at 1024 and double the space up if it doesn't fit, until + * it fits, or we exceed the threshold. + */ + do + { + pathlen = pathlen * 2; + modulepath = GNUNET_realloc (modulepath, pathlen * sizeof (wchar_t)); + SetLastError (0); + real_pathlen = GetModuleFileNameW (dll_instance, modulepath, pathlen * sizeof (wchar_t)); + } while (real_pathlen >= pathlen && pathlen < 16*1024); + if (real_pathlen >= pathlen) + GNUNET_assert (0); + /* To be safe */ + modulepath[real_pathlen] = '\0'; + + idx = modulepath + real_pathlen; + while ((idx > modulepath) && (*idx != L'\\') && (*idx != L'/')) + idx--; + *idx = L'\0'; + + /* Now modulepath holds full path to the directory where libtalerutil is. + * This directory should look like /bin or . + */ + if (wcschr (modulepath, L'/') || wcschr (modulepath, L'\\')) + { + /* At least one directory component (i.e. we're not in a root directory) */ + wchar_t *dirname = idx; + while ((dirname > modulepath) && (*dirname != L'\\') && (*dirname != L'/')) + dirname--; + *dirname = L'\0'; + if (dirname > modulepath) + { + dirname++; + /* Now modulepath holds full path to the parent directory of the directory + * where libtalerutil is. + * dirname holds the name of the directory where libtalerutil is. + */ + if (wcsicmp (dirname, L"bin") == 0) + { + /* pass */ + } + else + { + /* Roll back our changes to modulepath */ + dirname--; + *dirname = L'/'; + } + } + } + + /* modulepath is TALER_PREFIX */ + u8_string = u16_to_u8 (modulepath, wcslen (modulepath), NULL, &u8_string_length); + if (NULL == u8_string) + GNUNET_assert (0); + + upath = GNUNET_malloc (u8_string_length + 1); + memcpy (upath, u8_string, u8_string_length); + upath[u8_string_length] = '\0'; + + free (u8_string); + GNUNET_free (modulepath); + + return upath; +} +#endif + + +#if DARWIN +/** + * Signature of the '_NSGetExecutablePath" function. + * + * @param buf where to write the path + * @param number of bytes available in 'buf' + * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize' + */ +typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize); + + +/** + * Try to obtain the path of our executable using '_NSGetExecutablePath'. + * + * @return NULL on error + */ +static char * +get_path_from_NSGetExecutablePath () +{ + static char zero = '\0'; + char *path; + size_t len; + MyNSGetExecutablePathProto func; + + path = NULL; + if (NULL == (func = + (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath"))) + return NULL; + path = &zero; + len = 0; + /* get the path len, including the trailing \0 */ + (void) func (path, &len); + if (0 == len) + return NULL; + path = GNUNET_malloc (len); + if (0 != func (path, &len)) + { + GNUNET_free (path); + return NULL; + } + len = strlen (path); + while ((path[len] != '/') && (len > 0)) + len--; + path[len] = '\0'; + return path; +} + + +/** + * Try to obtain the path of our executable using '_dyld_image' API. + * + * @return NULL on error + */ +static char * +get_path_from_dyld_image () +{ + const char *path; + char *p; + char *s; + unsigned int i; + int c; + + c = _dyld_image_count (); + for (i = 0; i < c; i++) + { + if (((const void *) _dyld_get_image_header (i)) != (const void *)&_mh_dylib_header) + continue; + path = _dyld_get_image_name (i); + if ( (NULL == path) || (0 == strlen (path)) ) + continue; + p = GNUNET_strdup (path); + s = p + strlen (p); + while ((s > p) && ('/' != *s)) + s--; + s++; + *s = '\0'; + return p; + } + return NULL; +} +#endif + + +/** + * Return the actual path to a file found in the current + * PATH environment variable. + * + * @param binary the name of the file to find + * @return path to binary, NULL if not found + */ +static char * +get_path_from_PATH (const char *binary) +{ + char *path; + char *pos; + char *end; + char *buf; + const char *p; + + if (NULL == (p = getenv ("PATH"))) + return NULL; +#if WINDOWS + /* On W32 look in CWD first. */ + GNUNET_asprintf (&path, ".%c%s", PATH_SEPARATOR, p); +#else + path = GNUNET_strdup (p); /* because we write on it */ +#endif + buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1); + pos = path; + while (NULL != (end = strchr (pos, PATH_SEPARATOR))) + { + *end = '\0'; + sprintf (buf, "%s/%s", pos, binary); + if (GNUNET_DISK_file_test (buf) == GNUNET_YES) + { + pos = GNUNET_strdup (pos); + GNUNET_free (buf); + GNUNET_free (path); + return pos; + } + pos = end + 1; + } + sprintf (buf, "%s/%s", pos, binary); + if (GNUNET_YES == GNUNET_DISK_file_test (buf)) + { + pos = GNUNET_strdup (pos); + GNUNET_free (buf); + GNUNET_free (path); + return pos; + } + GNUNET_free (buf); + GNUNET_free (path); + return NULL; +} + + +/** + * Try to obtain the installation path using the "TALER_PREFIX" environment + * variable. + * + * @return NULL on error (environment variable not set) + */ +static char * +get_path_from_TALER_PREFIX () +{ + const char *p; + + if (NULL != (p = getenv ("TALER_PREFIX"))) + return GNUNET_strdup (p); + return NULL; +} + + +/** + * @brief get the path to Taler bin/ or lib/, prefering the lib/ path + * @author Milan + * + * @return a pointer to the executable path, or NULL on error + */ +static char * +os_get_taler_path () +{ + char *ret; + + if (NULL != (ret = get_path_from_TALER_PREFIX ())) + return ret; +#if LINUX + if (NULL != (ret = get_path_from_proc_maps ())) + return ret; + /* try path *first*, before /proc/exe, as /proc/exe can be wrong */ + if (NULL != (ret = get_path_from_PATH ("taler-mint-httpd"))) + return ret; + if (NULL != (ret = get_path_from_proc_exe ())) + return ret; +#endif +#if WINDOWS + if (NULL != (ret = get_path_from_module_filename ())) + return ret; +#endif +#if DARWIN + if (NULL != (ret = get_path_from_dyld_image ())) + return ret; + if (NULL != (ret = get_path_from_NSGetExecutablePath ())) + return ret; +#endif + if (NULL != (ret = get_path_from_PATH ("taler-mint-httpd"))) + return ret; + /* other attempts here */ + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Could not determine installation path for %s. Set `%s' environment variable.\n"), + "Taler", "TALER_PREFIX"); + return NULL; +} + + +/** + * @brief get the path to current app's bin/ + * @author Milan + * + * @return a pointer to the executable path, or NULL on error + */ +static char * +os_get_exec_path () +{ + char *ret = NULL; + +#if LINUX + if (NULL != (ret = get_path_from_proc_exe ())) + return ret; +#endif +#if WINDOWS + if (NULL != (ret = get_path_from_module_filename ())) + return ret; +#endif +#if DARWIN + if (NULL != (ret = get_path_from_NSGetExecutablePath ())) + return ret; +#endif + /* other attempts here */ + return ret; +} + + +/** + * @brief get the path to a specific Taler installation directory or, + * with #TALER_OS_IPK_SELF_PREFIX, the current running apps installation directory + * @author Milan + * @return a pointer to the dir path (to be freed by the caller) + */ +char * +TALER_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind) +{ + size_t n; + const char *dirname; + char *execpath = NULL; + char *tmp; + char *multiarch; + char *libdir; + int isbasedir; + + /* if wanted, try to get the current app's bin/ */ + if (dirkind == GNUNET_OS_IPK_SELF_PREFIX) + execpath = os_get_exec_path (); + + /* try to get Taler's bin/ or lib/, or if previous was unsuccessful some + * guess for the current app */ + if (NULL == execpath) + execpath = os_get_taler_path (); + + if (NULL == execpath) + return NULL; + + n = strlen (execpath); + if (0 == n) + { + /* should never happen, but better safe than sorry */ + GNUNET_free (execpath); + return NULL; + } + /* remove filename itself */ + while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1])) + execpath[--n] = '\0'; + + isbasedir = 1; + if ((n > 6) && + ((0 == strcasecmp (&execpath[n - 6], "/lib32")) || + (0 == strcasecmp (&execpath[n - 6], "/lib64")))) + { + if ( (GNUNET_OS_IPK_LIBDIR != dirkind) && + (GNUNET_OS_IPK_LIBEXECDIR != dirkind) ) + { + /* strip '/lib32' or '/lib64' */ + execpath[n - 6] = '\0'; + n -= 6; + } + else + isbasedir = 0; + } + else if ((n > 4) && + ((0 == strcasecmp (&execpath[n - 4], "/bin")) || + (0 == strcasecmp (&execpath[n - 4], "/lib")))) + { + /* strip '/bin' or '/lib' */ + execpath[n - 4] = '\0'; + n -= 4; + } + multiarch = NULL; + if (NULL != (libdir = strstr (execpath, "/lib/"))) + { + /* test for multi-arch path of the form "PREFIX/lib/MULTIARCH/"; + here we need to re-add 'multiarch' to lib and libexec paths later! */ + multiarch = &libdir[5]; + if (NULL == strchr (multiarch, '/')) + libdir[0] = '\0'; /* Debian multiarch format, cut of from 'execpath' but preserve in multicarch */ + else + multiarch = NULL; /* maybe not, multiarch still has a '/', which is not OK */ + } + /* in case this was a directory named foo-bin, remove "foo-" */ + while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR)) + execpath[--n] = '\0'; + switch (dirkind) + { + case GNUNET_OS_IPK_PREFIX: + case GNUNET_OS_IPK_SELF_PREFIX: + dirname = DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_BINDIR: + dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_LIBDIR: + if (isbasedir) + { + GNUNET_asprintf (&tmp, + "%s%s%s%s%s", + execpath, + DIR_SEPARATOR_STR "lib", + (NULL != multiarch) ? DIR_SEPARATOR_STR : "", + (NULL != multiarch) ? multiarch : "", + DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR); + if (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) + { + GNUNET_free (execpath); + return tmp; + } + GNUNET_free (tmp); + tmp = NULL; + if (4 == sizeof (void *)) + { + dirname = + DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR; + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + if (8 == sizeof (void *)) + { + dirname = + DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR; + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + + if ( (NULL != tmp) && + (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) ) + { + GNUNET_free (execpath); + return tmp; + } + GNUNET_free (tmp); + } + dirname = DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_DATADIR: + dirname = + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_LOCALEDIR: + dirname = + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_ICONDIR: + dirname = + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "icons" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_DOCDIR: + dirname = + DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "doc" DIR_SEPARATOR_STR \ + "gnunet" DIR_SEPARATOR_STR; + break; + case GNUNET_OS_IPK_LIBEXECDIR: + if (isbasedir) + { + dirname = + DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR; + GNUNET_asprintf (&tmp, + "%s%s%s%s", + execpath, + DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR, + (NULL != multiarch) ? multiarch : "", + dirname); + if (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) + { + GNUNET_free (execpath); + return tmp; + } + GNUNET_free (tmp); + tmp = NULL; + if (4 == sizeof (void *)) + { + dirname = + DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR \ + "libexec" DIR_SEPARATOR_STR; + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + if (8 == sizeof (void *)) + { + dirname = + DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR \ + "libexec" DIR_SEPARATOR_STR; + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + } + if ( (NULL != tmp) && + (GNUNET_YES == + GNUNET_DISK_directory_test (tmp, GNUNET_YES)) ) + { + GNUNET_free (execpath); + return tmp; + } + + GNUNET_free (tmp); + } + dirname = + DIR_SEPARATOR_STR "taler" DIR_SEPARATOR_STR \ + "libexec" DIR_SEPARATOR_STR; + break; + default: + GNUNET_free (execpath); + return NULL; + } + GNUNET_asprintf (&tmp, + "%s%s", + execpath, + dirname); + GNUNET_free (execpath); + return tmp; +} + + +/** + * Given the name of a taler-helper, taler-service or taler-daemon + * binary, try to prefix it with the libexec/-directory to get the + * full path. + * + * @param progname name of the binary + * @return full path to the binary, if possible, otherwise copy of 'progname' + */ +char * +TALER_OS_get_libexec_binary_path (const char *progname) +{ + static char *cache; + char *libexecdir; + char *binary; + + if ( (DIR_SEPARATOR == progname[0]) || + (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL)) ) + return GNUNET_strdup (progname); + if (NULL != cache) + libexecdir = cache; + else + libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR); + if (NULL == libexecdir) + return GNUNET_strdup (progname); + GNUNET_asprintf (&binary, + "%s%s", + libexecdir, + progname); + cache = libexecdir; + return binary; +} + + + +/* end of os_installation.c */