From 8d8779e945cb8537e8ce669eae1c36390766e9ff Mon Sep 17 00:00:00 2001 From: Marcello Stanisci Date: Tue, 17 Nov 2015 11:50:37 +0100 Subject: Depositing all the coins. Memory corruption when destroying the MHD response object. --- src/backend/taler-merchant-httpd_obsolete.c | 986 ---------------------------- src/backend/taler-merchant-httpd_pay.c | 89 ++- src/include/merchant.h | 9 +- 3 files changed, 66 insertions(+), 1018 deletions(-) delete mode 100644 src/backend/taler-merchant-httpd_obsolete.c diff --git a/src/backend/taler-merchant-httpd_obsolete.c b/src/backend/taler-merchant-httpd_obsolete.c deleted file mode 100644 index a96dad59..00000000 --- a/src/backend/taler-merchant-httpd_obsolete.c +++ /dev/null @@ -1,986 +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 -#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-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c index c933699e..eff11fd2 100644 --- a/src/backend/taler-merchant-httpd_pay.c +++ b/src/backend/taler-merchant-httpd_pay.c @@ -132,11 +132,6 @@ struct PayContext */ uint64_t transaction_id; - /** - * Offset of this coin into the array of all coins outcomes - */ - unsigned int index; - /** * How many coins this paymen is made of. */ @@ -151,6 +146,25 @@ struct PayContext }; +/** + * Information needed by a single /deposit callback to refer to its + * own coin inside the confirmations array, namely `struct MERCHANT_DepositConfirmation *dc` + * above. Note: this information can NOT be shared between all the callbacks. + */ +struct DepositCallbackContext +{ + + /** + * Offset of this coin into the array of all coins outcomes + */ + unsigned int index; + + /** + * Reference to the main PayContext + */ + struct PayContext *pc; + +}; /** * Callback to handle a deposit permission's response. @@ -169,46 +183,43 @@ deposit_cb (void *cls, unsigned int http_status, json_t *proof) { - struct PayContext *pc = cls; - /* NOTE: what if the mint doesn't respond? Does this callback get - called? */ + struct DepositCallbackContext *dcc = cls; int i; /*FIXME the index is the same for every individual cb */ - printf ("deposit cb [coins_index %d]\n", pc->index); + printf ("deposit cb [coins_index %d]\n", dcc->index); if (GNUNET_SYSERR == MERCHANT_DB_update_deposit_permission (db_conn, - pc->transaction_id, + dcc->pc->transaction_id, 0)) /* TODO */ printf ("db error\n"); printf ("/deposit ack'd\n"); - pc->dc[pc->index].ackd = 1; - pc->dc[pc->index].exit_status = http_status; - pc->dc[pc->index].proof = proof; + dcc->pc->dc[dcc->index].ackd = 1; + dcc->pc->dc[dcc->index].exit_status = http_status; + dcc->pc->dc[dcc->index].proof = proof; /* loop through the confirmation array and return accordingly */ - for (i = 0; i < pc->coins_cnt; i++) + for (i = 0; i < dcc->pc->coins_cnt; i++) { /* just return if there is at least one coin to be still confirmed */ - if (! pc->dc[i].ackd) + if (! dcc->pc->dc[i].ackd) { printf ("still vacant coins\n"); return; } } - /* Clean up what we can already */ - GNUNET_free (pc->dc); - pc->dc = NULL; - + printf ("All /deposit(s) ack'd\n"); - pc->response = MHD_create_response_from_buffer (strlen ("All coins ack'd by the mint\n"), - "All coins ack'd by the mint\n", - MHD_RESPMEM_MUST_FREE); - pc->response_code = MHD_HTTP_OK; - MHD_resume_connection (pc->connection); + dcc->pc->response = MHD_create_response_from_buffer (strlen ("All coins ack'd by the mint\n"), + "All coins ack'd by the mint\n", + MHD_RESPMEM_MUST_FREE); + printf ("response [%p]\n", dcc->pc->response); + dcc->pc->response_code = MHD_HTTP_OK; + /* Clean up what we can already */ + MHD_resume_connection (dcc->pc->connection); TM_trigger_daemon (); /* we resumed, kick MHD */ } @@ -216,9 +227,14 @@ deposit_cb (void *cls, static void pay_context_cleanup (struct TM_HandlerContext *hc) { + int i; struct PayContext *pc = (struct PayContext *) hc; + for (i = 0; i < pc->coins_cnt; i++) + GNUNET_free_non_null (pc->dc[i].dcc); + TMH_PARSE_post_cleanup_callback (pc->json_parse_context); + printf ("response cu [%p]\n", pc->response); if (NULL != pc->response) { MHD_destroy_response (pc->response); @@ -315,8 +331,12 @@ MH_handler_pay (struct TMH_RequestHandler *rh, res = MHD_queue_response (connection, pc->response_code, pc->response); - MHD_destroy_response (pc->response); - pc->response = NULL; + printf ("response main [%p]\n", pc->response); + if (pc->response != NULL) + { + MHD_destroy_response (pc->response); + pc->response = NULL; + } return res; } @@ -447,6 +467,12 @@ MH_handler_pay (struct TMH_RequestHandler *rh, /* suspend connection until the last coin has been ack'd to the cb. That last cb will finally resume the connection and send back a response */ MHD_suspend_connection (connection); + + pc->dc = dc; + pc->coins_cnt = coins_cnt; + pc->transaction_id = transaction_id; + + json_array_foreach (coins, coins_index, coin_aggregate) { @@ -477,10 +503,11 @@ MH_handler_pay (struct TMH_RequestHandler *rh, if (GNUNET_OK != res) return res; /* may return GNUNET_NO */ - pc->index = coins_index; - pc->dc = dc; - pc->coins_cnt = coins_cnt; - pc->transaction_id = transaction_id; + /* c */ + struct DepositCallbackContext *percoin_dcc = GNUNET_new (struct DepositCallbackContext); + pc->dc[coins_index].dcc = percoin_dcc; + percoin_dcc->index = coins_index; + percoin_dcc->pc = pc; dh = TALER_MINT_deposit (mints[mint_index].conn, &amount, @@ -496,7 +523,7 @@ MH_handler_pay (struct TMH_RequestHandler *rh, refund_deadline, &coin_sig, &deposit_cb, - pc); /*FIXME TODO instantiate an individual cls for each + percoin_dcc); /*FIXME TODO instantiate an individual cls for each cb: each of them needs an index which points the array of all the confirmations */ if (NULL == dh) diff --git a/src/include/merchant.h b/src/include/merchant.h index 582c4cf1..eb430360 100644 --- a/src/include/merchant.h +++ b/src/include/merchant.h @@ -41,10 +41,17 @@ } while (0) /** - * Outcome of a /deposit request for a coin + * Outcome of a /deposit request for a coin. Typically forming an array enclosed + * into the unique PayContext */ struct MERCHANT_DepositConfirmation { + /** + * Reference to the per-deposit-handler Context. Needed by the + * cleanup function to get it freed + */ + struct DepositCallbackContext *dcc; + /** * True if this coin's outcome has been read from * its cb -- cgit v1.2.3