diff options
author | Florian Dold <florian.dold@gmail.com> | 2018-01-16 01:30:57 +0100 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2018-01-16 01:30:57 +0100 |
commit | c4fe61ac6d1a74320638eb4f40de7c18d30a4d8c (patch) | |
tree | 59b48019b249bf9fc01478bd63ed05649503036b /src | |
parent | 50c7fe8506a8f33ffa0f4633c1e693a4c8c9b961 (diff) |
fix proposal test cases, fix use after free in refunds
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 5 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 2 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_proposal.c | 84 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_refund.c | 9 | ||||
-rw-r--r-- | src/backenddb/plugin_merchantdb_postgres.c | 1 | ||||
-rw-r--r-- | src/include/taler_merchant_service.h | 20 | ||||
-rw-r--r-- | src/lib/merchant_api_proposal.c | 144 | ||||
-rw-r--r-- | src/lib/test_merchant_api.c | 99 | ||||
-rw-r--r-- | src/lib/test_merchant_api.conf | 5 | ||||
-rw-r--r-- | src/merchant-tools/taler-merchant-generate-payments.c | 75 |
10 files changed, 349 insertions, 95 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 2bf927db..998d3b04 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -269,7 +269,8 @@ url_handler (void *cls, &TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND }; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Handling request for URL `%s'\n", + "Handling request (%s) for URL `%s'\n", + method, url); for (unsigned int i=0;NULL != handlers[i].url;i++) { @@ -857,6 +858,8 @@ run (void *cls, char *wireformat; int fh; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "running taler-merchant-httpd\n"); + result = GNUNET_SYSERR; GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h index 1e917e7f..a1b0f845 100644 --- a/src/backend/taler-merchant-httpd.h +++ b/src/backend/taler-merchant-httpd.h @@ -72,7 +72,7 @@ struct IterateInstancesCls /** - * Information that defines a merchant "instance". That way, a single + * Information that defines a merchant "instance". Tha4673t way, a single * backend can account for several merchants, as used to do in donation * shops */ diff --git a/src/backend/taler-merchant-httpd_proposal.c b/src/backend/taler-merchant-httpd_proposal.c index c79e2a81..01d3c5a5 100644 --- a/src/backend/taler-merchant-httpd_proposal.c +++ b/src/backend/taler-merchant-httpd_proposal.c @@ -135,8 +135,6 @@ proposal_put (struct MHD_Connection *connection, { int res; struct MerchantInstance *mi; - struct TALER_ProposalDataPS pdps; - struct GNUNET_CRYPTO_EddsaSignature merchant_sig; struct TALER_Amount total; const char *order_id; json_t *products; @@ -317,9 +315,6 @@ proposal_put (struct MHD_Connection *connection, TALER_EC_CONTRACT_INSTANCE_UNKNOWN, "Unknown instance given"); } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Signing contract on behalf of instance '%s'\n", - mi->id); /* add fields to the contract that the backend should provide */ json_object_set (order, "exchanges", @@ -337,23 +332,31 @@ proposal_put (struct MHD_Connection *connection, "merchant_pub", GNUNET_JSON_from_data_auto (&mi->pubkey)); - /* create proposal signature */ - pdps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); - pdps.purpose.size = htonl (sizeof (pdps)); - if (GNUNET_OK != - TALER_JSON_hash (order, - &pdps.hash)) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Inserting order '%s' for instance '%s'\n", + order_id, + mi->id); + + json_t *dummy_contract_terms; + qs = db->find_orders (db->cls, + &dummy_contract_terms, + order_id, + &mi->pubkey); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) { - GNUNET_break (0); - GNUNET_JSON_parse_free (spec); - return TMH_RESPONSE_reply_internal_error (connection, - TALER_EC_INTERNAL_LOGIC_ERROR, - "Could not hash order"); + if ( (GNUNET_DB_STATUS_SOFT_ERROR == qs) || + (GNUNET_DB_STATUS_HARD_ERROR == qs) ) + { + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_PROPOSAL_STORE_DB_ERROR, + "db error: could not check for existing order"); + } + return TMH_RESPONSE_reply_external_error (connection, + TALER_EC_PROPOSAL_STORE_DB_ERROR, + "proposal already exists"); } - GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, - &pdps.purpose, - &merchant_sig); + for (unsigned int i=0;i<MAX_RETRIES;i++) { @@ -377,9 +380,11 @@ proposal_put (struct MHD_Connection *connection, "db error: could not store this proposal's data into db"); } - res = TMH_RESPONSE_reply_json (connection, - json_object (), - MHD_HTTP_OK); + res = TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:s}", + "order_id", + order_id); GNUNET_JSON_parse_free (spec); return res; } @@ -579,7 +584,9 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh, } } - const char *stored_nonce = json_string_value (json_object_get(contract_terms, "nonce")); + GNUNET_assert (NULL != contract_terms); + + const char *stored_nonce = json_string_value (json_object_get (contract_terms, "nonce")); if (NULL == stored_nonce) { @@ -596,10 +603,33 @@ MH_handler_proposal_lookup (struct TMH_RequestHandler *rh, "mismatched nonce"); } - res = TMH_RESPONSE_reply_json (connection, - contract_terms, - MHD_HTTP_OK); - json_decref (contract_terms); + struct TALER_ProposalDataPS pdps; + struct GNUNET_CRYPTO_EddsaSignature merchant_sig; + + /* create proposal signature */ + pdps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_CONTRACT); + pdps.purpose.size = htonl (sizeof (pdps)); + if (GNUNET_OK != + TALER_JSON_hash (contract_terms, + &pdps.hash)) + { + GNUNET_break (0); + return TMH_RESPONSE_reply_internal_error (connection, + TALER_EC_INTERNAL_LOGIC_ERROR, + "Could not hash order"); + } + + GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, + &pdps.purpose, + &merchant_sig); + + res = TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_OK, + "{ s:o, s:o }", + "contract_terms", + contract_terms, + "sig", + GNUNET_JSON_from_data_auto (&merchant_sig)); return res; } diff --git a/src/backend/taler-merchant-httpd_refund.c b/src/backend/taler-merchant-httpd_refund.c index 47ebda0f..8df1ea46 100644 --- a/src/backend/taler-merchant-httpd_refund.c +++ b/src/backend/taler-merchant-httpd_refund.c @@ -260,15 +260,16 @@ MH_handler_refund_increase (struct TMH_RequestHandler *rh, * the information needed to generate the right response. */ - json_decref (contract_terms); - json_decref (root); - GNUNET_JSON_parse_free (spec); - confirmation.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND_OK); confirmation.purpose.size = htonl (sizeof (struct TALER_MerchantRefundConfirmationPS)); GNUNET_CRYPTO_hash (order_id, strlen (order_id), &confirmation.h_order_id); + + json_decref (contract_terms); + json_decref (root); + GNUNET_JSON_parse_free (spec); + if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mi->privkey.eddsa_priv, &confirmation.purpose, diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index ee8a947a..66a35293 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -76,6 +76,7 @@ postgres_drop_tables (void *cls) GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_pickups CASCADE;"), GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserve_credits CASCADE;"), GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_tip_reserves CASCADE;"), + GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS merchant_orders CASCADE;"), GNUNET_PQ_EXECUTE_STATEMENT_END }; diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index 51db1354..63c396a7 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -147,18 +147,14 @@ struct TALER_MERCHANT_ProposalOperation; * 0 if the backend's reply is bogus (fails to follow the protocol) * @param ec taler-specific error code * @param obj raw JSON reply, or error details if the request failed - * @param contract_terms completed contract, NULL on error - * @param sig merchant's signature over the contract, NULL on error - * @param hash proposal data's hashcode, NULL on error + * @param order_id order id of the newly created order */ typedef void (*TALER_MERCHANT_ProposalCallback) (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, const json_t *obj, - const json_t *contract_terms, - const struct TALER_MerchantSignatureP *sig, - const struct GNUNET_HashCode *hash); + const char *order_id); /** @@ -207,7 +203,10 @@ struct TALER_MERCHANT_ProposalLookupOperation; typedef void (*TALER_MERCHANT_ProposalLookupOperationCallback) (void *cls, unsigned int http_status, - const json_t *body); + const json_t *body, + const json_t *contract_terms, + const struct TALER_MerchantSignatureP *sig, + const struct GNUNET_HashCode *hash); /** @@ -216,7 +215,9 @@ typedef void * * @param ctx execution context * @param backend_url base URL of the merchant backend - * @param transaction_id transaction id used to perform the lookup + * @param order_id order id used to perform the lookup + * @param nonce nonce to use, only used when requesting the proposal the first time, + * can be NULL to omit the nonce (after the first request) * @param plo_cb callback which will work the response gotten from the backend * @param plo_cb_cls closure to pass to @a history_cb * @return handle for this operation, NULL upon errors @@ -224,8 +225,9 @@ typedef void struct TALER_MERCHANT_ProposalLookupOperation * TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx, const char *backend_url, - const char *transaction_id, + const char *order_id, const char *instance, + const struct GNUNET_CRYPTO_EddsaPublicKey *nonce, TALER_MERCHANT_ProposalLookupOperationCallback plo_cb, void *plo_cb_cls); diff --git a/src/lib/merchant_api_proposal.c b/src/lib/merchant_api_proposal.c index fa7e6b68..88d902b4 100644 --- a/src/lib/merchant_api_proposal.c +++ b/src/lib/merchant_api_proposal.c @@ -99,6 +99,16 @@ struct TALER_MERCHANT_ProposalLookupOperation */ struct GNUNET_CURL_Context *ctx; + /** + * Should we send the lookup operation with a nonce? + */ + int has_nonce; + + /** + * Nonce, only initialized if has_nonce is GNUNET_YES. + */ + struct GNUNET_CRYPTO_EddsaPublicKey nonce; + }; @@ -116,29 +126,19 @@ handle_proposal_finished (void *cls, const json_t *json) { struct TALER_MERCHANT_ProposalOperation *po = cls; - json_t *contract_terms; - const struct TALER_MerchantSignatureP *sigp; - const struct GNUNET_HashCode *hashp; - struct TALER_MerchantSignatureP sig; - struct GNUNET_HashCode hash; + const char *order_id; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("order_id", &order_id), + GNUNET_JSON_spec_end() + }; po->job = NULL; - contract_terms = NULL; - sigp = NULL; - hashp = NULL; switch (response_code) { case 0: break; case MHD_HTTP_OK: { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("data", &contract_terms), - GNUNET_JSON_spec_fixed_auto ("sig", &sig), - GNUNET_JSON_spec_fixed_auto ("hash", &hash), - GNUNET_JSON_spec_end() - }; - if (GNUNET_OK != GNUNET_JSON_parse (json, spec, @@ -148,8 +148,6 @@ handle_proposal_finished (void *cls, response_code = 0; break; } - hashp = &hash; - sigp = &sig; } break; case MHD_HTTP_BAD_REQUEST: @@ -183,11 +181,8 @@ handle_proposal_finished (void *cls, response_code, TALER_JSON_get_error_code (json), json, - contract_terms, - sigp, - hashp); - if (NULL != contract_terms) - json_decref (contract_terms); + order_id); + GNUNET_JSON_parse_free (spec); TALER_MERCHANT_proposal_cancel (po); } @@ -267,6 +262,70 @@ handle_proposal_lookup_finished (void *cls, const json_t *json) { struct TALER_MERCHANT_ProposalLookupOperation *plo = cls; + json_t *contract_terms; + struct TALER_MerchantSignatureP sig; + struct GNUNET_HashCode hash; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("contract_terms", &contract_terms), + GNUNET_JSON_spec_fixed_auto ("sig", &sig), + GNUNET_JSON_spec_end() + }; + + if (MHD_HTTP_OK != response_code) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "proposal lookup failed with HTTP status code %u\n", + (unsigned int) response_code); + char *s = json_dumps (json, JSON_COMPACT); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "error json: %s\n", + s); + GNUNET_free_non_null (s); + GNUNET_break_op (0); + plo->cb (plo->cb_cls, + response_code, + json, + NULL, + NULL, + NULL); + return; + } + + if (GNUNET_OK != + GNUNET_JSON_parse (json, + spec, + NULL, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "proposal lookup failed to parse JSON\n"); + char *s = json_dumps (json, JSON_COMPACT); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "proposal json was: %s\n", + s); + GNUNET_free_non_null (s); + GNUNET_break_op (0); + plo->cb (plo->cb_cls, + 0, + json, + NULL, + NULL, + NULL); + return; + } + + if (GNUNET_OK != TALER_JSON_hash (contract_terms, &hash)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "proposal lookup failed to hash proposal JSON\n"); + GNUNET_break (0); + plo->cb (plo->cb_cls, + 0, + json, + NULL, + NULL, + NULL); + return; + } plo->job = NULL; /** @@ -275,7 +334,10 @@ handle_proposal_lookup_finished (void *cls, */ plo->cb (plo->cb_cls, response_code, - json); + json, + contract_terms, + &sig, + &hash); TALER_MERCHANT_proposal_lookup_cancel (plo); } @@ -286,7 +348,8 @@ handle_proposal_lookup_finished (void *cls, * * @param ctx execution context * @param backend_uri base URL of the merchant backend - * @param transaction_id transaction id used to perform the lookup + * @param order_id order id used to perform the lookup + * @param nonce nonce used to perform the lookup * @param plo_cb callback which will work the response gotten from the backend * @param plo_cb_cls closure to pass to @a history_cb * @return handle for this operation, NULL upon errors @@ -296,6 +359,7 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx, const char *backend_uri, const char *order_id, const char *instance, + const struct GNUNET_CRYPTO_EddsaPublicKey *nonce, TALER_MERCHANT_ProposalLookupOperationCallback plo_cb, void *plo_cb_cls) { @@ -309,11 +373,29 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx, plo->cb_cls = plo_cb_cls; base = MAH_path_to_url_ (backend_uri, "/proposal"); - GNUNET_asprintf (&plo->url, - "%s?order_id=%s&instance=%s", - base, - order_id, - instance); + if (NULL != nonce) + { + char *nonce_str; + plo->has_nonce = GNUNET_YES; + plo->nonce = *nonce; + nonce_str = GNUNET_STRINGS_data_to_string_alloc (nonce, sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + GNUNET_assert (NULL != nonce_str); + GNUNET_asprintf (&plo->url, + "%s?order_id=%s&instance=%s&nonce=%s", + base, + order_id, + instance, + nonce_str); + GNUNET_free (nonce_str); + } + else + { + GNUNET_asprintf (&plo->url, + "%s?order_id=%s&instance=%s", + base, + order_id, + instance); + } GNUNET_free (base); eh = curl_easy_init (); if (CURLE_OK != curl_easy_setopt (eh, @@ -324,6 +406,10 @@ TALER_MERCHANT_proposal_lookup (struct GNUNET_CURL_Context *ctx, return NULL; } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "looking up proposal from %s\n", + plo->url); + if (NULL == (plo->job = GNUNET_CURL_job_add (ctx, eh, GNUNET_YES, diff --git a/src/lib/test_merchant_api.c b/src/lib/test_merchant_api.c index b09d78a3..ea119a12 100644 --- a/src/lib/test_merchant_api.c +++ b/src/lib/test_merchant_api.c @@ -500,6 +500,11 @@ struct Command struct TALER_MERCHANT_ProposalOperation *po; /** + * Handle to the active GET /proposal operation, or NULL. + */ + struct TALER_MERCHANT_ProposalLookupOperation *plo; + + /** * Full contract in JSON, set by the /contract operation. * FIXME: verify in the code that this bit is actually proposal * data and not the whole proposal. @@ -516,6 +521,12 @@ struct Command */ struct GNUNET_HashCode hash; + /** + * The nonce set by the customer looking up the contract + * the first time. + */ + struct GNUNET_CRYPTO_EddsaPublicKey nonce; + } proposal; /** @@ -1538,6 +1549,39 @@ reserve_withdraw_cb (void *cls, /** + * Callback for GET /proposal issued at backend. + * Used to initialize the proposal after it was created. + * + * @param cls closure + * @param http_status HTTP status code we got + * @param json full response we got + */ +static void +proposal_lookup_initial_cb (void *cls, + unsigned int http_status, + const json_t *json, + const json_t *contract_terms, + const struct TALER_MerchantSignatureP *sig, + const struct GNUNET_HashCode *hash) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + if (cmd->expected_response_code != http_status) + { + fail (is); + } + + cmd->details.proposal.hash = *hash; + cmd->details.proposal.merchant_sig = *sig; + cmd->details.proposal.contract_terms = json_deep_copy (contract_terms); + + cmd->details.proposal.plo = NULL; + next_command (is); +} + + +/** * Callback that works POST /proposal's output. * * @param cls closure @@ -1556,9 +1600,7 @@ proposal_cb (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, const json_t *obj, - const json_t *contract_terms, - const struct TALER_MerchantSignatureP *sig, - const struct GNUNET_HashCode *hash) + const char *order_id) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; @@ -1567,22 +1609,32 @@ proposal_cb (void *cls, switch (http_status) { case MHD_HTTP_OK: - cmd->details.proposal.contract_terms = json_incref ((json_t *) contract_terms); - cmd->details.proposal.merchant_sig = *sig; - cmd->details.proposal.hash = *hash; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Hashed proposal is `%s'\n", - GNUNET_h2s (hash)); break; - default: + default: { + char *s = json_dumps (obj, JSON_COMPACT); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected status code from /proposal: %u. Step %u\n", + "Unexpected status code from /proposal: %u. Step %u, response: %s\n", http_status, - is->ip); + is->ip, + s); + GNUNET_free_non_null (s); fail (is); + } return; } - next_command (is); + + if (NULL == (cmd->details.proposal.plo + = TALER_MERCHANT_proposal_lookup (ctx, + MERCHANT_URL, + order_id, + instance, + &cmd->details.proposal.nonce, + proposal_lookup_initial_cb, + is))) + { + GNUNET_break (0); + fail (is); + } } @@ -2011,7 +2063,10 @@ track_transfer_cb (void *cls, static void proposal_lookup_cb (void *cls, unsigned int http_status, - const json_t *json) + const json_t *json, + const json_t *contract_terms, + const struct TALER_MerchantSignatureP *sig, + const struct GNUNET_HashCode *hash) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; @@ -2421,12 +2476,20 @@ cleanup_state (struct InterpreterState *is) if (NULL != cmd->details.proposal.po) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Command %u (%s) did not complete\n", + "Command %u (%s) did not complete (proposal put)\n", i, cmd->label); TALER_MERCHANT_proposal_cancel (cmd->details.proposal.po); cmd->details.proposal.po = NULL; } + if (NULL != cmd->details.proposal.plo) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete (proposal lookup)\n", + i, + cmd->label); + TALER_MERCHANT_proposal_lookup_cancel (cmd->details.proposal.plo); + } if (NULL != cmd->details.proposal.contract_terms) { json_decref (cmd->details.proposal.contract_terms); @@ -2861,11 +2924,13 @@ interpreter_run (void *cls) order_id = json_string_value (json_object_get (ref->details.proposal.contract_terms, "order_id")); + if (NULL == (cmd->details.proposal_lookup.plo = TALER_MERCHANT_proposal_lookup (ctx, MERCHANT_URL, order_id, instance, + &ref->details.proposal.nonce, proposal_lookup_cb, is))) { @@ -3034,6 +3099,10 @@ interpreter_run (void *cls) GNUNET_assert (NULL != (order = json_loads (cmd->details.proposal.order, JSON_REJECT_DUPLICATES, &error))); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &cmd->details.proposal.nonce, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)); + if (NULL != instance) { json_t *merchant; diff --git a/src/lib/test_merchant_api.conf b/src/lib/test_merchant_api.conf index c4a4eb7f..67ef46bf 100644 --- a/src/lib/test_merchant_api.conf +++ b/src/lib/test_merchant_api.conf @@ -77,22 +77,27 @@ MASTER_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG [merchant-instance-default] KEYFILE = test_merchant.priv +NAME = Kudos Inc. [merchant-instance-wireformat-default] TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json [merchant-instance-tor] KEYFILE = tor_merchant.priv +NAME = The Tor Project [merchant-instance-tip] KEYFILE = reserve_tip.priv TIP_EXCHANGE = http://localhost:8084/ TIP_RESERVE_PRIV_FILENAME = reserve_key.priv +NAME = Test Tipping Merchant [merchant-instance-dtip] KEYFILE = reserve_dtip.priv TIP_EXCHANGE = http://localhost:8084/ TIP_RESERVE_PRIV_FILENAME = reserve_dkey.priv +NAME = Test Tipping Merchant 2 + [merchant-instance-wireformat-tor] TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/test.json diff --git a/src/merchant-tools/taler-merchant-generate-payments.c b/src/merchant-tools/taler-merchant-generate-payments.c index 887b9db4..7d40473f 100644 --- a/src/merchant-tools/taler-merchant-generate-payments.c +++ b/src/merchant-tools/taler-merchant-generate-payments.c @@ -387,6 +387,11 @@ struct Command struct TALER_MERCHANT_ProposalOperation *po; /** + * Handle to the active GET /proposal operation, or NULL + */ + struct TALER_MERCHANT_ProposalLookupOperation *plo; + + /** * Full contract in JSON, set by the /contract operation. * FIXME: verify in the code that this bit is actually proposal * data and not the whole proposal. @@ -519,7 +524,7 @@ next_command (struct InterpreterState *is) /** - * Callback that works PUT /proposal's output. + * Callback that processes GET /proposal's output. * * @param cls closure * @param http_status HTTP response code, 200 indicates success; @@ -533,18 +538,17 @@ next_command (struct InterpreterState *is) * @param h_contract hash of the contract, NULL on error */ static void -proposal_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - const json_t *obj, - const json_t *contract_terms, - const struct TALER_MerchantSignatureP *sig, - const struct GNUNET_HashCode *hash) +proposal_lookup_cb (void *cls, + unsigned int http_status, + const json_t *obj, + const json_t *contract_terms, + const struct TALER_MerchantSignatureP *sig, + const struct GNUNET_HashCode *hash) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; - cmd->details.proposal.po = NULL; + cmd->details.proposal.plo = NULL; switch (http_status) { case MHD_HTTP_OK: @@ -568,6 +572,59 @@ proposal_cb (void *cls, next_command (is); } +/** + * Callback that works PUT /proposal's output. + * + * @param cls closure + * @param http_status HTTP response code, 200 indicates success; + * 0 if the backend's reply is bogus (fails to follow the protocol) + * @param ec taler-specific error code + * @param obj the full received JSON reply, or + * error details if the request failed + */ +static void +proposal_cb (void *cls, + unsigned int http_status, + enum TALER_ErrorCode ec, + const json_t *obj, + const char *order_id) +{ + struct InterpreterState *is = cls; + struct Command *cmd = &is->commands[is->ip]; + + cmd->details.proposal.po = NULL; + switch (http_status) + { + case MHD_HTTP_OK: { + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + struct GNUNET_CRYPTO_EddsaPublicKey pub; + + priv = GNUNET_CRYPTO_eddsa_key_create (); + GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub); + GNUNET_free (priv); + + cmd->details.proposal.plo + = TALER_MERCHANT_proposal_lookup (ctx, + merchant_url, + order_id, + instance, + &pub, + &proposal_lookup_cb, + is); + } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "unexpected status code from /proposal: %u. Step %u\n", + http_status, + is->ip); + json_dumpf (obj, stderr, 0); + GNUNET_break (0); + fail (is); + return; + } +} + /** * Function called with the result of a /pay operation. |