From b0097e0389376fe5b1fc78684c53c2b0de647582 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 23 Oct 2015 19:56:07 +0200 Subject: Restructuring merchant: - Calling URLs handlers as the mint does - Moving URLs handlers into dedicated functions (moved inside dedicated source files) - Some file renaming - Fixing some warnings about 'const' variables used in test_contract --- src/backend-lib/Makefile.am | 6 +- src/backend-lib/merchant_api_contract.c | 121 +++ src/backend-lib/merchant_api_deposit.c | 88 +++ src/backend-lib/merchant_db.c | 3 - src/backend-lib/taler-merchant-httpd_contract.c | 121 --- src/backend-lib/taler-merchant-httpd_deposit.c | 88 --- src/backend/Makefile.am | 2 + src/backend/taler-merchant-httpd.c | 744 +++--------------- src/backend/taler-merchant-httpd_contract.c | 74 ++ src/backend/taler-merchant-httpd_obsolete.c | 985 ++++++++++++++++++++++++ src/backend/taler-mint-httpd.h | 85 ++ src/backend/taler-mint-httpd_mhd.c | 154 ++++ src/backend/taler-mint-httpd_mhd.h | 111 +++ src/backend/taler-mint-httpd_responses.c | 21 + src/backend/taler-mint-httpd_responses.h | 10 + src/tests/test_contract.c | 8 +- 16 files changed, 1756 insertions(+), 865 deletions(-) create mode 100644 src/backend-lib/merchant_api_contract.c create mode 100644 src/backend-lib/merchant_api_deposit.c delete mode 100644 src/backend-lib/taler-merchant-httpd_contract.c delete mode 100644 src/backend-lib/taler-merchant-httpd_deposit.c create mode 100644 src/backend/taler-merchant-httpd_contract.c create mode 100644 src/backend/taler-merchant-httpd_obsolete.c create mode 100644 src/backend/taler-mint-httpd.h create mode 100644 src/backend/taler-mint-httpd_mhd.c create mode 100644 src/backend/taler-mint-httpd_mhd.h diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am index e4cdb7a3..230cb066 100644 --- a/src/backend-lib/Makefile.am +++ b/src/backend-lib/Makefile.am @@ -7,11 +7,13 @@ lib_LTLIBRARIES = \ include_HEADERS = \ taler_merchant_lib.h \ taler_merchant_contract_lib.h + taler_merchant_deposit_lib.h libtalermerchant_la_SOURCES = \ - taler-merchant-httpd_contract.c \ - taler-merchant-httpd_deposit.c \ + merchant_api_contract.c \ + merchant_api_deposit.c \ taler_merchant_contract_lib.h \ + taler_merchant_deposit_lib.h \ merchant_db.c merchant_db.h \ merchant.h diff --git a/src/backend-lib/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c new file mode 100644 index 00000000..5f6744aa --- /dev/null +++ b/src/backend-lib/merchant_api_contract.c @@ -0,0 +1,121 @@ +#include "platform.h" +#include +#include +#include +#include "merchant.h" +#include "merchant_db.h" +#include "taler_merchant_contract_lib.h" + +/** + * Take the global wire details and return a JSON containing them, + * compliantly with the Taler's API. + * @param wire the merchant's wire details + * @param nounce the nounce for hashing the wire details with + * @param edate when the beneficiary wants this transfer to take place + * @return JSON representation of the wire details, NULL upon errors + */ + +json_t * +MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, + uint64_t nounce, + const struct GNUNET_TIME_Absolute edate) + +{ + + json_t *root; + json_t *j_edate; + json_t *j_nounce; + + j_nounce = json_integer (nounce); + j_edate = TALER_json_from_abs (edate); + + if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:o, s:I}", + "type", "SEPA", + "IBAN", wire->iban, + "name", wire->name, + "bic", wire->bic, + "edate", j_edate, + "r", json_integer_value (j_nounce)))) + return NULL; + + return root; +} + + + +/** +* Take from the frontend the (partly) generated contract and fill +* the missing values in it; for example, the SEPA details. +* Moreover, it stores the contract in the DB. +* @param j_contract parsed contract, originated by the frontend. It will be +* hold the new values. +* @param db_conn the handle to the local DB +* @param contract where to store the (subset of the) contract to be (still) signed +* @param timestamp contract's timestamp (shall be generated by the merchant) +* @param expiry the time when the contract will expire +* @param edate when the merchant wants to receive the wire transfer corresponding +* to this deal (this value is also a field inside the 'wire' JSON format) +* @param refund deadline until which the merchant can return the paid amount +* @param nounce the nounce used to hash the wire details +* @param a will be pointed to the (allocated) stringified 0-terminated contract +* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the +* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors. +*/ + +/** +* TODO: inspect reference counting and, accordingly, free those json_t*(s) +* still allocated */ + +uint32_t +MERCHANT_handle_contract (json_t *j_contract, + PGconn *db_conn, + struct Contract *contract, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute expiry, + struct GNUNET_TIME_Absolute edate, + struct GNUNET_TIME_Absolute refund, + char **a, + uint64_t nounce) +{ + json_t *j_amount; + json_int_t j_product_id; + json_int_t j_trans_id; + char *contract_str; + + struct TALER_Amount amount; + + + + /* Extracting values useful for DB work. Only gettable from the JSON + since they are generated by the frontend */ + if (-1 == json_unpack (j_contract, "{s:o, s:I, s:{s:I}}", + "amount", &j_amount, + "trans_id", &j_trans_id, + "details", "product_id", + &j_product_id)) + { + printf ("no unpacking\n"); + return GNUNET_SYSERR; + } + + TALER_json_to_amount (j_amount, &amount); + contract_str = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER); + *a = contract_str; + GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, + &contract->h_contract_details); + contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); + contract->purpose.size = htonl (sizeof (struct Contract)); + + // DB mgmt + return MERCHANT_DB_contract_create (db_conn, + timestamp, + expiry, + edate, + refund, + &amount, + &contract->h_contract_details, + (uint64_t) j_trans_id, // safe? + contract_str, + nounce, + (uint64_t) j_product_id); +} diff --git a/src/backend-lib/merchant_api_deposit.c b/src/backend-lib/merchant_api_deposit.c new file mode 100644 index 00000000..41d4ca3d --- /dev/null +++ b/src/backend-lib/merchant_api_deposit.c @@ -0,0 +1,88 @@ +#include "platform.h" +#include +#include +#include +#include +#include "merchant.h" +#include "merchant_db.h" +#include "taler_merchant_contract_lib.h" + +/** +* Verify the signature on a successful deposit permission +* @param h_contract the hashed stringification of this contract +* @param h_wire the hashed 'wire' object holdign the merchant bank's details +* @param timestamp the 32bit wide number representing the number of seconds +* since the Epoch +* @param refund the refund deadline for this deal, expressed in seconds as @a +* timestamp +* @param trans_id an id number for this deal +* @param amount_minus_fee what paid minus its deposit fee +* @param coin_pub the coin's public key +* @param sig the mint's signature +* @param mint_pub mint's key to verify this signature against +* @return GNUNET_OK if the verification succeeds, GNUNET_NO if not, +* GNUNET_SYSERR upon errors +*/ + +uint32_t +MERCHANT_verify_confirmation (const struct GNUNET_HashCode *h_contract, + const struct GNUNET_HashCode *h_wire, + struct GNUNET_TIME_Absolute timestamp, + struct GNUNET_TIME_Absolute refund, + uint64_t trans_id, + const struct TALER_Amount *amount_minus_fee, + const struct TALER_CoinSpendPublicKeyP *coin, + const struct TALER_MerchantPublicKeyP *merchant, + const struct GNUNET_CRYPTO_EddsaSignature *sig, + const struct TALER_MintPublicKeyP *mint_pub) +{ + struct TALER_DepositConfirmationPS dc; + + dc.h_contract = *h_contract; + dc.h_wire = *h_wire; + + dc.merchant = *merchant; + dc.coin_pub = *coin; + + dc.timestamp = GNUNET_TIME_absolute_hton (timestamp); + dc.refund_deadline = GNUNET_TIME_absolute_hton (refund); + TALER_amount_hton (&dc.amount_without_fee, amount_minus_fee); + dc.transaction_id = GNUNET_htonll (trans_id); + + #ifdef DEBUG + char *hwire_enc; + char *hcontract_enc; + char *merchant_enc; + char *coinpub_enc; + + hwire_enc = GNUNET_STRINGS_data_to_string_alloc (h_wire, sizeof (struct GNUNET_HashCode)); + hcontract_enc = GNUNET_STRINGS_data_to_string_alloc (h_contract, sizeof (struct GNUNET_HashCode)); + merchant_enc = GNUNET_STRINGS_data_to_string_alloc (&merchant.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + coinpub_enc = GNUNET_STRINGS_data_to_string_alloc (&coin.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + + printf ("Signing Confirmation:\nH_wire: %s\nH_contract: %s\nmerchant_pub: %s\ncoin_pub: %s\n" + "timestamp: %llu,\nrefund: %llu,\namount: %s %llu.%lu,\ntrid: %llu\n", + hwire_enc, + hcontract_enc, + merchant_enc, + coinpub_enc, + timestamp_abs.abs_value_us, + refund_abs.abs_value_us, + amount_minus_fee->currency, + amount_minus_fee->value, + amount_minus_fee->fraction, + trans_id); + #endif + + dc.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT); + dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)); + + if (GNUNET_SYSERR == + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT, + &dc.purpose, + sig, + &mint_pub->eddsa_pub)) + return GNUNET_NO; + return GNUNET_OK; +} + diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c index 035a6646..3db048db 100644 --- a/src/backend-lib/merchant_db.c +++ b/src/backend-lib/merchant_db.c @@ -314,9 +314,6 @@ MERCHANT_DB_contract_create (PGconn *conn, PQclear (res); return GNUNET_OK; - EXITIF_exit: - PQclear (res); - return GNUNET_SYSERR; } long long diff --git a/src/backend-lib/taler-merchant-httpd_contract.c b/src/backend-lib/taler-merchant-httpd_contract.c deleted file mode 100644 index 5f6744aa..00000000 --- a/src/backend-lib/taler-merchant-httpd_contract.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "platform.h" -#include -#include -#include -#include "merchant.h" -#include "merchant_db.h" -#include "taler_merchant_contract_lib.h" - -/** - * Take the global wire details and return a JSON containing them, - * compliantly with the Taler's API. - * @param wire the merchant's wire details - * @param nounce the nounce for hashing the wire details with - * @param edate when the beneficiary wants this transfer to take place - * @return JSON representation of the wire details, NULL upon errors - */ - -json_t * -MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, - uint64_t nounce, - const struct GNUNET_TIME_Absolute edate) - -{ - - json_t *root; - json_t *j_edate; - json_t *j_nounce; - - j_nounce = json_integer (nounce); - j_edate = TALER_json_from_abs (edate); - - if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:o, s:I}", - "type", "SEPA", - "IBAN", wire->iban, - "name", wire->name, - "bic", wire->bic, - "edate", j_edate, - "r", json_integer_value (j_nounce)))) - return NULL; - - return root; -} - - - -/** -* Take from the frontend the (partly) generated contract and fill -* the missing values in it; for example, the SEPA details. -* Moreover, it stores the contract in the DB. -* @param j_contract parsed contract, originated by the frontend. It will be -* hold the new values. -* @param db_conn the handle to the local DB -* @param contract where to store the (subset of the) contract to be (still) signed -* @param timestamp contract's timestamp (shall be generated by the merchant) -* @param expiry the time when the contract will expire -* @param edate when the merchant wants to receive the wire transfer corresponding -* to this deal (this value is also a field inside the 'wire' JSON format) -* @param refund deadline until which the merchant can return the paid amount -* @param nounce the nounce used to hash the wire details -* @param a will be pointed to the (allocated) stringified 0-terminated contract -* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the -* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors. -*/ - -/** -* TODO: inspect reference counting and, accordingly, free those json_t*(s) -* still allocated */ - -uint32_t -MERCHANT_handle_contract (json_t *j_contract, - PGconn *db_conn, - struct Contract *contract, - struct GNUNET_TIME_Absolute timestamp, - struct GNUNET_TIME_Absolute expiry, - struct GNUNET_TIME_Absolute edate, - struct GNUNET_TIME_Absolute refund, - char **a, - uint64_t nounce) -{ - json_t *j_amount; - json_int_t j_product_id; - json_int_t j_trans_id; - char *contract_str; - - struct TALER_Amount amount; - - - - /* Extracting values useful for DB work. Only gettable from the JSON - since they are generated by the frontend */ - if (-1 == json_unpack (j_contract, "{s:o, s:I, s:{s:I}}", - "amount", &j_amount, - "trans_id", &j_trans_id, - "details", "product_id", - &j_product_id)) - { - printf ("no unpacking\n"); - return GNUNET_SYSERR; - } - - TALER_json_to_amount (j_amount, &amount); - contract_str = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER); - *a = contract_str; - GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, - &contract->h_contract_details); - contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); - contract->purpose.size = htonl (sizeof (struct Contract)); - - // DB mgmt - return MERCHANT_DB_contract_create (db_conn, - timestamp, - expiry, - edate, - refund, - &amount, - &contract->h_contract_details, - (uint64_t) j_trans_id, // safe? - contract_str, - nounce, - (uint64_t) j_product_id); -} diff --git a/src/backend-lib/taler-merchant-httpd_deposit.c b/src/backend-lib/taler-merchant-httpd_deposit.c deleted file mode 100644 index 41d4ca3d..00000000 --- a/src/backend-lib/taler-merchant-httpd_deposit.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "platform.h" -#include -#include -#include -#include -#include "merchant.h" -#include "merchant_db.h" -#include "taler_merchant_contract_lib.h" - -/** -* Verify the signature on a successful deposit permission -* @param h_contract the hashed stringification of this contract -* @param h_wire the hashed 'wire' object holdign the merchant bank's details -* @param timestamp the 32bit wide number representing the number of seconds -* since the Epoch -* @param refund the refund deadline for this deal, expressed in seconds as @a -* timestamp -* @param trans_id an id number for this deal -* @param amount_minus_fee what paid minus its deposit fee -* @param coin_pub the coin's public key -* @param sig the mint's signature -* @param mint_pub mint's key to verify this signature against -* @return GNUNET_OK if the verification succeeds, GNUNET_NO if not, -* GNUNET_SYSERR upon errors -*/ - -uint32_t -MERCHANT_verify_confirmation (const struct GNUNET_HashCode *h_contract, - const struct GNUNET_HashCode *h_wire, - struct GNUNET_TIME_Absolute timestamp, - struct GNUNET_TIME_Absolute refund, - uint64_t trans_id, - const struct TALER_Amount *amount_minus_fee, - const struct TALER_CoinSpendPublicKeyP *coin, - const struct TALER_MerchantPublicKeyP *merchant, - const struct GNUNET_CRYPTO_EddsaSignature *sig, - const struct TALER_MintPublicKeyP *mint_pub) -{ - struct TALER_DepositConfirmationPS dc; - - dc.h_contract = *h_contract; - dc.h_wire = *h_wire; - - dc.merchant = *merchant; - dc.coin_pub = *coin; - - dc.timestamp = GNUNET_TIME_absolute_hton (timestamp); - dc.refund_deadline = GNUNET_TIME_absolute_hton (refund); - TALER_amount_hton (&dc.amount_without_fee, amount_minus_fee); - dc.transaction_id = GNUNET_htonll (trans_id); - - #ifdef DEBUG - char *hwire_enc; - char *hcontract_enc; - char *merchant_enc; - char *coinpub_enc; - - hwire_enc = GNUNET_STRINGS_data_to_string_alloc (h_wire, sizeof (struct GNUNET_HashCode)); - hcontract_enc = GNUNET_STRINGS_data_to_string_alloc (h_contract, sizeof (struct GNUNET_HashCode)); - merchant_enc = GNUNET_STRINGS_data_to_string_alloc (&merchant.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); - coinpub_enc = GNUNET_STRINGS_data_to_string_alloc (&coin.eddsa_pub, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); - - printf ("Signing Confirmation:\nH_wire: %s\nH_contract: %s\nmerchant_pub: %s\ncoin_pub: %s\n" - "timestamp: %llu,\nrefund: %llu,\namount: %s %llu.%lu,\ntrid: %llu\n", - hwire_enc, - hcontract_enc, - merchant_enc, - coinpub_enc, - timestamp_abs.abs_value_us, - refund_abs.abs_value_us, - amount_minus_fee->currency, - amount_minus_fee->value, - amount_minus_fee->fraction, - trans_id); - #endif - - dc.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT); - dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)); - - if (GNUNET_SYSERR == - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_DEPOSIT, - &dc.purpose, - sig, - &mint_pub->eddsa_pub)) - return GNUNET_NO; - return GNUNET_OK; -} - diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 6d38a37e..11b49002 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -10,6 +10,8 @@ taler_merchant_httpd_SOURCES = \ ../backend-lib/merchant_db.c ../backend-lib/merchant_db.h \ taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ + taler-mint-httpd.h \ + taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ ../backend-lib/taler-merchant-httpd_contract.h taler_merchant_httpd_LDADD = \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 2b489040..014b6fa6 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -25,24 +25,14 @@ #include #include #include -#include +#include #include #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_responses.h" #include "merchant_db.h" #include "merchant.h" #include "taler_merchant_lib.h" - -extern struct MERCHANT_WIREFORMAT_Sepa * -TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg); - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) +#include "taler-mint-httpd_mhd.h" /** * Our hostname @@ -65,30 +55,35 @@ struct GNUNET_CRYPTO_EddsaPrivateKey *privkey; char *keyfile; /** - * The MHD Daemon + * Mint context */ -static struct MHD_Daemon *mhd; +static struct TALER_MINT_Context *mctx; /** - * Connection handle to the our database + * Mints' URL,port,key triples */ -PGconn *db_conn; +struct MERCHANT_MintInfo *mint_infos; /** - * merchant's conf handle + * Shutdown task identifier */ -struct GNUNET_CONFIGURATION_Handle *cfg; +static struct GNUNET_SCHEDULER_Task *shutdown_task; /** - * Shutdown task identifier + * Hashmap to store the mint context information */ -static struct GNUNET_SCHEDULER_Task *shutdown_task; +static struct GNUNET_CONTAINER_MultiPeerMap *mints_map; /** * Our wireformat */ static struct MERCHANT_WIREFORMAT_Sepa *wire; +/** + * The number of accepted mints + */ +unsigned int nmints; + /** * Should we do a dry run where temporary tables are used for storing the data. */ @@ -100,9 +95,15 @@ static int dry; static int result; /** - * Mint context + * Connection handle to the our database */ -static struct TALER_MINT_Context *mctx; +PGconn *db_conn; + +/** + * Hashmap (with 'big entries') to make a mint's base URL + * to point to some mint-describing structure + */ +static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap; /** * Context information of the mints we trust @@ -121,214 +122,9 @@ struct Mint }; /** - * Hashmap to store the mint context information - */ -static struct GNUNET_CONTAINER_MultiPeerMap *mints_map; - -/** - * Hashmap (with 'big entries') to make a mint's base URL - * to point to some mint-describing structure - */ -static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap; - - - -/** - * Mints' URL,port,key triples - */ -struct MERCHANT_MintInfo *mint_infos; - -/** - * The number of accepted mints - */ -unsigned int nmints; - -struct Mint_Response -{ - char *ptr; - size_t size; - -}; - - -/** - * Generate the 'hello world' response - * @param connection a MHD connection - * @param resp where to store the response for the calling function. - * Note that in its original implementation this parameter was preceeded - * by a '_'. Still not clear why. - * @return HTTP status code reflecting the operation outcome - * - */ -static unsigned int -generate_hello (struct MHD_Response **resp) -{ - - const char *hello = "Hello customer\n"; - unsigned int ret; - - *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello, - MHD_RESPMEM_PERSISTENT); - ret = 200; - return ret; -} - -/** - * Return the given message to the other end of connection - * @msg (0-terminated) message to show - * @param connection a MHD connection - * @param resp where to store the response for the calling function - * @return HTTP status code reflecting the operation outcome - * - */ -static unsigned int -generate_message (struct MHD_Response **resp, const char *msg) -{ - - unsigned int ret; - - *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg, - MHD_RESPMEM_MUST_FREE); - ret = 200; - return ret; -} - -/** - * Callback to pass to curl used to store a HTTP response - * in a custom memory location. - * See http://curl.haxx.se/libcurl/c/getinmemory.html for a - * detailed example - * - * @param contents the data gotten so far from the server - * @param size symbolic (arbitrarily chosen by libcurl) unit - * of bytes - * @param nmemb factor to multiply by @a size to get the real - * size of @a contents - * @param userdata a pointer to a memory location which remains - * the same across all the calls to this callback (i.e. it has - * to be grown at each invocation of this callback) - * @return number of written bytes - * See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html - * for official documentation - * - */ -size_t -get_response_in_memory (char *contents, - size_t size, - size_t nmemb, - void *userdata) -{ - struct Mint_Response *mr; - size_t realsize; - - realsize = size * nmemb; - mr = userdata; - mr->ptr = realloc (mr->ptr, mr->size + realsize + 1); - - if (mr->ptr == NULL) { - printf ("Out of memory, could not get in memory mint's" - "response"); - return 0; - } - memcpy(&(mr->ptr[mr->size]), contents, realsize); - mr->size += realsize; - mr->ptr[mr->size] = 0; - - return realsize; - -} - -#ifdef PANIC_MGMT -/** - * Callback for catching serious error conditions from MHD. - * - * @param cls user specified value - * @param file where the error occured - * @param line where the error occured - * @param reason error detail, may be NULL - */ -static void -mhd_panic_cb (void *cls, - const char *file, - unsigned int line, - const char *reason) -{ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "MHD panicked at %s:%u: %s", - file, line, reason); - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); -} -#endif - -/** - * Manage a non 200 HTTP status. I.e. it shows a 'failure' page to - * the client - * @param connection the channel thorugh which send the message - * @status the HTTP status to examine - * @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error + * The MHD Daemon */ -static int -failure_resp (struct MHD_Connection *connection, unsigned int status) -{ - static char page_MHD_HTTP_NOT_FOUND[]="\ - \ -Resource not found
\ -

The resource you are looking for is not found.

\ -
"; - static char page_MHD_HTTP_BAD_REQUEST[]="\ - \ -Bad request
\ -

Malformed POSTed JSON.

\ -
"; -static char page_MHD_HTTP_METHOD_NOT_ALLOWED[]="\ - \ -Method NOT allowed
\ -

ONLY POSTs are allowed.

\ -
"; - static char page_MHD_HTTP_INTERNAL_SERVER_ERROR[]="\ - Internal Server Error
\ -

The server experienced an internal error and hence cannot serve your \ -request

"; - struct MHD_Response *resp; - char *page; - size_t size; -#define PAGE(number) \ - do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0) - - GNUNET_assert (MHD_HTTP_BAD_REQUEST <= status); - resp = NULL; - switch (status) - { - case MHD_HTTP_NOT_FOUND : - PAGE(MHD_HTTP_NOT_FOUND); - break; - case MHD_HTTP_BAD_REQUEST: - PAGE(MHD_HTTP_BAD_REQUEST); - break; - case MHD_HTTP_METHOD_NOT_ALLOWED: - PAGE(MHD_HTTP_METHOD_NOT_ALLOWED); - break; - default: - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - PAGE(MHD_HTTP_INTERNAL_SERVER_ERROR); - } -#undef PAGE - - EXITIF (NULL == (resp = MHD_create_response_from_buffer (size, - page, - MHD_RESPMEM_PERSISTENT))); - EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); - MHD_destroy_response (resp); - return GNUNET_OK; - - EXITIF_exit: - if (NULL != resp) - MHD_destroy_response (resp); - return GNUNET_SYSERR; -} - +static struct MHD_Daemon *mhd; /** * A client has requested the given url using the given method @@ -377,423 +173,100 @@ url_handler (void *cls, const char *version, const char *upload_data, size_t *upload_data_size, - void **connection_cls) + void **con_cls) { - - /*printf ("%s\n", url);*/ - unsigned long status; - unsigned int no_destroy; - struct GNUNET_CRYPTO_EddsaSignature c_sig; - struct GNUNET_CRYPTO_EddsaSignature deposit_confirm_sig; - struct GNUNET_CRYPTO_EddsaPublicKey pub; - #ifdef OBSOLETE - struct ContractNBO contract; - #else - struct Contract contract; - #endif - struct MHD_Response *resp; - json_t *root; - json_t *j_sig_enc; - json_t *j_h_contract; - json_t *j_tmp; - json_t *eddsa_pub_enc; - json_t *response; - json_t *j_mints; - json_t *j_mint; - json_t *j_wire; - int cnt; /* loop counter */ - char *deposit_body; - json_t *j_contract_add; - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Absolute expiry; - struct GNUNET_TIME_Absolute edate; - struct GNUNET_TIME_Absolute refund; - struct GNUNET_HashCode h_json_wire; - json_t *j_h_json_wire; - struct curl_slist *slist; - char *contract_str; - struct GNUNET_HashCode h_contract_str; - struct MERCHANT_contract_handle ch; - struct TALER_MintPublicKeyP mint_pub; - uint64_t nounce; - - CURL *curl; - CURLcode curl_res; - - uint32_t res = GNUNET_SYSERR; - - #define URL_HELLO "/hello" - #define URL_CONTRACT "/contract" - #define URL_PAY "/pay" - no_destroy = 0; - resp = NULL; - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - - if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO))) - { - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - status = generate_hello (&resp); - else + static struct TMH_RequestHandler handlers[] = { - status = MHD_HTTP_METHOD_NOT_ALLOWED; - } - } + /* Landing page, tell humans to go away. */ + { "/", MHD_HTTP_METHOD_GET, "text/plain", + "Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.\n", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - if (0 == strncasecmp (url, URL_PAY, sizeof (URL_PAY))) - { - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - { - status = MHD_HTTP_METHOD_NOT_ALLOWED; - goto end; + /* Further test page */ + { "/hello", MHD_HTTP_METHOD_GET, "text/plain", + "Hello, Customer.\n", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - } - else - res = TMH_PARSE_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); - if (GNUNET_SYSERR == res) - { - status = MHD_HTTP_BAD_REQUEST; - goto end; - } - - /* the POST's body has to be further fetched */ - if ((GNUNET_NO == res) || (NULL == root)) - return MHD_YES; - - /* Firstly, check if the wallet is paying against an approved - mint */ - json_t *j_chosen_mint; - j_chosen_mint = json_object_get (root, "mint"); - struct GNUNET_HashCode hash_key; - char *chosen_mint; - - chosen_mint = json_string_value (j_chosen_mint); - GNUNET_CRYPTO_hash (chosen_mint, strlen (chosen_mint), &hash_key); - - if (NULL == - GNUNET_CONTAINER_multihashmap_get (mints_hashmap, &hash_key)) - { - printf ("Untrusted mint\n"); - status = MHD_HTTP_FORBIDDEN; - goto end; - - } - - /* NOTE: from now on, the mint's base URL is pointed by 'chosen_mint' */ - - /* The merchant will only add its 'wire' object to the JSON - it got from the wallet */ + { "/contract", MHD_HTTP_METHOD_GET, "application/json", + NULL, 0, + &MH_handler_contract, MHD_HTTP_OK }, - /* Get this dep. perm.'s H_contract */ - - if (NULL == (j_h_contract = json_object_get (root, "H_contract"))) - { - printf ("H_contract field missing\n"); - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - TALER_json_to_data (j_h_contract, &h_contract_str, sizeof (struct GNUNET_HashCode)); + { "/contract", NULL, "text/plain", + "Only POST is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - nounce = 0; - edate.abs_value_us = 0; - - if (GNUNET_SYSERR == - MERCHANT_DB_get_contract_values (db_conn, - &h_contract_str, - &nounce, - &edate)) - { - printf ("not existing contract\n"); - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - /* Reproducing the wire object */ - if (NULL == (j_wire = MERCHANT_get_wire_json (wire, - nounce, - edate))) - - { - printf ("wire object not reproduced\n"); - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - if (-1 == json_object_set (root, "wire", j_wire)) - { - printf ("depperm not augmented\n"); - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - /* POST to mint's "/deposit" */ - curl = curl_easy_init (); - - struct Mint_Response mr; - mr.ptr = malloc(1); - mr.size = 0; + {NULL, NULL, NULL, NULL, 0, 0 } + }; - if (curl) + static struct TMH_RequestHandler h404 = { - - char deposit_url[strlen (chosen_mint) + strlen ("http://") + strlen ("/deposit") + 1]; - sprintf (deposit_url, "http://%s/deposit", chosen_mint); - slist = curl_slist_append (slist, "Content-type: application/json"); - curl_easy_setopt (curl, CURLOPT_HTTPHEADER, slist); - - curl_easy_setopt (curl, CURLOPT_URL, deposit_url); - curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, get_response_in_memory); - curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) &mr); - - /* NOTE: hopefully, this string won't need any URL-encoding, since as for the - Jansson specs, any space and-or newline are not in place using JSON_COMPACT - flag */ - deposit_body = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, deposit_body); - - curl_res = curl_easy_perform (curl); - - curl_slist_free_all(slist); - if(curl_res != CURLE_OK) - { - printf ("deposit not sent\n"); - goto end; - } - else - printf ("\ndeposit request issued\n"); - - curl_easy_cleanup(curl); - - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status); - - /* If the request was successful, the deposit confirmation - has to be verified*/ - - if (MHD_HTTP_OK != result) - /* Jump here to error mgmt, see issue #3800 (TODO) */ - - if (GNUNET_SYSERR == - MERCHANT_DB_get_contract_handle (db_conn, &h_contract_str, &ch)) - { - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - root = json_loads (mr.ptr, 0, NULL); - j_tmp = json_object_get (root, "sig"); - TALER_json_to_data (j_tmp, - &deposit_confirm_sig, - sizeof (struct GNUNET_CRYPTO_EddsaSignature)); - j_tmp = json_object_get (root, "pub"); - - TALER_json_to_data (j_tmp, - &mint_pub.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); - - GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); - - - /* The following check could be done once the merchant is able to - retrieve the deposit fee for each coin it gets a payment with. - That happens because the signature made on a deposit confirmation - uses, among its values of interest, the subtraction of the deposit - fee from the used coin. That fee is never communicated by the wallet - to the merchant, so the only way for the merchant to get this - information is to fetch all the mint's keys, namely it needs to - invoke "/keys", and store what gotten in its DB */ - - #ifdef DENOMMGMT - if (GNUNET_NO == - MERCHANT_verify_confirmation (&h_contract_str, - &h_json_wire, - ch.timestamp, - ch.refund_deadline, - ch.contract_id, - amount_minus_fee, /* MISSING */ - coin_pub, /* MISSING */ - &pub, - &deposit_confirm_sig, - &mint_pub)) - { - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - - } - - #endif - - /* Place here the successful message to issue to the frontend */ - status = MHD_HTTP_OK; - generate_message (&resp, "fullfillment page here"); - GNUNET_free (mr.ptr); - - } + "", NULL, "text/html", + "404: not found", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND + }; + /* Compiler complains about non returning a value in a non-void + declared function: the FIX is to return what the handler for + a particular URL returns */ - } + struct TMH_RequestHandler *rh; + unsigned int i; - /* - * To be called by the frontend passing the contract with some "holes" - * which will be completed, stored in DB, signed, and returned - * - */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling request for URL '%s'\n", + url); - if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT))) + for (i=0;NULL != handlers[i].url;i++) { - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - { - status = MHD_HTTP_METHOD_NOT_ALLOWED; - goto end; - - } - else - res = TMH_PARSE_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); - if (GNUNET_SYSERR == res) - { - status = MHD_HTTP_BAD_REQUEST; - goto end; - } - - - /* the POST's body has to be fetched furthermore */ - if ((GNUNET_NO == res) || (NULL == root)) - return MHD_YES; - - j_mints = json_array (); - for (cnt = 0; cnt < nmints; cnt++) - { - j_mint = json_pack ("{s:s}", - mint_infos[cnt].hostname, - GNUNET_CRYPTO_eddsa_public_key_to_string (&mint_infos[cnt].pubkey)); - json_array_append_new (j_mints, j_mint); - - } - - /* timestamp */ - now = GNUNET_TIME_absolute_get (); - /* expiry */ - expiry = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); - /* edate, note: this value must be generated now (and not when the - wallet sends back a deposit permission because the hashed 'wire' object, - which carries this values in it, has to be included in the signed bundle - by the wallet) */ - edate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); - refund = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); - - TALER_round_abs_time (&now); - TALER_round_abs_time (&expiry); - TALER_round_abs_time (&edate); - TALER_round_abs_time (&refund); - - /* getting the SEPA-aware JSON */ - /* nounce for hashing the wire object */ - nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - - /* get wire object */ - - if (NULL == (j_wire = MERCHANT_get_wire_json (wire, - nounce, - edate))) - { - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - - /* hash wire objcet */ - if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire)) - goto end; - - j_h_json_wire = TALER_json_from_data ((void *) &h_json_wire, sizeof (struct GNUNET_HashCode)); - - GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); - eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); - - if (NULL == (j_contract_add = json_pack ("{s:s, s:s, s:o, s:o, s:o}", - "merchant_pub", json_string_value (eddsa_pub_enc), - "H_wire", json_string_value (j_h_json_wire), - "timestamp", TALER_json_from_abs (now), - "refund", TALER_json_from_abs (refund), - "mints", j_mints))) - { - printf ("BAD contract enhancement\n"); - goto end; - } - - /* melt to what received from the wallet */ - if (-1 == json_object_update (root, j_contract_add)) - { - printf ("depperm response not built\n"); - goto end; - } - - res = MERCHANT_handle_contract (root, - db_conn, - &contract, - now, - expiry, - edate, - refund, - &contract_str, - nounce); - if (GNUNET_SYSERR == res) - { - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } - if (GNUNET_NO == res) - { - status = MHD_HTTP_METHOD_NOT_ACCEPTABLE; - goto end; - } - - GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig); - GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str); - - j_sig_enc = TALER_json_from_eddsa_sig (&contract.purpose, &c_sig); - - response = json_pack ("{s:o, s:o, s:o}", - "contract", root, - "sig", j_sig_enc, - "h_contract", - TALER_json_from_data ((void *) &h_contract_str, sizeof (struct GNUNET_HashCode))); + rh = &handlers[i]; + if ( (0 == strcasecmp (url, + rh->url)) && + ( (NULL == rh->method) || + (0 == strcasecmp (method, + rh->method)) ) ) + return rh->handler (rh, + connection, + con_cls, + upload_data, + upload_data_size); + } + return TMH_MHD_handler_static_response (&h404, + connection, + con_cls, + upload_data, + upload_data_size); - GNUNET_free (contract_str); +} - TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK); - return MHD_YES; +/** + * Function called with information about who is auditing + * a particular mint and what key the mint is using. + * + * @param cls closure + * @param keys information about the various keys used + * by the mint + */ +static void +keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) +{ + /* which kind of mint's keys a merchant should need? Sign + keys? It has already the mint's master key from the conf file */ + + /* HOT UPDATE: the merchants needs the denomination keys! + Because it wants to (firstly) verify the deposit confirmation + sent by the mint, and the signed blob depends (among the + other things) on the coin's deposit fee. That information + is never communicated by the wallet to the merchant. + Again, the merchant needs it because it wants to verify that + the wallet didn't exceede the limit imposed by the merchant + on the total deposit fee for a purchase */ - } - end: + return; - if (NULL != resp) - { - EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); - return MHD_YES; - if (!no_destroy) - MHD_destroy_response (resp); - } - else - { - - EXITIF (GNUNET_OK != failure_resp (connection, status)); - return MHD_YES; - - } - - EXITIF_exit: - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return MHD_NO; } + /** * Shutdown task (magically invoked when the application is being * quit) @@ -822,25 +295,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } -/** - * Function called with information about who is auditing - * a particular mint and what key the mint is using. - * - * @param cls closure - * @param keys information about the various keys used - * by the mint - */ -static void -keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) -{ - /* which kind of mint's keys a merchant should need? Sign - keys? It has already the mint's master key from the conf file */ - return; - -} - - - /** * Main function that will be run by the scheduler. * diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c new file mode 100644 index 00000000..cbbf2616 --- /dev/null +++ b/src/backend/taler-merchant-httpd_contract.c @@ -0,0 +1,74 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file merchant/backend/taler-merchant-httpd.c + * @brief HTTP serving layer mainly intended to communicate with the frontend + * @author Marcello Stanisci + */ + +#include "platform.h" +#include +#include +#include +#include +#include +#include +#include "taler-mint-httpd_parsing.h" +#include "taler-mint-httpd_responses.h" +#include "merchant_db.h" +#include "merchant.h" +#include "taler_merchant_lib.h" + +/** + * Manage a contract request + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * + * @return MHD result code + */ +int +MH_handler_contract (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + /* 1. Generate preferred mint(s) array. + + a. Add the configuration lines to specify the mint's business + address. + b. Add this address to the 'locations' object, that should be + already present in the 'proposition' gotten from the frontend. + c. Point the 'address' field's label to the one added to 'locations' + + The 'mint' JSON layout is as follows: + + { "address": "address_label", + "url": "mint_base_url", + "master_pub": "base32 mint's master public key" } + + */ + + + + + +} diff --git a/src/backend/taler-merchant-httpd_obsolete.c b/src/backend/taler-merchant-httpd_obsolete.c new file mode 100644 index 00000000..ddbda549 --- /dev/null +++ b/src/backend/taler-merchant-httpd_obsolete.c @@ -0,0 +1,985 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file merchant/backend/taler-merchant-httpd.c + * @brief HTTP serving layer mainly intended to communicate with the frontend + * @author Marcello Stanisci + */ + +#include "platform.h" +#include +#include +#include +#include +#include +#include +#include "taler-mint-httpd_parsing.h" +#include "taler-mint-httpd_responses.h" +#include "merchant_db.h" +#include "merchant.h" +#include "taler_merchant_lib.h" + +extern struct MERCHANT_WIREFORMAT_Sepa * +TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Shorthand for exit jumps. + */ +#define EXITIF(cond) \ + do { \ + if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ + } while (0) + +/** + * Our hostname + */ +static char *hostname; + +/** + * The port we are running on + */ +static long long unsigned port; + +/** + * Merchant's private key + */ +struct GNUNET_CRYPTO_EddsaPrivateKey *privkey; + +/** + * File holding the merchant's private key + */ +char *keyfile; + +/** + * The MHD Daemon + */ +static struct MHD_Daemon *mhd; + +/** + * Connection handle to the our database + */ +PGconn *db_conn; + +/** + * merchant's conf handle + */ +struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Shutdown task identifier + */ +static struct GNUNET_SCHEDULER_Task *shutdown_task; + +/** + * Our wireformat + */ +static struct MERCHANT_WIREFORMAT_Sepa *wire; + +/** + * Should we do a dry run where temporary tables are used for storing the data. + */ +static int dry; + +/** + * Global return code + */ +static int result; + +/** + * Mint context + */ +static struct TALER_MINT_Context *mctx; + +/** + * Context information of the mints we trust + */ +struct Mint +{ + /** + * Public key of this mint + */ + struct GNUNET_CRYPTO_EddsaPublicKey pubkey; + + /** + * Connection handle to this mint + */ + struct TALER_MINT_Handle *conn; +}; + +/** + * Hashmap to store the mint context information + */ +static struct GNUNET_CONTAINER_MultiPeerMap *mints_map; + +/** + * Hashmap (with 'big entries') to make a mint's base URL + * to point to some mint-describing structure + */ +static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap; + +/** + * Mints' URL,port,key triples + */ +struct MERCHANT_MintInfo *mint_infos; + +/** + * The number of accepted mints + */ +unsigned int nmints; + +struct Mint_Response +{ + char *ptr; + size_t size; + +}; + + +/** + * Generate the 'hello world' response + * @param connection a MHD connection + * @param resp where to store the response for the calling function. + * Note that in its original implementation this parameter was preceeded + * by a '_'. Still not clear why. + * @return HTTP status code reflecting the operation outcome + * + */ +static unsigned int +generate_hello (struct MHD_Response **resp) +{ + + const char *hello = "Hello customer\n"; + unsigned int ret; + + *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello, + MHD_RESPMEM_PERSISTENT); + ret = 200; + return ret; +} + +/** + * Return the given message to the other end of connection + * @msg (0-terminated) message to show + * @param connection a MHD connection + * @param resp where to store the response for the calling function + * @return HTTP status code reflecting the operation outcome + * + */ +static unsigned int +generate_message (struct MHD_Response **resp, const char *msg) +{ + + unsigned int ret; + + *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg, + MHD_RESPMEM_MUST_FREE); + ret = 200; + return ret; +} + +/** + * Callback to pass to curl used to store a HTTP response + * in a custom memory location. + * See http://curl.haxx.se/libcurl/c/getinmemory.html for a + * detailed example + * + * @param contents the data gotten so far from the server + * @param size symbolic (arbitrarily chosen by libcurl) unit + * of bytes + * @param nmemb factor to multiply by @a size to get the real + * size of @a contents + * @param userdata a pointer to a memory location which remains + * the same across all the calls to this callback (i.e. it has + * to be grown at each invocation of this callback) + * @return number of written bytes + * See http://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html + * for official documentation + * + */ +size_t +get_response_in_memory (char *contents, + size_t size, + size_t nmemb, + void *userdata) +{ + struct Mint_Response *mr; + size_t realsize; + + realsize = size * nmemb; + mr = userdata; + mr->ptr = realloc (mr->ptr, mr->size + realsize + 1); + + if (mr->ptr == NULL) { + printf ("Out of memory, could not get in memory mint's" + "response"); + return 0; + } + memcpy(&(mr->ptr[mr->size]), contents, realsize); + mr->size += realsize; + mr->ptr[mr->size] = 0; + + return realsize; + +} + +#ifdef PANIC_MGMT +/** + * Callback for catching serious error conditions from MHD. + * + * @param cls user specified value + * @param file where the error occured + * @param line where the error occured + * @param reason error detail, may be NULL + */ +static void +mhd_panic_cb (void *cls, + const char *file, + unsigned int line, + const char *reason) +{ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "MHD panicked at %s:%u: %s", + file, line, reason); + result = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); +} +#endif + +/** + * Manage a non 200 HTTP status. I.e. it shows a 'failure' page to + * the client + * @param connection the channel thorugh which send the message + * @status the HTTP status to examine + * @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error + */ +static int +failure_resp (struct MHD_Connection *connection, unsigned int status) +{ + static char page_MHD_HTTP_NOT_FOUND[]="\ + \ +Resource not found
\ +

The resource you are looking for is not found.

\ +
"; + static char page_MHD_HTTP_BAD_REQUEST[]="\ + \ +Bad request
\ +

Malformed POSTed JSON.

\ +
"; +static char page_MHD_HTTP_METHOD_NOT_ALLOWED[]="\ + \ +Method NOT allowed
\ +

ONLY POSTs are allowed.

\ +
"; + static char page_MHD_HTTP_INTERNAL_SERVER_ERROR[]="\ + Internal Server Error
\ +

The server experienced an internal error and hence cannot serve your \ +request

"; + struct MHD_Response *resp; + char *page; + size_t size; +#define PAGE(number) \ + do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0) + + GNUNET_assert (MHD_HTTP_BAD_REQUEST <= status); + resp = NULL; + switch (status) + { + case MHD_HTTP_NOT_FOUND : + PAGE(MHD_HTTP_NOT_FOUND); + break; + case MHD_HTTP_BAD_REQUEST: + PAGE(MHD_HTTP_BAD_REQUEST); + break; + case MHD_HTTP_METHOD_NOT_ALLOWED: + PAGE(MHD_HTTP_METHOD_NOT_ALLOWED); + break; + default: + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + PAGE(MHD_HTTP_INTERNAL_SERVER_ERROR); + } +#undef PAGE + + EXITIF (NULL == (resp = MHD_create_response_from_buffer (size, + page, + MHD_RESPMEM_PERSISTENT))); + EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); + MHD_destroy_response (resp); + return GNUNET_OK; + + EXITIF_exit: + if (NULL != resp) + MHD_destroy_response (resp); + return GNUNET_SYSERR; +} + + +/** + * A client has requested the given url using the given method + * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, + * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback + * must call MHD callbacks to provide content to give back to the + * client and return an HTTP status code (i.e. #MHD_HTTP_OK, + * #MHD_HTTP_NOT_FOUND, etc.). + * + * @param cls argument given together with the function + * pointer when the handler was registered with MHD + * @param url the requested url + * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, + * #MHD_HTTP_METHOD_PUT, etc.) + * @param version the HTTP version string (i.e. + * #MHD_HTTP_VERSION_1_1) + * @param upload_data the data being uploaded (excluding HEADERS, + * for a POST that fits into memory and that is encoded + * with a supported encoding, the POST data will NOT be + * given in upload_data and is instead available as + * part of #MHD_get_connection_values; very large POST + * data *will* be made available incrementally in + * @a upload_data) + * @param upload_data_size set initially to the size of the + * @a upload_data provided; the method must update this + * value to the number of bytes NOT processed; + * @param con_cls pointer that the callback can set to some + * address and that will be preserved by MHD for future + * calls for this request; since the access handler may + * be called many times (i.e., for a PUT/POST operation + * with plenty of upload data) this allows the application + * to easily associate some request-specific state. + * If necessary, this state can be cleaned up in the + * global #MHD_RequestCompletedCallback (which + * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). + * Initially, `*con_cls` will be NULL. + * @return #MHD_YES if the connection was handled successfully, + * #MHD_NO if the socket must be closed due to a serios + * error while handling the request + */ +static int +url_handler (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **connection_cls) +{ + + /*printf ("%s\n", url);*/ + unsigned long status; + unsigned int no_destroy; + struct GNUNET_CRYPTO_EddsaSignature c_sig; + struct GNUNET_CRYPTO_EddsaSignature deposit_confirm_sig; + struct GNUNET_CRYPTO_EddsaPublicKey pub; + #ifdef OBSOLETE + struct ContractNBO contract; + #else + struct Contract contract; + #endif + struct MHD_Response *resp; + json_t *root; + json_t *j_sig_enc; + json_t *j_h_contract; + json_t *j_tmp; + json_t *eddsa_pub_enc; + json_t *response; + json_t *j_mints; + json_t *j_mint; + json_t *j_wire; + int cnt; /* loop counter */ + char *deposit_body; + json_t *j_contract_add; + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Absolute expiry; + struct GNUNET_TIME_Absolute edate; + struct GNUNET_TIME_Absolute refund; + struct GNUNET_HashCode h_json_wire; + json_t *j_h_json_wire; + struct curl_slist *slist; + char *contract_str; + struct GNUNET_HashCode h_contract_str; + struct MERCHANT_contract_handle ch; + struct TALER_MintPublicKeyP mint_pub; + uint64_t nounce; + + CURL *curl; + CURLcode curl_res; + + uint32_t res = GNUNET_SYSERR; + + #define URL_HELLO "/hello" + #define URL_CONTRACT "/contract" + #define URL_PAY "/pay" + no_destroy = 0; + resp = NULL; + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + + if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO))) + { + if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) + status = generate_hello (&resp); + else + { + status = MHD_HTTP_METHOD_NOT_ALLOWED; + } + } + + if (0 == strncasecmp (url, URL_PAY, sizeof (URL_PAY))) + { + if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) + { + status = MHD_HTTP_METHOD_NOT_ALLOWED; + goto end; + + } + else + res = TMH_PARSE_post_json (connection, + connection_cls, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + { + status = MHD_HTTP_BAD_REQUEST; + goto end; + } + + /* the POST's body has to be further fetched */ + if ((GNUNET_NO == res) || (NULL == root)) + return MHD_YES; + + /* Firstly, check if the wallet is paying against an approved + mint */ + json_t *j_chosen_mint; + j_chosen_mint = json_object_get (root, "mint"); + struct GNUNET_HashCode hash_key; + char *chosen_mint; + + chosen_mint = json_string_value (j_chosen_mint); + GNUNET_CRYPTO_hash (chosen_mint, strlen (chosen_mint), &hash_key); + + if (NULL == + GNUNET_CONTAINER_multihashmap_get (mints_hashmap, &hash_key)) + { + printf ("Untrusted mint\n"); + status = MHD_HTTP_FORBIDDEN; + goto end; + + } + + /* NOTE: from now on, the mint's base URL is pointed by 'chosen_mint' */ + + /* The merchant will only add its 'wire' object to the JSON + it got from the wallet */ + + /* Get this dep. perm.'s H_contract */ + + if (NULL == (j_h_contract = json_object_get (root, "H_contract"))) + { + printf ("H_contract field missing\n"); + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + TALER_json_to_data (j_h_contract, &h_contract_str, sizeof (struct GNUNET_HashCode)); + + nounce = 0; + edate.abs_value_us = 0; + + if (GNUNET_SYSERR == + MERCHANT_DB_get_contract_values (db_conn, + &h_contract_str, + &nounce, + &edate)) + { + printf ("not existing contract\n"); + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + /* Reproducing the wire object */ + if (NULL == (j_wire = MERCHANT_get_wire_json (wire, + nounce, + edate))) + + { + printf ("wire object not reproduced\n"); + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + if (-1 == json_object_set (root, "wire", j_wire)) + { + printf ("depperm not augmented\n"); + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + /* POST to mint's "/deposit" */ + curl = curl_easy_init (); + + struct Mint_Response mr; + mr.ptr = malloc(1); + mr.size = 0; + + if (curl) + { + + char deposit_url[strlen (chosen_mint) + strlen ("http://") + strlen ("/deposit") + 1]; + sprintf (deposit_url, "http://%s/deposit", chosen_mint); + slist = curl_slist_append (slist, "Content-type: application/json"); + curl_easy_setopt (curl, CURLOPT_HTTPHEADER, slist); + + curl_easy_setopt (curl, CURLOPT_URL, deposit_url); + curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, get_response_in_memory); + curl_easy_setopt (curl, CURLOPT_WRITEDATA, (void *) &mr); + + /* NOTE: hopefully, this string won't need any URL-encoding, since as for the + Jansson specs, any space and-or newline are not in place using JSON_COMPACT + flag */ + deposit_body = json_dumps (root, JSON_COMPACT | JSON_PRESERVE_ORDER); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, deposit_body); + + curl_res = curl_easy_perform (curl); + + curl_slist_free_all(slist); + if(curl_res != CURLE_OK) + { + printf ("deposit not sent\n"); + goto end; + } + else + printf ("\ndeposit request issued\n"); + + curl_easy_cleanup(curl); + + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &status); + + /* If the request was successful, the deposit confirmation + has to be verified*/ + + if (MHD_HTTP_OK != result) + /* Jump here to error mgmt, see issue #3800 (TODO) */ + + if (GNUNET_SYSERR == + MERCHANT_DB_get_contract_handle (db_conn, &h_contract_str, &ch)) + { + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + root = json_loads (mr.ptr, 0, NULL); + j_tmp = json_object_get (root, "sig"); + TALER_json_to_data (j_tmp, + &deposit_confirm_sig, + sizeof (struct GNUNET_CRYPTO_EddsaSignature)); + j_tmp = json_object_get (root, "pub"); + + TALER_json_to_data (j_tmp, + &mint_pub.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + + GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); + + + /* The following check could be done once the merchant is able to + retrieve the deposit fee for each coin it gets a payment with. + That happens because the signature made on a deposit confirmation + uses, among its values of interest, the subtraction of the deposit + fee from the used coin. That fee is never communicated by the wallet + to the merchant, so the only way for the merchant to get this + information is to fetch all the mint's keys, namely it needs to + invoke "/keys", and store what gotten in its DB */ + + #ifdef DENOMMGMT + if (GNUNET_NO == + MERCHANT_verify_confirmation (&h_contract_str, + &h_json_wire, + ch.timestamp, + ch.refund_deadline, + ch.contract_id, + amount_minus_fee, /* MISSING */ + coin_pub, /* MISSING */ + &pub, + &deposit_confirm_sig, + &mint_pub)) + { + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + + } + + #endif + + /* Place here the successful message to issue to the frontend */ + status = MHD_HTTP_OK; + generate_message (&resp, "fullfillment page here"); + GNUNET_free (mr.ptr); + + } + + + } + + /* + * To be called by the frontend passing the contract with some "holes" + * which will be completed, stored in DB, signed, and returned + * + */ + + if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT))) + { + if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) + { + status = MHD_HTTP_METHOD_NOT_ALLOWED; + goto end; + + } + else + res = TMH_PARSE_post_json (connection, + connection_cls, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + { + status = MHD_HTTP_BAD_REQUEST; + goto end; + } + + + /* the POST's body has to be fetched furthermore */ + if ((GNUNET_NO == res) || (NULL == root)) + return MHD_YES; + + j_mints = json_array (); + for (cnt = 0; cnt < nmints; cnt++) + { + j_mint = json_pack ("{s:s}", + mint_infos[cnt].hostname, + GNUNET_CRYPTO_eddsa_public_key_to_string (&mint_infos[cnt].pubkey)); + json_array_append_new (j_mints, j_mint); + + } + + /* timestamp */ + now = GNUNET_TIME_absolute_get (); + /* expiry */ + expiry = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); + /* edate, note: this value must be generated now (and not when the + wallet sends back a deposit permission because the hashed 'wire' object, + which carries this values in it, has to be included in the signed bundle + by the wallet) */ + edate = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); + refund = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_WEEKS); + + TALER_round_abs_time (&now); + TALER_round_abs_time (&expiry); + TALER_round_abs_time (&edate); + TALER_round_abs_time (&refund); + + /* getting the SEPA-aware JSON */ + /* nounce for hashing the wire object */ + nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + + /* get wire object */ + + if (NULL == (j_wire = MERCHANT_get_wire_json (wire, + nounce, + edate))) + { + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + + /* hash wire objcet */ + if (GNUNET_SYSERR == TALER_hash_json (j_wire, &h_json_wire)) + goto end; + + j_h_json_wire = TALER_json_from_data ((void *) &h_json_wire, sizeof (struct GNUNET_HashCode)); + + GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); + eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); + + if (NULL == (j_contract_add = json_pack ("{s:s, s:s, s:o, s:o, s:o}", + "merchant_pub", json_string_value (eddsa_pub_enc), + "H_wire", json_string_value (j_h_json_wire), + "timestamp", TALER_json_from_abs (now), + "refund", TALER_json_from_abs (refund), + "mints", j_mints))) + { + printf ("BAD contract enhancement\n"); + goto end; + } + + /* melt to what received from the wallet */ + if (-1 == json_object_update (root, j_contract_add)) + { + printf ("depperm response not built\n"); + goto end; + } + + res = MERCHANT_handle_contract (root, + db_conn, + &contract, + now, + expiry, + edate, + refund, + &contract_str, + nounce); + if (GNUNET_SYSERR == res) + { + status = MHD_HTTP_INTERNAL_SERVER_ERROR; + goto end; + } + if (GNUNET_NO == res) + { + status = MHD_HTTP_METHOD_NOT_ACCEPTABLE; + goto end; + } + + GNUNET_CRYPTO_eddsa_sign (privkey, &contract.purpose, &c_sig); + GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, &h_contract_str); + + j_sig_enc = TALER_json_from_eddsa_sig (&contract.purpose, &c_sig); + + response = json_pack ("{s:o, s:o, s:o}", + "contract", root, + "sig", j_sig_enc, + "h_contract", + TALER_json_from_data ((void *) &h_contract_str, sizeof (struct GNUNET_HashCode))); + + GNUNET_free (contract_str); + + TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK); + return MHD_YES; + + } + + end: + + if (NULL != resp) + { + EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); + return MHD_YES; + if (!no_destroy) + MHD_destroy_response (resp); + } + else + { + + EXITIF (GNUNET_OK != failure_resp (connection, status)); + return MHD_YES; + + } + + EXITIF_exit: + result = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return MHD_NO; +} + +/** + * Shutdown task (magically invoked when the application is being + * quit) + * + * @param cls NULL + * @param tc scheduler task context + */ +static void +do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + + if (NULL != mhd) + { + MHD_stop_daemon (mhd); + mhd = NULL; + } + + if (NULL != db_conn) + { + MERCHANT_DB_disconnect (db_conn); + db_conn = NULL; + } + if (keyfile != NULL) + GNUNET_free (privkey); + + +} + +/** + * Function called with information about who is auditing + * a particular mint and what key the mint is using. + * + * @param cls closure + * @param keys information about the various keys used + * by the mint + */ +static void +keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) +{ + /* which kind of mint's keys a merchant should need? Sign + keys? It has already the mint's master key from the conf file */ + + /* HOT UPDATE: the merchants needs the denomination keys! + Because it wants to (firstly) verify the deposit confirmation + sent by the mint, and the signed blob depends (among the + other things) on the coin's deposit fee. That information + is never communicated by the wallet to the merchant. + Again, the merchant needs it because it wants to verify that + the wallet didn't exceede the limit imposed by the merchant + on the total deposit fee for a purchase */ + + + return; + +} + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param config configuration + */ +void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *config) +{ + + unsigned int cnt; + void *keys_mgmt_cls; + + keys_mgmt_cls = NULL; + mint_infos = NULL; + keyfile = NULL; + result = GNUNET_SYSERR; + shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &do_shutdown, NULL); + EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config, + &mint_infos))); + EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); + EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config, + "merchant", + "KEYFILE", + &keyfile)); + EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile))); + EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config))); + EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_YES)); + EXITIF (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_number (config, + "merchant", + "port", + &port)); + EXITIF (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (config, + "merchant", + "hostname", + &hostname)); + + EXITIF (NULL == (mctx = TALER_MINT_init ())); + /* Still not used */ + EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES))); + /* Used when the wallet points out which mint it want to deal with. + That indication is made through the mint's base URL, which will be + the hash-key for this table */ + EXITIF (NULL == (mints_hashmap = GNUNET_CONTAINER_multihashmap_create (nmints, GNUNET_NO))); + + for (cnt = 0; cnt < nmints; cnt++) + { + struct Mint *mint; + struct GNUNET_HashCode mint_key; + + mint = GNUNET_new (struct Mint); + mint->pubkey = mint_infos[cnt].pubkey; + /* port this to the new API */ + mint->conn = TALER_MINT_connect (mctx, + mint_infos[cnt].hostname, + &keys_mgmt_cb, + keys_mgmt_cls); /*<- safe?segfault friendly?*/ + + /* NOTE: the keys mgmt callback should roughly do what the following lines do */ + EXITIF (NULL == mint->conn); + + EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put + (mints_map, + (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey, + mint, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + + /* 1 create hash key + 2 create big entry + 3 put + */ + GNUNET_CRYPTO_hash (mint_infos[cnt].hostname, + strlen (mint_infos[cnt].hostname), + &mint_key); + GNUNET_CONTAINER_multihashmap_put (mints_hashmap, + &mint_key, + &mint_infos[cnt], + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + } + + mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, + port, + NULL, NULL, + &url_handler, NULL, + MHD_OPTION_END); + + EXITIF (NULL == mhd); + + /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that + mandatory ? */ + result = GNUNET_OK; + + EXITIF_exit: + if (GNUNET_OK != result) + GNUNET_SCHEDULER_shutdown (); + GNUNET_free_non_null (keyfile); + if (GNUNET_OK != result) + GNUNET_SCHEDULER_shutdown (); + +} + +/** + * The main function of the serve tool + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'t', "temp", NULL, + gettext_noop ("Use temporary database tables"), GNUNET_NO, + &GNUNET_GETOPT_set_one, &dry}, + GNUNET_GETOPT_OPTION_END + }; + + + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, + "taler-merchant-serve", + "Serve merchant's HTTP interface", + options, &run, NULL)) + return 3; + return (GNUNET_OK == result) ? 0 : 1; + +} diff --git a/src/backend/taler-mint-httpd.h b/src/backend/taler-mint-httpd.h new file mode 100644 index 00000000..ad8702f0 --- /dev/null +++ b/src/backend/taler-mint-httpd.h @@ -0,0 +1,85 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-httpd.h + * @brief Global declarations for the mint + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + * + * FIXME: Consider which of these need to really be globals... + */ +#ifndef TALER_MINT_HTTPD_H +#define TALER_MINT_HTTPD_H + +#include + +/** + * @brief Struct describing an URL and the handler for it. + */ +struct TMH_RequestHandler +{ + + /** + * URL the handler is for. + */ + const char *url; + + /** + * Method the handler is for, NULL for "all". + */ + const char *method; + + /** + * Mime type to use in reply (hint, can be NULL). + */ + const char *mime_type; + + /** + * Raw data for the @e handler + */ + const void *data; + + /** + * Number of bytes in @e data, 0 for 0-terminated. + */ + size_t data_size; + + /** + * Function to call to handle the request. + * + * @param rh this struct + * @param mime_type the @e mime_type for the reply (hint, can be NULL) + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ + int (*handler)(struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + /** + * Default response code. + */ + int response_code; +}; + + +#endif diff --git a/src/backend/taler-mint-httpd_mhd.c b/src/backend/taler-mint-httpd_mhd.c new file mode 100644 index 00000000..419c4fb0 --- /dev/null +++ b/src/backend/taler-mint-httpd_mhd.c @@ -0,0 +1,154 @@ +/* + This file is part of TALER + Copyright (C) 2014 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_mhd.c + * @brief helpers for MHD interaction; these are TALER_MINT_handler_ functions + * that generate simple MHD replies that do not require any real operations + * to be performed (error handling, static pages, etc.) + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include +#include "taler-mint-httpd_responses.h" +#include "taler-mint-httpd.h" +#include "taler-mint-httpd_mhd.h" +#include "taler-mint-httpd_responses.h" + + +/** + * Function to call to handle the request by sending + * back static data from the @a rh. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + struct MHD_Response *response; + int ret; + + if (0 == rh->data_size) + rh->data_size = strlen ((const char *) rh->data); + response = MHD_create_response_from_buffer (rh->data_size, + (void *) rh->data, + MHD_RESPMEM_PERSISTENT); + if (NULL == response) + { + GNUNET_break (0); + return MHD_NO; + } + TMH_RESPONSE_add_global_headers (response); + if (NULL != rh->mime_type) + (void) MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_TYPE, + rh->mime_type); + ret = MHD_queue_response (connection, + rh->response_code, + response); + MHD_destroy_response (response); + return ret; +} + + +/** + * Function to call to handle the request by sending + * back a redirect to the AGPL source code. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + const char *agpl = + "This server is licensed under the Affero GPL. You will now be redirected to the source code."; + struct MHD_Response *response; + int ret; + + response = MHD_create_response_from_buffer (strlen (agpl), + (void *) agpl, + MHD_RESPMEM_PERSISTENT); + if (NULL == response) + { + GNUNET_break (0); + return MHD_NO; + } + TMH_RESPONSE_add_global_headers (response); + if (NULL != rh->mime_type) + (void) MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_TYPE, + rh->mime_type); + MHD_add_response_header (response, + MHD_HTTP_HEADER_LOCATION, + "http://www.git.taler.net/?p=mint.git"); + ret = MHD_queue_response (connection, + rh->response_code, + response); + MHD_destroy_response (response); + return ret; +} + + +/** + * Function to call to handle the request by building a JSON + * reply with an error message from @a rh. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + return TMH_RESPONSE_reply_json_pack (connection, + rh->response_code, + "{s:s}", + "error", + rh->data); +} + + +/* end of taler-mint-httpd_mhd.c */ diff --git a/src/backend/taler-mint-httpd_mhd.h b/src/backend/taler-mint-httpd_mhd.h new file mode 100644 index 00000000..a9f575df --- /dev/null +++ b/src/backend/taler-mint-httpd_mhd.h @@ -0,0 +1,111 @@ +/* + This file is part of TALER + Copyright (C) 2014 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_mhd.h + * @brief helpers for MHD interaction, used to generate simple responses + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_MINT_HTTPD_MHD_H +#define TALER_MINT_HTTPD_MHD_H +#include +#include +#include "taler-mint-httpd.h" + + +/** + * Function to call to handle the request by sending + * back static data from the @a rh. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_MHD_handler_static_response (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +/** + * Function to call to handle the request by sending + * back a redirect to the AGPL source code. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_MHD_handler_agpl_redirect (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +/** + * Function to call to handle the request by building a JSON + * reply from varargs. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param response_code HTTP response code to use + * @param do_cache can the response be cached? (0: no, 1: yes) + * @param fmt format string for pack + * @param ... varargs + * @return MHD result code + */ +int +TMH_MHD_helper_send_json_pack (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void *connection_cls, + int response_code, + int do_cache, + const char *fmt, + ...); + + +/** + * Function to call to handle the request by building a JSON + * reply with an error message from @a rh. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +#endif diff --git a/src/backend/taler-mint-httpd_responses.c b/src/backend/taler-mint-httpd_responses.c index aa8f0bf8..70cf7e52 100644 --- a/src/backend/taler-mint-httpd_responses.c +++ b/src/backend/taler-mint-httpd_responses.c @@ -165,4 +165,25 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection) "invalid json"); } +/** + * Add headers we want to return in every response. + * Useful for testing, like if we want to always close + * connections. + * + * @param response response to modify + */ +void +TMH_RESPONSE_add_global_headers (struct MHD_Response *response) +{ + int TMH_mint_connection_close; + TMH_mint_connection_close = 0; + + /* this test is taken verbatim from the mint's code, + so there is no particular need to do that for a merchant */ + if (TMH_mint_connection_close) + (void) MHD_add_response_header (response, + MHD_HTTP_HEADER_CONNECTION, + "close"); +} + /* end of taler-mint-httpd_responses.c */ diff --git a/src/backend/taler-mint-httpd_responses.h b/src/backend/taler-mint-httpd_responses.h index b1a49d42..2b05c69c 100644 --- a/src/backend/taler-mint-httpd_responses.h +++ b/src/backend/taler-mint-httpd_responses.h @@ -89,4 +89,14 @@ TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection, int TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection); +/** + * Add headers we want to return in every response. + * Useful for testing, like if we want to always close + * connections. + * + * @param response response to modify + */ +void +TMH_RESPONSE_add_global_headers (struct MHD_Response *response); + #endif diff --git a/src/tests/test_contract.c b/src/tests/test_contract.c index 47b3b0e6..d086449d 100644 --- a/src/tests/test_contract.c +++ b/src/tests/test_contract.c @@ -23,7 +23,7 @@ #include "platform.h" #include #include -#include +#include #include "merchant.h" #include "merchant_db.h" #include @@ -80,7 +80,6 @@ run (void *cls, char *const *args, const char *cfgfile, { json_t *j_fake_contract; - json_t *j_wire; json_t *j_details; json_t *j_mints; json_t *j_item; @@ -98,7 +97,6 @@ run (void *cls, char *const *args, const char *cfgfile, json_t *j_merchant_zipcode; json_t *j_lnames; json_t *j_deldate; - char *contract_tmp_str; char *desc; struct TALER_Amount amount; int64_t t_id; @@ -113,7 +111,7 @@ run (void *cls, char *const *args, const char *cfgfile, uint64_t nounce; struct GNUNET_HashCode h_contract_str; char *aa; - char *fancy_time; + const char *fancy_time; uint32_t ret; db_conn = NULL; @@ -278,8 +276,6 @@ run (void *cls, char *const *args, const char *cfgfile, nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - j_wire = MERCHANT_get_wire_json (wire, nounce, now); - ret = MERCHANT_handle_contract (j_fake_contract, db_conn, &contract, -- cgit v1.2.3 From 4254332bd1bd01c41088a158d21d3e1e4ab4eb90 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Mon, 26 Oct 2015 16:28:11 +0100 Subject: Fetching mint's details (compliant to the new API) from the configuration file. To test. --- src/backend-lib/Makefile.am | 2 +- src/backend-lib/merchant_api_contract.c | 22 ++++++ src/backend-lib/merchant_api_deposit.c | 23 ++++++ src/backend/Makefile.am | 3 +- src/backend/merchant.c | 95 ++++++++++++++++++++++- src/backend/merchant.conf | 7 ++ src/backend/taler-merchant-httpd.c | 113 ++++++++++++++++++++++------ src/backend/taler-merchant-httpd_contract.c | 4 +- src/backend/taler-merchant-httpd_contract.h | 46 +++++++++++ src/include/merchant.h | 44 +++++++++++ 10 files changed, 329 insertions(+), 30 deletions(-) create mode 100644 src/backend/taler-merchant-httpd_contract.h diff --git a/src/backend-lib/Makefile.am b/src/backend-lib/Makefile.am index 230cb066..583c4d03 100644 --- a/src/backend-lib/Makefile.am +++ b/src/backend-lib/Makefile.am @@ -6,7 +6,7 @@ lib_LTLIBRARIES = \ include_HEADERS = \ taler_merchant_lib.h \ - taler_merchant_contract_lib.h + taler_merchant_contract_lib.h \ taler_merchant_deposit_lib.h libtalermerchant_la_SOURCES = \ diff --git a/src/backend-lib/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c index 5f6744aa..42e1e064 100644 --- a/src/backend-lib/merchant_api_contract.c +++ b/src/backend-lib/merchant_api_contract.c @@ -1,3 +1,25 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file merchant/merchant_db.c + * @brief DB work related to contract management + * @author Marcello Stanisci + */ + #include "platform.h" #include #include diff --git a/src/backend-lib/merchant_api_deposit.c b/src/backend-lib/merchant_api_deposit.c index 41d4ca3d..a3cae20c 100644 --- a/src/backend-lib/merchant_api_deposit.c +++ b/src/backend-lib/merchant_api_deposit.c @@ -1,3 +1,26 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file merchant/merchant_db.c + * @brief DB and crypto work related to deposit management + * @author Marcello Stanisci + */ + + #include "platform.h" #include #include diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 11b49002..f0c543ea 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -12,7 +12,8 @@ taler_merchant_httpd_SOURCES = \ taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ taler-mint-httpd.h \ taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ - ../backend-lib/taler-merchant-httpd_contract.h + taler-merchant-httpd_contract.c \ + taler-merchant-httpd_contract.h taler_merchant_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ diff --git a/src/backend/merchant.c b/src/backend/merchant.c index f124a030..31223aeb 100644 --- a/src/backend/merchant.c +++ b/src/backend/merchant.c @@ -48,10 +48,18 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, char *token_nf; /* do no free (nf) */ char *mint_section; char *mint_hostname; + char *mint_country; + char *mint_city; + char *mint_state; + char *mint_region; + char *mint_province; char *mint_pubkey_enc; + char *mint_street; struct MERCHANT_MintInfo *r_mints; struct MERCHANT_MintInfo mint; unsigned long long mint_port; + unsigned long long mint_zip_code; + unsigned long long mint_street_no; unsigned int cnt; int OK; @@ -60,6 +68,15 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, token_nf = NULL; mint_section = NULL; mint_hostname = NULL; + mint_port = 0; + mint_country = NULL; + mint_city = NULL; + mint_state = NULL; + mint_region = NULL; + mint_province = NULL; + mint_zip_code = 0; + mint_street = NULL; + mint_street_no = 0; mint_pubkey_enc = NULL; r_mints = NULL; cnt = 0; @@ -73,6 +90,8 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, { GNUNET_assert (0 < GNUNET_asprintf (&mint_section, "mint-%s", token_nf)); + memset (&mint, 0, sizeof (mint)); + EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, mint_section, @@ -92,14 +111,86 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc, strlen (mint_pubkey_enc), &mint.pubkey)); + + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + mint_section, + "COUNTRY", + &mint_country)); + + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + mint_section, + "CITY", + &mint_city)); + + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + mint_section, + "STATE", + &mint_state)) + mint.state = mint_state; + + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + mint_section, + "REGION", + &mint_region)) + mint.region = mint_region; + + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, + mint_section, + "PROVINCE", + &mint_province)) + mint.province = mint_province; + + + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + mint_section, + "ZIP_CODE", + &mint_zip_code)); + + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + mint_section, + "STREET", + &mint_street)); + + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + mint_section, + "STREET_NUMBER", + &mint_street_no)); mint.hostname = mint_hostname; mint.port = (uint16_t) mint_port; + mint.country = mint_country; + mint.city = mint_city; + mint.zip_code = (uint16_t) mint_zip_code; + mint.street = mint_street; + mint.street_no = (uint16_t) mint_street_no; GNUNET_array_append (r_mints, cnt, mint); + GNUNET_free (mint_hostname); mint_hostname = NULL; - GNUNET_free (mint_pubkey_enc); + GNUNET_free (mint_country); + mint_country = NULL; + GNUNET_free (mint_city); + mint_city = NULL; + GNUNET_free_non_null (mint_state); + mint_state = NULL; + GNUNET_free_non_null (mint_region); + mint_region = NULL; + GNUNET_free_non_null (mint_province); + mint_province = NULL; + GNUNET_free_non_null (mint_pubkey_enc); mint_pubkey_enc = NULL; - GNUNET_free (mint_section); + GNUNET_free_non_null (mint_section); mint_section = NULL; + } OK = 1; diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf index 3b637448..1f069a58 100644 --- a/src/backend/merchant.conf +++ b/src/backend/merchant.conf @@ -8,6 +8,13 @@ KEYFILE = merchant.priv HOSTNAME = demo.taler.net PORT = 80 PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0 +COUNTRY = France +CITY = Rennes +REGION = Bretagne +PROVINCE = Ile-et-Vilaine +ZIP_CODE = 35510 +STREET = Avenue du General Leclerc +STREET_NUMBER = 21 [merchant-db] CONFIG = postgres:///taler diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 014b6fa6..0438f511 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -33,6 +33,7 @@ #include "merchant.h" #include "taler_merchant_lib.h" #include "taler-mint-httpd_mhd.h" +#include "taler-merchant-httpd_contract.h" /** * Our hostname @@ -60,7 +61,7 @@ char *keyfile; static struct TALER_MINT_Context *mctx; /** - * Mints' URL,port,key triples + * Collection of all trusted mints informations */ struct MERCHANT_MintInfo *mint_infos; @@ -231,10 +232,10 @@ url_handler (void *cls, upload_data_size); } return TMH_MHD_handler_static_response (&h404, - connection, - con_cls, - upload_data, - upload_data_size); + connection, + con_cls, + upload_data, + upload_data_size); } @@ -295,6 +296,65 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } +/** + * Debugging function which prints all non-null fields within + * a mint descriptor. FIXME: Convert 'printf' in 'GNUNET_log' + * @param mint mint whose values are getting dumped + */ +void +dump_mint (struct MERCHANT_MintInfo *mint) +{ + char dump[]; + + #define GET_MINT_VALUE_STRING(fieldname) \ + do { if (NULL != mint->fieldname) \ + { \ + dump = GNUNET_realloc (dump, strlen (dump) \ + + strlen (mint->fieldname) \ + + strlen ("fieldname: ") \ + + 2); \ + sprintf (dump[strlen (dump)], "fieldname: %s\n", mint->fieldname); \ + } \ + } while (0); + + #define GET_MINT_VALUE_UINT16(fieldname) \ + do { if (0 != mint->fieldname && mint->fieldname < 65536) \ + { \ + dump = GNUNET_realloc (dump, strlen (dump) \ + + 5 \ + + strlen ("fieldname: ") \ + + 2); \ + sprintf (dump[strlen (dump)], "fieldname: %d\n", mint->fieldname); \ + } \ + } while (0); + + dump = GNUNET_malloc (1); + + // TODO public key fetch + + #define MAXUINT16 65536 + if (0 != mint->port && mint->port <= MAXUINT16) + { + dump = GNUNET_realloc (dump, strlen (dump) + + 5 + + strlen ("port: ") + + 1); + + sprintf (dump[strlen (dump) + 1], "port: %d\n", mint->port); + } + + GET_MINT_VALUE_STRING(state); + GET_MINT_VALUE_STRING(region); + GET_MINT_VALUE_STRING(province); + GET_MINT_VALUE_UINT16(zip_code); + GET_MINT_VALUE_STRING(street); + GET_MINT_VALUE_UINT16(street_no); + + printf ("Dumping mint:\n%s", dump); + GNUNET_free (dump); + +} + /** * Main function that will be run by the scheduler. * @@ -317,6 +377,8 @@ run (void *cls, char *const *args, const char *cfgfile, result = GNUNET_SYSERR; shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown, NULL); + + EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config, &mint_infos))); EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); @@ -348,37 +410,38 @@ run (void *cls, char *const *args, const char *cfgfile, for (cnt = 0; cnt < nmints; cnt++) { + dump_mint (&mint_infos[cnt]); + struct Mint *mint; struct GNUNET_HashCode mint_key; mint = GNUNET_new (struct Mint); mint->pubkey = mint_infos[cnt].pubkey; + /* port this to the new API */ + + /* ToTest mint->conn = TALER_MINT_connect (mctx, mint_infos[cnt].hostname, &keys_mgmt_cb, - keys_mgmt_cls); /*<- safe?segfault friendly?*/ - - /* NOTE: the keys mgmt callback should roughly do what the following lines do */ + keys_mgmt_cls); EXITIF (NULL == mint->conn); + */ - EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put - (mints_map, - (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey, - mint, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); - - /* 1 create hash key - 2 create big entry - 3 put - */ - GNUNET_CRYPTO_hash (mint_infos[cnt].hostname, - strlen (mint_infos[cnt].hostname), - &mint_key); - GNUNET_CONTAINER_multihashmap_put (mints_hashmap, - &mint_key, - &mint_infos[cnt], - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + EXITIF (GNUNET_SYSERR == + GNUNET_CONTAINER_multipeermap_put (mints_map, + (struct GNUNET_PeerIdentity *) &mint->pubkey, + mint, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + + GNUNET_CRYPTO_hash (mint_infos[cnt].hostname, + strlen (mint_infos[cnt].hostname), + &mint_key); + + GNUNET_CONTAINER_multihashmap_put (mints_hashmap, + &mint_key, + &mint_infos[cnt], + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); } mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index cbbf2616..71b09ef7 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -27,6 +27,7 @@ #include #include #include +#include "taler-mint-httpd.h" #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_responses.h" #include "merchant_db.h" @@ -68,7 +69,8 @@ MH_handler_contract (struct TMH_RequestHandler *rh, */ - + /* To suppress compilation warning */ + return 0; } diff --git a/src/backend/taler-merchant-httpd_contract.h b/src/backend/taler-merchant-httpd_contract.h new file mode 100644 index 00000000..5e72c514 --- /dev/null +++ b/src/backend/taler-merchant-httpd_contract.h @@ -0,0 +1,46 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file merchant/backend/taler-merchant-httpd_contract.h + * @brief headers for /contract handler + * @author Marcello Stanisci + */ + +#ifndef TALER_MINT_HTTPD_CONTRACT_H +#define TALER_MINT_HTTPD_CONTRACT_H +#include +#include "taler-mint-httpd.h" + +/** + * Manage a contract request + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * + * @return MHD result code + */ +int +MH_handler_contract (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + +#endif diff --git a/src/include/merchant.h b/src/include/merchant.h index a5273507..dcd4388e 100644 --- a/src/include/merchant.h +++ b/src/include/merchant.h @@ -58,6 +58,50 @@ struct MERCHANT_MintInfo { */ uint16_t port; + /* According to the mint's location, some of the following + fields may be omitted. In case of numbers, they will be set + to zero, otherwise SET TO nuLL */ + + /** + * The Country where the mint operates from + */ + char *country; + + /** + * The city where the mint operates from + */ + char *city; + + /** + * The State (within a Country) where the mint + * operates from + */ + char *state; + + /** + * The region where the mint operates from + */ + char *region; + + /** + * The province where the mint operates from + */ + char *province; + + /** + * The ZIP code where the mint operates from + */ + uint16_t zip_code; + + /** + * The street's name where the mint operates from + */ + char *street; + + /** + * The street number where the mint operates from + */ + uint16_t street_no; }; -- cgit v1.2.3 From 125c9ec8da551943965e7405b78675cb8013a31b Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Mon, 26 Oct 2015 17:00:30 +0100 Subject: moving dating in frontend --- src/frontend/generate_taler_contract.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index 33cb2a47..bcfeaeb9 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -48,6 +48,7 @@ else { $receiver = "Test Receiver"; $amount = 5; + $currency = "Test"; } @@ -71,6 +72,9 @@ $teatax = array ('value' => 1, 'fraction' => 0, 'currency' => $currency); +// Take a timestamp +$now = new DateTime('now'); + // pack the JSON for the contract // --- FIXME: exact format needs review! $json = json_encode (array ('amount' => array ('value' => $value, @@ -90,6 +94,8 @@ $json = json_encode (array ('amount' => array ('value' => $value, 'taxes' => array (array ('teatax' => $teatax)), 'delivery_date' => "Some Date Format", 'delivery_location' => 'LNAME1')), + 'timestamp' => "Date(" . $now->getTimestamp() . ")", + 'refund_deadline' => "Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")", 'merchant' => array ('address' => 'LNAME2', 'name' => 'test merchant', 'jurisdiction' => 'LNAME3'), @@ -115,9 +121,9 @@ $json = json_encode (array ('amount' => array ('value' => $value, 'state' => 'Test State', 'region' => 'Test Region', 'province' => 'Test Province', - 'ZIP code' => 4908)))); //, JSON_PRETTY_PRINT); + 'ZIP code' => 4908))), JSON_PRETTY_PRINT); -if ($cli_debug && TRUE) +if ($cli_debug) { echo $json . "\n"; exit; -- cgit v1.2.3 From 37cc93e1511c0072b6a801d3071932114364392d Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Mon, 26 Oct 2015 21:06:44 +0100 Subject: fixing macro notation for using arguments as string constants --- src/backend/merchant.c | 8 -------- src/backend/merchant.conf | 2 +- src/backend/taler-merchant-httpd.c | 30 +++++++++-------------------- src/backend/taler-merchant-httpd_contract.c | 1 + 4 files changed, 11 insertions(+), 30 deletions(-) diff --git a/src/backend/merchant.c b/src/backend/merchant.c index 31223aeb..47ccdc48 100644 --- a/src/backend/merchant.c +++ b/src/backend/merchant.c @@ -111,36 +111,28 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc, strlen (mint_pubkey_enc), &mint.pubkey)); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, mint_section, "COUNTRY", &mint_country)); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, mint_section, "CITY", &mint_city)); - - if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, mint_section, "STATE", &mint_state)) mint.state = mint_state; - - if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, mint_section, "REGION", &mint_region)) mint.region = mint_region; - - if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, mint_section, diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf index 1f069a58..851a6062 100644 --- a/src/backend/merchant.conf +++ b/src/backend/merchant.conf @@ -17,7 +17,7 @@ STREET = Avenue du General Leclerc STREET_NUMBER = 21 [merchant-db] -CONFIG = postgres:///taler +CONFIG = postgres:///talerdemo [wire-sepa] IBAN = DE67830654080004822650 diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 0438f511..2c804b5b 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -292,8 +292,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } if (keyfile != NULL) GNUNET_free (privkey); - - } /** @@ -304,16 +302,16 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) void dump_mint (struct MERCHANT_MintInfo *mint) { - char dump[]; + char *dump; #define GET_MINT_VALUE_STRING(fieldname) \ do { if (NULL != mint->fieldname) \ { \ dump = GNUNET_realloc (dump, strlen (dump) \ + strlen (mint->fieldname) \ - + strlen ("fieldname: ") \ + + strlen (#fieldname ": ") \ + 2); \ - sprintf (dump[strlen (dump)], "fieldname: %s\n", mint->fieldname); \ + sprintf (dump + strlen (dump), #fieldname ": %s\n", mint->fieldname); \ } \ } while (0); @@ -322,9 +320,9 @@ dump_mint (struct MERCHANT_MintInfo *mint) { \ dump = GNUNET_realloc (dump, strlen (dump) \ + 5 \ - + strlen ("fieldname: ") \ + + strlen (#fieldname ": ") \ + 2); \ - sprintf (dump[strlen (dump)], "fieldname: %d\n", mint->fieldname); \ + sprintf (dump + strlen (dump), #fieldname ": %d\n", mint->fieldname); \ } \ } while (0); @@ -332,24 +330,14 @@ dump_mint (struct MERCHANT_MintInfo *mint) // TODO public key fetch - #define MAXUINT16 65536 - if (0 != mint->port && mint->port <= MAXUINT16) - { - dump = GNUNET_realloc (dump, strlen (dump) - + 5 - + strlen ("port: ") - + 1); - - sprintf (dump[strlen (dump) + 1], "port: %d\n", mint->port); - } - - GET_MINT_VALUE_STRING(state); - GET_MINT_VALUE_STRING(region); + GET_MINT_VALUE_STRING(city); GET_MINT_VALUE_STRING(province); GET_MINT_VALUE_UINT16(zip_code); + GET_MINT_VALUE_UINT16(port); GET_MINT_VALUE_STRING(street); + GET_MINT_VALUE_STRING(country); GET_MINT_VALUE_UINT16(street_no); - + printf ("country = %s\n", mint->country); printf ("Dumping mint:\n%s", dump); GNUNET_free (dump); diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index 71b09ef7..de4cda7c 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include "taler-mint-httpd.h" -- cgit v1.2.3 From 86a7aa73e66bc6088f8f4abe87fff43fc4017cdc Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Tue, 27 Oct 2015 09:57:33 +0100 Subject: Reverting to the state whithout the mint details being fetched from the configuration file. I.e. the state when the mint had no physical address in its JSON --- src/backend/merchant.c | 87 +------------------------------- src/backend/taler-merchant-httpd.c | 101 +++++++++---------------------------- 2 files changed, 27 insertions(+), 161 deletions(-) diff --git a/src/backend/merchant.c b/src/backend/merchant.c index 47ccdc48..f124a030 100644 --- a/src/backend/merchant.c +++ b/src/backend/merchant.c @@ -48,18 +48,10 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, char *token_nf; /* do no free (nf) */ char *mint_section; char *mint_hostname; - char *mint_country; - char *mint_city; - char *mint_state; - char *mint_region; - char *mint_province; char *mint_pubkey_enc; - char *mint_street; struct MERCHANT_MintInfo *r_mints; struct MERCHANT_MintInfo mint; unsigned long long mint_port; - unsigned long long mint_zip_code; - unsigned long long mint_street_no; unsigned int cnt; int OK; @@ -68,15 +60,6 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, token_nf = NULL; mint_section = NULL; mint_hostname = NULL; - mint_port = 0; - mint_country = NULL; - mint_city = NULL; - mint_state = NULL; - mint_region = NULL; - mint_province = NULL; - mint_zip_code = 0; - mint_street = NULL; - mint_street_no = 0; mint_pubkey_enc = NULL; r_mints = NULL; cnt = 0; @@ -90,8 +73,6 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, { GNUNET_assert (0 < GNUNET_asprintf (&mint_section, "mint-%s", token_nf)); - memset (&mint, 0, sizeof (mint)); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, mint_section, @@ -111,78 +92,14 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc, strlen (mint_pubkey_enc), &mint.pubkey)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "COUNTRY", - &mint_country)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "CITY", - &mint_city)); - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "STATE", - &mint_state)) - mint.state = mint_state; - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "REGION", - &mint_region)) - mint.region = mint_region; - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "PROVINCE", - &mint_province)) - mint.province = mint_province; - - - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - mint_section, - "ZIP_CODE", - &mint_zip_code)); - - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "STREET", - &mint_street)); - - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - mint_section, - "STREET_NUMBER", - &mint_street_no)); mint.hostname = mint_hostname; mint.port = (uint16_t) mint_port; - mint.country = mint_country; - mint.city = mint_city; - mint.zip_code = (uint16_t) mint_zip_code; - mint.street = mint_street; - mint.street_no = (uint16_t) mint_street_no; GNUNET_array_append (r_mints, cnt, mint); - GNUNET_free (mint_hostname); mint_hostname = NULL; - GNUNET_free (mint_country); - mint_country = NULL; - GNUNET_free (mint_city); - mint_city = NULL; - GNUNET_free_non_null (mint_state); - mint_state = NULL; - GNUNET_free_non_null (mint_region); - mint_region = NULL; - GNUNET_free_non_null (mint_province); - mint_province = NULL; - GNUNET_free_non_null (mint_pubkey_enc); + GNUNET_free (mint_pubkey_enc); mint_pubkey_enc = NULL; - GNUNET_free_non_null (mint_section); + GNUNET_free (mint_section); mint_section = NULL; - } OK = 1; diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 2c804b5b..014b6fa6 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -33,7 +33,6 @@ #include "merchant.h" #include "taler_merchant_lib.h" #include "taler-mint-httpd_mhd.h" -#include "taler-merchant-httpd_contract.h" /** * Our hostname @@ -61,7 +60,7 @@ char *keyfile; static struct TALER_MINT_Context *mctx; /** - * Collection of all trusted mints informations + * Mints' URL,port,key triples */ struct MERCHANT_MintInfo *mint_infos; @@ -232,10 +231,10 @@ url_handler (void *cls, upload_data_size); } return TMH_MHD_handler_static_response (&h404, - connection, - con_cls, - upload_data, - upload_data_size); + connection, + con_cls, + upload_data, + upload_data_size); } @@ -292,55 +291,8 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } if (keyfile != NULL) GNUNET_free (privkey); -} -/** - * Debugging function which prints all non-null fields within - * a mint descriptor. FIXME: Convert 'printf' in 'GNUNET_log' - * @param mint mint whose values are getting dumped - */ -void -dump_mint (struct MERCHANT_MintInfo *mint) -{ - char *dump; - #define GET_MINT_VALUE_STRING(fieldname) \ - do { if (NULL != mint->fieldname) \ - { \ - dump = GNUNET_realloc (dump, strlen (dump) \ - + strlen (mint->fieldname) \ - + strlen (#fieldname ": ") \ - + 2); \ - sprintf (dump + strlen (dump), #fieldname ": %s\n", mint->fieldname); \ - } \ - } while (0); - - #define GET_MINT_VALUE_UINT16(fieldname) \ - do { if (0 != mint->fieldname && mint->fieldname < 65536) \ - { \ - dump = GNUNET_realloc (dump, strlen (dump) \ - + 5 \ - + strlen (#fieldname ": ") \ - + 2); \ - sprintf (dump + strlen (dump), #fieldname ": %d\n", mint->fieldname); \ - } \ - } while (0); - - dump = GNUNET_malloc (1); - - // TODO public key fetch - - GET_MINT_VALUE_STRING(city); - GET_MINT_VALUE_STRING(province); - GET_MINT_VALUE_UINT16(zip_code); - GET_MINT_VALUE_UINT16(port); - GET_MINT_VALUE_STRING(street); - GET_MINT_VALUE_STRING(country); - GET_MINT_VALUE_UINT16(street_no); - printf ("country = %s\n", mint->country); - printf ("Dumping mint:\n%s", dump); - GNUNET_free (dump); - } /** @@ -365,8 +317,6 @@ run (void *cls, char *const *args, const char *cfgfile, result = GNUNET_SYSERR; shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown, NULL); - - EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config, &mint_infos))); EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); @@ -398,38 +348,37 @@ run (void *cls, char *const *args, const char *cfgfile, for (cnt = 0; cnt < nmints; cnt++) { - dump_mint (&mint_infos[cnt]); - struct Mint *mint; struct GNUNET_HashCode mint_key; mint = GNUNET_new (struct Mint); mint->pubkey = mint_infos[cnt].pubkey; - /* port this to the new API */ - - /* ToTest mint->conn = TALER_MINT_connect (mctx, mint_infos[cnt].hostname, &keys_mgmt_cb, - keys_mgmt_cls); + keys_mgmt_cls); /*<- safe?segfault friendly?*/ + + /* NOTE: the keys mgmt callback should roughly do what the following lines do */ EXITIF (NULL == mint->conn); - */ - EXITIF (GNUNET_SYSERR == - GNUNET_CONTAINER_multipeermap_put (mints_map, - (struct GNUNET_PeerIdentity *) &mint->pubkey, - mint, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); - - GNUNET_CRYPTO_hash (mint_infos[cnt].hostname, - strlen (mint_infos[cnt].hostname), - &mint_key); - - GNUNET_CONTAINER_multihashmap_put (mints_hashmap, - &mint_key, - &mint_infos[cnt], - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put + (mints_map, + (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey, + mint, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + + /* 1 create hash key + 2 create big entry + 3 put + */ + GNUNET_CRYPTO_hash (mint_infos[cnt].hostname, + strlen (mint_infos[cnt].hostname), + &mint_key); + GNUNET_CONTAINER_multihashmap_put (mints_hashmap, + &mint_key, + &mint_infos[cnt], + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); } mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, -- cgit v1.2.3 From b3c7ea82a6a1ade4a310a6958c232207560d4c1a Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Tue, 27 Oct 2015 12:48:47 +0100 Subject: minor fix --- src/backend/taler-merchant-httpd.c | 1 + src/backend/taler-merchant-httpd_contract.c | 11 ++--------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 014b6fa6..550de8c1 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -33,6 +33,7 @@ #include "merchant.h" #include "taler_merchant_lib.h" #include "taler-mint-httpd_mhd.h" +#include "taler-merchant-httpd_contract.h" /** * Our hostname diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index de4cda7c..143746b6 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -55,17 +55,10 @@ MH_handler_contract (struct TMH_RequestHandler *rh, { /* 1. Generate preferred mint(s) array. - a. Add the configuration lines to specify the mint's business - address. - b. Add this address to the 'locations' object, that should be - already present in the 'proposition' gotten from the frontend. - c. Point the 'address' field's label to the one added to 'locations' - The 'mint' JSON layout is as follows: - { "address": "address_label", - "url": "mint_base_url", - "master_pub": "base32 mint's master public key" } + { "url": "mint_base_url", + "master_pub": "base32 mint's master public key" } */ -- cgit v1.2.3 From 2fbdccc8dc18157e2df7486e4ecedee343c81d8d Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Tue, 27 Oct 2015 21:58:14 +0100 Subject: Adding context event loop, plus some code cleaning --- src/backend/melted/Makefile.am | 62 --- src/backend/melted/README | 9 - src/backend/melted/copy.sh | 17 - src/backend/melted/merchant.c | 173 ------- src/backend/melted/merchant.conf | 18 - src/backend/melted/merchant.h | 110 ---- src/backend/melted/merchant_db.c | 354 ------------- src/backend/melted/merchant_db.h | 101 ---- src/backend/melted/myconf.sh | 3 - src/backend/melted/taler-merchant-httpd.c | 821 ------------------------------ src/backend/merchant.c | 6 +- src/backend/taler-merchant-httpd.c | 178 ++++--- src/include/merchant.h | 49 +- src/tests/Makefile.am | 2 +- src/tests/merchant.c | 173 ------- 15 files changed, 116 insertions(+), 1960 deletions(-) delete mode 100644 src/backend/melted/Makefile.am delete mode 100644 src/backend/melted/README delete mode 100755 src/backend/melted/copy.sh delete mode 100644 src/backend/melted/merchant.c delete mode 100644 src/backend/melted/merchant.conf delete mode 100644 src/backend/melted/merchant.h delete mode 100644 src/backend/melted/merchant_db.c delete mode 100644 src/backend/melted/merchant_db.h delete mode 100644 src/backend/melted/myconf.sh delete mode 100644 src/backend/melted/taler-merchant-httpd.c delete mode 100644 src/tests/merchant.c diff --git a/src/backend/melted/Makefile.am b/src/backend/melted/Makefile.am deleted file mode 100644 index 66d784ec..00000000 --- a/src/backend/melted/Makefile.am +++ /dev/null @@ -1,62 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - -bin_PROGRAMS = \ - taler-mint-httpd \ - taler-merchant-httpd - -taler_mint_httpd_SOURCES = \ - taler-mint-httpd.c taler-mint-httpd.h \ - taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \ - taler-mint-httpd_db.c taler-mint-httpd_db.h \ - taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ - taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ - taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ - taler-mint-httpd_admin.c taler-mint-httpd_admin.h \ - taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \ - taler-mint-httpd_withdraw.c taler-mint-httpd_withdraw.h \ - taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h -taler_mint_httpd_LDADD = \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ - $(top_builddir)/src/mintdb/libtalermintdb.la \ - -lmicrohttpd \ - -ljansson \ - -lgnunetutil \ - -lpthread -taler_merchant_httpd_SOURCES = \ - taler-merchant-httpd.c \ - merchant.c merchant.h \ - merchant_db.c merchant_db.h \ - taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \ - taler-mint-httpd_db.c taler-mint-httpd_db.h \ - taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ - taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ - taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ - taler-mint-httpd_admin.c taler-mint-httpd_admin.h \ - taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \ - taler-mint-httpd_withdraw.c taler-mint-httpd_withdraw.h \ - taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h - -taler_merchant_httpd_LDADD = \ - $(LIBGCRYPT_LIBS) \ - $(top_builddir)/src/util/libtalerutil.la \ - $(top_builddir)/src/mintdb/libtalermintdb.la \ - -lmicrohttpd \ - -ljansson \ - -lgnunetutil \ - -ltalermint \ - -ltalerpq \ - -lgnunetpostgres \ - -lpq \ - -lpthread - -if HAVE_DEVELOPER -taler_mint_httpd_SOURCES += \ - taler-mint-httpd_test.c taler-mint-httpd_test.h -endif diff --git a/src/backend/melted/README b/src/backend/melted/README deleted file mode 100644 index 45c4fb80..00000000 --- a/src/backend/melted/README +++ /dev/null @@ -1,9 +0,0 @@ -Since the merchant's backend makes use of several routines that are native -to the mint (in particular, those aimed to parse JSON object in HTTP POSTs), -and since there is no possibility to export some of them in a shared library, -then the files in this directory need to be moved *inside* the mint/src/mint -directory in order to be compiled. Use 'copy.sh SRC DST' (STILL NOT TESTED) -to copy the files in the desired location. - -Lastly, passing CFLAGS='-I/usr/include/postgresql' to ./configure seems to be -mandatory, in order to compile the merchant http daemon. diff --git a/src/backend/melted/copy.sh b/src/backend/melted/copy.sh deleted file mode 100755 index 570bf545..00000000 --- a/src/backend/melted/copy.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# copy all the backend relevant files from the first to the second -# directory given in the arguments. -# The intended use is to move back and forth those files -# (since they are git'ed on the merchant's repository) as long as not -# configure flag like --enable-merchant will be available from the mint - -# STILL NOT TESTED - -cp -t $2 \ -$1/Makefile.am \ -$1/merchant.c \ -$1/merchant_db.c \ -$1/merchant_db.h \ -$1/merchant.h \ -$1/taler-merchant-httpd.c \ -$1/merchant.conf diff --git a/src/backend/melted/merchant.c b/src/backend/melted/merchant.c deleted file mode 100644 index f124a030..00000000 --- a/src/backend/melted/merchant.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file merchant/merchant.c - * @brief Common utility functions for merchant - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include -#include "merchant.h" - - -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - -/** - * Parses mints from the configuration. - * - * @param cfg the configuration - * @param mints the array of mints upon successful parsing. Will be NULL upon - * error. - * @return the number of mints in the above array; GNUNET_SYSERR upon error in - * parsing. - */ -int -TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct MERCHANT_MintInfo **mints) -{ - char *mints_str; - char *token_nf; /* do no free (nf) */ - char *mint_section; - char *mint_hostname; - char *mint_pubkey_enc; - struct MERCHANT_MintInfo *r_mints; - struct MERCHANT_MintInfo mint; - unsigned long long mint_port; - unsigned int cnt; - int OK; - - OK = 0; - mints_str = NULL; - token_nf = NULL; - mint_section = NULL; - mint_hostname = NULL; - mint_pubkey_enc = NULL; - r_mints = NULL; - cnt = 0; - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "merchant", - "TRUSTED_MINTS", - &mints_str)); - for (token_nf = strtok (mints_str, " "); - NULL != token_nf; - token_nf = strtok (NULL, " ")) - { - GNUNET_assert (0 < GNUNET_asprintf (&mint_section, - "mint-%s", token_nf)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "HOSTNAME", - &mint_hostname)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - mint_section, - "PORT", - &mint_port)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "PUBKEY", - &mint_pubkey_enc)); - EXITIF (GNUNET_OK != - GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc, - strlen (mint_pubkey_enc), - &mint.pubkey)); - mint.hostname = mint_hostname; - mint.port = (uint16_t) mint_port; - GNUNET_array_append (r_mints, cnt, mint); - mint_hostname = NULL; - GNUNET_free (mint_pubkey_enc); - mint_pubkey_enc = NULL; - GNUNET_free (mint_section); - mint_section = NULL; - } - OK = 1; - - EXITIF_exit: - GNUNET_free_non_null (mints_str); - GNUNET_free_non_null (mint_section); - GNUNET_free_non_null (mint_hostname); - GNUNET_free_non_null (mint_pubkey_enc); - if (!OK) - { - GNUNET_free_non_null (r_mints); - return GNUNET_SYSERR; - } - - *mints = r_mints; - return cnt; -} - - -/** - * Parse the SEPA information from the configuration. If any of the required - * fileds is missing return NULL. - * - * @param cfg the configuration - * @return Sepa details as a structure; NULL upon error - */ -struct MERCHANT_WIREFORMAT_Sepa * -TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct MERCHANT_WIREFORMAT_Sepa *wf; - - wf = GNUNET_new (struct MERCHANT_WIREFORMAT_Sepa); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "wire-sepa", - "IBAN", - &wf->iban)); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "wire-sepa", - "NAME", - &wf->name)); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "wire-sepa", - "BIC", - &wf->bic)); - return wf; - - EXITIF_exit: - GNUNET_free_non_null (wf->iban); - GNUNET_free_non_null (wf->name); - GNUNET_free_non_null (wf->bic); - GNUNET_free (wf); - return NULL; - -} - - -/** - * Destroy and free resouces occupied by the wireformat structure - * - * @param wf the wireformat structure - */ -void -TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf) -{ - GNUNET_free_non_null (wf->iban); - GNUNET_free_non_null (wf->name); - GNUNET_free_non_null (wf->bic); - GNUNET_free (wf); -} - -/* end of merchant.c */ diff --git a/src/backend/melted/merchant.conf b/src/backend/melted/merchant.conf deleted file mode 100644 index 3b637448..00000000 --- a/src/backend/melted/merchant.conf +++ /dev/null @@ -1,18 +0,0 @@ -[merchant] -PORT = 9966 -HOSTNAME = localhost -TRUSTED_MINTS = taler -KEYFILE = merchant.priv - -[mint-taler] -HOSTNAME = demo.taler.net -PORT = 80 -PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0 - -[merchant-db] -CONFIG = postgres:///taler - -[wire-sepa] -IBAN = DE67830654080004822650 -NAME = GNUNET E.V -BIC = GENODEF1SRL diff --git a/src/backend/melted/merchant.h b/src/backend/melted/merchant.h deleted file mode 100644 index c66131ed..00000000 --- a/src/backend/melted/merchant.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file merchant/merchant.c - * @brief Common utility functions for merchant - * @author Sree Harsha Totakura - */ - -#ifndef MERCHANT_H -#define MERCHANT_H - -#include -#include - -/** - * A mint - */ -struct MERCHANT_MintInfo { - /** - * Hostname - */ - char *hostname; - - /** - * The public key of the mint - */ - struct GNUNET_CRYPTO_EddsaPublicKey pubkey; - - /** - * The port where the mint's service is running - */ - uint16_t port; - -}; - - -/** - * Parses mints from the configuration. - * - * @param cfg the configuration - * @param mints the array of mints upon successful parsing. Will be NULL upon - * error. - * @return the number of mints in the above array; GNUNET_SYSERR upon error in - * parsing. - */ -int -TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct MERCHANT_MintInfo **mints); - - -GNUNET_NETWORK_STRUCT_BEGIN -struct MERCHANT_WIREFORMAT_Sepa -{ - /** - * The international bank account number - */ - char *iban; - - /** - * Name of the bank account holder - */ - char *name; - - /** - *The bank identification code - */ - char *bic; - - /** - * The latest payout date when the payment corresponding to this account has - * to take place. A value of 0 indicates a transfer as soon as possible. - */ - struct GNUNET_TIME_AbsoluteNBO payout; -}; -GNUNET_NETWORK_STRUCT_END - -/** - * Parse the SEPA information from the configuration. If any of the required - * fileds is missing return NULL. - * - * @param cfg the configuration - * @return Sepa details as a structure; NULL upon error - */ -struct MERCHANT_WIREFORMAT_Sepa * -TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg); - - -/** - * Destroy and free resouces occupied by the wireformat structure - * - * @param wf the wireformat structure - */ -void -TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf); - -#endif /* MERCHANT_H */ diff --git a/src/backend/melted/merchant_db.c b/src/backend/melted/merchant_db.c deleted file mode 100644 index 274de25a..00000000 --- a/src/backend/melted/merchant_db.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file merchant/merchant_db.c - * @brief database helper functions used by the merchant - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include -#include -#include -#include "merchant_db.h" - - -#define PQSQL_strerror(kind, cmd, res) \ - GNUNET_log_from (kind, "merchant-db", \ - "SQL %s failed at %s:%u with error: %s", \ - cmd, __FILE__, __LINE__, PQresultErrorMessage (res)); - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - -/** - * Connect to postgresql database - * - * @param cfg the configuration handle - * @return connection to the postgresql database; NULL upon error - */ -PGconn * -MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - return GNUNET_POSTGRES_connect (cfg, "merchant-db"); -} - - -/** - * Disconnect from the database - * - * @param conn database handle to close - */ -void -MERCHANT_DB_disconnect (PGconn *conn) -{ - PQfinish (conn); -} - - -/** - * Initialize merchant tables - * - * @param conn the connection handle to postgres db. - * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their - * contents are dropped when the @a conn is closed - * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure - */ -int -MERCHANT_DB_initialize (PGconn *conn, int tmp) -{ - const char *tmp_str = (1 == tmp) ? "TEMPORARY" : ""; - char *sql; - PGresult *res; - ExecStatusType status; - int ret; - - res = NULL; - (void) GNUNET_asprintf (&sql, - "BEGIN TRANSACTION;" - "CREATE %1$s TABLE IF NOT EXISTS contracts (" - "contract_id INT8 PRIMARY KEY," - "amount INT8 NOT NULL," - "amount_fraction INT4 NOT NULL," - "amount_currency VARCHAR(" TALER_CURRENCY_LEN_STR ") NOT NULL," - "description TEXT NOT NULL," - "nounce INT8 NOT NULL," - "expiry INT8 NOT NULL," - "product INT8 NOT NULL);" - "CREATE %1$s TABLE IF NOT EXISTS checkouts (" - "coin_pub BYTEA PRIMARY KEY," - "contract_id INT8 REFERENCES contracts(contract_id)," - "amount INT4 NOT NULL," - "amount_fraction INT4 NOT NULL," - "coin_sig BYTEA NOT NULL);", - tmp_str); - ret = GNUNET_POSTGRES_exec (conn, sql); - (void) GNUNET_POSTGRES_exec (conn, - (GNUNET_OK == ret) ? "COMMIT;" : "ROLLBACK;"); - GNUNET_free (sql); - if (GNUNET_OK != ret) - return ret; - - while (NULL != (res = PQgetResult (conn))) - { - status = PQresultStatus (res); - PQclear (res); - } - - EXITIF (NULL == (res = PQprepare - (conn, - "contract_create", - "INSERT INTO contracts" - "(contract_id, amount, amount_fraction, amount_currency," - "description, nounce, expiry, product) VALUES" - "($1, $2, $3, $4, $5, $6, $7, $8)", - 8, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - EXITIF (NULL == (res = PQprepare - (conn, - "get_contract_product", - "SELECT (" - "product" - ") FROM contracts " - "WHERE (" - "contract_id=$1" - ")", - 1, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); - PQclear (res); - - EXITIF (NULL == (res = PQprepare - (conn, - "checkout_create", - "INSERT INTO checkouts (" - "coin_pub," - "contract_id," - "amount," - "amount_fraction," - "coin_sig" - ") VALUES (" - "$1, $2, $3, $4, $5" - ")", - 5, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); - PQclear (res); - - EXITIF (NULL == (res = PQprepare - (conn, - "get_checkout_product", - "SELECT (" - "product" - ") FROM contracts " - "WHERE " - "contract_id IN (" - "SELECT (contract_id) FROM checkouts " - "WHERE coin_pub=$1" - ")", - 1, NULL))); - EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus (res))); - PQclear (res); - - return GNUNET_OK; - - EXITIF_exit: - if (NULL != res) - { - PQSQL_strerror (GNUNET_ERROR_TYPE_ERROR, "PQprepare", res); - PQclear (res); - } - return GNUNET_SYSERR; -} - - -/** - * Inserts a contract record into the database and if successfull returns the - * serial number of the inserted row. - * - * @param conn the database connection - * @param expiry the time when the contract will expire - * @param amount the taler amount corresponding to the contract - * @param c_id contract's id - * @param desc descripition of the contract - * @param nounce a random 64-bit nounce - * @param product description to identify a product - * @return GNUNET_OK on success, GNUNET_SYSERR upon error - */ - -uint32_t -MERCHANT_DB_contract_create (PGconn *conn, - const struct GNUNET_TIME_Absolute *expiry, - const struct TALER_Amount *amount, - uint64_t c_id, - const char *desc, - uint64_t nounce, - uint64_t product) -{ - PGresult *res; - #if 0 - uint64_t expiry_ms_nbo; - uint64_t value_nbo; - uint32_t fraction_nbo; - uint64_t nounce_nbo; - #endif - ExecStatusType status; - - #if 0 - /* - NOTE: the conversion to nl(l) happens *inside* the query param helpers; since - the policy imposes this format for storing values. */ - value_nbo = GNUNET_htonll (amount->value); - fraction_nbo = GNUNET_htonll (amount->fraction); - nounce_nbo = GNUNET_htonll (nounce); - expiry_ms_nbo = GNUNET_htonll (expiry.abs_value_us); - product = GNUNET_htonll (product); - #endif - - /* ported. To be tested/compiled */ - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_uint64 (&c_id), - TALER_PQ_query_param_amount (amount), - /* a *string* is being put in the following statement, - though the API talks about a *blob*. Will this be liked by - the DB ? */ - // the following inserts a string as a blob. Will Taler provide a param-from-string helper? - TALER_PQ_query_param_fixed_size (desc, strlen(desc)), - TALER_PQ_query_param_uint64 (&nounce), - TALER_PQ_query_param_absolute_time (expiry), - TALER_PQ_query_param_uint64 (&product), - TALER_PQ_query_param_end - }; - - /* NOTE: the statement is prepared by MERCHANT_DB_initialize function */ - res = TALER_PQ_exec_prepared (conn, "contract_create", params); - status = PQresultStatus (res); - EXITIF (PGRES_COMMAND_OK != status); - PQclear (res); - return GNUNET_OK; - - EXITIF_exit: - PQclear (res); - return GNUNET_SYSERR; -} - -long long -MERCHANT_DB_get_contract_product (PGconn *conn, - uint64_t contract_id) -{ - PGresult *res; - uint64_t product; - ExecStatusType status; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_uint64 (&contract_id), - TALER_PQ_query_param_end - }; - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_uint64 ("product", &product), - TALER_PQ_result_spec_end - }; - - contract_id = GNUNET_htonll (contract_id); - res = TALER_PQ_exec_prepared (conn, "get_contract_product", params); - status = PQresultStatus (res); - EXITIF (PGRES_TUPLES_OK != status); - EXITIF (1 != PQntuples (res)); - EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); - PQclear (res); - return GNUNET_ntohll ((uint64_t) product); - - EXITIF_exit: - PQclear (res); - return -1; -} - -unsigned int -MERCHANT_DB_checkout_create (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub, - uint64_t transaction_id, - struct TALER_Amount *amount, - struct GNUNET_CRYPTO_rsa_Signature *coin_sig) -{ - PGresult *res; - ExecStatusType status; - uint32_t value_nbo; - uint32_t fraction_nbo; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_rsa_public_key (coin_pub), - TALER_PQ_query_param_uint64 (&transaction_id), - TALER_PQ_query_param_uint32 (&value_nbo), - TALER_PQ_query_param_uint32 (&fraction_nbo), - TALER_PQ_query_param_rsa_signature (coin_sig), - TALER_PQ_query_param_end - }; - - transaction_id = GNUNET_htonll (transaction_id); - value_nbo = htonl (amount->value); - fraction_nbo = htonl (amount->fraction); - res = TALER_PQ_exec_prepared (conn, "checkout_create", params); - status = PQresultStatus (res); - EXITIF (PGRES_COMMAND_OK != status); - PQclear (res); - return GNUNET_OK; - - EXITIF_exit: - PQclear (res); - return GNUNET_SYSERR; -} - - -long long -MERCHANT_DB_get_checkout_product (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub) -{ - PGresult *res; - ExecStatusType status; - uint64_t product; - struct TALER_PQ_QueryParam params[] = { - TALER_PQ_query_param_rsa_public_key (coin_pub), - TALER_PQ_query_param_end - }; - struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_uint64 ("product", &product), - TALER_PQ_result_spec_end - }; - - product = -1; - res = TALER_PQ_exec_prepared (conn, "get_checkout_product", params); - status = PQresultStatus (res); - EXITIF (PGRES_TUPLES_OK != status); - if (0 == PQntuples (res)) - { - TALER_LOG_DEBUG ("Checkout not found for given coin"); - goto EXITIF_exit; - } - EXITIF (1 != PQntuples (res)); - EXITIF (GNUNET_YES != TALER_PQ_extract_result (res, rs, 0)); - PQclear (res); - return GNUNET_ntohll ((uint64_t) product); - - EXITIF_exit: - PQclear (res); - return -1; -} -/* end of merchant-db.c */ diff --git a/src/backend/melted/merchant_db.h b/src/backend/melted/merchant_db.h deleted file mode 100644 index a723b229..00000000 --- a/src/backend/melted/merchant_db.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file merchant/merchant_db.h - * @brief database helper functions used by the merchant - * @author Sree Harsha Totakura - */ - -#ifndef MERCHANT_DB_H -#define MERCHANT_DB_H - -#include -#include - -/** - * Connect to postgresql database - * - * @param cfg the configuration handle - * @return connection to the postgresql database; NULL upon error - */ -PGconn * -MERCHANT_DB_connect (const struct GNUNET_CONFIGURATION_Handle *cfg); - - -/** - * Disconnect from the database - * - * @param conn database handle to close - */ -void -MERCHANT_DB_disconnect (PGconn *conn); - - -/** - * Initialize merchant tables - * - * @param conn the connection handle to postgres db. - * @param tmp GNUNET_YES if the tables are to be made temporary i.e. their - * contents are dropped when the @a conn is closed - * @return GNUNET_OK upon success; GNUNET_SYSERR upon failure - */ -int -MERCHANT_DB_initialize (PGconn *conn, int tmp); - - -/** - * Inserts a contract record into the database and if successfull returns the - * serial number of the inserted row. - * - * @param conn the database connection - * @param expiry the time when the contract will expire - * @param amount the taler amount corresponding to the contract - * @param c_id this contract's identification number - * @param desc descripition of the contract - * @param nounce a random 64-bit nounce - * @param product description to identify a product - * @return GNUNET_OK on success, GNUNET_SYSERR upon error - */ - -uint32_t -MERCHANT_DB_contract_create (PGconn *conn, - const struct GNUNET_TIME_Absolute *expiry, - const struct TALER_Amount *amount, - uint64_t c_id, - const char *desc, - uint64_t nounce, - uint64_t product); - -long long -MERCHANT_DB_get_contract_product (PGconn *conn, - uint64_t contract_id); - -unsigned int -MERCHANT_DB_checkout_create (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub, - uint64_t transaction_id, - struct TALER_Amount *amount, - struct GNUNET_CRYPTO_rsa_Signature *coin_sig); - - -long long -MERCHANT_DB_get_checkout_product (PGconn *conn, - struct GNUNET_CRYPTO_rsa_PublicKey *coin_pub); - -#endif /* MERCHANT_DB_H */ - -/* end of merchant-db.h */ diff --git a/src/backend/melted/myconf.sh b/src/backend/melted/myconf.sh deleted file mode 100644 index 0b7d2594..00000000 --- a/src/backend/melted/myconf.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -./configure CFLAGS='-I/usr/include/postgresql' diff --git a/src/backend/melted/taler-merchant-httpd.c b/src/backend/melted/taler-merchant-httpd.c deleted file mode 100644 index 46379809..00000000 --- a/src/backend/melted/taler-merchant-httpd.c +++ /dev/null @@ -1,821 +0,0 @@ -/* - This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** -* @file merchant/backend/taler-merchant-httpd.c -* @brief HTTP serving layer mainly intended to communicate with the frontend -* @author Marcello Stanisci -*/ - -#include "platform.h" -#include -#include -#include -#include -#include -#include "taler-mint-httpd_parsing.h" -#include "taler-mint-httpd_mhd.h" -#include "taler-mint-httpd_admin.h" -#include "taler-mint-httpd_deposit.h" -#include "taler-mint-httpd_withdraw.h" -#include "taler-mint-httpd_refresh.h" -#include "taler-mint-httpd_keystate.h" -#include "taler-mint-httpd_responses.h" -#include "merchant.h" -#include "merchant_db.h" - -extern struct MERCHANT_WIREFORMAT_Sepa * -TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg); - -/** - * Shorthand for exit jumps. - */ -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - -/** - * Macro to round microseconds to seconds in GNUNET_TIME_* structs. - */ -#define ROUND_TO_SECS(name,us_field) name.us_field -= name.us_field % (1000 * 1000) - -/** - * Our hostname - */ -static char *hostname; - -/** - * The port we are running on - */ -static long long unsigned port; - -/** - * Merchant's private key - */ -struct GNUNET_CRYPTO_EddsaPrivateKey *privkey; - -/** - * The MHD Daemon - */ -static struct MHD_Daemon *mhd; - -/** - * Connection handle to the our database - */ -PGconn *db_conn; - -/** - * Which currency is used by this mint? - * (verbatim copy from mint's code, just to make this - * merchant's source compile) - */ -char *TMH_mint_currency_string; - -/* As above */ -struct TALER_MINTDB_Plugin *TMH_plugin; - - -/** - * As above, though the merchant does need some form of - * configuration - */ -struct GNUNET_CONFIGURATION_Handle *cfg; - - -/** - * As above - */ -int TMH_test_mode; - - -/** - * As above - */ -char *TMH_mint_directory; - - -/** - * As above - */ -struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key; - -/** - * As above - */ -char *TMH_expected_wire_format; - -/** - * Shutdown task identifier - */ -static struct GNUNET_SCHEDULER_Task *shutdown_task; - -/** - * Our wireformat - */ -static struct MERCHANT_WIREFORMAT_Sepa *wire; - -/** - * Hash of the wireformat - */ -static struct GNUNET_HashCode h_wire; - -/** - * Should we do a dry run where temporary tables are used for storing the data. - */ -static int dry; - -/** - * Global return code - */ -static int result; - -GNUNET_NETWORK_STRUCT_BEGIN - -struct Contract -{ - /** - * The signature of the merchant for this contract - */ - struct GNUNET_CRYPTO_EddsaSignature sig; - - /** - * Purpose header for the signature over contract - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * The transaction identifier - */ - char m[13]; - - /** - * Expiry time - */ - struct GNUNET_TIME_AbsoluteNBO t; - - /** - * The invoice amount - */ - struct TALER_AmountNBO amount; - - /** - * The hash of the preferred wire format + nounce - */ - struct GNUNET_HashCode h_wire; - - /** - * The contract data - */ - char a[]; -}; - -GNUNET_NETWORK_STRUCT_END - -/** - * Mint context - */ -static struct TALER_MINT_Context *mctx; - -/** - * Context information of the mints we trust - */ -struct Mint -{ - /** - * Public key of this mint - */ - struct GNUNET_CRYPTO_EddsaPublicKey pubkey; - - /** - * Connection handle to this mint - */ - struct TALER_MINT_Handle *conn; -}; - -/** - * Hashmap to store the mint context information - */ -static struct GNUNET_CONTAINER_MultiPeerMap *mints_map; - -/** -* Return the given message to the other end of connection -* @msg (0-terminated) message to show -* @param connection a MHD connection -* @param resp where to store the response for the calling function -* @return HTTP status code reflecting the operation outcome -* -*/ - -static unsigned int -generate_message (struct MHD_Response **resp, const char *msg) // this parameter was preceded by a '_' in its original file. Why? -{ - - unsigned int ret; - - *resp = MHD_create_response_from_buffer (strlen (msg), (void *) msg, - MHD_RESPMEM_PERSISTENT); - ret = 200; - return ret; - - -} - -/** -* Generate the 'hello world' response -* @param connection a MHD connection -* @param resp where to store the response for the calling function -* @return HTTP status code reflecting the operation outcome -* -*/ - -static unsigned int -generate_hello (struct MHD_Response **resp) // this parameter was preceded by a '_' in its original file. Why? -{ - - const char *hello = "Hello customer\n"; - unsigned int ret; - - *resp = MHD_create_response_from_buffer (strlen (hello), (void *) hello, - MHD_RESPMEM_PERSISTENT); - ret = 200; - return ret; - - -} - -/** - * Callback for catching serious error conditions from MHD. - * - * @param cls user specified value - * @param file where the error occured - * @param line where the error occured - * @param reason error detail, may be NULL - */ -static void -mhd_panic_cb (void *cls, - const char *file, - unsigned int line, - const char *reason) -{ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "MHD panicked at %s:%u: %s", - file, line, reason); - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); -} - -/** -* Manage a non 200 HTTP status. I.e. it shows a 'failure' page to -* the client -* @param connection the channel thorugh which send the message -* @status the HTTP status to examine -* @return GNUNET_OK on successful message sending, GNUNET_SYSERR upon error -* -*/ - -static int -failure_resp (struct MHD_Connection *connection, unsigned int status) -{ - static char page_404[]="\ - \ -Resource not found
\ -

The resource you are looking for is not found.

\ -
"; - static char page_500[]="\ - Internal Server Error
\ -

The server experienced an internal error and hence cannot serve your \ -request

"; - struct MHD_Response *resp; - char *page; - size_t size; -#define PAGE(number) \ - do {page=page_ ## number; size=sizeof(page_ ## number)-1;} while(0) - - GNUNET_assert (400 <= status); - resp = NULL; - switch (status) - { - case 404: - PAGE(404); - break; - default: - status = 500; - case 500: - PAGE(500); - } -#undef PAGE - - EXITIF (NULL == (resp = MHD_create_response_from_buffer (size, - page, - MHD_RESPMEM_PERSISTENT))); - EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); - MHD_destroy_response (resp); - return GNUNET_OK; - - EXITIF_exit: - if (NULL != resp) - MHD_destroy_response (resp); - return GNUNET_SYSERR; -} - - -/** -* Generate the hash containing the information (= a nounce + merchant's IBAN) to -* redeem money from mint in a subsequent /deposit operation -* @param nounce the nounce -* @return the hash to be included in the contract's blob -* -*/ - -static struct GNUNET_HashCode -hash_wireformat (uint64_t nounce) -{ - struct GNUNET_HashContext *hc; - struct GNUNET_HashCode hash; - - hc = GNUNET_CRYPTO_hash_context_start (); - GNUNET_CRYPTO_hash_context_read (hc, wire->iban, strlen (wire->iban)); - GNUNET_CRYPTO_hash_context_read (hc, wire->name, strlen (wire->name)); - GNUNET_CRYPTO_hash_context_read (hc, wire->bic, strlen (wire->bic)); - nounce = GNUNET_htonll (nounce); - GNUNET_CRYPTO_hash_context_read (hc, &nounce, sizeof (nounce)); - GNUNET_CRYPTO_hash_context_finish (hc, &hash); - return hash; -} - - - -/* -* Make a binary blob representing a contract, store it into the DB, sign it -* and return a pointer to it. -* @param a 0-terminated string representing the description of this -* @param c_id contract id provided by the frontend -* purchase (it should contain a human readable description of the good -* in question) -* @param product some product numerical id. Its indended use is to link the -* good, or service being sold to some entry in the DB managed by the frontend -* @price the cost of this good or service -* @return pointer to the allocated contract (which has a field, 'sig', holding -* its own signature), NULL upon errors -*/ - -struct Contract * -generate_and_store_contract (const char *a, uint64_t c_id, uint64_t product, struct TALER_Amount *price) -{ - - struct Contract *contract; - struct GNUNET_TIME_Absolute expiry; - uint64_t nounce; - uint64_t contract_id_nbo; - - expiry = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), - GNUNET_TIME_UNIT_DAYS); - ROUND_TO_SECS (expiry, abs_value_us); - nounce = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); - EXITIF (GNUNET_SYSERR == MERCHANT_DB_contract_create (db_conn, - &expiry, - price, - c_id, - a, - nounce, - product)); - contract_id_nbo = GNUNET_htonll ((uint64_t) c_id); - contract = GNUNET_malloc (sizeof (struct Contract) + strlen (a) + 1); - contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); - contract->purpose.size = htonl (sizeof (struct Contract) - - offsetof (struct Contract, purpose) - + strlen (a) + 1); - GNUNET_STRINGS_data_to_string (&contract_id_nbo, sizeof (contract_id_nbo), - contract->m, sizeof (contract->m)); - contract->t = GNUNET_TIME_absolute_hton (expiry); - (void) strcpy (contract->a, a); - contract->h_wire = hash_wireformat (nounce); - TALER_amount_hton (&contract->amount, price); - GNUNET_CRYPTO_eddsa_sign (privkey, &contract->purpose, &contract->sig); - return contract; - - /* legacy from old merchant */ - EXITIF_exit: - if (NULL != contract) - { - GNUNET_free (contract); - } - return NULL; -} - - -/** - * A client has requested the given url using the given method - * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT, - * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc). The callback - * must call MHD callbacks to provide content to give back to the - * client and return an HTTP status code (i.e. #MHD_HTTP_OK, - * #MHD_HTTP_NOT_FOUND, etc.). - * - * @param cls argument given together with the function - * pointer when the handler was registered with MHD - * @param url the requested url - * @param method the HTTP method used (#MHD_HTTP_METHOD_GET, - * #MHD_HTTP_METHOD_PUT, etc.) - * @param version the HTTP version string (i.e. - * #MHD_HTTP_VERSION_1_1) - * @param upload_data the data being uploaded (excluding HEADERS, - * for a POST that fits into memory and that is encoded - * with a supported encoding, the POST data will NOT be - * given in upload_data and is instead available as - * part of #MHD_get_connection_values; very large POST - * data *will* be made available incrementally in - * @a upload_data) - * @param upload_data_size set initially to the size of the - * @a upload_data provided; the method must update this - * value to the number of bytes NOT processed; - * @param con_cls pointer that the callback can set to some - * address and that will be preserved by MHD for future - * calls for this request; since the access handler may - * be called many times (i.e., for a PUT/POST operation - * with plenty of upload data) this allows the application - * to easily associate some request-specific state. - * If necessary, this state can be cleaned up in the - * global #MHD_RequestCompletedCallback (which - * can be set with the #MHD_OPTION_NOTIFY_COMPLETED). - * Initially, `*con_cls` will be NULL. - * @return #MHD_YES if the connection was handled successfully, - * #MHD_NO if the socket must be closed due to a serios - * error while handling the request - */ - -static int -url_handler (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **connection_cls) -{ - - unsigned int status; - unsigned int no_destroy; - json_int_t prod_id; - json_int_t contract_id; - struct Contract *contract; - struct MHD_Response *resp; - struct TALER_Amount price; - struct GNUNET_CRYPTO_EddsaPublicKey pub; - json_t *json_price; - json_t *root; - json_t *contract_enc; - json_t *sig_enc; - json_t *eddsa_pub_enc; - json_t *response; - - int res; - const char *desc; - - #define URL_HELLO "/hello" - #define URL_CONTRACT "/contract" - no_destroy = 0; - resp = NULL; - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - if (0 == strncasecmp (url, URL_HELLO, sizeof (URL_HELLO))) - { - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - status = generate_hello (&resp); //TBD - else - GNUNET_break (0); - } - - // to be called by the frontend passing all the product's information - // which are relevant for the contract's generation - if (0 == strncasecmp (url, URL_CONTRACT, sizeof (URL_CONTRACT))) - { - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - status = generate_message (&resp, "Sorry, only POST is allowed"); - else - res = TMH_PARSE_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); - - if (GNUNET_SYSERR == res) - { - status = generate_message (&resp, "unable to parse JSON root"); - return MHD_NO; - - } - if ((GNUNET_NO == res) || (NULL == root)) - return MHD_YES; - - /* The frontend should supply a JSON in the follwoing format: - - { - - "desc" : string human-readable describing this deal, - "product" : uint64-like integer referring to the product in some - DB adminstered by the frontend, - "cid" : uint64-like integer, this contract's id - "price" : a 'struct TALER_Amount' in the Taler compliant JSON format } - - */ - - #if 0 - /*res = json_typeof (root); <- seg fault*/ - json_int_t id; - const char *desc_test; - const char *cur_test; - json_t *id_json; - json_t *desc_json; - json_t *cur_json; - id_json = json_object_get (root, "product"); - desc_json = json_object_get (root, "desc"); - id = json_integer_value (id_json); - desc_test = json_string_value (desc_json); - json_price = json_object_get (root, "price"); - json_typeof (json_price); - cur_json = json_object_get (json_price, "currency"); - cur_test = json_string_value (cur_json); - printf ("id is %" JSON_INTEGER_FORMAT "\n", id); - printf ("desc is %s\n", desc_test); - TALER_json_to_amount (json_price, &price); - printf ("cur_test is %s\n", price.currency); - json_error_t err; - if (res = json_unpack_ex (root, &err, JSON_VALIDATE_ONLY, "{s:s, s:I, s:o}", - "desc", - //&desc, - "product", - //&prod_id, - "price"//, - //json_price - )) - #else - if ((res = json_unpack (root, "{s:s, s:I, s:I, s:o}", - "desc", - &desc, - "product", - &prod_id, - "cid", - &contract_id, - "price", - &json_price - ))) - #endif - - - /* still not possible to return a taler-compliant error message - since this JSON format is not among the taler officials ones */ - { - status = generate_message (&resp, "unable to parse /contract JSON\n"); - } - else - { - - if (GNUNET_OK != TALER_json_to_amount (json_price, &price)) - {/* still not possible to return a taler-compliant error message - since this JSON format is not among the taler officials ones */ - status = generate_message (&resp, "unable to parse `price' field in /contract JSON");} - else - { - /* Let's generate this contract! */ - if (NULL == (contract = generate_and_store_contract (desc, contract_id, prod_id, &price))) - { - /* status equals 500, so the user will get a "Internal server error" */ - //failure_resp (connection, status); - status = generate_message (&resp, "unable to generate and store this contract"); - //return MHD_YES; - - } - else - { - json_decref (root); - json_decref (json_price); - - printf ("Good contract\n"); - /* the contract is good and stored in DB, produce now JSON to return. - As of now, the format is {"contract" : base32contract, - "sig" : contractSignature, - "eddsa_pub" : keyToCheckSignatureAgainst - } - - */ - - sig_enc = TALER_json_from_eddsa_sig (&contract->purpose, &contract->sig); - GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pub); - eddsa_pub_enc = TALER_json_from_data ((void *) &pub, sizeof (pub)); - /* cutting of the signature at the beginning */ - contract_enc = TALER_json_from_data (&contract->purpose, sizeof (*contract) - - offsetof (struct Contract, purpose) - + strlen (desc) +1); - response = json_pack ("{s:o, s:o, s:o}", "contract", contract_enc, "sig", sig_enc, - "eddsa_pub", eddsa_pub_enc); - TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK); - printf ("Got something?\n"); - return MHD_YES; - /* FRONTIER - CODE ABOVE STILL NOT TESTED */ - } - } - } - } - - if (NULL != resp) - { - EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); - if (!no_destroy) - MHD_destroy_response (resp); - } - else - EXITIF (GNUNET_OK != failure_resp (connection, status)); - return MHD_YES; - - EXITIF_exit: - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return MHD_NO; -} - -/** - * Shutdown task (magically invoked when the application is being - * quit) - * - * @param cls NULL - * @param tc scheduler task context - */ -static void -do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - - if (NULL != mhd) - { - MHD_stop_daemon (mhd); - mhd = NULL; - } - - if (NULL != db_conn) - { - MERCHANT_DB_disconnect (db_conn); - db_conn = NULL; - } - - -} - - - -/** - * Function called with information about who is auditing - * a particular mint and what key the mint is using. - * - * @param cls closure - * @param keys information about the various keys used - * by the mint - */ -void -keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) -{ - /* which kind of mint's keys a merchant should need? Sign - keys? It has already the mint's (master?) public key from - the conf file */ - return; - -} - - - -/** - * Main function that will be run by the scheduler. - * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param config configuration - */ -static void -run (void *cls, char *const *args, const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *config) -{ - - char *keyfile; - unsigned int nmints; - unsigned int cnt; - struct MERCHANT_MintInfo *mint_infos; - void *keys_mgmt_cls; - - mint_infos = NULL; - keyfile = NULL; - result = GNUNET_SYSERR; - shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, - &do_shutdown, NULL); - EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config, - &mint_infos))); - EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config, - "merchant", - "KEYFILE", - &keyfile)); - EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile))); - EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config))); - EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_NO)); - EXITIF (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_number (config, - "merchant", - "port", - &port)); - EXITIF (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (config, - "merchant", - "hostname", - &hostname)); - - EXITIF (NULL == (mctx = TALER_MINT_init ())); - EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES))); - - for (cnt = 0; cnt < nmints; cnt++) - { - struct Mint *mint; - - mint = GNUNET_new (struct Mint); - mint->pubkey = mint_infos[cnt].pubkey; - /* port this to the new API */ - mint->conn = TALER_MINT_connect (mctx, - mint_infos[cnt].hostname, - &keys_mgmt_cb, - keys_mgmt_cls); /*<- safe?segfault friendly?*/ - - /* NOTE: the keys mgmt callback should roughly do what the following lines do */ - EXITIF (NULL == mint->conn); - - EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put - (mints_map, - (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey, - mint, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); - } - - - mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, - port, - NULL, NULL, - &url_handler, NULL, - MHD_OPTION_END); - - - EXITIF (NULL == mhd); - /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that - mandatory ? */ - GNUNET_CRYPTO_hash (wire, sizeof (*wire), &h_wire); - result = GNUNET_OK; - - EXITIF_exit: - if (GNUNET_OK != result) - GNUNET_SCHEDULER_shutdown (); - GNUNET_free_non_null (keyfile); - if (GNUNET_OK != result) - GNUNET_SCHEDULER_shutdown (); - -} - -/** - * The main function of the serve tool - * - * @param argc number of arguments from the command line - * @param argv command line arguments - * @return 0 ok, 1 on error - */ -int -main (int argc, char *const *argv) -{ - - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - {'t', "temp", NULL, - gettext_noop ("Use temporary database tables"), GNUNET_NO, - &GNUNET_GETOPT_set_one, &dry}, - GNUNET_GETOPT_OPTION_END - }; - - - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, argv, - "taler-merchant-serve", - "Serve merchant's HTTP interface", - options, &run, NULL)) - return 3; - return (GNUNET_OK == result) ? 0 : 1; - - - -} diff --git a/src/backend/merchant.c b/src/backend/merchant.c index f124a030..58005394 100644 --- a/src/backend/merchant.c +++ b/src/backend/merchant.c @@ -42,15 +42,15 @@ */ int TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct MERCHANT_MintInfo **mints) + struct MERCHANT_Mint **mints) { char *mints_str; char *token_nf; /* do no free (nf) */ char *mint_section; char *mint_hostname; char *mint_pubkey_enc; - struct MERCHANT_MintInfo *r_mints; - struct MERCHANT_MintInfo mint; + struct MERCHANT_Mint *r_mints; + struct MERCHANT_Mint mint; unsigned long long mint_port; unsigned int cnt; int OK; diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 550de8c1..91a6fdf8 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -16,7 +16,8 @@ /** * @file merchant/backend/taler-merchant-httpd.c - * @brief HTTP serving layer mainly intended to communicate with the frontend + * @brief HTTP serving layer intended to perform crypto-work and + * communication with the mint * @author Marcello Stanisci */ @@ -63,7 +64,7 @@ static struct TALER_MINT_Context *mctx; /** * Mints' URL,port,key triples */ -struct MERCHANT_MintInfo *mint_infos; +struct MERCHANT_Mint *mints; /** * Shutdown task identifier @@ -71,9 +72,9 @@ struct MERCHANT_MintInfo *mint_infos; static struct GNUNET_SCHEDULER_Task *shutdown_task; /** - * Hashmap to store the mint context information + * Context "poller" identifier */ -static struct GNUNET_CONTAINER_MultiPeerMap *mints_map; +static struct GNUNET_SCHEDULER_Task *poller_task; /** * Our wireformat @@ -100,12 +101,6 @@ static int result; */ PGconn *db_conn; -/** - * Hashmap (with 'big entries') to make a mint's base URL - * to point to some mint-describing structure - */ -static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap; - /** * Context information of the mints we trust */ @@ -253,7 +248,7 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) /* which kind of mint's keys a merchant should need? Sign keys? It has already the mint's master key from the conf file */ - /* HOT UPDATE: the merchants needs the denomination keys! + /* HOT UPDATE: the merchants need the denomination keys! Because it wants to (firstly) verify the deposit confirmation sent by the mint, and the signed blob depends (among the other things) on the coin's deposit fee. That information @@ -261,10 +256,7 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) Again, the merchant needs it because it wants to verify that the wallet didn't exceede the limit imposed by the merchant on the total deposit fee for a purchase */ - - - return; - + printf ("Unregistering connection currently at %p\n", cls); } @@ -279,6 +271,12 @@ static void do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { + if (NULL != poller_task) + { + GNUNET_SCHEDULER_cancel (poller_task); + poller_task = NULL; + } + if (NULL != mhd) { MHD_stop_daemon (mhd); @@ -290,18 +288,77 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) MERCHANT_DB_disconnect (db_conn); db_conn = NULL; } - if (keyfile != NULL) + if (NULL != keyfile) GNUNET_free (privkey); } +/** + * Task that runs the context's event loop with the GNUnet scheduler. + * + * @param cls unused + * @param tc scheduler context (unused) + */ +static void +context_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + long timeout; + int max_fd; + fd_set read_fd_set; + fd_set write_fd_set; + fd_set except_fd_set; + struct GNUNET_NETWORK_FDSet *rs; + struct GNUNET_NETWORK_FDSet *ws; + struct GNUNET_TIME_Relative delay; + + poller_task = NULL; + TALER_MINT_perform (mctx); + max_fd = -1; + timeout = -1; + FD_ZERO (&read_fd_set); + FD_ZERO (&write_fd_set); + FD_ZERO (&except_fd_set); + TALER_MINT_get_select_info (mctx, + &read_fd_set, + &write_fd_set, + &except_fd_set, + &max_fd, + &timeout); + if (timeout >= 0) + delay = + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + timeout); + else + delay = GNUNET_TIME_UNIT_FOREVER_REL; + rs = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (rs, + &read_fd_set, + max_fd + 1); + ws = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (ws, + &write_fd_set, + max_fd + 1); + poller_task = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + delay, + rs, + ws, + &context_task, + cls); + GNUNET_NETWORK_fdset_destroy (rs); + GNUNET_NETWORK_fdset_destroy (ws); +} + + /** * Main function that will be run by the scheduler. * * @param cls closure * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfgfile name of the configuration file used (for saving, can be + * NULL!) * @param config configuration */ void @@ -313,21 +370,32 @@ run (void *cls, char *const *args, const char *cfgfile, void *keys_mgmt_cls; keys_mgmt_cls = NULL; - mint_infos = NULL; + mints = NULL; keyfile = NULL; result = GNUNET_SYSERR; - shutdown_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, - &do_shutdown, NULL); - EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config, - &mint_infos))); - EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (config, - "merchant", - "KEYFILE", - &keyfile)); - EXITIF (NULL == (privkey = GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile))); - EXITIF (NULL == (db_conn = MERCHANT_DB_connect (config))); - EXITIF (GNUNET_OK != MERCHANT_DB_initialize (db_conn, GNUNET_YES)); + shutdown_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &do_shutdown, + NULL); + EXITIF (GNUNET_SYSERR == + (nmints = + TALER_MERCHANT_parse_mints (config, + &mints))); + EXITIF (NULL == + (wire = + TALER_MERCHANT_parse_wireformat_sepa (config))); + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (config, + "merchant", + "KEYFILE", + &keyfile)); + EXITIF (NULL == + (privkey = + GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile))); + EXITIF (NULL == + (db_conn = MERCHANT_DB_connect (config))); + EXITIF (GNUNET_OK != + MERCHANT_DB_initialize (db_conn, GNUNET_YES)); EXITIF (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (config, "merchant", @@ -338,48 +406,17 @@ run (void *cls, char *const *args, const char *cfgfile, "merchant", "hostname", &hostname)); - EXITIF (NULL == (mctx = TALER_MINT_init ())); - /* Still not used */ - EXITIF (NULL == (mints_map = GNUNET_CONTAINER_multipeermap_create (nmints, GNUNET_YES))); - /* Used when the wallet points out which mint it want to deal with. - That indication is made through the mint's base URL, which will be - the hash-key for this table */ - EXITIF (NULL == (mints_hashmap = GNUNET_CONTAINER_multihashmap_create (nmints, GNUNET_NO))); - for (cnt = 0; cnt < nmints; cnt++) { - struct Mint *mint; - struct GNUNET_HashCode mint_key; - - mint = GNUNET_new (struct Mint); - mint->pubkey = mint_infos[cnt].pubkey; - /* port this to the new API */ - mint->conn = TALER_MINT_connect (mctx, - mint_infos[cnt].hostname, - &keys_mgmt_cb, - keys_mgmt_cls); /*<- safe?segfault friendly?*/ - - /* NOTE: the keys mgmt callback should roughly do what the following lines do */ - EXITIF (NULL == mint->conn); - - EXITIF (GNUNET_SYSERR == GNUNET_CONTAINER_multipeermap_put - (mints_map, - (struct GNUNET_PeerIdentity *) /* to retrieve now from cb's args -> */&mint->pubkey, - mint, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); - - /* 1 create hash key - 2 create big entry - 3 put - */ - GNUNET_CRYPTO_hash (mint_infos[cnt].hostname, - strlen (mint_infos[cnt].hostname), - &mint_key); - GNUNET_CONTAINER_multihashmap_put (mints_hashmap, - &mint_key, - &mint_infos[cnt], - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + mints[cnt].conn = TALER_MINT_connect (mctx, + mints[cnt].hostname, + &keys_mgmt_cb, + &mints[cnt]); + printf ("trying to, %p\n", &mints[cnt]); + EXITIF (NULL == mints[cnt].conn); + poller_task = + GNUNET_SCHEDULER_add_now (&context_task, mctx); } mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, @@ -389,9 +426,6 @@ run (void *cls, char *const *args, const char *cfgfile, MHD_OPTION_END); EXITIF (NULL == mhd); - - /* WARNING: a 'poll_mhd ()' call is here in the original merchant. Is that - mandatory ? */ result = GNUNET_OK; EXITIF_exit: diff --git a/src/include/merchant.h b/src/include/merchant.h index dcd4388e..e207b4e3 100644 --- a/src/include/merchant.h +++ b/src/include/merchant.h @@ -25,6 +25,7 @@ #include #include +#include /** * Macro to round microseconds to seconds in GNUNET_TIME_* structs. @@ -42,7 +43,8 @@ /** * A mint */ -struct MERCHANT_MintInfo { +struct MERCHANT_Mint +{ /** * Hostname */ @@ -58,50 +60,11 @@ struct MERCHANT_MintInfo { */ uint16_t port; - /* According to the mint's location, some of the following - fields may be omitted. In case of numbers, they will be set - to zero, otherwise SET TO nuLL */ - - /** - * The Country where the mint operates from - */ - char *country; - - /** - * The city where the mint operates from - */ - char *city; - - /** - * The State (within a Country) where the mint - * operates from - */ - char *state; - /** - * The region where the mint operates from + * A connection to this mint */ - char *region; + struct TALER_MINT_Handle *conn; - /** - * The province where the mint operates from - */ - char *province; - - /** - * The ZIP code where the mint operates from - */ - uint16_t zip_code; - - /** - * The street's name where the mint operates from - */ - char *street; - - /** - * The street number where the mint operates from - */ - uint16_t street_no; }; @@ -116,7 +79,7 @@ struct MERCHANT_MintInfo { */ int TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct MERCHANT_MintInfo **mints); + struct MERCHANT_Mint **mints); GNUNET_NETWORK_STRUCT_BEGIN diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index e73ff6ef..30b1e04b 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -9,7 +9,7 @@ bin_PROGRAMS = \ test_contract_SOURCES = \ test_contract.c \ - merchant.c \ + ../backend/merchant.c \ ../backend-lib/merchant_db.c ../backend-lib/merchant_db.h test_contract_LDADD = \ diff --git a/src/tests/merchant.c b/src/tests/merchant.c deleted file mode 100644 index f124a030..00000000 --- a/src/tests/merchant.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - This file is part of TALER - (C) 2014 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ - -/** - * @file merchant/merchant.c - * @brief Common utility functions for merchant - * @author Sree Harsha Totakura - */ - -#include "platform.h" -#include -#include "merchant.h" - - -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - - -/** - * Parses mints from the configuration. - * - * @param cfg the configuration - * @param mints the array of mints upon successful parsing. Will be NULL upon - * error. - * @return the number of mints in the above array; GNUNET_SYSERR upon error in - * parsing. - */ -int -TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct MERCHANT_MintInfo **mints) -{ - char *mints_str; - char *token_nf; /* do no free (nf) */ - char *mint_section; - char *mint_hostname; - char *mint_pubkey_enc; - struct MERCHANT_MintInfo *r_mints; - struct MERCHANT_MintInfo mint; - unsigned long long mint_port; - unsigned int cnt; - int OK; - - OK = 0; - mints_str = NULL; - token_nf = NULL; - mint_section = NULL; - mint_hostname = NULL; - mint_pubkey_enc = NULL; - r_mints = NULL; - cnt = 0; - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "merchant", - "TRUSTED_MINTS", - &mints_str)); - for (token_nf = strtok (mints_str, " "); - NULL != token_nf; - token_nf = strtok (NULL, " ")) - { - GNUNET_assert (0 < GNUNET_asprintf (&mint_section, - "mint-%s", token_nf)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "HOSTNAME", - &mint_hostname)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - mint_section, - "PORT", - &mint_port)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "PUBKEY", - &mint_pubkey_enc)); - EXITIF (GNUNET_OK != - GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc, - strlen (mint_pubkey_enc), - &mint.pubkey)); - mint.hostname = mint_hostname; - mint.port = (uint16_t) mint_port; - GNUNET_array_append (r_mints, cnt, mint); - mint_hostname = NULL; - GNUNET_free (mint_pubkey_enc); - mint_pubkey_enc = NULL; - GNUNET_free (mint_section); - mint_section = NULL; - } - OK = 1; - - EXITIF_exit: - GNUNET_free_non_null (mints_str); - GNUNET_free_non_null (mint_section); - GNUNET_free_non_null (mint_hostname); - GNUNET_free_non_null (mint_pubkey_enc); - if (!OK) - { - GNUNET_free_non_null (r_mints); - return GNUNET_SYSERR; - } - - *mints = r_mints; - return cnt; -} - - -/** - * Parse the SEPA information from the configuration. If any of the required - * fileds is missing return NULL. - * - * @param cfg the configuration - * @return Sepa details as a structure; NULL upon error - */ -struct MERCHANT_WIREFORMAT_Sepa * -TALER_MERCHANT_parse_wireformat_sepa (const struct GNUNET_CONFIGURATION_Handle *cfg) -{ - struct MERCHANT_WIREFORMAT_Sepa *wf; - - wf = GNUNET_new (struct MERCHANT_WIREFORMAT_Sepa); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "wire-sepa", - "IBAN", - &wf->iban)); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "wire-sepa", - "NAME", - &wf->name)); - EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, - "wire-sepa", - "BIC", - &wf->bic)); - return wf; - - EXITIF_exit: - GNUNET_free_non_null (wf->iban); - GNUNET_free_non_null (wf->name); - GNUNET_free_non_null (wf->bic); - GNUNET_free (wf); - return NULL; - -} - - -/** - * Destroy and free resouces occupied by the wireformat structure - * - * @param wf the wireformat structure - */ -void -TALER_MERCHANT_destroy_wireformat_sepa (struct MERCHANT_WIREFORMAT_Sepa *wf) -{ - GNUNET_free_non_null (wf->iban); - GNUNET_free_non_null (wf->name); - GNUNET_free_non_null (wf->bic); - GNUNET_free (wf); -} - -/* end of merchant.c */ -- cgit v1.2.3 From 9cec564ddc9bec9ea34ed7b0ca308800651cabd6 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 28 Oct 2015 17:40:30 +0100 Subject: Adding main behaviour to contract mgmt, plus importing additional features from the mint code base --- src/backend-lib/merchant_api_contract.c | 16 +- src/backend-lib/taler_merchant_contract_lib.h | 6 +- src/backend/merchant.c | 21 - src/backend/merchant.conf | 12 +- src/backend/taler-merchant-httpd.c | 92 ++- src/backend/taler-merchant-httpd_contract.c | 103 +++- src/backend/taler-mint-httpd_parsing.c | 783 ++++++++++++++++++++++++++ src/frontend/generate_taler_contract.php | 1 + src/include/merchant.h | 11 +- 9 files changed, 963 insertions(+), 82 deletions(-) diff --git a/src/backend-lib/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c index 42e1e064..239e29fb 100644 --- a/src/backend-lib/merchant_api_contract.c +++ b/src/backend-lib/merchant_api_contract.c @@ -32,32 +32,28 @@ * Take the global wire details and return a JSON containing them, * compliantly with the Taler's API. * @param wire the merchant's wire details - * @param nounce the nounce for hashing the wire details with + * @param salt the nounce for hashing the wire details with * @param edate when the beneficiary wants this transfer to take place * @return JSON representation of the wire details, NULL upon errors */ json_t * MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, - uint64_t nounce, - const struct GNUNET_TIME_Absolute edate) + uint64_t salt) { json_t *root; - json_t *j_edate; - json_t *j_nounce; + json_t *j_salt; - j_nounce = json_integer (nounce); - j_edate = TALER_json_from_abs (edate); + j_nounce = json_integer (salt); - if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:o, s:I}", + if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:I}", "type", "SEPA", "IBAN", wire->iban, "name", wire->name, "bic", wire->bic, - "edate", j_edate, - "r", json_integer_value (j_nounce)))) + "r", json_integer_value (j_salt)))) return NULL; return root; diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h index ca799ff2..1d2d0eba 100644 --- a/src/backend-lib/taler_merchant_contract_lib.h +++ b/src/backend-lib/taler_merchant_contract_lib.h @@ -20,15 +20,13 @@ struct Contract * Take the global wire details and return a JSON containing them, * compliantly with the Taler's API. * @param wire the merchant's wire details - * @param nounce the nounce for hashing the wire details with - * @param edate when the beneficiary wants this transfer to take place + * @param salt the nounce for hashing the wire details with * @return JSON representation of the wire details, NULL upon errors */ json_t * MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, - uint64_t nounce, - struct GNUNET_TIME_Absolute edate); + uint64_t salt); /** * Take from the frontend the (partly) generated contract and fill diff --git a/src/backend/merchant.c b/src/backend/merchant.c index 58005394..4fa66f16 100644 --- a/src/backend/merchant.c +++ b/src/backend/merchant.c @@ -48,10 +48,8 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, char *token_nf; /* do no free (nf) */ char *mint_section; char *mint_hostname; - char *mint_pubkey_enc; struct MERCHANT_Mint *r_mints; struct MERCHANT_Mint mint; - unsigned long long mint_port; unsigned int cnt; int OK; @@ -60,7 +58,6 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, token_nf = NULL; mint_section = NULL; mint_hostname = NULL; - mint_pubkey_enc = NULL; r_mints = NULL; cnt = 0; EXITIF (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, @@ -78,26 +75,9 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, mint_section, "HOSTNAME", &mint_hostname)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - mint_section, - "PORT", - &mint_port)); - EXITIF (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - mint_section, - "PUBKEY", - &mint_pubkey_enc)); - EXITIF (GNUNET_OK != - GNUNET_CRYPTO_eddsa_public_key_from_string (mint_pubkey_enc, - strlen (mint_pubkey_enc), - &mint.pubkey)); mint.hostname = mint_hostname; - mint.port = (uint16_t) mint_port; GNUNET_array_append (r_mints, cnt, mint); mint_hostname = NULL; - GNUNET_free (mint_pubkey_enc); - mint_pubkey_enc = NULL; GNUNET_free (mint_section); mint_section = NULL; } @@ -107,7 +87,6 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_free_non_null (mints_str); GNUNET_free_non_null (mint_section); GNUNET_free_non_null (mint_hostname); - GNUNET_free_non_null (mint_pubkey_enc); if (!OK) { GNUNET_free_non_null (r_mints); diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf index 851a6062..f24611a9 100644 --- a/src/backend/merchant.conf +++ b/src/backend/merchant.conf @@ -6,15 +6,9 @@ KEYFILE = merchant.priv [mint-taler] HOSTNAME = demo.taler.net -PORT = 80 -PUBKEY = Q1WVGRGC1F4W7RYC6M23AEGFEXQEHQ730K3GG0B67VPHQSRR75H0 -COUNTRY = France -CITY = Rennes -REGION = Bretagne -PROVINCE = Ile-et-Vilaine -ZIP_CODE = 35510 -STREET = Avenue du General Leclerc -STREET_NUMBER = 21 + +[mint-localmint] +HOSTNAME = localmint [merchant-db] CONFIG = postgres:///talerdemo diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 91a6fdf8..8da6ce21 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -61,6 +61,11 @@ char *keyfile; */ static struct TALER_MINT_Context *mctx; +/** + * To make 'TMH_PARSE_navigate_json ()' compile + */ +char *TMH_mint_currency_string; + /** * Mints' URL,port,key triples */ @@ -79,7 +84,12 @@ static struct GNUNET_SCHEDULER_Task *poller_task; /** * Our wireformat */ -static struct MERCHANT_WIREFORMAT_Sepa *wire; +struct MERCHANT_WIREFORMAT_Sepa *wire; + +/** + * Salt used to hash the wire object + */ +long long salt; /** * The number of accepted mints @@ -101,22 +111,6 @@ static int result; */ PGconn *db_conn; -/** - * Context information of the mints we trust - */ -struct Mint -{ - /** - * Public key of this mint - */ - struct GNUNET_CRYPTO_EddsaPublicKey pubkey; - - /** - * Connection handle to this mint - */ - struct TALER_MINT_Handle *conn; -}; - /** * The MHD Daemon */ @@ -238,16 +232,20 @@ url_handler (void *cls, * Function called with information about who is auditing * a particular mint and what key the mint is using. * - * @param cls closure + * @param cls closure, will be 'struct MERCHANT_Mint' so that + * when this function gets called, it will change the flag 'pending' + * to 'false'. Note: 'keys' is automatically saved inside the mint's + * handle, which is contained inside 'struct MERCHANT_Mint', when + * this callback is called. Thus, once 'pending' turns 'false', + * it is safe to call 'TALER_MINT_get_keys()' on the mint's handle, + * in order to get the "good" keys. + * * @param keys information about the various keys used * by the mint */ static void keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) { - /* which kind of mint's keys a merchant should need? Sign - keys? It has already the mint's master key from the conf file */ - /* HOT UPDATE: the merchants need the denomination keys! Because it wants to (firstly) verify the deposit confirmation sent by the mint, and the signed blob depends (among the @@ -256,7 +254,8 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) Again, the merchant needs it because it wants to verify that the wallet didn't exceede the limit imposed by the merchant on the total deposit fee for a purchase */ - printf ("Unregistering connection currently at %p\n", cls); + + ((struct MERCHANT_Mint *) cls)->pending = 0; } @@ -270,7 +269,14 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) static void do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { + unsigned int cnt; + for (cnt = 0; cnt < nmints; cnt++) + { + if (NULL != mints[cnt].conn) + TALER_MINT_disconnect (mints[cnt].conn); + + } if (NULL != poller_task) { GNUNET_SCHEDULER_cancel (poller_task); @@ -290,8 +296,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } if (NULL != keyfile) GNUNET_free (privkey); - - } /** @@ -351,6 +355,31 @@ context_task (void *cls, GNUNET_NETWORK_fdset_destroy (ws); } +/** + * Function called whenever MHD is done with a request. If the + * request was a POST, we may have stored a `struct Buffer *` in the + * @a con_cls that might still need to be cleaned up. Call the + * respective function to free the memory. + * + * @param cls client-defined closure + * @param connection connection handle + * @param con_cls value as set by the last call to + * the #MHD_AccessHandlerCallback + * @param toe reason for request termination + * @see #MHD_OPTION_NOTIFY_COMPLETED + * @ingroup request + */ +static void +handle_mhd_completion_callback (void *cls, + struct MHD_Connection *connection, + void **con_cls, + enum MHD_RequestTerminationCode toe) +{ + if (NULL == *con_cls) + return; + TMH_PARSE_post_cleanup_callback (*con_cls); + *con_cls = NULL; +} /** * Main function that will be run by the scheduler. @@ -367,9 +396,7 @@ run (void *cls, char *const *args, const char *cfgfile, { unsigned int cnt; - void *keys_mgmt_cls; - keys_mgmt_cls = NULL; mints = NULL; keyfile = NULL; result = GNUNET_SYSERR; @@ -406,14 +433,22 @@ run (void *cls, char *const *args, const char *cfgfile, "merchant", "hostname", &hostname)); + EXITIF (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (config, + "merchant", + "currency", + &TMH_mint_currency_string)); + + salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT64_MAX); EXITIF (NULL == (mctx = TALER_MINT_init ())); for (cnt = 0; cnt < nmints; cnt++) { + mints[cnt].pending = 1; mints[cnt].conn = TALER_MINT_connect (mctx, mints[cnt].hostname, &keys_mgmt_cb, &mints[cnt]); - printf ("trying to, %p\n", &mints[cnt]); EXITIF (NULL == mints[cnt].conn); poller_task = GNUNET_SCHEDULER_add_now (&context_task, mctx); @@ -423,6 +458,9 @@ run (void *cls, char *const *args, const char *cfgfile, port, NULL, NULL, &url_handler, NULL, + MHD_OPTION_NOTIFY_COMPLETED, + &handle_mhd_completion_callback, NULL, + MHD_OPTION_END); EXITIF (NULL == mhd); diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index 143746b6..04726839 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -35,6 +35,13 @@ #include "merchant.h" #include "taler_merchant_lib.h" +extern struct MERCHANT_Mint *mints; +extern struct GNUNET_CRYPTO_EddsaPrivateKey privkey; +extern const struct MERCHANT_WIREFORMAT_Sepa *wire; +extern unsigned int nmints; +extern PGconn *db_conn; +extern long long salt; + /** * Manage a contract request * @@ -53,18 +60,108 @@ MH_handler_contract (struct TMH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { + json_t *root; + json_t *trusted_mints; + json_t *mint; + json_t *j_wire; + const struct TALER_MINT_Keys *keys; + int res; + int cnt; + uint64_t trans_id; + struct TALER_Amount amount; + struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_TIME_Absolute refund; + struct GNUNET_TIME_Absolute expiry; + struct GNUNET_TIME_Absolute edate; + struct GNUNET_HashCode h_wire; + struct GNUNET_CRYPTO_EddsaPublicKey pubkey; + struct TMH_PARSE_FieldSpecification spec[] = + { + TMH_PARSE_member_time_abs ("timestamp", ×tamp), + TMH_PARSE_member_time_abs ("refund_deadline", &refund), + TMH_PARSE_member_time_abs ("expiry", &expiry), + TMH_PARSE_member_amount ("amount", &amount), + TMH_PARSE_member_uint64 ("trans_id", &trans_id), + TMH_PARSE_MEMBER_END + }; + + res = TMH_PARSE_post_json (connection, + connection_cls, + upload_data, + upload_data_size, + &root); + + if (GNUNET_SYSERR == res) + return MHD_NO; + /* the POST's body has to be further fetched */ + if ((GNUNET_NO == res) || (NULL == root)) + return MHD_YES; + /* 1. Generate preferred mint(s) array. The 'mint' JSON layout is as follows: { "url": "mint_base_url", "master_pub": "base32 mint's master public key" } - */ + trusted_mints = json_array (); + for (cnt = 0; cnt < nmints; cnt++) + { + if (!mints[cnt].pending) + { + keys = TALER_MINT_get_keys (mints[cnt].conn); + mint = json_pack ("{s:s, s:o}", + "url", mints[cnt].hostname, + "master_pub", + TALER_json_from_data + (&keys->master_pub.eddsa_pub, + sizeof (keys->master_pub.eddsa_pub))); + json_array_append_new (trusted_mints, mint); + } + } + + /* Return badly if no mints are trusted (or ready). WARNING: it + may be possible that a mint trusted by the wallet is good, but + still pending; that case must be handled with some "polling-style" + routine, simply ignored, or ended with an invitation to the wallet + to just retry later */ + if (!json_array_size (trusted_mints)) + return MHD_NO; + + json_object_set_new (root, "mints", trusted_mints); - /* To suppress compilation warning */ - return 0; + res = TMH_PARSE_json_data (connection, root, spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + return MHD_YES; /* failure */ + + /* (indicative) deadline which the merchant wants its wire tranfer + before */ + edate = GNUNET_TIME_absolute_add (timestamp, GNUNET_TIME_UNIT_WEEKS); + TALER_round_abs_time (&edate); + if (NULL == (j_wire = MERCHANT_get_wire_json (wire, + salt))) + return MHD_NO; + + /* hash wire objcet */ + if (GNUNET_SYSERR == + TALER_hash_json (j_wire, &h_wire)) + return MHD_NO; + + json_object_set_new (root, + "H_wire", + TALER_json_from_data (&h_wire, sizeof (h_wire))); + + GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey); + json_object_set_new (root, + "merchant_pub", + TALER_json_from_data (&pubkey, sizeof (pubkey))); + + /* store relevant values for this contract, in DB */ + //res = MERCHANT_handle_contract (); + } diff --git a/src/backend/taler-mint-httpd_parsing.c b/src/backend/taler-mint-httpd_parsing.c index 8d9e3f9f..a4dc43ab 100644 --- a/src/backend/taler-mint-httpd_parsing.c +++ b/src/backend/taler-mint-httpd_parsing.c @@ -27,6 +27,13 @@ #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_responses.h" +/* Although the following declaration isn't in any case useful + to a merchant's activity, it's needed here to make the function + 'TMH_PARSE_nagivate_json ()' compile fine; so its value will be + kept on some merchant's accepted currency. For multi currencies + merchants, that of course would require a patch */ + +extern char *TMH_mint_currency_string; /** * Initial size for POST request buffer. */ @@ -138,6 +145,107 @@ buffer_append (struct Buffer *buf, return GNUNET_OK; } +/** + * Function called whenever we are done with a request + * to clean up our state. + * + * @param con_cls value as it was left by + * #TMH_PARSE_post_json(), to be cleaned up + */ +void +TMH_PARSE_post_cleanup_callback (void *con_cls) +{ + struct Buffer *r = con_cls; + + if (NULL != r) + { + buffer_deinit (r); + GNUNET_free (r); + } +} + +/** + * Release all memory allocated for the variable-size fields in + * the parser specification. + * + * @param spec specification to free + * @param spec_len number of items in @a spec to look at + */ +static void +release_data (struct TMH_PARSE_FieldSpecification *spec, + unsigned int spec_len) +{ + unsigned int i; + + for (i=0; i < spec_len; i++) + { + switch (spec[i].command) + { + case TMH_PARSE_JNC_FIELD: + GNUNET_break (0); + return; + case TMH_PARSE_JNC_INDEX: + GNUNET_break (0); + return; + case TMH_PARSE_JNC_RET_DATA: + break; + case TMH_PARSE_JNC_RET_DATA_VAR: + if (NULL != spec[i].destination) + { + GNUNET_free (* (void**) spec[i].destination); + *(void**) spec[i].destination = NULL; + *spec[i].destination_size_out = 0; + } + break; + case TMH_PARSE_JNC_RET_TYPED_JSON: + { + json_t *json; + + json = *(json_t **) spec[i].destination; + if (NULL != json) + { + json_decref (json); + *(json_t**) spec[i].destination = NULL; + } + } + break; + case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY: + { + struct TALER_DenominationPublicKey *pk; + + pk = spec[i].destination; + if (NULL != pk->rsa_public_key) + { + GNUNET_CRYPTO_rsa_public_key_free (pk->rsa_public_key); + pk->rsa_public_key = NULL; + } + } + break; + case TMH_PARSE_JNC_RET_RSA_SIGNATURE: + { + struct TALER_DenominationSignature *sig; + + sig = spec[i].destination; + if (NULL != sig->rsa_signature) + { + GNUNET_CRYPTO_rsa_signature_free (sig->rsa_signature); + sig->rsa_signature = NULL; + } + } + break; + case TMH_PARSE_JNC_RET_AMOUNT: + memset (spec[i].destination, + 0, + sizeof (struct TALER_Amount)); + break; + case TMH_PARSE_JNC_RET_TIME_ABSOLUTE: + break; + case TMH_PARSE_JNC_RET_UINT64: + break; + } + } +} + /** * Process a POST request containing a JSON object. This function * realizes an MHD POST processor that will (incrementally) process @@ -239,4 +347,679 @@ TMH_PARSE_post_json (struct MHD_Connection *connection, return GNUNET_YES; } + +/** + * Generate line in parser specification for 64-bit integer + * given as an integer in JSON. + * + * @param field name of the field + * @param[out] u64 integer to initialize + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_uint64 (const char *field, + uint64_t *u64) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, (void *) u64, sizeof (uint64_t), NULL, TMH_PARSE_JNC_RET_UINT64, 0 }; + return ret; +} + + +/** + * Generate line in parser specification for JSON object value. + * + * @param field name of the field + * @param[out] jsonp address of pointer to JSON to initialize + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_object (const char *field, + json_t **jsonp) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_OBJECT }; + *jsonp = NULL; + return ret; +} + + +/** + * Generate line in parser specification for JSON array value. + * + * @param field name of the field + * @param[out] jsonp address of JSON pointer to initialize + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_array (const char *field, + json_t **jsonp) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_ARRAY }; + *jsonp = NULL; + return ret; +} + + +/** + * Generate line in parser specification for an absolute time. + * + * @param field name of the field + * @param[out] atime time to initialize + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_time_abs (const char *field, + struct GNUNET_TIME_Absolute *atime) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, atime, sizeof(struct GNUNET_TIME_Absolute), NULL, TMH_PARSE_JNC_RET_TIME_ABSOLUTE, 0 }; + return ret; +} + + +/** + * Generate line in parser specification for RSA public key. + * + * @param field name of the field + * @param[out] pk key to initialize + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_denomination_public_key (const char *field, + struct TALER_DenominationPublicKey *pk) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, pk, 0, NULL, TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, 0 }; + pk->rsa_public_key = NULL; + return ret; +} + + +/** + * Generate line in parser specification for RSA public key. + * + * @param field name of the field + * @param sig the signature to initialize + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_denomination_signature (const char *field, + struct TALER_DenominationSignature *sig) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, sig, 0, NULL, TMH_PARSE_JNC_RET_RSA_SIGNATURE, 0 }; + sig->rsa_signature = NULL; + return ret; +} + + +/** + * Generate line in parser specification for an amount. + * + * @param field name of the field + * @param amount a `struct TALER_Amount *` to initialize + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_amount (const char *field, + struct TALER_Amount *amount) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, amount, sizeof(struct TALER_Amount), NULL, TMH_PARSE_JNC_RET_AMOUNT, 0 }; + memset (amount, 0, sizeof (struct TALER_Amount)); + return ret; +} + + +/** + * Generate line in parser specification for variable-size value. + * + * @param field name of the field + * @param[out] ptr pointer to initialize + * @param[out] ptr_size size to initialize + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_variable (const char *field, + void **ptr, + size_t *ptr_size) +{ + struct TMH_PARSE_FieldSpecification ret = + { field, ptr, 0, ptr_size, TMH_PARSE_JNC_RET_DATA_VAR, 0 }; + *ptr = NULL; + return ret; +} + +/** + * Navigate through a JSON tree. + * + * Sends an error response if navigation is impossible (i.e. + * the JSON object is invalid) + * + * @param connection the connection to send an error response to + * @param root the JSON node to start the navigation at. + * @param ... navigation specification (see `enum TMH_PARSE_JsonNavigationCommand`) + * @return + * #GNUNET_YES if navigation was successful + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error (no response was generated, + * connection must be closed) + */ +int +TMH_PARSE_navigate_json (struct MHD_Connection *connection, + const json_t *root, + ...) +{ + va_list argp; + int ret; + json_t *path; /* what's our current path from 'root'? */ + + path = json_array (); + va_start (argp, root); + ret = 2; /* just not any of the valid return values */ + while (2 == ret) + { + enum TMH_PARSE_JsonNavigationCommand command + = va_arg (argp, + enum TMH_PARSE_JsonNavigationCommand); + + switch (command) + { + case TMH_PARSE_JNC_FIELD: + { + const char *fname = va_arg(argp, const char *); + + json_array_append_new (path, + json_string (fname)); + root = json_object_get (root, + fname); + if (NULL == root) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s, s:O}", + "error", "missing field in JSON", + "field", fname, + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + break; + + case TMH_PARSE_JNC_INDEX: + { + int fnum = va_arg(argp, int); + + json_array_append_new (path, + json_integer (fnum)); + root = json_array_get (root, + fnum); + if (NULL == root) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "missing index in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + break; + + case TMH_PARSE_JNC_RET_DATA: + { + void *where = va_arg (argp, void *); + size_t len = va_arg (argp, size_t); + const char *str; + int res; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "string expected", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + where, len); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "malformed binary data in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + ret = GNUNET_OK; + } + break; + + case TMH_PARSE_JNC_RET_DATA_VAR: + { + void **where = va_arg (argp, void **); + size_t *len = va_arg (argp, size_t *); + const char *str; + int res; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_internal_error (connection, + "json_string_value() failed")) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + *len = (strlen (str) * 5) / 8; + if (NULL != where) + { + *where = GNUNET_malloc (*len); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + *where, + *len); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + GNUNET_free (*where); + *where = NULL; + *len = 0; + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "malformed binary data in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + ret = GNUNET_OK; + } + break; + + case TMH_PARSE_JNC_RET_TYPED_JSON: + { + int typ = va_arg (argp, int); + const json_t **r_json = va_arg (argp, const json_t **); + + if ( (NULL == root) || + ( (-1 != typ) && + (json_typeof (root) != typ)) ) + { + GNUNET_break_op (0); + *r_json = NULL; + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:i, s:i, s:O}", + "error", "wrong JSON field type", + "type_expected", typ, + "type_actual", json_typeof (root), + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + *r_json = root; + json_incref ((json_t *) root); + ret = GNUNET_OK; + } + break; + + case TMH_PARSE_JNC_RET_UINT64: + { + uint64_t *r_u64 = va_arg (argp, uint64_t *); + + if (json_typeof (root) != JSON_INTEGER) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s, s:i, s:O}", + "error", "wrong JSON field type", + "type_expected", "integer", + "type_actual", json_typeof (root), + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + *r_u64 = (uint64_t) json_integer_value (root); + ret = GNUNET_OK; + } + break; + + case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY: + { + struct TALER_DenominationPublicKey *where; + size_t len; + const char *str; + int res; + void *buf; + + where = va_arg (argp, + struct TALER_DenominationPublicKey *); + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "string expected", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + len = (strlen (str) * 5) / 8; + buf = GNUNET_malloc (len); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + buf, + len); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + GNUNET_free (buf); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "malformed binary data in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + where->rsa_public_key = GNUNET_CRYPTO_rsa_public_key_decode (buf, + len); + GNUNET_free (buf); + if (NULL == where->rsa_public_key) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "malformed RSA public key in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + ret = GNUNET_OK; + break; + } + + case TMH_PARSE_JNC_RET_RSA_SIGNATURE: + { + struct TALER_DenominationSignature *where; + size_t len; + const char *str; + int res; + void *buf; + + where = va_arg (argp, + struct TALER_DenominationSignature *); + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "string expected", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + len = (strlen (str) * 5) / 8; + buf = GNUNET_malloc (len); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + buf, + len); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + GNUNET_free (buf); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "malformed binary data in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + where->rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (buf, + len); + GNUNET_free (buf); + if (NULL == where->rsa_signature) + { + GNUNET_break_op (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "malformed RSA signature in JSON", + "path", path)) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + ret = GNUNET_OK; + break; + } + + case TMH_PARSE_JNC_RET_AMOUNT: + { + struct TALER_Amount *where = va_arg (argp, void *); + + if (GNUNET_OK != + TALER_json_to_amount ((json_t *) root, + where)) + { + GNUNET_break_op (0); + ret = (MHD_YES != + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O}", + "error", "Bad format", + "path", path)) + ? GNUNET_SYSERR : GNUNET_NO; + break; + } + if (0 != strcmp (where->currency, + TMH_mint_currency_string)) + { + GNUNET_break_op (0); + ret = (MHD_YES != + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:O, s:s}", + "error", "Currency not supported", + "path", path, + "currency", where->currency)) + ? GNUNET_SYSERR : GNUNET_NO; + memset (where, 0, sizeof (struct TALER_Amount)); + break; + } + ret = GNUNET_OK; + break; + } + + case TMH_PARSE_JNC_RET_TIME_ABSOLUTE: + { + struct GNUNET_TIME_Absolute *where = va_arg (argp, void *); + + if (GNUNET_OK != + TALER_json_to_abs ((json_t *) root, + where)) + { + GNUNET_break_op (0); + ret = (MHD_YES != + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s, s:O}", + "error", "Bad format", + "hint", "expected absolute time", + "path", path)) + ? GNUNET_SYSERR : GNUNET_NO; + break; + } + ret = GNUNET_OK; + break; + } + + default: + GNUNET_break (0); + ret = (MHD_YES == + TMH_RESPONSE_reply_internal_error (connection, + "unhandled value in switch")) + ? GNUNET_NO : GNUNET_SYSERR; + break; + } + } + va_end (argp); + json_decref (path); + return ret; +} + + + +/** + * Parse JSON object into components based on the given field + * specification. + * + * @param connection the connection to send an error response to + * @param root the JSON node to start the navigation at. + * @param spec field specification for the parser + * @return + * #GNUNET_YES if navigation was successful (caller is responsible + * for freeing allocated variable-size data using + * #TMH_PARSE_release_data() when done) + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error + */ +int +TMH_PARSE_json_data (struct MHD_Connection *connection, + const json_t *root, + struct TMH_PARSE_FieldSpecification *spec) +{ + unsigned int i; + int ret; + + ret = GNUNET_YES; + for (i=0; NULL != spec[i].field_name; i++) + { + if (GNUNET_YES != ret) + break; + switch (spec[i].command) + { + case TMH_PARSE_JNC_FIELD: + GNUNET_break (0); + return GNUNET_SYSERR; + case TMH_PARSE_JNC_INDEX: + GNUNET_break (0); + return GNUNET_SYSERR; + case TMH_PARSE_JNC_RET_DATA: + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_DATA, + spec[i].destination, + spec[i].destination_size_in); + break; + case TMH_PARSE_JNC_RET_DATA_VAR: + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_DATA_VAR, + (void **) spec[i].destination, + spec[i].destination_size_out); + break; + case TMH_PARSE_JNC_RET_TYPED_JSON: + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_TYPED_JSON, + spec[i].type, + spec[i].destination); + break; + case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY: + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, + spec[i].destination); + break; + case TMH_PARSE_JNC_RET_RSA_SIGNATURE: + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_RSA_SIGNATURE, + spec[i].destination); + break; + case TMH_PARSE_JNC_RET_AMOUNT: + GNUNET_assert (sizeof (struct TALER_Amount) == + spec[i].destination_size_in); + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_AMOUNT, + spec[i].destination); + break; + case TMH_PARSE_JNC_RET_TIME_ABSOLUTE: + GNUNET_assert (sizeof (struct GNUNET_TIME_Absolute) == + spec[i].destination_size_in); + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_TIME_ABSOLUTE, + spec[i].destination); + break; + case TMH_PARSE_JNC_RET_UINT64: + GNUNET_assert (sizeof (uint64_t) == + spec[i].destination_size_in); + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_UINT64, + spec[i].destination); + break; + } + } + if (GNUNET_YES != ret) + release_data (spec, + i - 1); + return ret; +} + + /* end of taler-mint-httpd_parsing.c */ diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index bcfeaeb9..52db0d22 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -95,6 +95,7 @@ $json = json_encode (array ('amount' => array ('value' => $value, 'delivery_date' => "Some Date Format", 'delivery_location' => 'LNAME1')), 'timestamp' => "Date(" . $now->getTimestamp() . ")", + 'expiry' => "Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")", 'refund_deadline' => "Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")", 'merchant' => array ('address' => 'LNAME2', 'name' => 'test merchant', diff --git a/src/include/merchant.h b/src/include/merchant.h index e207b4e3..1368c575 100644 --- a/src/include/merchant.h +++ b/src/include/merchant.h @@ -51,14 +51,10 @@ struct MERCHANT_Mint char *hostname; /** - * The public key of the mint + * Flag which indicates whether some HTTP transfer between + * this merchant and the mint is still ongoing */ - struct GNUNET_CRYPTO_EddsaPublicKey pubkey; - - /** - * The port where the mint's service is running - */ - uint16_t port; + int pending; /** * A connection to this mint @@ -67,7 +63,6 @@ struct MERCHANT_Mint }; - /** * Parses mints from the configuration. * -- cgit v1.2.3 From 872e69d8606fbcfbde93989031d832defc5aae8c Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 28 Oct 2015 22:25:37 +0100 Subject: minor fix --- src/backend-lib/merchant_api_contract.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend-lib/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c index 239e29fb..da55ce5e 100644 --- a/src/backend-lib/merchant_api_contract.c +++ b/src/backend-lib/merchant_api_contract.c @@ -46,7 +46,7 @@ MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, json_t *root; json_t *j_salt; - j_nounce = json_integer (salt); + j_salt = json_integer (salt); if (NULL == (root = json_pack ("{s:s, s:s, s:s, s:s, s:I}", "type", "SEPA", -- cgit v1.2.3 From 6d18f1bfba939c2446c86c1152c274029bd5d63c Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 28 Oct 2015 22:47:25 +0100 Subject: minor change --- src/backend/taler-merchant-httpd_contract.c | 1 + src/frontend/generate_taler_contract.php | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index 04726839..65ec323b 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -163,5 +163,6 @@ MH_handler_contract (struct TMH_RequestHandler *rh, /* store relevant values for this contract, in DB */ //res = MERCHANT_handle_contract (); + return TMH_RESPONSE_reply_json (connection, root, MHD_HTTP_OK); } diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index 52db0d22..1b9648bb 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -24,7 +24,8 @@ 3. forward the response with the contract from the backend to to the wallet */ -$cli_debug = false; +$cli_debug = true; +$backend_test = true; // 1) recover the session information session_start(); @@ -124,7 +125,7 @@ $json = json_encode (array ('amount' => array ('value' => $value, 'province' => 'Test Province', 'ZIP code' => 4908))), JSON_PRETTY_PRINT); -if ($cli_debug) +if ($cli_debug && !$backend_test) { echo $json . "\n"; exit; -- cgit v1.2.3 From bfcb32339fbed0513090b0f546b04c90c11ea474 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 11:33:03 +0100 Subject: - Adding cli debugging feature to contract generation (i.e. it is possible to GET the frontend with 'curl' in order to invoke the backend to generate a contract. See 'generate_taler_contract.php' to get instructions on how to do it) - Removing unneeded functions from the 'contract' shared library (which presusmably will be entirely removed) - Some minor fixes --- src/backend-lib/merchant_api_contract.c | 79 --------------------------- src/backend-lib/taler_merchant_contract_lib.h | 52 ------------------ src/backend/taler-merchant-httpd.c | 10 ++-- src/backend/taler-merchant-httpd_contract.c | 67 +++++++---------------- src/frontend/generate_taler_contract.php | 23 +++++++- src/include/merchant.h | 20 +++++++ 6 files changed, 65 insertions(+), 186 deletions(-) diff --git a/src/backend-lib/merchant_api_contract.c b/src/backend-lib/merchant_api_contract.c index da55ce5e..20b69cd3 100644 --- a/src/backend-lib/merchant_api_contract.c +++ b/src/backend-lib/merchant_api_contract.c @@ -58,82 +58,3 @@ MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, return root; } - - - -/** -* Take from the frontend the (partly) generated contract and fill -* the missing values in it; for example, the SEPA details. -* Moreover, it stores the contract in the DB. -* @param j_contract parsed contract, originated by the frontend. It will be -* hold the new values. -* @param db_conn the handle to the local DB -* @param contract where to store the (subset of the) contract to be (still) signed -* @param timestamp contract's timestamp (shall be generated by the merchant) -* @param expiry the time when the contract will expire -* @param edate when the merchant wants to receive the wire transfer corresponding -* to this deal (this value is also a field inside the 'wire' JSON format) -* @param refund deadline until which the merchant can return the paid amount -* @param nounce the nounce used to hash the wire details -* @param a will be pointed to the (allocated) stringified 0-terminated contract -* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the -* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors. -*/ - -/** -* TODO: inspect reference counting and, accordingly, free those json_t*(s) -* still allocated */ - -uint32_t -MERCHANT_handle_contract (json_t *j_contract, - PGconn *db_conn, - struct Contract *contract, - struct GNUNET_TIME_Absolute timestamp, - struct GNUNET_TIME_Absolute expiry, - struct GNUNET_TIME_Absolute edate, - struct GNUNET_TIME_Absolute refund, - char **a, - uint64_t nounce) -{ - json_t *j_amount; - json_int_t j_product_id; - json_int_t j_trans_id; - char *contract_str; - - struct TALER_Amount amount; - - - - /* Extracting values useful for DB work. Only gettable from the JSON - since they are generated by the frontend */ - if (-1 == json_unpack (j_contract, "{s:o, s:I, s:{s:I}}", - "amount", &j_amount, - "trans_id", &j_trans_id, - "details", "product_id", - &j_product_id)) - { - printf ("no unpacking\n"); - return GNUNET_SYSERR; - } - - TALER_json_to_amount (j_amount, &amount); - contract_str = json_dumps (j_contract, JSON_COMPACT | JSON_PRESERVE_ORDER); - *a = contract_str; - GNUNET_CRYPTO_hash (contract_str, strlen (contract_str) + 1, - &contract->h_contract_details); - contract->purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); - contract->purpose.size = htonl (sizeof (struct Contract)); - - // DB mgmt - return MERCHANT_DB_contract_create (db_conn, - timestamp, - expiry, - edate, - refund, - &amount, - &contract->h_contract_details, - (uint64_t) j_trans_id, // safe? - contract_str, - nounce, - (uint64_t) j_product_id); -} diff --git a/src/backend-lib/taler_merchant_contract_lib.h b/src/backend-lib/taler_merchant_contract_lib.h index 1d2d0eba..3f95841f 100644 --- a/src/backend-lib/taler_merchant_contract_lib.h +++ b/src/backend-lib/taler_merchant_contract_lib.h @@ -1,21 +1,3 @@ -/** - * The contract sent by the merchant to the wallet - */ -struct Contract -{ - /** - * Purpose header for the signature over contract - */ - struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - - /** - * Hash of the JSON contract in UTF-8 including 0-termination, - * using JSON_COMPACT encoding with sorted fields. - */ - struct GNUNET_HashCode h_contract_details; - -}; - /** * Take the global wire details and return a JSON containing them, * compliantly with the Taler's API. @@ -27,37 +9,3 @@ struct Contract json_t * MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire, uint64_t salt); - -/** -* Take from the frontend the (partly) generated contract and fill -* the missing values in it; for example, the SEPA details. -* Moreover, it stores the contract in the DB. -* @param j_contract parsed contract, originated by the frontend. It will be -* hold the new values. -* @param db_conn the handle to the local DB -* @param contract where to store the (subset of the) contract to be (still) signed -* @param timestamp contract's timestamp (shall be generated by the merchant) -* @param expiry the time when the contract will expire -* @param edate when the merchant wants to receive the wire transfer corresponding -* to this deal (this value is also a field inside the 'wire' JSON format) -* @param refund deadline until which the merchant can return the paid amount -* @param nounce the nounce used to hash the wire details -* @param a will be pointed to the (allocated) stringified 0-terminated contract -* @return GNUNET_OK on success, GNUNET_NO if attempting to double insert the -* same contract, GNUNET_SYSERR in case of other (mostly DB related) errors. -*/ - -/** -* TODO: inspect reference counting and, accordingly, free those json_t*(s) -* still allocated */ - -uint32_t -MERCHANT_handle_contract (json_t *j_contract, - PGconn *db_conn, - struct Contract *contract, - struct GNUNET_TIME_Absolute timestamp, - struct GNUNET_TIME_Absolute expiry, - struct GNUNET_TIME_Absolute edate, - struct GNUNET_TIME_Absolute refund, - char **a, - uint64_t nounce); diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 8da6ce21..b7a914da 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -177,7 +177,7 @@ url_handler (void *cls, "Hello, Customer.\n", 0, &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - { "/contract", MHD_HTTP_METHOD_GET, "application/json", + { "/contract", MHD_HTTP_METHOD_POST, "application/json", NULL, 0, &MH_handler_contract, MHD_HTTP_OK }, @@ -396,7 +396,6 @@ run (void *cls, char *const *args, const char *cfgfile, { unsigned int cnt; - mints = NULL; keyfile = NULL; result = GNUNET_SYSERR; @@ -426,21 +425,22 @@ run (void *cls, char *const *args, const char *cfgfile, EXITIF (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_number (config, "merchant", - "port", + "PORT", &port)); EXITIF (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config, "merchant", - "hostname", + "HOSTNAME", &hostname)); EXITIF (GNUNET_SYSERR == GNUNET_CONFIGURATION_get_value_string (config, "merchant", - "currency", + "CURRENCY", &TMH_mint_currency_string)); salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); + EXITIF (NULL == (mctx = TALER_MINT_init ())); for (cnt = 0; cnt < nmints; cnt++) { diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index 65ec323b..e7278c7c 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -43,14 +43,16 @@ extern PGconn *db_conn; extern long long salt; /** - * Manage a contract request - * + * Manage a contract request. In practical terms, it adds the fields 'mints', + * 'merchant_pub', and 'H_wire' to the contract 'proposition' gotten from the + * frontend. Finally, it adds (outside of the contract) a signature of the + * (hashed stringification) of this contract and the hashed stringification + * of this contract to the final bundle sent back to the frontend. * @param rh context of the handler * @param connection the MHD connection to handle * @param[in,out] connection_cls the connection's closure (can be updated) * @param upload_data upload data * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * * @return MHD result code */ int @@ -67,44 +69,24 @@ MH_handler_contract (struct TMH_RequestHandler *rh, const struct TALER_MINT_Keys *keys; int res; int cnt; - uint64_t trans_id; - struct TALER_Amount amount; - struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_TIME_Absolute refund; - struct GNUNET_TIME_Absolute expiry; - struct GNUNET_TIME_Absolute edate; struct GNUNET_HashCode h_wire; struct GNUNET_CRYPTO_EddsaPublicKey pubkey; - struct TMH_PARSE_FieldSpecification spec[] = - { - TMH_PARSE_member_time_abs ("timestamp", ×tamp), - TMH_PARSE_member_time_abs ("refund_deadline", &refund), - TMH_PARSE_member_time_abs ("expiry", &expiry), - TMH_PARSE_member_amount ("amount", &amount), - TMH_PARSE_member_uint64 ("trans_id", &trans_id), - TMH_PARSE_MEMBER_END - }; - + struct MERCHANT_Contract contract; + char *contract_str; + res = TMH_PARSE_post_json (connection, connection_cls, upload_data, upload_data_size, &root); - if (GNUNET_SYSERR == res) return MHD_NO; /* the POST's body has to be further fetched */ if ((GNUNET_NO == res) || (NULL == root)) return MHD_YES; - /* 1. Generate preferred mint(s) array. + /* Generate preferred mint(s) array. */ - The 'mint' JSON layout is as follows: - - { "url": "mint_base_url", - "master_pub": "base32 mint's master public key" } - */ - trusted_mints = json_array (); for (cnt = 0; cnt < nmints; cnt++) { @@ -121,31 +103,22 @@ MH_handler_contract (struct TMH_RequestHandler *rh, } } - /* Return badly if no mints are trusted (or ready). WARNING: it - may be possible that a mint trusted by the wallet is good, but - still pending; that case must be handled with some "polling-style" - routine, simply ignored, or ended with an invitation to the wallet - to just retry later */ + /** + * Return badly if no mints are trusted (or no call to /keys has still + * returned the expected data). WARNING: it + * may be possible that a mint trusted by the wallet is good, but + * still pending; that case must be handled with some "polling-style" + * routine, simply ignored, or ended with an invitation to the wallet + * to just retry later + */ if (!json_array_size (trusted_mints)) return MHD_NO; json_object_set_new (root, "mints", trusted_mints); - res = TMH_PARSE_json_data (connection, root, spec); - - if (GNUNET_SYSERR == res) - return MHD_NO; /* hard failure */ - if (GNUNET_NO == res) - return MHD_YES; /* failure */ - - - /* (indicative) deadline which the merchant wants its wire tranfer - before */ - edate = GNUNET_TIME_absolute_add (timestamp, GNUNET_TIME_UNIT_WEEKS); - TALER_round_abs_time (&edate); if (NULL == (j_wire = MERCHANT_get_wire_json (wire, salt))) - return MHD_NO; + return MHD_NO; /* hash wire objcet */ if (GNUNET_SYSERR == @@ -161,8 +134,8 @@ MH_handler_contract (struct TMH_RequestHandler *rh, "merchant_pub", TALER_json_from_data (&pubkey, sizeof (pubkey))); - /* store relevant values for this contract, in DB */ - //res = MERCHANT_handle_contract (); + /* Sign */ + return TMH_RESPONSE_reply_json (connection, root, MHD_HTTP_OK); } diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index 1b9648bb..52e11940 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -23,10 +23,27 @@ 2. generate the JSON to forward to the backend 3. forward the response with the contract from the backend to to the wallet -*/ -$cli_debug = true; + + To test this feature from the command line, issue: + + - $ curl http://merchant_url/generate_taler_contract.php?cli_debug=yes + if the whole "journey" to the backend is begin tested + - $ curl http://merchant_url/generate_taler_contract.php?backend_test=no + if just the frontend job is being tested +*/ + +$cli_debug = false; $backend_test = true; +if ($_GET['cli_debug'] == 'yes') + $cli_debug = true; + +if ($_GET['backend_test'] == 'no') +{ + $cli_debug = true; + $backend_test = false; +} + // 1) recover the session information session_start(); if (!$cli_debug && ((! isset($_SESSION['receiver'])) || @@ -84,7 +101,7 @@ $json = json_encode (array ('amount' => array ('value' => $value, 'max_fee' => array ('value' => 3, 'fraction' => 01010, 'currency' => $currency), - 'trans_id' => $transaction_id, + 'transaction_id' => $transaction_id, 'products' => array ( array ('description' => $desc, 'quantity' => 1, diff --git a/src/include/merchant.h b/src/include/merchant.h index 1368c575..04dd253d 100644 --- a/src/include/merchant.h +++ b/src/include/merchant.h @@ -63,6 +63,26 @@ struct MERCHANT_Mint }; +/** + * The contract sent by the merchant to the wallet + */ +struct MERCHANT_Contract +{ + /** + * Purpose header for the signature over contract + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Hash of the JSON contract in UTF-8 including 0-termination, + * using JSON_COMPACT encoding with sorted fields. + */ + struct GNUNET_HashCode h_contract_details; + +}; + + + /** * Parses mints from the configuration. * -- cgit v1.2.3 From 4a435a29de050d17ef5de531cc2e61a366998422 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 16:17:37 +0100 Subject: completing /contract REST call --- configure.ac | 1 - src/Makefile.am | 2 +- src/backend/taler-merchant-httpd_contract.c | 19 +++++++++++++++++-- src/backend/taler-merchant-httpd_obsolete.c | 1 + src/include/merchant.h | 4 ++-- src/tests/test_contract.c | 4 ---- 6 files changed, 21 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 55d1e196..67d84f11 100644 --- a/configure.ac +++ b/configure.ac @@ -180,7 +180,6 @@ AC_CHECK_FUNCS([strdup]) AC_CONFIG_FILES([Makefile src/Makefile src/include/Makefile -src/tests/Makefile src/backend/Makefile src/backend-lib/Makefile]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 9a5b5646..665be7d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,2 +1,2 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include -SUBDIRS = include backend-lib backend tests +SUBDIRS = include backend-lib backend diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index e7278c7c..d28ad4ed 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,7 @@ MH_handler_contract (struct TMH_RequestHandler *rh, struct GNUNET_CRYPTO_EddsaPublicKey pubkey; struct MERCHANT_Contract contract; char *contract_str; + struct GNUNET_CRYPTO_EddsaSignature contract_sig; res = TMH_PARSE_post_json (connection, connection_cls, @@ -135,7 +137,20 @@ MH_handler_contract (struct TMH_RequestHandler *rh, TALER_json_from_data (&pubkey, sizeof (pubkey))); /* Sign */ - - return TMH_RESPONSE_reply_json (connection, root, MHD_HTTP_OK); + contract_str = json_dumps (root, JSON_COMPACT | JSON_SORT_KEYS); + GNUNET_CRYPTO_hash (contract_str, strlen (contract_str), &contract.h_contract); + contract.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); + contract.purpose.size = htonl (sizeof (contract)); + GNUNET_CRYPTO_eddsa_sign (&privkey, &contract.purpose, &contract_sig); + + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:o, s:o, s:o}", + "contract", root, + "sig", TALER_json_from_data + (&contract_sig, sizeof (contract_sig)), + "h_contract", TALER_json_from_data + (&contract.h_contract, + sizeof (contract.h_contract))); } diff --git a/src/backend/taler-merchant-httpd_obsolete.c b/src/backend/taler-merchant-httpd_obsolete.c index ddbda549..a96dad59 100644 --- a/src/backend/taler-merchant-httpd_obsolete.c +++ b/src/backend/taler-merchant-httpd_obsolete.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include "taler-mint-httpd_parsing.h" diff --git a/src/include/merchant.h b/src/include/merchant.h index 04dd253d..0efd4388 100644 --- a/src/include/merchant.h +++ b/src/include/merchant.h @@ -75,9 +75,9 @@ struct MERCHANT_Contract /** * Hash of the JSON contract in UTF-8 including 0-termination, - * using JSON_COMPACT encoding with sorted fields. + * using JSON_COMPACT | JSON_SORT_KEYS */ - struct GNUNET_HashCode h_contract_details; + struct GNUNET_HashCode h_contract; }; diff --git a/src/tests/test_contract.c b/src/tests/test_contract.c index d086449d..147ea4f3 100644 --- a/src/tests/test_contract.c +++ b/src/tests/test_contract.c @@ -101,11 +101,7 @@ run (void *cls, char *const *args, const char *cfgfile, struct TALER_Amount amount; int64_t t_id; int64_t p_id; - #ifdef OBSOLETE - struct ContractNBO contract; - #else struct Contract contract; - #endif struct GNUNET_TIME_Absolute edate; struct GNUNET_TIME_Absolute now; uint64_t nounce; -- cgit v1.2.3 From 4a41e898d281377d5b0b7ff822f2bfa14485b686 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 16:47:28 +0100 Subject: Adding /pay skeleton --- src/backend/taler-merchant-httpd.c | 14 +++++++- src/backend/taler-merchant-httpd_pay.c | 62 ++++++++++++++++++++++++++++++++ src/frontend/checkout.php | 2 +- src/frontend/generate_taler_contract.php | 2 +- src/tests/test_contract_README | 10 ++++++ 5 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 src/backend/taler-merchant-httpd_pay.c create mode 100644 src/tests/test_contract_README diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index b7a914da..4ec86b25 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -61,6 +61,12 @@ char *keyfile; */ static struct TALER_MINT_Context *mctx; +/** + * This value tells the mint by which date this merchant would like + * to receive the funds for a deposited payment + */ +struct GNUNET_TIME_Relative edate; + /** * To make 'TMH_PARSE_navigate_json ()' compile */ @@ -177,7 +183,7 @@ url_handler (void *cls, "Hello, Customer.\n", 0, &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - { "/contract", MHD_HTTP_METHOD_POST, "application/json", + { "/contract", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, &MH_handler_contract, MHD_HTTP_OK }, @@ -438,6 +444,12 @@ run (void *cls, char *const *args, const char *cfgfile, "CURRENCY", &TMH_mint_currency_string)); + EXITIF (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (config, + "merchant", + "EDATE", + &edate)); + salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c new file mode 100644 index 00000000..b03da899 --- /dev/null +++ b/src/backend/taler-merchant-httpd_pay.c @@ -0,0 +1,62 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file merchant/backend/taler-merchant-httpd.c + * @brief HTTP serving layer mainly intended to communicate with the frontend + * @author Marcello Stanisci + */ + +#include "platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "taler-mint-httpd.h" +#include "taler-mint-httpd_parsing.h" +#include "taler-mint-httpd_responses.h" +#include "merchant_db.h" +#include "merchant.h" +#include "taler_merchant_lib.h" + +extern struct MERCHANT_Mint *mints; +extern const struct MERCHANT_WIREFORMAT_Sepa *wire; +extern PGconn *db_conn; +extern long long salt; + +/** + * Accomplish this payment. + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +MH_handler_pay (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + json_t *root; + json_t *j_wire; +} diff --git a/src/frontend/checkout.php b/src/frontend/checkout.php index 72e78944..7acfd2f5 100644 --- a/src/frontend/checkout.php +++ b/src/frontend/checkout.php @@ -89,7 +89,7 @@ function handle_contract(json_contract) function taler_pay(form) { var contract_request = new XMLHttpRequest(); - contract_request.open("POST", "/generate_taler_contract.php", true); + contract_request.open("GET", "/generate_taler_contract.php", true); contract_request.onload = function (e) { if (contract_request.readyState == 4) diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index 52e11940..817aa0ed 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -151,7 +151,7 @@ if ($cli_debug && !$backend_test) // Craft the HTTP request, note that the backend // could be on an entirely different machine if // desired. -$req = new http\Client\Request ("POST", +$req = new http\Client\Request ("GET", "http://" . $_SERVER["SERVER_NAME"] . "/backend/contract", array ("Content-Type" => "application/json")); $req->getBody()->append ($json); diff --git a/src/tests/test_contract_README b/src/tests/test_contract_README new file mode 100644 index 00000000..a6a25e69 --- /dev/null +++ b/src/tests/test_contract_README @@ -0,0 +1,10 @@ +- In order to test the "/contract" facility of the merchant, + just issue + + - curl http://merchant-url/generate_taler_contract.php?cli_debug=yes + (this form tests the whole communication between frontend and backend) + + - curl http://merchant-url/generate_taler_contract.php?cli_debug=yes + (this form test only the contract proposition generation of the frontend, + so it doesn't further connect to the backend to get the proposition JSON + enhanced) -- cgit v1.2.3 From d277c659ee126c2d46a5d9d48f3f411672fe5f71 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 17:03:48 +0100 Subject: minor change --- src/frontend/pay.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/frontend/pay.php b/src/frontend/pay.php index c09f83a0..8178b6cd 100644 --- a/src/frontend/pay.php +++ b/src/frontend/pay.php @@ -23,21 +23,8 @@ */ - /* -// recover the session - session_start(); - if(!isset($_SESSION['contract'])){ -// http_response_code(404); - echo "Sorry.."; - } - else echo "Paid"; - - session_destroy(); - - -*/ ?> -Payment successful, thanks! + -- cgit v1.2.3 From a2789c90b6cda2bcdb26e98c3f371e5937e632bc Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 22:14:34 +0100 Subject: - moving pay URL indication *inside* the contract proposition - adding fronthand handler for receiving payments --- src/backend/taler-merchant-httpd.c | 2 +- src/frontend/checkout.php | 4 +--- src/frontend/generate_taler_contract.php | 9 +++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 4ec86b25..d6d81124 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -445,7 +445,7 @@ run (void *cls, char *const *args, const char *cfgfile, &TMH_mint_currency_string)); EXITIF (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (config, + GNUNET_CONFIGURATION_get_value_time (config, "merchant", "EDATE", &edate)); diff --git a/src/frontend/checkout.php b/src/frontend/checkout.php index 7acfd2f5..ab1c2c28 100644 --- a/src/frontend/checkout.php +++ b/src/frontend/checkout.php @@ -76,9 +76,7 @@ pass it to the extension */ function handle_contract(json_contract) { - var cEvent = new CustomEvent('taler-contract', - { detail: json_contract, - target: "/taler/pay"}); + var cEvent = new CustomEvent('taler-contract', { detail: json_contract }); document.body.dispatchEvent(cEvent); }; diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index 817aa0ed..090213a2 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -66,7 +66,7 @@ else { $receiver = "Test Receiver"; $amount = 5; - $currency = "Test"; + $currency = "KUDOS"; } @@ -112,9 +112,10 @@ $json = json_encode (array ('amount' => array ('value' => $value, 'taxes' => array (array ('teatax' => $teatax)), 'delivery_date' => "Some Date Format", 'delivery_location' => 'LNAME1')), - 'timestamp' => "Date(" . $now->getTimestamp() . ")", - 'expiry' => "Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")", - 'refund_deadline' => "Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")", + 'timestamp' => "/Date(" . $now->getTimestamp() . ")/", + 'pay_url' => "/taler/pay", + 'expiry' => "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/", + 'refund_deadline' => "/Date(" . $now->add(new DateInterval('P3M'))->getTimestamp() . ")/", 'merchant' => array ('address' => 'LNAME2', 'name' => 'test merchant', 'jurisdiction' => 'LNAME3'), -- cgit v1.2.3 From aafc4a5ab30da39132f0dda8862762bfa3e5e19d Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 22:19:24 +0100 Subject: adding deposit sample data for testing --- src/frontend/deposit.php | 26 ++++++++++++++++++++++++++ src/tests/deposit_permission.sample | 29 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/frontend/deposit.php create mode 100644 src/tests/deposit_permission.sample diff --git a/src/frontend/deposit.php b/src/frontend/deposit.php new file mode 100644 index 00000000..27cf9360 --- /dev/null +++ b/src/frontend/deposit.php @@ -0,0 +1,26 @@ + + +*/ + +$post_body = file_get_contents('php://input'); + +$post_obj = json_decode($post_body); + +//debug and get test-ish data for the backend +file_put_contents('/tmp/deposit_permission', + json_encode($post_obj, JSON_PRETTY_PRINT)); +?> diff --git a/src/tests/deposit_permission.sample b/src/tests/deposit_permission.sample new file mode 100644 index 00000000..3f27f123 --- /dev/null +++ b/src/tests/deposit_permission.sample @@ -0,0 +1,29 @@ +{ + "H_wire": "X3E6RM65VAWNS1H01SNJ80QBNJ97YDQD2Y6VXBH57WNGAXCF6HNP94T217WZN4MCFSVJF14BZHMNZ24JVGN52PRZF0S2EXGEKRS6DA0", + "merchant_pub": "SGRZA7R2FGDA5Z44FZ5ZN65TN5SCJB199VPNKET21BH4MZQPE7V0", + "mint": "localmint", + "coins": [ + { + "f": { + "value": 0, + "fraction": 100000, + "currency": "KUDOS" + }, + "coin_pub": "V7TPHXYX9P52YEX4NQB41FPJC6HC0PGEH72JHVP5SP9Y2J4SA8G0", + "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H9N71346D1R6MW46D228N334E1G60W32HJ374VKAC2660S3GGSJ691KEHA570V44CSP70RMCGHR8MV46GJ66MS44CT170R4CHHN752M8DHS68WKAD9G6WR3ACT66GRM6H216CSM6H9R88VKCH9Q8MWK8E226H2K6CT16CW4AHHG6N238HA26X2KGD1Q8GRMADJ5712KEEA470TKGC9S8MWKGGHP64W46E1N6N336C2365136CHM8RT3CCHH6MV34GSR60S3CCHR8S0K0HHN8RWKGGHK6MS4CH1K8MS38E1G88RMCDA384T44DHJ6534CD9M6N0M6GT58RTM6GA26N24CGHQ8N0MACJ68S2K4E9P74S48DHS6RR3JGA38N0K4E9P6X23ACA684WKAH1H8RSK4CH35452081918G2J2G0", + "coin_sig": 5326752 + }, + { + "f": { + "value": 0, + "fraction": 100000, + "currency": "KUDOS" + }, + "coin_pub": "5PZKEGB0G7QYEAZAE0WWEPNN7EEK91Y4S8XSQGQ14GXXK481C01G", + "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C1R6GR38H227524CCSS6X0K8GHS691K8G9J6RWK4H1G8S23AD1Q88W4CH2560T3EC226S1K6DA68N346C258CVKEC9M8H344HHP6RT44HJ56123ADHN8N1K0DHJ6933CD1M6X142CHM8RS4AD9M8GV30D1M6X14CE1P74TKAC9P8GWMAH9M750K0E9N8S34CCSR61136H9H6X0K0GT68RSM8CSH8N1K8HHJ8CSM4DT58H24AEA16CR3EH268CR30E1H8N1KGH9P8RS44HJ18RRK4CT46WSK4HHN8GSMADT38MTM2E1R8CR3EH1P6S2KGE1G8D342DHS88TKGCT66N0K8G9N752K6CHN70V3EH9H651M2H1M8N132E9J64S42DT668T3GG9H74S3JDJ68D13CH135452081918G2J2G0", + "coin_sig": 5326752 + } + ] +} \ No newline at end of file -- cgit v1.2.3 From 1a345ed83345c54631540f9f509129d048bdc33f Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 22:59:58 +0100 Subject: Adding HTTP capability to frontend /pay handler, plus fixing compilation errors relative to /pay handler (in backend) --- src/backend/Makefile.am | 5 ++- src/backend/taler-merchant-httpd.c | 12 ++++++- src/backend/taler-merchant-httpd_contract.c | 8 ++--- src/backend/taler-merchant-httpd_pay.c | 22 ++++++++++-- src/backend/taler-merchant-httpd_pay.h | 46 ++++++++++++++++++++++++ src/frontend/deposit.php | 56 ++++++++++++++++++++++++++--- src/frontend/generate_taler_contract.php | 2 +- 7 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 src/backend/taler-merchant-httpd_pay.h diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index f0c543ea..d5b283b8 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -13,7 +13,10 @@ taler_merchant_httpd_SOURCES = \ taler-mint-httpd.h \ taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ taler-merchant-httpd_contract.c \ - taler-merchant-httpd_contract.h + taler-merchant-httpd_contract.h \ + taler-merchant-httpd_pay.c \ + taler-merchant-httpd_pay.h + taler_merchant_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index d6d81124..3806115a 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -35,6 +35,7 @@ #include "taler_merchant_lib.h" #include "taler-mint-httpd_mhd.h" #include "taler-merchant-httpd_contract.h" +#include "taler-merchant-httpd_pay.h" /** * Our hostname @@ -183,7 +184,7 @@ url_handler (void *cls, "Hello, Customer.\n", 0, &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - { "/contract", MHD_HTTP_METHOD_GET, "application/json", + { "/contract", MHD_HTTP_METHOD_POST, "application/json", NULL, 0, &MH_handler_contract, MHD_HTTP_OK }, @@ -191,6 +192,15 @@ url_handler (void *cls, "Only POST is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + { "/pay", MHD_HTTP_METHOD_POST, "application/json", + NULL, 0, + &MH_handler_pay, MHD_HTTP_OK }, + + { "/pay", NULL, "text/plain", + "Only POST is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, + + {NULL, NULL, NULL, NULL, 0, 0 } }; diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index d28ad4ed..4d1584b3 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -77,10 +77,10 @@ MH_handler_contract (struct TMH_RequestHandler *rh, struct GNUNET_CRYPTO_EddsaSignature contract_sig; res = TMH_PARSE_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); + connection_cls, + upload_data, + upload_data_size, + &root); if (GNUNET_SYSERR == res) return MHD_NO; /* the POST's body has to be further fetched */ diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index b03da899..fb74cda8 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -45,9 +45,11 @@ extern long long salt; * Accomplish this payment. * @param rh context of the handler * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) + * @param[in,out] connection_cls the connection's closure + * (can be updated) * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @param[in,out] upload_data_size number of bytes (left) in @a + * upload_data * @return MHD result code */ int @@ -57,6 +59,20 @@ MH_handler_pay (struct TMH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { + json_t *root; - json_t *j_wire; + int res; + + res = TMH_PARSE_post_json (connection, + connection_cls, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + return MHD_NO; + /* the POST's body has to be further fetched */ + if ((GNUNET_NO == res) || (NULL == root)) + return MHD_YES; + + /* 1 Check if the total deposit fee is leq the limit */ } diff --git a/src/backend/taler-merchant-httpd_pay.h b/src/backend/taler-merchant-httpd_pay.h new file mode 100644 index 00000000..6a796e06 --- /dev/null +++ b/src/backend/taler-merchant-httpd_pay.h @@ -0,0 +1,46 @@ +/* + This file is part of TALER + (C) 2014 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file merchant/backend/taler-merchant-httpd_contract.h + * @brief headers for /pay handler + * @author Marcello Stanisci + */ + +#ifndef TALER_MINT_HTTPD_PAY_H +#define TALER_MINT_HTTPD_PAY_H +#include +#include "taler-mint-httpd.h" + +/** + * Manage a payment + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * + * @return MHD result code + */ +int +MH_handler_pay (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + +#endif diff --git a/src/frontend/deposit.php b/src/frontend/deposit.php index 27cf9360..aabc456e 100644 --- a/src/frontend/deposit.php +++ b/src/frontend/deposit.php @@ -16,11 +16,59 @@ */ +/* + This serving module adds the 'max_fee' field to the object which + sends to the backend, and optionally the field 'edate' (indicating + to the mint the tollerated deadline to receive funds for this payment) + NOTE: 'max_fee' must be consistent with the same value indicated within + the contract; thus, a "real" merchant must implement such a mapping + +*/ + $post_body = file_get_contents('php://input'); -$post_obj = json_decode($post_body); -//debug and get test-ish data for the backend -file_put_contents('/tmp/deposit_permission', - json_encode($post_obj, JSON_PRETTY_PRINT)); +$deposit_permission = json_decode ($post_body); +$max_fee = array ('max_fee' => array ('value' => 3, + 'fraction' => 01010, + 'currency' => $currency)); + + +$new_deposit_permission = array_merge ($deposit_permission, $max_fee); +// Craft the HTTP request, note that the backend +// could be on an entirely different machine if +// desired. +file_put_contents('/tmp/deposit_permission_maxfee.sample', json_encode($new_deposit_permission, JSON_PRETTY_PRINT)); +return; + +$req = new http\Client\Request ("GET", + "http://" . $_SERVER["SERVER_NAME"] . "/backend/pay", + array ("Content-Type" => "application/json")); +$req->getBody()->append ($json); + +// Execute the HTTP request +$client = new http\Client; +$client->enqueue($req)->send (); + +// Fetch the response +$resp = $client->getResponse (); +$status_code = $resp->getResponseCode (); + +// Our response code is the same we got from the backend: +http_response_code ($status_code); + +// Now generate our body +if ($status_code != 200) +{ + /* error: just forwarding to the wallet what + gotten from the backend (which is forwarding 'as is' + the error gotten from the mint) */ + echo $resp->body->toString (); + +} +else +{ + echo "Payment succedeed!\n"; +} + ?> diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index 090213a2..2010fefd 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -152,7 +152,7 @@ if ($cli_debug && !$backend_test) // Craft the HTTP request, note that the backend // could be on an entirely different machine if // desired. -$req = new http\Client\Request ("GET", +$req = new http\Client\Request ("POST", "http://" . $_SERVER["SERVER_NAME"] . "/backend/contract", array ("Content-Type" => "application/json")); $req->getBody()->append ($json); -- cgit v1.2.3 From 0380bbaab8fd98735f980df160faec5b32782b59 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Thu, 29 Oct 2015 23:13:37 +0100 Subject: adding edate to /pay handler in frontend --- src/frontend/deposit.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/frontend/deposit.php b/src/frontend/deposit.php index aabc456e..f2196cb9 100644 --- a/src/frontend/deposit.php +++ b/src/frontend/deposit.php @@ -28,17 +28,23 @@ $post_body = file_get_contents('php://input'); +$now = new DateTime('now'); +$edate = array ('edate' => + "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/"); + $deposit_permission = json_decode ($post_body); $max_fee = array ('max_fee' => array ('value' => 3, 'fraction' => 01010, 'currency' => $currency)); - $new_deposit_permission = array_merge ($deposit_permission, $max_fee); +$new_deposit_permission_edate = array_merge ($new_deposit_permission, $edate); + // Craft the HTTP request, note that the backend // could be on an entirely different machine if // desired. file_put_contents('/tmp/deposit_permission_maxfee.sample', json_encode($new_deposit_permission, JSON_PRETTY_PRINT)); + return; $req = new http\Client\Request ("GET", -- cgit v1.2.3 From 66bfaf5cd16bad25d87f65900b570d126bb6a5eb Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 30 Oct 2015 06:28:18 +0100 Subject: Adding new test samples. Fixing minor problems in the frontend "/pay" handler. --- src/frontend/deposit.php | 80 ---------------------------- src/frontend/generate_taler_contract.php | 1 - src/frontend/pay.php | 89 ++++++++++++++++++++++++++++---- src/tests/deposit_permission.sample | 23 +++++---- src/tests/test_contract_README | 2 +- 5 files changed, 93 insertions(+), 102 deletions(-) delete mode 100644 src/frontend/deposit.php diff --git a/src/frontend/deposit.php b/src/frontend/deposit.php deleted file mode 100644 index f2196cb9..00000000 --- a/src/frontend/deposit.php +++ /dev/null @@ -1,80 +0,0 @@ - - -*/ - -/* - This serving module adds the 'max_fee' field to the object which - sends to the backend, and optionally the field 'edate' (indicating - to the mint the tollerated deadline to receive funds for this payment) - NOTE: 'max_fee' must be consistent with the same value indicated within - the contract; thus, a "real" merchant must implement such a mapping - -*/ - -$post_body = file_get_contents('php://input'); - - -$now = new DateTime('now'); -$edate = array ('edate' => - "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/"); - -$deposit_permission = json_decode ($post_body); -$max_fee = array ('max_fee' => array ('value' => 3, - 'fraction' => 01010, - 'currency' => $currency)); - -$new_deposit_permission = array_merge ($deposit_permission, $max_fee); -$new_deposit_permission_edate = array_merge ($new_deposit_permission, $edate); - -// Craft the HTTP request, note that the backend -// could be on an entirely different machine if -// desired. -file_put_contents('/tmp/deposit_permission_maxfee.sample', json_encode($new_deposit_permission, JSON_PRETTY_PRINT)); - -return; - -$req = new http\Client\Request ("GET", - "http://" . $_SERVER["SERVER_NAME"] . "/backend/pay", - array ("Content-Type" => "application/json")); -$req->getBody()->append ($json); - -// Execute the HTTP request -$client = new http\Client; -$client->enqueue($req)->send (); - -// Fetch the response -$resp = $client->getResponse (); -$status_code = $resp->getResponseCode (); - -// Our response code is the same we got from the backend: -http_response_code ($status_code); - -// Now generate our body -if ($status_code != 200) -{ - /* error: just forwarding to the wallet what - gotten from the backend (which is forwarding 'as is' - the error gotten from the mint) */ - echo $resp->body->toString (); - -} -else -{ - echo "Payment succedeed!\n"; -} - -?> diff --git a/src/frontend/generate_taler_contract.php b/src/frontend/generate_taler_contract.php index 2010fefd..9849dc82 100644 --- a/src/frontend/generate_taler_contract.php +++ b/src/frontend/generate_taler_contract.php @@ -142,7 +142,6 @@ $json = json_encode (array ('amount' => array ('value' => $value, 'region' => 'Test Region', 'province' => 'Test Province', 'ZIP code' => 4908))), JSON_PRETTY_PRINT); - if ($cli_debug && !$backend_test) { echo $json . "\n"; diff --git a/src/frontend/pay.php b/src/frontend/pay.php index 8178b6cd..1c1428b2 100644 --- a/src/frontend/pay.php +++ b/src/frontend/pay.php @@ -1,13 +1,6 @@ - - - -Fullfillment page - - - +/* + This serving module adds the 'max_fee' field to the object which + sends to the backend, and optionally the field 'edate' (indicating + to the mint the tollerated deadline to receive funds for this payment) + NOTE: 'max_fee' must be consistent with the same value indicated within + the contract; thus, a "real" merchant must implement such a mapping + +*/ + +$cli_debug = false; +$backend_test = true; + +if ($_GET['cli_debug'] == 'yes') + $cli_debug = true; + +if ($_GET['backend_test'] == 'no') +{ + $cli_debug = true; + $backend_test = false; +} + +$post_body = file_get_contents('php://input'); + +$now = new DateTime('now'); +$edate = array ('edate' => + "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/"); + +$deposit_permission = json_decode ($post_body, true); - - +$max_fee = array ('max_fee' => array ('value' => 3, + 'fraction' => 8, + 'currency' => "KUDOS")); +$new_deposit_permission = array_merge ($deposit_permission, $max_fee); +$new_deposit_permission_edate = array_merge ($new_deposit_permission, $edate); + +/* Craft the HTTP request, note that the backend + could be on an entirely different machine if + desired. */ + +if ($cli_debug && !$backend_test) +{ + file_put_contents('/tmp/noedate', json_encode ($new_deposit_permission, JSON_PRETTY_PRINT)); + file_put_contents('/tmp/yesedate', json_encode ($new_deposit_permission_edate, JSON_PRETTY_PRINT)); + + exit; +} + +$req = new http\Client\Request ("POST", + "http://" . $_SERVER["SERVER_NAME"] . "/backend/pay", + array ("Content-Type" => "application/json")); +$req->getBody()->append ($new_deposit_permission); + +// Execute the HTTP request +$client = new http\Client; +$client->enqueue($req)->send (); + +// Fetch the response +$resp = $client->getResponse (); +$status_code = $resp->getResponseCode (); + +// Our response code is the same we got from the backend: +http_response_code ($status_code); + +// Now generate our body +if ($status_code != 200) +{ + /* error: just forwarding to the wallet what + gotten from the backend (which is forwarding 'as is' + the error gotten from the mint) */ + echo $resp->body->toString (); + +} +else +{ + echo "Payment succedeed!\n"; +} + +?> diff --git a/src/tests/deposit_permission.sample b/src/tests/deposit_permission.sample index 3f27f123..161a72d0 100644 --- a/src/tests/deposit_permission.sample +++ b/src/tests/deposit_permission.sample @@ -1,6 +1,6 @@ { - "H_wire": "X3E6RM65VAWNS1H01SNJ80QBNJ97YDQD2Y6VXBH57WNGAXCF6HNP94T217WZN4MCFSVJF14BZHMNZ24JVGN52PRZF0S2EXGEKRS6DA0", - "merchant_pub": "SGRZA7R2FGDA5Z44FZ5ZN65TN5SCJB199VPNKET21BH4MZQPE7V0", + "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR", + "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70", "mint": "localmint", "coins": [ { @@ -9,10 +9,10 @@ "fraction": 100000, "currency": "KUDOS" }, - "coin_pub": "V7TPHXYX9P52YEX4NQB41FPJC6HC0PGEH72JHVP5SP9Y2J4SA8G0", + "coin_pub": "AH86D5WV9G6RT0A3CHYJW598BNMH8848GAKYTEH1JX6X2BKZWTX0", "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", - "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H9N71346D1R6MW46D228N334E1G60W32HJ374VKAC2660S3GGSJ691KEHA570V44CSP70RMCGHR8MV46GJ66MS44CT170R4CHHN752M8DHS68WKAD9G6WR3ACT66GRM6H216CSM6H9R88VKCH9Q8MWK8E226H2K6CT16CW4AHHG6N238HA26X2KGD1Q8GRMADJ5712KEEA470TKGC9S8MWKGGHP64W46E1N6N336C2365136CHM8RT3CCHH6MV34GSR60S3CCHR8S0K0HHN8RWKGGHK6MS4CH1K8MS38E1G88RMCDA384T44DHJ6534CD9M6N0M6GT58RTM6GA26N24CGHQ8N0MACJ68S2K4E9P74S48DHS6RR3JGA38N0K4E9P6X23ACA684WKAH1H8RSK4CH35452081918G2J2G0", - "coin_sig": 5326752 + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C9J8D1MCHHJ84S34H1K8H1M6HHN60V3GH9S84R3GH9K6MVM6GSM70RKCDT188VM4CHM74RM8GSN6GSKEHHS6CT44H9Q8CWKCD1P68SK2G9S8GR34D1P60T4CCJ67523EDJ28S1KJD9N8H330CT58RS38CSM8H346GT58N13CGJ16GS34CHQ6GT42CSS6CTM4C9H8N1KCG9G74T36C1K8GT44E1N8RRK0D218GV46G9R6CT4CHA4890M6H1H8GV34E1Q8N244DHM8CRK2H1R8CV3ADA37113JDHR88W36E9R61242GT5752KEE9R84WK8H9G8GT34GT26S2MAE1P70RMAGSK74W30C1J60S32GSK64WKAC218MWM6GHM8S332DSN70SM2DHR6H146E228CTM8D135452081918G2J2G0", + "coin_sig": "MFTHC54GFYHA3CGKHC8SSDSTYX8YMEJDNQA7AEY5M7JBK3WPDQ9NCJ54ZSPQRZ4QCJC0CPREP0XRWH9JQ509ENSEXWKNNM5FEVRG238" }, { "f": { @@ -20,10 +20,15 @@ "fraction": 100000, "currency": "KUDOS" }, - "coin_pub": "5PZKEGB0G7QYEAZAE0WWEPNN7EEK91Y4S8XSQGQ14GXXK481C01G", + "coin_pub": "MQY067A8AGG90TSH11C8JNRW8P1R669JAAQX4V2HGFHGJ2WY4ZA0", "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", - "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C1R6GR38H227524CCSS6X0K8GHS691K8G9J6RWK4H1G8S23AD1Q88W4CH2560T3EC226S1K6DA68N346C258CVKEC9M8H344HHP6RT44HJ56123ADHN8N1K0DHJ6933CD1M6X142CHM8RS4AD9M8GV30D1M6X14CE1P74TKAC9P8GWMAH9M750K0E9N8S34CCSR61136H9H6X0K0GT68RSM8CSH8N1K8HHJ8CSM4DT58H24AEA16CR3EH268CR30E1H8N1KGH9P8RS44HJ18RRK4CT46WSK4HHN8GSMADT38MTM2E1R8CR3EH1P6S2KGE1G8D342DHS88TKGCT66N0K8G9N752K6CHN70V3EH9H651M2H1M8N132E9J64S42DT668T3GG9H74S3JDJ68D13CH135452081918G2J2G0", - "coin_sig": 5326752 + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H1J60R4AHHG6N1KEC268N23ECT664VKAGSJ8GRK4CA56H2KAH1H8CS4AHA188SKJE266RR3EDA58N148DSS74TM6CA58N24AC9K6X1M4C9Q6RW48CT674SMCE2264V42CSH8D336GSQ84SM6CT564TKECSR8MRMAGHG8GW3JEA56WT32D9S8H334DJ564SK8GSS60WM6E1P8S346H238GRM8DSN6MR36C256GVM8H9M6WV30C246X0MCH9R84TK2D1M70V44CA260VKJGHN6RR4ACT488R3GCA46MW3GDHK6GWMAHHP6S242H9G6D134D9N8RV32DSJ8GSKGC1J6RTK2GHQ8H0K2DHH6WT38GSH74WM4EA574RKEE1M6D0M4DHK8MW4ACT68CT4CE236H236GH35452081918G2J2G0", + "coin_sig": "K9S5273GT4QKF5Y9FYJ62BV710WGEFE1DSXV75A37X272ADWBCV0ERZV9TF2VYTCSH1837R3F7A39R5QEPCC0NYW3JQ5S70X8MJG008" } - ] + ], + "max_fee": { + "value": 3, + "fraction": 8, + "currency": "KUDOS" + } } \ No newline at end of file diff --git a/src/tests/test_contract_README b/src/tests/test_contract_README index a6a25e69..9fd337cf 100644 --- a/src/tests/test_contract_README +++ b/src/tests/test_contract_README @@ -4,7 +4,7 @@ - curl http://merchant-url/generate_taler_contract.php?cli_debug=yes (this form tests the whole communication between frontend and backend) - - curl http://merchant-url/generate_taler_contract.php?cli_debug=yes + - curl http://merchant-url/generate_taler_contract.php?backend_test=no (this form test only the contract proposition generation of the frontend, so it doesn't further connect to the backend to get the proposition JSON enhanced) -- cgit v1.2.3 From c8011ae1bd004575c5c11b0b30952cbbd8bbc983 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 30 Oct 2015 07:01:45 +0100 Subject: Adding further test instructions, fixing minor bugs --- src/frontend/pay.php | 8 ++++-- src/tests/deposit_permission.sample | 9 ++---- src/tests/deposit_permission_backend.sample | 34 ++++++++++++++++++++++ src/tests/deposit_permission_edate_backend.sample | 35 +++++++++++++++++++++++ src/tests/test_pay_README | 26 +++++++++++++++++ 5 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 src/tests/deposit_permission_backend.sample create mode 100644 src/tests/deposit_permission_edate_backend.sample create mode 100644 src/tests/test_pay_README diff --git a/src/frontend/pay.php b/src/frontend/pay.php index 1c1428b2..29fb0d84 100644 --- a/src/frontend/pay.php +++ b/src/frontend/pay.php @@ -43,6 +43,8 @@ $now = new DateTime('now'); $edate = array ('edate' => "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/"); +echo $post_body; + $deposit_permission = json_decode ($post_body, true); $max_fee = array ('max_fee' => array ('value' => 3, @@ -58,9 +60,11 @@ $new_deposit_permission_edate = array_merge ($new_deposit_permission, $edate); if ($cli_debug && !$backend_test) { - file_put_contents('/tmp/noedate', json_encode ($new_deposit_permission, JSON_PRETTY_PRINT)); - file_put_contents('/tmp/yesedate', json_encode ($new_deposit_permission_edate, JSON_PRETTY_PRINT)); + /* NOTE the newline at the end of 'echo's argument */ + //echo json_encode ($new_deposit_permission_edate, JSON_PRETTY_PRINT) + echo json_encode ($new_deposit_permission, JSON_PRETTY_PRINT) + . "\n"; exit; } diff --git a/src/tests/deposit_permission.sample b/src/tests/deposit_permission.sample index 161a72d0..8070d603 100644 --- a/src/tests/deposit_permission.sample +++ b/src/tests/deposit_permission.sample @@ -25,10 +25,5 @@ "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H1J60R4AHHG6N1KEC268N23ECT664VKAGSJ8GRK4CA56H2KAH1H8CS4AHA188SKJE266RR3EDA58N148DSS74TM6CA58N24AC9K6X1M4C9Q6RW48CT674SMCE2264V42CSH8D336GSQ84SM6CT564TKECSR8MRMAGHG8GW3JEA56WT32D9S8H334DJ564SK8GSS60WM6E1P8S346H238GRM8DSN6MR36C256GVM8H9M6WV30C246X0MCH9R84TK2D1M70V44CA260VKJGHN6RR4ACT488R3GCA46MW3GDHK6GWMAHHP6S242H9G6D134D9N8RV32DSJ8GSKGC1J6RTK2GHQ8H0K2DHH6WT38GSH74WM4EA574RKEE1M6D0M4DHK8MW4ACT68CT4CE236H236GH35452081918G2J2G0", "coin_sig": "K9S5273GT4QKF5Y9FYJ62BV710WGEFE1DSXV75A37X272ADWBCV0ERZV9TF2VYTCSH1837R3F7A39R5QEPCC0NYW3JQ5S70X8MJG008" } - ], - "max_fee": { - "value": 3, - "fraction": 8, - "currency": "KUDOS" - } -} \ No newline at end of file + ] +} diff --git a/src/tests/deposit_permission_backend.sample b/src/tests/deposit_permission_backend.sample new file mode 100644 index 00000000..161a72d0 --- /dev/null +++ b/src/tests/deposit_permission_backend.sample @@ -0,0 +1,34 @@ +{ + "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR", + "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70", + "mint": "localmint", + "coins": [ + { + "f": { + "value": 0, + "fraction": 100000, + "currency": "KUDOS" + }, + "coin_pub": "AH86D5WV9G6RT0A3CHYJW598BNMH8848GAKYTEH1JX6X2BKZWTX0", + "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C9J8D1MCHHJ84S34H1K8H1M6HHN60V3GH9S84R3GH9K6MVM6GSM70RKCDT188VM4CHM74RM8GSN6GSKEHHS6CT44H9Q8CWKCD1P68SK2G9S8GR34D1P60T4CCJ67523EDJ28S1KJD9N8H330CT58RS38CSM8H346GT58N13CGJ16GS34CHQ6GT42CSS6CTM4C9H8N1KCG9G74T36C1K8GT44E1N8RRK0D218GV46G9R6CT4CHA4890M6H1H8GV34E1Q8N244DHM8CRK2H1R8CV3ADA37113JDHR88W36E9R61242GT5752KEE9R84WK8H9G8GT34GT26S2MAE1P70RMAGSK74W30C1J60S32GSK64WKAC218MWM6GHM8S332DSN70SM2DHR6H146E228CTM8D135452081918G2J2G0", + "coin_sig": "MFTHC54GFYHA3CGKHC8SSDSTYX8YMEJDNQA7AEY5M7JBK3WPDQ9NCJ54ZSPQRZ4QCJC0CPREP0XRWH9JQ509ENSEXWKNNM5FEVRG238" + }, + { + "f": { + "value": 0, + "fraction": 100000, + "currency": "KUDOS" + }, + "coin_pub": "MQY067A8AGG90TSH11C8JNRW8P1R669JAAQX4V2HGFHGJ2WY4ZA0", + "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H1J60R4AHHG6N1KEC268N23ECT664VKAGSJ8GRK4CA56H2KAH1H8CS4AHA188SKJE266RR3EDA58N148DSS74TM6CA58N24AC9K6X1M4C9Q6RW48CT674SMCE2264V42CSH8D336GSQ84SM6CT564TKECSR8MRMAGHG8GW3JEA56WT32D9S8H334DJ564SK8GSS60WM6E1P8S346H238GRM8DSN6MR36C256GVM8H9M6WV30C246X0MCH9R84TK2D1M70V44CA260VKJGHN6RR4ACT488R3GCA46MW3GDHK6GWMAHHP6S242H9G6D134D9N8RV32DSJ8GSKGC1J6RTK2GHQ8H0K2DHH6WT38GSH74WM4EA574RKEE1M6D0M4DHK8MW4ACT68CT4CE236H236GH35452081918G2J2G0", + "coin_sig": "K9S5273GT4QKF5Y9FYJ62BV710WGEFE1DSXV75A37X272ADWBCV0ERZV9TF2VYTCSH1837R3F7A39R5QEPCC0NYW3JQ5S70X8MJG008" + } + ], + "max_fee": { + "value": 3, + "fraction": 8, + "currency": "KUDOS" + } +} \ No newline at end of file diff --git a/src/tests/deposit_permission_edate_backend.sample b/src/tests/deposit_permission_edate_backend.sample new file mode 100644 index 00000000..db7609d5 --- /dev/null +++ b/src/tests/deposit_permission_edate_backend.sample @@ -0,0 +1,35 @@ +{ + "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR", + "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70", + "mint": "localmint", + "coins": [ + { + "f": { + "value": 0, + "fraction": 100000, + "currency": "KUDOS" + }, + "coin_pub": "AH86D5WV9G6RT0A3CHYJW598BNMH8848GAKYTEH1JX6X2BKZWTX0", + "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26C9J8D1MCHHJ84S34H1K8H1M6HHN60V3GH9S84R3GH9K6MVM6GSM70RKCDT188VM4CHM74RM8GSN6GSKEHHS6CT44H9Q8CWKCD1P68SK2G9S8GR34D1P60T4CCJ67523EDJ28S1KJD9N8H330CT58RS38CSM8H346GT58N13CGJ16GS34CHQ6GT42CSS6CTM4C9H8N1KCG9G74T36C1K8GT44E1N8RRK0D218GV46G9R6CT4CHA4890M6H1H8GV34E1Q8N244DHM8CRK2H1R8CV3ADA37113JDHR88W36E9R61242GT5752KEE9R84WK8H9G8GT34GT26S2MAE1P70RMAGSK74W30C1J60S32GSK64WKAC218MWM6GHM8S332DSN70SM2DHR6H146E228CTM8D135452081918G2J2G0", + "coin_sig": "MFTHC54GFYHA3CGKHC8SSDSTYX8YMEJDNQA7AEY5M7JBK3WPDQ9NCJ54ZSPQRZ4QCJC0CPREP0XRWH9JQ509ENSEXWKNNM5FEVRG238" + }, + { + "f": { + "value": 0, + "fraction": 100000, + "currency": "KUDOS" + }, + "coin_pub": "MQY067A8AGG90TSH11C8JNRW8P1R669JAAQX4V2HGFHGJ2WY4ZA0", + "denom_pub": "51R7ARKCD5HJTTV5F4G0M818E9SP280A40G2GVH04CR30HHG74SM8GSM6D33EGSR84R46E246H23GCHK690M4G9R8GS4CD9S8GRMCDSP64T34GA56GV34DA3851M6H9S6MWK8H9K8D13CD1M85344GSP61244GT28N132HA56N1MCHA56513AD2160RK2CSN6D23AGJ56X33AGSM68W4CH1S88WK4D1H68SMACHJ88TM2CA28MRM4D248GS4ADHR8GSM4C2364V38E1N88S30HA564TKJE1N750KGC9R6RTKJE2370R4AC268D0MACHN6N242D1N68TKAC1M65234DJ26CRK4D9M8MSK6C9J64R32D1R70VKEE25852MCG9N84W32GJ68914CD1J691M6GA360S3GCHP8GWK8HHH68TK8CSK70S4AE9N712K0H9K6X34AC9354520818CMG26C1H60R30C935452081918G2J2G0", + "ub_sig": "51SPJSSDESGPR80A40M74WV140520818ECG26H1J60R4AHHG6N1KEC268N23ECT664VKAGSJ8GRK4CA56H2KAH1H8CS4AHA188SKJE266RR3EDA58N148DSS74TM6CA58N24AC9K6X1M4C9Q6RW48CT674SMCE2264V42CSH8D336GSQ84SM6CT564TKECSR8MRMAGHG8GW3JEA56WT32D9S8H334DJ564SK8GSS60WM6E1P8S346H238GRM8DSN6MR36C256GVM8H9M6WV30C246X0MCH9R84TK2D1M70V44CA260VKJGHN6RR4ACT488R3GCA46MW3GDHK6GWMAHHP6S242H9G6D134D9N8RV32DSJ8GSKGC1J6RTK2GHQ8H0K2DHH6WT38GSH74WM4EA574RKEE1M6D0M4DHK8MW4ACT68CT4CE236H236GH35452081918G2J2G0", + "coin_sig": "K9S5273GT4QKF5Y9FYJ62BV710WGEFE1DSXV75A37X272ADWBCV0ERZV9TF2VYTCSH1837R3F7A39R5QEPCC0NYW3JQ5S70X8MJG008" + } + ], + "max_fee": { + "value": 3, + "fraction": 8, + "currency": "KUDOS" + }, + "edate": "\/Date(1447392206)\/" +} \ No newline at end of file diff --git a/src/tests/test_pay_README b/src/tests/test_pay_README new file mode 100644 index 00000000..1704d6b7 --- /dev/null +++ b/src/tests/test_pay_README @@ -0,0 +1,26 @@ +The merchant's "/pay" facility can be tested either directly by querying the +backend or by querying the frontend (which in turn will query the backend). +Each file *.sample is sample JSON to POST to both components (with the '_backend' +named version being dedicated to just the backend). + +Note: the sample 'deposit_permission_edate_backend.sample' contains the optional +'edate' field to the deposit permission (that field indicates the tollerated delay +by this merchant to receive funds relative to this payment) + +The frontend test admits two versions of queries, as in the following examples: + + * curl -H 'Content-type: application/json' -d@deposit_permission.sample \ + http://merchant-url/pay.php?cli_debug=yes + + (this form tests the whole communication between frontend and backend) + + * curl -d@deposit_permission.sample http://merchant-url/pay.php?backend_test=no + + (this form tests only the deposit permission enhancement made by the + frontend, so it doesn't further connect to the backend to actually POST + that data. By default, the frontend doesn't add the 'edate' field, so it + is necessary to uncomment the related line of code to test this feature) + +Finally, to POST to the backend, issue + * curl -H 'Content-type: application/json' -d@*_backend.sample http://backend-url/pay + -- cgit v1.2.3 From d0871c3b8fcd2cbeb37175579d6f114d048520e9 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 30 Oct 2015 07:48:23 +0100 Subject: adding /deposit roadmap in _pay.c comments, fixing minor things --- src/backend/taler-merchant-httpd_pay.c | 36 +++++++++++++++++++++++++++++++++- src/frontend/pay.php | 5 +---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index fb74cda8..59aa211c 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -61,7 +61,12 @@ MH_handler_pay (struct TMH_RequestHandler *rh, { json_t *root; + json_t *coins; + int res; + struct TALER_Amount max_deposit_fee; + //struct TALER_Amount acc_deposit_fee; + //struct TALER_Amount coin_deposit_fee; res = TMH_PARSE_post_json (connection, connection_cls, @@ -74,5 +79,34 @@ MH_handler_pay (struct TMH_RequestHandler *rh, if ((GNUNET_NO == res) || (NULL == root)) return MHD_YES; - /* 1 Check if the total deposit fee is leq the limit */ + //printf ("/pay\n"); + + /* 0 What if the coin gives zero-length coins array? */ + + /* 1 Check if the total deposit fee is \leq the limit */ + + /* 2 Check if the chosen mint is among the merchant's preferred. + + An error in this case could be due to: + + * the wallet indicated a non existent mint + * the wallet indicated a non trusted mint + + NOTE: by preventively checking this, the merchant + avoids getting HTTP response codes from random + websites that may mislead the wallet in the way + of managing the error. Of course, that protect the + merchant from POSTing coins to untrusted mints. + + */ + + /* 3 For each coin in DB + + a. Generate a deposit permission + b. store it in DB + c. POST to the mint (see mint-lib for this) + (retry until getting a persisten state) + */ + /* 4 Return response code: success, or whatever data the + mint sent back regarding some bad coin */ } diff --git a/src/frontend/pay.php b/src/frontend/pay.php index 29fb0d84..a23b9481 100644 --- a/src/frontend/pay.php +++ b/src/frontend/pay.php @@ -43,10 +43,7 @@ $now = new DateTime('now'); $edate = array ('edate' => "/Date(" . $now->add(new DateInterval('P2W'))->getTimestamp() . ")/"); -echo $post_body; - $deposit_permission = json_decode ($post_body, true); - $max_fee = array ('max_fee' => array ('value' => 3, 'fraction' => 8, 'currency' => "KUDOS")); @@ -71,7 +68,7 @@ if ($cli_debug && !$backend_test) $req = new http\Client\Request ("POST", "http://" . $_SERVER["SERVER_NAME"] . "/backend/pay", array ("Content-Type" => "application/json")); -$req->getBody()->append ($new_deposit_permission); +$req->getBody()->append (json_encode ($new_deposit_permission)); // Execute the HTTP request $client = new http\Client; -- cgit v1.2.3 From a5658415628c57d7405036059eb03a9112a03596 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 30 Oct 2015 08:25:26 +0100 Subject: adding auditors indication in contract generation, and auditor entry parsing from the config file --- src/backend/merchant.c | 67 +++++++++++++++++++++++++++++ src/backend/merchant.conf | 9 ++-- src/backend/taler-merchant-httpd.c | 16 ++++++- src/backend/taler-merchant-httpd_contract.c | 21 ++++++++- src/include/merchant.h | 23 +++++++++- 5 files changed, 129 insertions(+), 7 deletions(-) diff --git a/src/backend/merchant.c b/src/backend/merchant.c index 4fa66f16..02b37fb8 100644 --- a/src/backend/merchant.c +++ b/src/backend/merchant.c @@ -97,6 +97,73 @@ TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, return cnt; } +/** + * Parses auditors from the configuration. + * + * @param cfg the configuration + * @param mints the array of auditors upon successful parsing. Will be NULL upon + * error. + * @return the number of auditors in the above array; GNUNET_SYSERR upon error in + * parsing. + */ +int +TALER_MERCHANT_parse_auditors (const struct GNUNET_CONFIGURATION_Handle *cfg, + struct MERCHANT_Auditor **auditors) +{ + char *auditors_str; + char *token_nf; /* do no free (nf) */ + char *auditor_section; + char *auditor_name; + struct MERCHANT_Auditor *r_auditors; + struct MERCHANT_Auditor auditor; + unsigned int cnt; + int OK; + + OK = 0; + auditors_str = NULL; + token_nf = NULL; + auditor_section = NULL; + auditor_name = NULL; + r_auditors = NULL; + cnt = 0; + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "merchant", + "AUDITORS", + &auditors_str)); + for (token_nf = strtok (auditors_str, " "); + NULL != token_nf; + token_nf = strtok (NULL, " ")) + { + GNUNET_assert (0 < GNUNET_asprintf (&auditor_section, + "auditor-%s", token_nf)); + EXITIF (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + auditor_section, + "NAME", + &auditor_name)); + auditor.name = auditor_name; + GNUNET_array_append (r_auditors, cnt, auditor); + auditor_name = NULL; + GNUNET_free (auditor_section); + auditor_section = NULL; + } + OK = 1; + + EXITIF_exit: + GNUNET_free_non_null (auditors_str); + GNUNET_free_non_null (auditor_section); + GNUNET_free_non_null (auditor_name); + if (!OK) + { + GNUNET_free_non_null (r_auditors); + return GNUNET_SYSERR; + } + + *auditors = r_auditors; + return cnt; +} + /** * Parse the SEPA information from the configuration. If any of the required diff --git a/src/backend/merchant.conf b/src/backend/merchant.conf index f24611a9..68aeacfe 100644 --- a/src/backend/merchant.conf +++ b/src/backend/merchant.conf @@ -3,13 +3,16 @@ PORT = 9966 HOSTNAME = localhost TRUSTED_MINTS = taler KEYFILE = merchant.priv +CURRENCY = KUDOS +EDATE = 3 week +AUDITORS = france [mint-taler] -HOSTNAME = demo.taler.net - -[mint-localmint] HOSTNAME = localmint +[auditor-france] +NAME = Charles De Gaulle + [merchant-db] CONFIG = postgres:///talerdemo diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 3806115a..b0b37546 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -74,10 +74,15 @@ struct GNUNET_TIME_Relative edate; char *TMH_mint_currency_string; /** - * Mints' URL,port,key triples + * Trusted mints */ struct MERCHANT_Mint *mints; +/** + * Active auditors + */ +struct MERCHANT_Auditor *auditors; + /** * Shutdown task identifier */ @@ -103,6 +108,11 @@ long long salt; */ unsigned int nmints; +/** + * The number of active auditors + */ +unsigned int nauditors; + /** * Should we do a dry run where temporary tables are used for storing the data. */ @@ -423,6 +433,10 @@ run (void *cls, char *const *args, const char *cfgfile, (nmints = TALER_MERCHANT_parse_mints (config, &mints))); + EXITIF (GNUNET_SYSERR == + (nauditors = + TALER_MERCHANT_parse_auditors (config, + &auditors))); EXITIF (NULL == (wire = TALER_MERCHANT_parse_wireformat_sepa (config))); diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index 4d1584b3..834cfce8 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -37,9 +37,11 @@ #include "taler_merchant_lib.h" extern struct MERCHANT_Mint *mints; +extern struct MERCHANT_Auditor *auditors; extern struct GNUNET_CRYPTO_EddsaPrivateKey privkey; extern const struct MERCHANT_WIREFORMAT_Sepa *wire; extern unsigned int nmints; +extern unsigned int nauditors; extern PGconn *db_conn; extern long long salt; @@ -65,6 +67,8 @@ MH_handler_contract (struct TMH_RequestHandler *rh, { json_t *root; json_t *trusted_mints; + json_t *j_auditors; + json_t *auditor; json_t *mint; json_t *j_wire; const struct TALER_MINT_Keys *keys; @@ -83,8 +87,7 @@ MH_handler_contract (struct TMH_RequestHandler *rh, &root); if (GNUNET_SYSERR == res) return MHD_NO; - /* the POST's body has to be further fetched */ - if ((GNUNET_NO == res) || (NULL == root)) + /* the POST's body has to be further fetched */ if ((GNUNET_NO == res) || (NULL == root)) return MHD_YES; /* Generate preferred mint(s) array. */ @@ -104,6 +107,13 @@ MH_handler_contract (struct TMH_RequestHandler *rh, json_array_append_new (trusted_mints, mint); } } + auditors = json_array (); + for (cnt = 0; cnt < nauditors; cnt++) + { + auditor = json_pack ("{s:s}", + "name", auditors[cnt].name); + json_array_append_new (j_auditors, auditor); + } /** * Return badly if no mints are trusted (or no call to /keys has still @@ -116,7 +126,14 @@ MH_handler_contract (struct TMH_RequestHandler *rh, if (!json_array_size (trusted_mints)) return MHD_NO; + /** + * Hard error, no action can be taken by a wallet + */ + if (!json_array_size (auditors)) + return MHD_NO; + json_object_set_new (root, "mints", trusted_mints); + json_object_set_new (root, "auditors", j_auditors); if (NULL == (j_wire = MERCHANT_get_wire_json (wire, salt))) diff --git a/src/include/merchant.h b/src/include/merchant.h index 0efd4388..8289a337 100644 --- a/src/include/merchant.h +++ b/src/include/merchant.h @@ -41,7 +41,7 @@ } while (0) /** - * A mint + * Mint */ struct MERCHANT_Mint { @@ -63,6 +63,15 @@ struct MERCHANT_Mint }; +struct MERCHANT_Auditor +{ + /** + * Auditor's legal name + */ + char *name; + +}; + /** * The contract sent by the merchant to the wallet */ @@ -96,6 +105,18 @@ int TALER_MERCHANT_parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg, struct MERCHANT_Mint **mints); +/** + * Parses auditors from the configuration. + * + * @param cfg the configuration + * @param mints the array of auditors upon successful parsing. Will be NULL upon + * error. + * @return the number of auditors in the above array; GNUNET_SYSERR upon error in + * parsing. + */ +int +TALER_MERCHANT_parse_auditors (const struct GNUNET_CONFIGURATION_Handle *cfg, + struct MERCHANT_Auditor **auditors); GNUNET_NETWORK_STRUCT_BEGIN struct MERCHANT_WIREFORMAT_Sepa -- cgit v1.2.3 From a4c5e087f714d0e2cafff3edd47887e4602470a8 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 30 Oct 2015 15:36:37 +0100 Subject: Adding initial behaviour to associate a deposit fee to a coin. To be used when iterating over the array of coins gotten within a payment, in order to check if the wallet is below the max. limit allowed (by the merchant) for deposit fee. --- src/backend/taler-merchant-httpd_contract.c | 4 +- src/backend/taler-merchant-httpd_pay.c | 58 +++++++++++++++++++++++++++-- src/backend/taler-mint-httpd_responses.c | 17 +++++++++ 3 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c index 834cfce8..c157b859 100644 --- a/src/backend/taler-merchant-httpd_contract.c +++ b/src/backend/taler-merchant-httpd_contract.c @@ -107,7 +107,7 @@ MH_handler_contract (struct TMH_RequestHandler *rh, json_array_append_new (trusted_mints, mint); } } - auditors = json_array (); + j_auditors = json_array (); for (cnt = 0; cnt < nauditors; cnt++) { auditor = json_pack ("{s:s}", @@ -129,7 +129,7 @@ MH_handler_contract (struct TMH_RequestHandler *rh, /** * Hard error, no action can be taken by a wallet */ - if (!json_array_size (auditors)) + if (!json_array_size (j_auditors)) return MHD_NO; json_object_set_new (root, "mints", trusted_mints); diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index 59aa211c..02c2ef04 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -41,6 +41,38 @@ extern const struct MERCHANT_WIREFORMAT_Sepa *wire; extern PGconn *db_conn; extern long long salt; +/** + * Fetch the deposit fee related to the given coin aggregate. + * @param connection the connection to send an error response to + * @param coin_aggregate a coin "aggregate" is the JSON set of + * values contained in a single cell of the 'coins' array sent + * in a payment + * @param deposit_fee where to store the resulting deposi fee + * @param mint_index the index which points the chosen mint within + * the global 'mints' array + * @return GNUNET_OK if successful, GNUNET_NO if the data supplied + * is invalid, GNUNET_SYSERR upon internal errors + */ +int +deposit_fee_from_coin_json (struct MHD_Connection *connection, + json_t *coin_aggregate, + struct TALER_Amount *deposit_fee, + unsigned int mint_index) +{ + int res; + struct TALER_DenominationPublicKey denom; + struct TMH_PARSE_FieldSpecification spec[] = { + TMH_PARSE_member_denomination_public_key ("denom_pub", &denom); + }; + + res = TMH_PARSE_json_data (connection, + coin_aggregate, + spec); + if (GNUNET_OK != res) + return res; + /* Iterate over the mint keys to get the wanted data */ +} + /** * Accomplish this payment. * @param rh context of the handler @@ -62,12 +94,17 @@ MH_handler_pay (struct TMH_RequestHandler *rh, json_t *root; json_t *coins; - + json_t *chosen_mint; + json_t *coin_aggregate; + unsigned int ncoins; int res; + struct TMH_PARSE_FieldSpecification spec[] = { + TMH_PARSE_member_array ("coins", &coins); + TMH_PARSE_member_object ("mint", &chosen_mint); + }; struct TALER_Amount max_deposit_fee; //struct TALER_Amount acc_deposit_fee; //struct TALER_Amount coin_deposit_fee; - res = TMH_PARSE_post_json (connection, connection_cls, upload_data, @@ -83,9 +120,17 @@ MH_handler_pay (struct TMH_RequestHandler *rh, /* 0 What if the coin gives zero-length coins array? */ - /* 1 Check if the total deposit fee is \leq the limit */ + + res = TMH_PARSE_json_data (connection, + coin_aggregate, + spec); - /* 2 Check if the chosen mint is among the merchant's preferred. + if (GNUNET_YES != res) + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + if (0 == ncoins) + return TMH_RESPONSE_reply_external_error (connection, + "empty coin array"); + /* 1 Check if the chosen mint is among the merchant's preferred. An error in this case could be due to: @@ -100,6 +145,11 @@ MH_handler_pay (struct TMH_RequestHandler *rh, */ + /* 2 Check if the total deposit fee is \leq the limit */ + ncoins = json_array_size (coins); + if (NULL == (coin_aggregate = json_array_get (coins, 0))) + return MHD_NO; + /* 3 For each coin in DB a. Generate a deposit permission diff --git a/src/backend/taler-mint-httpd_responses.c b/src/backend/taler-mint-httpd_responses.c index 70cf7e52..00a4d25f 100644 --- a/src/backend/taler-mint-httpd_responses.c +++ b/src/backend/taler-mint-httpd_responses.c @@ -186,4 +186,21 @@ TMH_RESPONSE_add_global_headers (struct MHD_Response *response) "close"); } +/** + * Send a response indicating an external error. + * + * @param connection the MHD connection to use + * @param hint hint about the error's nature + * @return a MHD result code + */ +int +TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection, + const char *hint) +{ + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s}", + "error", "client error", + "hint", hint); +} /* end of taler-mint-httpd_responses.c */ -- cgit v1.2.3 From 3e61752ad11275b4bff31a4d743253947a89271c Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Fri, 30 Oct 2015 15:55:19 +0100 Subject: Adding the check to see if the mint indicated by the wallet during a payment is a trusted one. To test --- src/backend/taler-merchant-httpd_pay.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index 02c2ef04..4ac1f355 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -97,6 +97,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, json_t *chosen_mint; json_t *coin_aggregate; unsigned int ncoins; + unsigned int mint_index; //pointing global array int res; struct TMH_PARSE_FieldSpecification spec[] = { TMH_PARSE_member_array ("coins", &coins); @@ -118,15 +119,15 @@ MH_handler_pay (struct TMH_RequestHandler *rh, //printf ("/pay\n"); - /* 0 What if the coin gives zero-length coins array? */ - - res = TMH_PARSE_json_data (connection, coin_aggregate, spec); if (GNUNET_YES != res) return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + + /* 0 What if the coin gives zero-length coins array? */ + ncoins = json_array_size (coins); if (0 == ncoins) return TMH_RESPONSE_reply_external_error (connection, "empty coin array"); @@ -144,9 +145,18 @@ MH_handler_pay (struct TMH_RequestHandler *rh, merchant from POSTing coins to untrusted mints. */ + for (mint_index = 0; mint_index < nmints; mint_index++) + { + if (0 == strcmp (mints[mint_index].hostname, json_string_value (chosen_mint))) + break; + mint_index = -1; + } + + if (-1 == mint_index) + + /* TODO notify the wallet that it indicated an unknown mint */ /* 2 Check if the total deposit fee is \leq the limit */ - ncoins = json_array_size (coins); if (NULL == (coin_aggregate = json_array_get (coins, 0))) return MHD_NO; -- cgit v1.2.3 From c239900bb1c5529267015311e104d461da36d737 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Mon, 2 Nov 2015 16:28:02 +0100 Subject: minor changes --- src/backend/taler-merchant-httpd.c | 4 +- src/backend/taler-merchant-httpd_pay.c | 105 +++++++++++++++++++++++-------- src/backend/taler-mint-httpd_responses.h | 10 +++ 3 files changed, 90 insertions(+), 29 deletions(-) diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index b0b37546..95dbec6b 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -66,7 +66,7 @@ static struct TALER_MINT_Context *mctx; * This value tells the mint by which date this merchant would like * to receive the funds for a deposited payment */ -struct GNUNET_TIME_Relative edate; +struct GNUNET_TIME_Relative edate_delay; /** * To make 'TMH_PARSE_navigate_json ()' compile @@ -472,7 +472,7 @@ run (void *cls, char *const *args, const char *cfgfile, GNUNET_CONFIGURATION_get_value_time (config, "merchant", "EDATE", - &edate)); + &edate_delay)); salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, UINT64_MAX); diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index 4ac1f355..5258b6d7 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -40,6 +40,8 @@ extern struct MERCHANT_Mint *mints; extern const struct MERCHANT_WIREFORMAT_Sepa *wire; extern PGconn *db_conn; extern long long salt; +extern unsigned int nmints; +extern struct GNUNET_TIME_Relative edate_delay; /** * Fetch the deposit fee related to the given coin aggregate. @@ -54,23 +56,33 @@ extern long long salt; * is invalid, GNUNET_SYSERR upon internal errors */ int -deposit_fee_from_coin_json (struct MHD_Connection *connection, - json_t *coin_aggregate, - struct TALER_Amount *deposit_fee, - unsigned int mint_index) +deposit_fee_from_coin_aggregate (struct MHD_Connection *connection, + json_t *coin_aggregate, + struct TALER_Amount *deposit_fee, + unsigned int mint_index) { int res; + const struct TALER_MINT_Keys *keys; + const struct TALER_MINT_DenomPublicKey *denom_details; struct TALER_DenominationPublicKey denom; + struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_denomination_public_key ("denom_pub", &denom); + TMH_PARSE_member_denomination_public_key ("denom_pub", &denom) }; res = TMH_PARSE_json_data (connection, coin_aggregate, spec); if (GNUNET_OK != res) - return res; - /* Iterate over the mint keys to get the wanted data */ + return res; /* may return GNUNET_NO */ + + if (1 == mints[mint_index].pending) + return GNUNET_SYSERR; + keys = TALER_MINT_get_keys (mints[mint_index].conn); + + denom_details = TALER_MINT_get_denomination_key (keys, &denom); + *deposit_fee = denom_details->fee_deposit; + return GNUNET_OK; } /** @@ -94,18 +106,30 @@ MH_handler_pay (struct TMH_RequestHandler *rh, json_t *root; json_t *coins; - json_t *chosen_mint; + char *chosen_mint; + size_t chosen_mint_length; json_t *coin_aggregate; - unsigned int ncoins; + unsigned int coins_cnt; unsigned int mint_index; //pointing global array + uint64_t transaction_id; int res; + + struct TALER_Amount max_fee; + struct TALER_Amount acc_fee; + struct TALER_Amount coin_fee; + struct GNUNET_TIME_Absolute edate; + struct GNUNET_TIME_Absolute timestamp; + struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_array ("coins", &coins); - TMH_PARSE_member_object ("mint", &chosen_mint); + TMH_PARSE_member_array ("coins", &coins), + TMH_PARSE_member_variable ("mint", + (void *) &chosen_mint, + &chosen_mint_length), + + TMH_PARSE_member_amount ("max_fee", &max_fee), + TMH_PARSE_member_time_abs ("timestamp", ×tamp), + TMH_PARSE_member_uint64 ("transaction_id", &transaction_id), }; - struct TALER_Amount max_deposit_fee; - //struct TALER_Amount acc_deposit_fee; - //struct TALER_Amount coin_deposit_fee; res = TMH_PARSE_post_json (connection, connection_cls, upload_data, @@ -120,17 +144,12 @@ MH_handler_pay (struct TMH_RequestHandler *rh, //printf ("/pay\n"); res = TMH_PARSE_json_data (connection, - coin_aggregate, + root, spec); if (GNUNET_YES != res) return (GNUNET_NO == res) ? MHD_YES : MHD_NO; - /* 0 What if the coin gives zero-length coins array? */ - ncoins = json_array_size (coins); - if (0 == ncoins) - return TMH_RESPONSE_reply_external_error (connection, - "empty coin array"); /* 1 Check if the chosen mint is among the merchant's preferred. An error in this case could be due to: @@ -147,23 +166,55 @@ MH_handler_pay (struct TMH_RequestHandler *rh, */ for (mint_index = 0; mint_index < nmints; mint_index++) { - if (0 == strcmp (mints[mint_index].hostname, json_string_value (chosen_mint))) + if (0 == strncmp (mints[mint_index].hostname, + chosen_mint, chosen_mint_length)) break; mint_index = -1; } - if (-1 == mint_index) + if (-1 == mint_index); + return TMH_RESPONSE_reply_external_error (connection, "unknown mint"); - /* TODO notify the wallet that it indicated an unknown mint */ + printf ("mint idx %d", mint_index); - /* 2 Check if the total deposit fee is \leq the limit */ - if (NULL == (coin_aggregate = json_array_get (coins, 0))) - return MHD_NO; + /* no 'edate' from frontend. Generate it here; it will be timestamp + + a edate delay supplied in config file */ + if (NULL == json_object_get (root, "edate")) + { + edate = GNUNET_TIME_absolute_add (timestamp, edate_delay); + if (-1 == json_object_set (root, "edate", TALER_json_from_abs (edate))) + return MHD_NO; + } + + coins_cnt = json_array_size (coins); + + if (0 == coins_cnt) + return TMH_RESPONSE_reply_external_error (connection, "no coins given"); + json_array_foreach (coins, coins_cnt, coin_aggregate) + { + res = deposit_fee_from_coin_aggregate (connection, + coin_aggregate, + &coin_fee, + mint_index); + if (GNUNET_OK != res) + return MHD_NO; + + if (0 == coins_cnt) + acc_fee = coin_fee; + else + TALER_amount_add (&acc_fee, + &acc_fee, + &coin_fee); + } + + if (-1 == TALER_amount_cmp (&max_fee, &acc_fee)) + return MHD_HTTP_NOT_ACCEPTABLE; + /* 3 For each coin in DB a. Generate a deposit permission - b. store it in DB + b. store it and its tid in DB c. POST to the mint (see mint-lib for this) (retry until getting a persisten state) */ diff --git a/src/backend/taler-mint-httpd_responses.h b/src/backend/taler-mint-httpd_responses.h index 2b05c69c..f947bd57 100644 --- a/src/backend/taler-mint-httpd_responses.h +++ b/src/backend/taler-mint-httpd_responses.h @@ -80,6 +80,16 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection); int TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection, const char *hint); +/** + * Send a response indicating an external error. + * + * @param connection the MHD connection to use + * @param hint hint about the error's nature + * @return a MHD result code + */ +int +TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection, + const char *hint); /** * Send a response indicating that the request was too big. * -- cgit v1.2.3 From 3a3c842e0eb3efeea45155c5ef06d5662733f332 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Mon, 2 Nov 2015 21:49:23 +0100 Subject: adding fetch of strings in "TMH_PARSE_navigate_json ()". Not finished --- src/backend/taler-mint-httpd_parsing.c | 29 ++++++++++++++++++++++++++++- src/backend/taler-mint-httpd_parsing.h | 7 ++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/backend/taler-mint-httpd_parsing.c b/src/backend/taler-mint-httpd_parsing.c index a4dc43ab..3a076cef 100644 --- a/src/backend/taler-mint-httpd_parsing.c +++ b/src/backend/taler-mint-httpd_parsing.c @@ -347,6 +347,25 @@ TMH_PARSE_post_json (struct MHD_Connection *connection, return GNUNET_YES; } +/** + * Generate line in parser specification for string. The returned + * string is already nul-terminated internally by JSON, so no length + * information is provided. The string will live as long as the containg + * JSON will + * @param field name of the field + * @param[out] pointer to the string + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_string (const char *field, + char **out) +{ + struct TMH_PARSE_FieldSpecification ret = + {field, (void **) out, 0, NULL, TMH_PARSE_JNC_RET_STRING, 0}; + return ret; + + +} /** * Generate line in parser specification for 64-bit integer @@ -499,7 +518,8 @@ TMH_PARSE_member_variable (const char *field, * * @param connection the connection to send an error response to * @param root the JSON node to start the navigation at. - * @param ... navigation specification (see `enum TMH_PARSE_JsonNavigationCommand`) + * @param ... navigation specification (see + * `enum TMH_PARSE_JsonNavigationCommand`) * @return * #GNUNET_YES if navigation was successful * #GNUNET_NO if json is malformed, error response was generated @@ -611,6 +631,13 @@ TMH_PARSE_navigate_json (struct MHD_Connection *connection, } break; + case TMH_PARSE_JNC_RET_STRING: + { + void **where = va_arg (argp, void **); + *where = json_string_value (root); + ret = GNUNET_OK; + + } case TMH_PARSE_JNC_RET_DATA_VAR: { void **where = va_arg (argp, void **); diff --git a/src/backend/taler-mint-httpd_parsing.h b/src/backend/taler-mint-httpd_parsing.h index d6a2b4ea..e9fad54e 100644 --- a/src/backend/taler-mint-httpd_parsing.h +++ b/src/backend/taler-mint-httpd_parsing.h @@ -139,7 +139,12 @@ enum TMH_PARSE_JsonNavigationCommand * encoded as a JSON integer. * Param: uint64_t * */ - TMH_PARSE_JNC_RET_UINT64 + TMH_PARSE_JNC_RET_UINT64, + /** + * Return a 'char *' as returned from 'json_string_value ()'. + * So it will live as long as the containg JSON is not freed + */ + TMH_PARSE_JNC_RET_STRING }; -- cgit v1.2.3 From 2d198327bc71954ff4d78042f67612a3a3b40361 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Mon, 2 Nov 2015 22:28:35 +0100 Subject: minor change --- src/backend/taler-mint-httpd_parsing.c | 2 +- src/backend/taler-mint-httpd_parsing.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/taler-mint-httpd_parsing.c b/src/backend/taler-mint-httpd_parsing.c index 3a076cef..5c71e813 100644 --- a/src/backend/taler-mint-httpd_parsing.c +++ b/src/backend/taler-mint-httpd_parsing.c @@ -351,7 +351,7 @@ TMH_PARSE_post_json (struct MHD_Connection *connection, * Generate line in parser specification for string. The returned * string is already nul-terminated internally by JSON, so no length * information is provided. The string will live as long as the containg - * JSON will + * JSON will, and mut not be freed by the user * @param field name of the field * @param[out] pointer to the string * @return corresponding field spec diff --git a/src/backend/taler-mint-httpd_parsing.h b/src/backend/taler-mint-httpd_parsing.h index e9fad54e..0e959761 100644 --- a/src/backend/taler-mint-httpd_parsing.h +++ b/src/backend/taler-mint-httpd_parsing.h @@ -142,7 +142,8 @@ enum TMH_PARSE_JsonNavigationCommand TMH_PARSE_JNC_RET_UINT64, /** * Return a 'char *' as returned from 'json_string_value ()'. - * So it will live as long as the containg JSON is not freed + * So it will live as long as the containg JSON is not freed, + * and must not be freed by the user */ TMH_PARSE_JNC_RET_STRING -- cgit v1.2.3 From 6e80a32260f1edc4c36e758749dedaf1622cae62 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Tue, 3 Nov 2015 17:32:29 +0100 Subject: Adding: - new JNC for fetching strings within JSONs (tested, and presumably to be included into the mint codebase). - error handling if the the wallet gives (inside a deposit permission) a wrong denomination key for a right mint Fixing minor issues. --- src/backend/taler-merchant-httpd.c | 17 ++++--- src/backend/taler-merchant-httpd_pay.c | 59 +++++++++++++++-------- src/backend/taler-mint-httpd_parsing.c | 27 +++++++---- src/backend/taler-mint-httpd_parsing.h | 13 ++++- src/tests/deposit_permission.sample | 2 + src/tests/deposit_permission_backend.sample | 6 ++- src/tests/deposit_permission_edate_backend.sample | 10 ++-- 7 files changed, 92 insertions(+), 42 deletions(-) diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 95dbec6b..288aa330 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -247,10 +247,10 @@ url_handler (void *cls, upload_data_size); } return TMH_MHD_handler_static_response (&h404, - connection, - con_cls, - upload_data, - upload_data_size); + connection, + con_cls, + upload_data, + upload_data_size); } @@ -281,7 +281,12 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) the wallet didn't exceede the limit imposed by the merchant on the total deposit fee for a purchase */ - ((struct MERCHANT_Mint *) cls)->pending = 0; + if (NULL != keys) + { + ((struct MERCHANT_Mint *) cls)->pending = 0; + } + else + printf ("no keys gotten\n"); } @@ -532,7 +537,7 @@ main (int argc, char *const *argv) if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, - "taler-merchant-serve", + "taler-merchant-http", "Serve merchant's HTTP interface", options, &run, NULL)) return 3; diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index 5258b6d7..5f69a847 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -49,11 +49,12 @@ extern struct GNUNET_TIME_Relative edate_delay; * @param coin_aggregate a coin "aggregate" is the JSON set of * values contained in a single cell of the 'coins' array sent * in a payment - * @param deposit_fee where to store the resulting deposi fee + * @param deposit_fee where to store the resulting deposit fee * @param mint_index the index which points the chosen mint within * the global 'mints' array * @return GNUNET_OK if successful, GNUNET_NO if the data supplied - * is invalid, GNUNET_SYSERR upon internal errors + * is invalid (including the case when the key is not found), + * GNUNET_SYSERR upon internal errors */ int deposit_fee_from_coin_aggregate (struct MHD_Connection *connection, @@ -67,7 +68,8 @@ deposit_fee_from_coin_aggregate (struct MHD_Connection *connection, struct TALER_DenominationPublicKey denom; struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_denomination_public_key ("denom_pub", &denom) + TMH_PARSE_member_denomination_public_key ("denom_pub", &denom), + TMH_PARSE_MEMBER_END }; res = TMH_PARSE_json_data (connection, @@ -76,11 +78,24 @@ deposit_fee_from_coin_aggregate (struct MHD_Connection *connection, if (GNUNET_OK != res) return res; /* may return GNUNET_NO */ + /*printf ("mint %s (%d), pends: %d\n", + mints[mint_index].hostname, + mint_index, + mints[mint_index].pending);*/ + if (1 == mints[mint_index].pending) return GNUNET_SYSERR; keys = TALER_MINT_get_keys (mints[mint_index].conn); - denom_details = TALER_MINT_get_denomination_key (keys, &denom); + if (NULL == + (denom_details = TALER_MINT_get_denomination_key (keys, &denom))) + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "hint", "unknown denom to mint", + "denom_pub", TALER_json_from_rsa_public_key (denom.rsa_public_key)); + return GNUNET_NO; + *deposit_fee = denom_details->fee_deposit; return GNUNET_OK; } @@ -107,10 +122,9 @@ MH_handler_pay (struct TMH_RequestHandler *rh, json_t *root; json_t *coins; char *chosen_mint; - size_t chosen_mint_length; json_t *coin_aggregate; unsigned int coins_cnt; - unsigned int mint_index; //pointing global array + unsigned int mint_index; /*a cell in the global array*/ uint64_t transaction_id; int res; @@ -122,13 +136,11 @@ MH_handler_pay (struct TMH_RequestHandler *rh, struct TMH_PARSE_FieldSpecification spec[] = { TMH_PARSE_member_array ("coins", &coins), - TMH_PARSE_member_variable ("mint", - (void *) &chosen_mint, - &chosen_mint_length), - + TMH_PARSE_member_string ("mint", &chosen_mint), TMH_PARSE_member_amount ("max_fee", &max_fee), TMH_PARSE_member_time_abs ("timestamp", ×tamp), TMH_PARSE_member_uint64 ("transaction_id", &transaction_id), + TMH_PARSE_MEMBER_END }; res = TMH_PARSE_post_json (connection, connection_cls, @@ -141,8 +153,6 @@ MH_handler_pay (struct TMH_RequestHandler *rh, if ((GNUNET_NO == res) || (NULL == root)) return MHD_YES; - //printf ("/pay\n"); - res = TMH_PARSE_json_data (connection, root, spec); @@ -164,19 +174,26 @@ MH_handler_pay (struct TMH_RequestHandler *rh, merchant from POSTing coins to untrusted mints. */ - for (mint_index = 0; mint_index < nmints; mint_index++) + + for (mint_index = 0; mint_index <= nmints; mint_index++) { - if (0 == strncmp (mints[mint_index].hostname, - chosen_mint, chosen_mint_length)) + /* no mint found in array */ + if (mint_index == nmints) + { + mint_index = -1; break; - mint_index = -1; + } + + /* test it by checking public key */ + if (0 == strcmp (mints[mint_index].hostname, + chosen_mint)) + break; + } - if (-1 == mint_index); + if (-1 == mint_index) return TMH_RESPONSE_reply_external_error (connection, "unknown mint"); - printf ("mint idx %d", mint_index); - /* no 'edate' from frontend. Generate it here; it will be timestamp + a edate delay supplied in config file */ if (NULL == json_object_get (root, "edate")) @@ -197,7 +214,9 @@ MH_handler_pay (struct TMH_RequestHandler *rh, coin_aggregate, &coin_fee, mint_index); - if (GNUNET_OK != res) + if (GNUNET_NO == res) + return MHD_YES; + if (GNUNET_SYSERR == res) return MHD_NO; if (0 == coins_cnt) diff --git a/src/backend/taler-mint-httpd_parsing.c b/src/backend/taler-mint-httpd_parsing.c index 5c71e813..9efd6c23 100644 --- a/src/backend/taler-mint-httpd_parsing.c +++ b/src/backend/taler-mint-httpd_parsing.c @@ -184,6 +184,9 @@ release_data (struct TMH_PARSE_FieldSpecification *spec, case TMH_PARSE_JNC_FIELD: GNUNET_break (0); return; + case TMH_PARSE_JNC_RET_STRING: + GNUNET_break (0); + return; case TMH_PARSE_JNC_INDEX: GNUNET_break (0); return; @@ -351,7 +354,7 @@ TMH_PARSE_post_json (struct MHD_Connection *connection, * Generate line in parser specification for string. The returned * string is already nul-terminated internally by JSON, so no length * information is provided. The string will live as long as the containg - * JSON will, and mut not be freed by the user + * JSON will, and must not be freed by the user * @param field name of the field * @param[out] pointer to the string * @return corresponding field spec @@ -363,8 +366,6 @@ TMH_PARSE_member_string (const char *field, struct TMH_PARSE_FieldSpecification ret = {field, (void **) out, 0, NULL, TMH_PARSE_JNC_RET_STRING, 0}; return ret; - - } /** @@ -632,12 +633,12 @@ TMH_PARSE_navigate_json (struct MHD_Connection *connection, break; case TMH_PARSE_JNC_RET_STRING: - { - void **where = va_arg (argp, void **); - *where = json_string_value (root); - ret = GNUNET_OK; - - } + { + void **where = va_arg (argp, void **); + *where = (void*) json_string_value (root); + ret = GNUNET_OK; + } + break; case TMH_PARSE_JNC_RET_DATA_VAR: { void **where = va_arg (argp, void **); @@ -985,6 +986,14 @@ TMH_PARSE_json_data (struct MHD_Connection *connection, (void **) spec[i].destination, spec[i].destination_size_out); break; + case TMH_PARSE_JNC_RET_STRING: + ret = TMH_PARSE_navigate_json (connection, + root, + TMH_PARSE_JNC_FIELD, + spec[i].field_name, + TMH_PARSE_JNC_RET_STRING, + spec[i].destination); + break; case TMH_PARSE_JNC_RET_TYPED_JSON: ret = TMH_PARSE_navigate_json (connection, root, diff --git a/src/backend/taler-mint-httpd_parsing.h b/src/backend/taler-mint-httpd_parsing.h index 0e959761..dae65092 100644 --- a/src/backend/taler-mint-httpd_parsing.h +++ b/src/backend/taler-mint-httpd_parsing.h @@ -271,7 +271,18 @@ TMH_PARSE_member_variable (const char *field, void **ptr, size_t *ptr_size); - +/** + * Generate line in parser specification for string. The returned + * string is already nul-terminated internally by JSON, so no length + * information is provided. The string will live as long as the containg + * JSON will, and must not be freed by the user + * @param field name of the field + * @param[out] pointer to the string + * @return corresponding field spec + */ +struct TMH_PARSE_FieldSpecification +TMH_PARSE_member_string (const char *field, + char **out); /** * Generate line in parser specification for 64-bit integer * given as an integer in JSON. diff --git a/src/tests/deposit_permission.sample b/src/tests/deposit_permission.sample index 8070d603..c1f2d704 100644 --- a/src/tests/deposit_permission.sample +++ b/src/tests/deposit_permission.sample @@ -1,4 +1,6 @@ { + "transaction_id" : 1, + "timestamp": "\/Date(1447334003)\/", "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR", "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70", "mint": "localmint", diff --git a/src/tests/deposit_permission_backend.sample b/src/tests/deposit_permission_backend.sample index 161a72d0..3cd920ed 100644 --- a/src/tests/deposit_permission_backend.sample +++ b/src/tests/deposit_permission_backend.sample @@ -1,7 +1,9 @@ { + "transaction_id" : 1, + "timestamp": "\/Date(1447334003)\/", "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR", "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70", - "mint": "localmint", + "mint": "demo.taler.net", "coins": [ { "f": { @@ -31,4 +33,4 @@ "fraction": 8, "currency": "KUDOS" } -} \ No newline at end of file +} diff --git a/src/tests/deposit_permission_edate_backend.sample b/src/tests/deposit_permission_edate_backend.sample index db7609d5..259a62bc 100644 --- a/src/tests/deposit_permission_edate_backend.sample +++ b/src/tests/deposit_permission_edate_backend.sample @@ -1,7 +1,10 @@ { + "transaction_id" : 1, + "timestamp": "\/Date(1447334003)\/", + "edate": "\/Date(1447334003)\/", "H_wire": "V1F0F5AWSNX60E6TXZEYDZRE84J8HMBGXEM09AEVV3N97MM75P6JQRSWR5KQVC1RBBF2SRXY7P10H0ZM0VETWPFAJJRBVJSXNMDWTYR", "merchant_pub": "BQ0TMTBV2XEJ8G2PXA9KRDD5WDT5EV29KPVS6J9RBQJCS8BCPQ70", - "mint": "localmint", + "mint": "demo.taler.net", "coins": [ { "f": { @@ -30,6 +33,5 @@ "value": 3, "fraction": 8, "currency": "KUDOS" - }, - "edate": "\/Date(1447392206)\/" -} \ No newline at end of file + } +} -- cgit v1.2.3 From 39e4f50b0a0d921f4910d390aaebd93c6a173867 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 4 Nov 2015 12:04:18 +0100 Subject: adding (initial) DB mgmt for deposits --- src/backend-lib/merchant_db.c | 33 ++++++++++++++++++++++++++++++++- src/backend/taler-merchant-httpd.c | 3 +++ src/backend/taler-merchant-httpd_pay.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c index 3db048db..ed89b1ac 100644 --- a/src/backend-lib/merchant_db.c +++ b/src/backend-lib/merchant_db.c @@ -104,7 +104,12 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) "contract_id INT8 REFERENCES contracts(contract_id)," "amount INT4 NOT NULL," "amount_fraction INT4 NOT NULL," - "coin_sig BYTEA NOT NULL);", + "coin_sig BYTEA NOT NULL);" + "CREATE %1$s TABLE IF NOT EXISTS deposits (" + "dep_perm TEXT NOT NULL" + "transaction_id INT8 NOT NULL" + "pending INT4 NOT NULL" + "mint_url TEXT NOT NULL);", tmp_str); ret = GNUNET_POSTGRES_exec (conn, sql); (void) GNUNET_POSTGRES_exec (conn, @@ -163,6 +168,16 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); PQclear (res); + EXITIF (NULL == (res = PQprepare + (conn, + "store_deposit_permission", + "INSERT INTO deposits" + "(dep_perm, transaction_id, pending, mint_url) " + "VALUES ($1, $2, $3, $4);", 4, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + EXITIF (NULL == (res = PQprepare (conn, "get_contract_product", @@ -219,6 +234,22 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) } +MERCHANT_DB_store_deposit_permission (PGconn *conn, + const char *deposit_permission, + uint64_t transaction_id, + unsigned int pending, + const char *mint_url) +{ + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_fixed_size (deposit_permission, strlen (deposit_permission)), + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_uint32 (&pending), + TALER_PQ_query_param_fixed_size (mint_url, strlen (mint_url)), + TALER_PQ_query_param_end + }; + +} + /** * Insert a contract record into the database and if successfull * return the serial number of the inserted row. diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 288aa330..ae9a4613 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -434,6 +434,9 @@ run (void *cls, char *const *args, const char *cfgfile, GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_shutdown, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "merchant launched\n"); + EXITIF (GNUNET_SYSERR == (nmints = TALER_MERCHANT_parse_mints (config, diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index 5f69a847..1c2a39a6 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -42,6 +42,7 @@ extern PGconn *db_conn; extern long long salt; extern unsigned int nmints; extern struct GNUNET_TIME_Relative edate_delay; +extern struct GNUNET_CRYPTO_EddsaPrivateKey privkey; /** * Fetch the deposit fee related to the given coin aggregate. @@ -133,6 +134,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, struct TALER_Amount coin_fee; struct GNUNET_TIME_Absolute edate; struct GNUNET_TIME_Absolute timestamp; + struct GNUNET_CRYPTO_EddsaPublicKey pubkey; struct TMH_PARSE_FieldSpecification spec[] = { TMH_PARSE_member_array ("coins", &coins), @@ -229,7 +231,34 @@ MH_handler_pay (struct TMH_RequestHandler *rh, if (-1 == TALER_amount_cmp (&max_fee, &acc_fee)) return MHD_HTTP_NOT_ACCEPTABLE; + + /* cutting off unneeded fields from deposit permission as + gotten from the wallet */ + if (-1 == json_object_del (root, "mint")) + return TMH_RESPONSE_reply_external_error (connection, + "malformed/non-existent 'mint' field"); + if (-1 == json_object_del (root, "coins")) + return TMH_RESPONSE_reply_external_error (connection, + "malformed/non-existent 'coins' field"); + + /* adding our public key to deposit permission */ + GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey); + json_object_set_new (root, + "merchant_pub", + TALER_json_from_data (&pubkey, sizeof (pubkey))); + + json_array_foreach (coins, coins_cnt, coin_aggregate) + { + /* melt single coin with deposit permission "template" */ + if (-1 == json_object_update (root, coin_aggregate)) + return TMH_RESPONSE_reply_internal_error (connection, "deposit permission not generated"); + + /* store a stringification of it (paired with its transaction id) + into DB */ + char *deposit_permission_str = json_dumps (root, JSON_COMPACT); + } + /* 3 For each coin in DB a. Generate a deposit permission -- cgit v1.2.3 From 79abf2bff00101af5a14f18bcb8877873df8b973 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 4 Nov 2015 16:51:39 +0100 Subject: adding the call to /deposit (through mint-lib). To test --- src/backend-lib/merchant_db.c | 111 ++++++++++++++++++++++++++++++++- src/backend-lib/merchant_db.h | 17 +++++ src/backend/taler-merchant-httpd_pay.c | 95 ++++++++++++++++++++++++---- 3 files changed, 208 insertions(+), 15 deletions(-) diff --git a/src/backend-lib/merchant_db.c b/src/backend-lib/merchant_db.c index ed89b1ac..415ff223 100644 --- a/src/backend-lib/merchant_db.c +++ b/src/backend-lib/merchant_db.c @@ -106,9 +106,9 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) "amount_fraction INT4 NOT NULL," "coin_sig BYTEA NOT NULL);" "CREATE %1$s TABLE IF NOT EXISTS deposits (" - "dep_perm TEXT NOT NULL" - "transaction_id INT8 NOT NULL" - "pending INT4 NOT NULL" + "dep_perm TEXT NOT NULL," + "transaction_id INT8 PRIMARY KEY," + "pending INT4 NOT NULL," "mint_url TEXT NOT NULL);", tmp_str); ret = GNUNET_POSTGRES_exec (conn, sql); @@ -174,6 +174,18 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) "INSERT INTO deposits" "(dep_perm, transaction_id, pending, mint_url) " "VALUES ($1, $2, $3, $4);", 4, NULL))); + + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); + PQclear (res); + + + EXITIF (NULL == (res = PQprepare + (conn, + "update_deposit_permission", + "UPDATE deposits " + "SET pending = $1 " + "WHERE transaction_id = $2", 2, NULL))); + EXITIF (PGRES_COMMAND_OK != (status = PQresultStatus(res))); PQclear (res); @@ -233,13 +245,73 @@ MERCHANT_DB_initialize (PGconn *conn, int tmp) return GNUNET_SYSERR; } +/** + * Update the pending column of a deposit permission + * @param conn handle to DB + * @param transaction_id identification number of the deposit to + * update + * @param pending true if still pending, false otherwise (i.e. the + * mint did respond something) + * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors + */ +uint32_t +MERCHANT_DB_update_deposit_permission (PGconn *conn, + uint64_t transaction_id, + unsigned int pending) +{ + PGresult *res; + ExecStatusType status; + + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&transaction_id), + TALER_PQ_query_param_uint32 (&pending), + }; + + res = TALER_PQ_exec_prepared (conn, "update_deposit_permission", params); + status = PQresultStatus (res); + + if (PGRES_COMMAND_OK != status) + { + + const char *sqlstate; + + sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); + if (NULL == sqlstate) + { + /* very unexpected... */ + GNUNET_break (0); + PQclear (res); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database commit failure: %s\n", + sqlstate); + PQclear (res); + return GNUNET_SYSERR; + } +} +/** + * Store a deposit permission in DB. To be mainly used if /deposit should + * be retried; also, the merchant can benefit from this information in case + * he needs to later investigate about some transaction_id. + * @param conn DB handle + * @param transaction_id identification number of this payment (which is the + * same id of the related contract) + * @param pending if true, this payment got to a persistent state + * @param which mint is to get this deposit permission + * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors + */ +uint32_t MERCHANT_DB_store_deposit_permission (PGconn *conn, const char *deposit_permission, uint64_t transaction_id, unsigned int pending, const char *mint_url) { + PGresult *res; + ExecStatusType status; + struct TALER_PQ_QueryParam params[] = { TALER_PQ_query_param_fixed_size (deposit_permission, strlen (deposit_permission)), TALER_PQ_query_param_uint64 (&transaction_id), @@ -247,7 +319,40 @@ MERCHANT_DB_store_deposit_permission (PGconn *conn, TALER_PQ_query_param_fixed_size (mint_url, strlen (mint_url)), TALER_PQ_query_param_end }; + res = TALER_PQ_exec_prepared (conn, "store_deposit_permission", params); + status = PQresultStatus (res); + if (PGRES_COMMAND_OK != status) + { + const char *sqlstate; + + sqlstate = PQresultErrorField (res, PG_DIAG_SQLSTATE); + if (NULL == sqlstate) + { + /* very unexpected... */ + GNUNET_break (0); + PQclear (res); + return GNUNET_SYSERR; + } + /* 40P01: deadlock, 40001: serialization failure */ + if ( (0 == strcmp (sqlstate, + "23505"))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Inserting same transaction id twice\n"); + /* Primary key violation */ + PQclear (res); + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database commit failure: %s\n", + sqlstate); + PQclear (res); + return GNUNET_SYSERR; + } + + PQclear (res); + return GNUNET_OK; } /** diff --git a/src/backend-lib/merchant_db.h b/src/backend-lib/merchant_db.h index 540bddbd..c748002e 100644 --- a/src/backend-lib/merchant_db.h +++ b/src/backend-lib/merchant_db.h @@ -164,4 +164,21 @@ MERCHANT_DB_get_contract_handle (PGconn *conn, const struct GNUNET_HashCode *h_contract, struct MERCHANT_contract_handle *contract_handle); +/** + * Store a deposit permission in DB. To be mainly used if /deposit should + * be retried; also, the merchant can benefit from this information in case + * he needs to later investigate about some transaction_id. + * @param conn DB handle + * @param transaction_id identification number of this payment (which is the + * same id of the related contract) + * @param pending if true, this payment got to a persistent state + * @param which mint is to get this deposit permission + * @return GNUNET_OK if successful, GNUNET_SYSERR upon errors + */ +uint32_t +MERCHANT_DB_store_deposit_permission (PGconn *conn, + const char *deposit_permission, + uint64_t transaction_id, + unsigned int pending, + const char *mint_url); /* end of merchant-db.h */ diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index 1c2a39a6..a08dd5e2 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -101,6 +101,26 @@ deposit_fee_from_coin_aggregate (struct MHD_Connection *connection, return GNUNET_OK; } + +/** + * Callback to handle a deposit permission's response. How does this behave if the mint + * goes offline during a call? + * @param cls closure (in our invocation it will be the transaction_id, so the cb can refer + * to the right DB row for this deposit permission) + * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit; + * 0 if the mint's reply is bogus (fails to follow the protocol) + * @param proof the received JSON reply, should be kept as proof (and, in case of errors, + * be forwarded to the customer) + */ +void +deposit_cb (void *cls, unsigned int http_status, json_t *proof) +{ + if (MHD_HTTP_OK == http_status) + ; /* set pending to false in DB and notify the frontend with "OK" */ + else + ; /* set a timeout to retry */ +} + /** * Accomplish this payment. * @param rh context of the handler @@ -124,26 +144,47 @@ MH_handler_pay (struct TMH_RequestHandler *rh, json_t *coins; char *chosen_mint; json_t *coin_aggregate; + json_t *wire_details; unsigned int coins_cnt; unsigned int mint_index; /*a cell in the global array*/ uint64_t transaction_id; int res; + struct TALER_MINT_DepositHandle *dh; struct TALER_Amount max_fee; struct TALER_Amount acc_fee; struct TALER_Amount coin_fee; + struct TALER_Amount amount; struct GNUNET_TIME_Absolute edate; struct GNUNET_TIME_Absolute timestamp; - struct GNUNET_CRYPTO_EddsaPublicKey pubkey; + struct GNUNET_TIME_Absolute refund_deadline; + struct TALER_MerchantPublicKeyP pubkey; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_DenominationPublicKey denom_pub; + struct TALER_DenominationSignature ub_sig; + struct TALER_CoinSpendSignatureP coin_sig; + struct GNUNET_HashCode h_contract; struct TMH_PARSE_FieldSpecification spec[] = { TMH_PARSE_member_array ("coins", &coins), TMH_PARSE_member_string ("mint", &chosen_mint), TMH_PARSE_member_amount ("max_fee", &max_fee), TMH_PARSE_member_time_abs ("timestamp", ×tamp), + TMH_PARSE_member_time_abs ("refund_deadline", &refund_deadline), TMH_PARSE_member_uint64 ("transaction_id", &transaction_id), + TMH_PARSE_member_fixed ("H_contract", &h_contract), TMH_PARSE_MEMBER_END }; + + struct TMH_PARSE_FieldSpecification coin_aggregate_spec[] = { + TMH_PARSE_member_amount ("f", &amount), + TMH_PARSE_member_fixed ("coin_pub", &coin_pub.eddsa_pub), + TMH_PARSE_member_denomination_public_key ("denom_pub", &denom_pub), + TMH_PARSE_member_denomination_signature ("ub_sig", &ub_sig), + TMH_PARSE_member_fixed ("coin_sig", &coin_sig.eddsa_signature), + TMH_PARSE_MEMBER_END + }; + res = TMH_PARSE_post_json (connection, connection_cls, upload_data, @@ -242,30 +283,60 @@ MH_handler_pay (struct TMH_RequestHandler *rh, "malformed/non-existent 'coins' field"); /* adding our public key to deposit permission */ - GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey); + GNUNET_CRYPTO_eddsa_key_get_public (&privkey, &pubkey.eddsa_pub); json_object_set_new (root, "merchant_pub", TALER_json_from_data (&pubkey, sizeof (pubkey))); + wire_details = MERCHANT_get_wire_json (wire, salt); json_array_foreach (coins, coins_cnt, coin_aggregate) { - /* melt single coin with deposit permission "template" */ + + /* 3 For each coin in DB + + a. Generate a deposit permission + b. store it and its tid in DB + c. POST to the mint (see mint-lib for this) + (retry until getting a persisten state) + */ + + + /* a */ if (-1 == json_object_update (root, coin_aggregate)) return TMH_RESPONSE_reply_internal_error (connection, "deposit permission not generated"); - /* store a stringification of it (paired with its transaction id) - into DB */ + /* b */ char *deposit_permission_str = json_dumps (root, JSON_COMPACT); + if (GNUNET_OK != MERCHANT_DB_store_deposit_permission (db_conn, + deposit_permission_str, + transaction_id, + 1, + mints[mint_index].hostname)) + return TMH_RESPONSE_reply_internal_error (connection, "internal DB failure"); + res = TMH_PARSE_json_data (connection, + coin_aggregate, + coin_aggregate_spec); + if (GNUNET_OK != res) + return res; /* may return GNUNET_NO */ + + + dh = TALER_MINT_deposit (mints[mint_index].conn, + &amount, + wire_details, + &h_contract, + &coin_pub, + &ub_sig, + &denom_pub, + timestamp, + transaction_id, + &pubkey, + refund_deadline, + &coin_sig, + deposit_cb, + &transaction_id); /*may be destroyed by the time the cb gets called..*/ } - /* 3 For each coin in DB - - a. Generate a deposit permission - b. store it and its tid in DB - c. POST to the mint (see mint-lib for this) - (retry until getting a persisten state) - */ /* 4 Return response code: success, or whatever data the mint sent back regarding some bad coin */ } -- cgit v1.2.3 From 902cb22b5e6d0af4766f0a14045fb16dd979bf31 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 4 Nov 2015 21:11:56 +0100 Subject: adding wire_deadline to deposit arguments --- src/backend/taler-merchant-httpd_pay.c | 8 +++++++- src/frontend/pay.php | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index a08dd5e2..6a9c4af2 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -158,6 +158,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, struct GNUNET_TIME_Absolute edate; struct GNUNET_TIME_Absolute timestamp; struct GNUNET_TIME_Absolute refund_deadline; + struct GNUNET_TIME_Absolute wire_deadline; struct TALER_MerchantPublicKeyP pubkey; struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_DenominationPublicKey denom_pub; @@ -171,6 +172,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, TMH_PARSE_member_amount ("max_fee", &max_fee), TMH_PARSE_member_time_abs ("timestamp", ×tamp), TMH_PARSE_member_time_abs ("refund_deadline", &refund_deadline), + TMH_PARSE_member_time_abs ("edate", &wire_deadline), TMH_PARSE_member_uint64 ("transaction_id", &transaction_id), TMH_PARSE_member_fixed ("H_contract", &h_contract), TMH_PARSE_MEMBER_END @@ -303,7 +305,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, /* a */ if (-1 == json_object_update (root, coin_aggregate)) - return TMH_RESPONSE_reply_internal_error (connection, "deposit permission not generated"); + return TMH_RESPONSE_reply_internal_error (connection, "deposit permission not generated for storing"); /* b */ char *deposit_permission_str = json_dumps (root, JSON_COMPACT); @@ -320,8 +322,12 @@ MH_handler_pay (struct TMH_RequestHandler *rh, return res; /* may return GNUNET_NO */ + printf ("about to spend money\n"); + return MHD_NO; + dh = TALER_MINT_deposit (mints[mint_index].conn, &amount, + wire_deadline, wire_details, &h_contract, &coin_pub, diff --git a/src/frontend/pay.php b/src/frontend/pay.php index a23b9481..33f3b712 100644 --- a/src/frontend/pay.php +++ b/src/frontend/pay.php @@ -58,7 +58,7 @@ $new_deposit_permission_edate = array_merge ($new_deposit_permission, $edate); if ($cli_debug && !$backend_test) { - /* NOTE the newline at the end of 'echo's argument */ + /* DO NOTE the newline at the end of 'echo's argument */ //echo json_encode ($new_deposit_permission_edate, JSON_PRETTY_PRINT) echo json_encode ($new_deposit_permission, JSON_PRETTY_PRINT) . "\n"; -- cgit v1.2.3 From d99e30afaf337da28b527785cebc7aa22731e357 Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Wed, 4 Nov 2015 23:09:03 +0100 Subject: Deleting wire form in homepage, providing link to mint's website instead. --- src/frontend/index.html | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/src/frontend/index.html b/src/frontend/index.html index b2dcca7f..d73683df 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -68,39 +68,9 @@ obtain Taler coins. This is typically done using a wire transfer. However, as this is just a demonstrator, we will allow you to send the mint KUDOS coins using a simple - form on this website instead.

-

You begin by clicking on the Taler icon and selecting - "Create reserve". The extension will then display some - hexadecimal reserve public key, which you should copy to the - clipboard. After that, you can paste it into the form - below. When dealing with real currency, you would do - the same, except that you would have to copy the string - into the subject area for your wire transfer instead of - into this form. + form on the mint's website + (new tab).

-
-
-
- Paste your reserve public key here (right-click, "paste"): - - -
- Amount to credit to your reserve: - -
- -
-
-