diff options
Diffstat (limited to 'src/backend/taler-merchant-httpd.c')
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 971 |
1 files changed, 271 insertions, 700 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 2b489040..ae9a4613 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 */ @@ -25,24 +26,16 @@ #include <jansson.h> #include <gnunet/gnunet_util_lib.h> #include <curl/curl.h> -#include <taler/taler_json_lib.h> +#include <taler/taler_util.h> #include <taler/taler_mint_service.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_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" +#include "taler-merchant-httpd_contract.h" +#include "taler-merchant-httpd_pay.h" /** * Our hostname @@ -65,270 +58,80 @@ struct GNUNET_CRYPTO_EddsaPrivateKey *privkey; char *keyfile; /** - * The MHD Daemon - */ -static struct MHD_Daemon *mhd; - -/** - * Connection handle to the our database - */ -PGconn *db_conn; - -/** - * merchant's conf handle + * Mint context */ -struct GNUNET_CONFIGURATION_Handle *cfg; +static struct TALER_MINT_Context *mctx; /** - * Shutdown task identifier + * This value tells the mint by which date this merchant would like + * to receive the funds for a deposited payment */ -static struct GNUNET_SCHEDULER_Task *shutdown_task; +struct GNUNET_TIME_Relative edate_delay; /** - * Our wireformat + * To make 'TMH_PARSE_navigate_json ()' compile */ -static struct MERCHANT_WIREFORMAT_Sepa *wire; +char *TMH_mint_currency_string; /** - * Should we do a dry run where temporary tables are used for storing the data. + * Trusted mints */ -static int dry; +struct MERCHANT_Mint *mints; /** - * Global return code + * Active auditors */ -static int result; +struct MERCHANT_Auditor *auditors; /** - * Mint context - */ -static struct TALER_MINT_Context *mctx; - -/** - * Context information of the mints we trust + * Shutdown task identifier */ -struct Mint -{ - /** - * Public key of this mint - */ - struct GNUNET_CRYPTO_EddsaPublicKey pubkey; - - /** - * Connection handle to this mint - */ - struct TALER_MINT_Handle *conn; -}; +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; /** - * Hashmap (with 'big entries') to make a mint's base URL - * to point to some mint-describing structure + * Our wireformat */ -static struct GNUNET_CONTAINER_MultiHashMap *mints_hashmap; - - +struct MERCHANT_WIREFORMAT_Sepa *wire; /** - * Mints' URL,port,key triples + * Salt used to hash the wire object */ -struct MERCHANT_MintInfo *mint_infos; +long long salt; /** * 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 - * + * The number of active auditors */ -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; -} +unsigned int nauditors; /** - * 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 - * + * Should we do a dry run where temporary tables are used for storing the data. */ -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; -} +static int dry; /** - * 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 - * + * Global return code */ -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; - -} +static int result; -#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 + * Connection handle to the our database */ -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 +PGconn *db_conn; /** - * 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[]="\ -<!DOCTYPE html> \ -<html><title>Resource not found</title><body><center> \ -<h3>The resource you are looking for is not found.</h3> \ -</center></body></html>"; - static char page_MHD_HTTP_BAD_REQUEST[]="\ -<!DOCTYPE html> \ -<html><title>Bad request</title><body><center> \ -<h3>Malformed POSTed JSON.</h3> \ -</center></body></html>"; -static char page_MHD_HTTP_METHOD_NOT_ALLOWED[]="\ -<!DOCTYPE html> \ -<html><title>Method NOT allowed</title><body><center> \ -<h3>ONLY POSTs are allowed.</h3> \ -</center></body></html>"; - static char page_MHD_HTTP_INTERNAL_SERVER_ERROR[]="\ -<!DOCTYPE html> <html><title>Internal Server Error</title><body><center> \ -<h3>The server experienced an internal error and hence cannot serve your \ -request</h3></center></body></html>"; - 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 +180,116 @@ 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 - { - 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"))) + static struct TMH_RequestHandler handlers[] = { - 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)); + /* 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 }, - nounce = 0; - edate.abs_value_us = 0; + /* Further test page */ + { "/hello", MHD_HTTP_METHOD_GET, "text/plain", + "Hello, Customer.\n", 0, + &TMH_MHD_handler_static_response, MHD_HTTP_OK }, - 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; - } + { "/contract", MHD_HTTP_METHOD_POST, "application/json", + NULL, 0, + &MH_handler_contract, MHD_HTTP_OK }, - /* Reproducing the wire object */ - if (NULL == (j_wire = MERCHANT_get_wire_json (wire, - nounce, - edate))) + { "/contract", NULL, "text/plain", + "Only POST is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - { - printf ("wire object not reproduced\n"); - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } + { "/pay", MHD_HTTP_METHOD_POST, "application/json", + NULL, 0, + &MH_handler_pay, MHD_HTTP_OK }, - if (-1 == json_object_set (root, "wire", j_wire)) - { - printf ("depperm not augmented\n"); - status = MHD_HTTP_INTERNAL_SERVER_ERROR; - goto end; - } + { "/pay", NULL, "text/plain", + "Only POST is allowed", 0, + &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - /* 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", + "<html><title>404: not found</title></html>", 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))); - - GNUNET_free (contract_str); - - TMH_RESPONSE_reply_json (connection, response, MHD_HTTP_OK); - return MHD_YES; - + 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); - end: +} - if (NULL != resp) +/** + * Function called with information about who is auditing + * a particular mint and what key the mint is using. + * + * @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) +{ + /* 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 + 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 */ + + if (NULL != keys) { - EXITIF (MHD_YES != MHD_queue_response (connection, status, resp)); - return MHD_YES; - if (!no_destroy) - MHD_destroy_response (resp); + ((struct MERCHANT_Mint *) cls)->pending = 0; } else - { - - EXITIF (GNUNET_OK != failure_resp (connection, status)); - return MHD_YES; - - } - - EXITIF_exit: - result = GNUNET_SYSERR; - GNUNET_SCHEDULER_shutdown (); - return MHD_NO; + printf ("no keys gotten\n"); } + /** * Shutdown task (magically invoked when the application is being * quit) @@ -804,6 +300,19 @@ url_handler (void *cls, 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); + poller_task = NULL; + } if (NULL != mhd) { @@ -816,37 +325,100 @@ 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); - - } /** - * Function called with information about who is auditing - * a particular mint and what key the mint is using. + * Task that runs the context's event loop with the GNUnet scheduler. * - * @param cls closure - * @param keys information about the various keys used - * by the mint + * @param cls unused + * @param tc scheduler context (unused) */ static void -keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys) +context_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) { - /* 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; - + 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); } - +/** + * 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. * * @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 @@ -855,88 +427,87 @@ run (void *cls, char *const *args, const char *cfgfile, { unsigned int cnt; - 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); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "merchant launched\n"); + + EXITIF (GNUNET_SYSERR == + (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))); + 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", &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", + &TMH_mint_currency_string)); + + EXITIF (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_time (config, + "merchant", + "EDATE", + &edate_delay)); + + salt = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT64_MAX); 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].pending = 1; + mints[cnt].conn = TALER_MINT_connect (mctx, + mints[cnt].hostname, + &keys_mgmt_cb, + &mints[cnt]); + EXITIF (NULL == mints[cnt].conn); + poller_task = + GNUNET_SCHEDULER_add_now (&context_task, mctx); } mhd = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY, port, NULL, NULL, &url_handler, NULL, + MHD_OPTION_NOTIFY_COMPLETED, + &handle_mhd_completion_callback, 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: @@ -969,7 +540,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; |