diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index 4a2e775da..c0fd6949b 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -63,6 +63,7 @@ taler_mint_httpd_SOURCES = \ taler-mint-httpd.c \ taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \ taler-mint-httpd_responses.c taler-mint-httpd_responses.h \ + taler-mint-httpd_db.c taler-mint-httpd_db.h \ taler-mint-httpd_mhd.c \ taler-mint-httpd_keys.c \ taler-mint-httpd_deposit.c \ diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c index 6dc025877..372fc937d 100644 --- a/src/mint/mint_db.c +++ b/src/mint/mint_db.c @@ -22,6 +22,7 @@ #include "platform.h" #include "taler_db_lib.h" #include "taler_signatures.h" +#include "taler-mint-httpd_responses.h" #include "mint_db.h" #include "mint.h" #include diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h index eb7a105cb..5fab7c02a 100644 --- a/src/mint/mint_db.h +++ b/src/mint/mint_db.h @@ -24,10 +24,11 @@ #define _NEURO_MINT_DB_H #include +#include #include #include "taler_util.h" #include "taler_rsa.h" - +#include "taler-mint-httpd_db.h" /** * Public information about a coin. @@ -172,26 +173,6 @@ struct KnownCoin struct GNUNET_CRYPTO_EddsaPublicKey refresh_session_pub; }; -GNUNET_NETWORK_STRUCT_BEGIN - -struct Deposit -{ - /* FIXME: should be TALER_CoinPublicInfo */ - struct GNUNET_CRYPTO_EddsaPublicKey coin_pub; - struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; - struct TALER_RSA_Signature coin_sig; - struct TALER_RSA_SignaturePurpose purpose; - uint64_t transaction_id; - struct TALER_AmountNBO amount; - struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub; - struct GNUNET_HashCode h_contract; - struct GNUNET_HashCode h_wire; - /* TODO: uint16_t wire_size */ - char wire[]; /* string encoded wire JSON object */ -}; - -GNUNET_NETWORK_STRUCT_END - int TALER_MINT_DB_prepare (PGconn *db_conn); @@ -350,7 +331,6 @@ TALER_MINT_DB_insert_known_coin (PGconn *db_conn, const struct KnownCoin *known_coin); - /** * Get the thread-local database-handle. * Connect to the db if the connection does not exist yet. diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c new file mode 100644 index 000000000..45e6eeef2 --- /dev/null +++ b/src/mint/taler-mint-httpd_db.c @@ -0,0 +1,129 @@ +/* + 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 taler-mint-httpd_db.c + * @brief Database access abstraction for the mint. + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-mint-httpd_db.h" +#include "taler_signatures.h" +#include "taler-mint-httpd_responses.h" +#include "mint_db.h" +#include "mint.h" +#include + + +/** + * Execute a deposit. The validity of the coin and signature + * have already been checked. The database must now check that + * the coin is not (double or over) spent, and execute the + * transaction (record details, generate success or failure response). + * + * @param connection the MHD connection to handle + * @param deposit information about the deposit + * @return MHD result code + */ +int +TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, + const struct Deposit *deposit) +{ + PGconn *db_conn; + + if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) + { + GNUNET_break (0); + return TALER_MINT_reply_internal_error (connection, + "Failed to connect to database\n"); + } + + { + struct Deposit *existing_deposit; + int res; + + res = TALER_MINT_DB_get_deposit (db_conn, + &deposit->coin_pub, + &existing_deposit); + if (GNUNET_YES == res) + { + // FIXME: memory leak + if (0 == memcmp (existing_deposit, deposit, sizeof (struct Deposit))) + return TALER_MINT_reply_deposit_success (connection, deposit); + // FIXME: in the future, check if there's enough credits + // left on the coin. For now: refuse + // FIXME: return more information here + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_FORBIDDEN, + "{s:s}", + "error", + "double spending"); + } + + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + /* FIXME: return error message to client via MHD! */ + return MHD_NO; + } + } + + { + struct KnownCoin known_coin; + int res; + struct TALER_CoinPublicInfo coin_info; + + res = TALER_MINT_DB_get_known_coin (db_conn, &coin_info.coin_pub, &known_coin); + if (GNUNET_YES == res) + { + // coin must have been refreshed + // FIXME: check + // FIXME: return more information here + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_FORBIDDEN, + "{s:s}", + "error", "coin was refreshed"); + } + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + /* FIXME: return error message to client via MHD! */ + return MHD_NO; + } + + /* coin valid but not known => insert into DB */ + known_coin.is_refreshed = GNUNET_NO; + known_coin.expended_balance = TALER_amount_ntoh (deposit->amount); + known_coin.public_info = coin_info; + + if (GNUNET_OK != TALER_MINT_DB_insert_known_coin (db_conn, &known_coin)) + { + GNUNET_break (0); + /* FIXME: return error message to client via MHD! */ + return MHD_NO; + } + } + + if (GNUNET_OK != TALER_MINT_DB_insert_deposit (db_conn, deposit)) + { + GNUNET_break (0); + /* FIXME: return error message to client via MHD! */ + return MHD_NO; + } + // FIXME: check commit return value! + TALER_MINT_DB_commit (db_conn); + return TALER_MINT_reply_deposit_success (connection, deposit); +} diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h new file mode 100644 index 000000000..959b5582b --- /dev/null +++ b/src/mint/taler-mint-httpd_db.h @@ -0,0 +1,79 @@ +/* + 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 mint/taler-mint_httpd_db.h + * @brief Mint-specific database access + * @author Chrisitan Grothoff + */ +#ifndef TALER_MINT_HTTPD_DB_H +#define TALER_MINT_HTTPD_DB_H + +#include +#include +#include +#include "taler_util.h" +#include "taler_rsa.h" + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Specification for a /deposit operation. + */ +struct Deposit +{ + /* FIXME: should be TALER_CoinPublicInfo */ + struct GNUNET_CRYPTO_EddsaPublicKey coin_pub; + struct TALER_RSA_PublicKeyBinaryEncoded denom_pub; + struct TALER_RSA_Signature coin_sig; + + struct TALER_RSA_Signature ubsig; + + /** + * Type of the deposit (also purpose of the signature). Either + * #TALER_SIGNATURE_DEPOSIT or #TALER_SIGNATURE_INCREMENTAL_DEPOSIT. + */ + struct TALER_RSA_SignaturePurpose purpose; + uint64_t transaction_id; + struct TALER_AmountNBO amount; + struct GNUNET_CRYPTO_EddsaPublicKey merchant_pub; + struct GNUNET_HashCode h_contract; + struct GNUNET_HashCode h_wire; + + /* TODO: uint16_t wire_size */ + char wire[]; /* string encoded wire JSON object */ + +}; + +GNUNET_NETWORK_STRUCT_END + + +/** + * Execute a deposit. The validity of the coin and signature + * have already been checked. The database must now check that + * the coin is not (double or over) spent, and execute the + * transaction (record details, generate success or failure response). + * + * @param connection the MHD connection to handle + * @param deposit information about the deposit + * @return MHD result code + */ +int +TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, + const struct Deposit *deposit); + + +#endif /* _NEURO_MINT_DB_H */ diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index 7c19e060e..84dfd6d93 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -15,7 +15,9 @@ */ /** * @file taler-mint-httpd_deposit.c - * @brief Handle /deposit requests + * @brief Handle /deposit requests; parses the POST and JSON and + * verifies the coin signature before handing things off + * to the database. * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff @@ -33,33 +35,140 @@ #include "taler_json_lib.h" #include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_keys.h" +#include "taler-mint-httpd_db.h" #include "taler-mint-httpd_deposit.h" #include "taler-mint-httpd_responses.h" /** - * Send confirmation of deposit success to client. + * We have parsed the JSON information about the deposit, do some + * basic sanity checks (especially that the signature on the coin is + * valid, and that this type of coin exists) and then execute the + * deposit. * - * @param connection connection to the client - * @param deposit deposit request to confirm + * @param connection the MHD connection to handle + * @param deposit information about the deposit * @return MHD result code */ -// FIXME: this should be in taler-mint-httpd_responses.c static int -helper_deposit_send_response_success (struct MHD_Connection *connection, - struct Deposit *deposit) +verify_and_execute_deposit (struct MHD_Connection *connection, + const struct Deposit *deposit) { - // FIXME: return more information here - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:s}", - "status", - "DEPOSIT_OK"); + struct MintKeyState *key_state; + struct TALER_CoinPublicInfo coin_info; + + memcpy (&coin_info.coin_pub, + &deposit->coin_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); + coin_info.denom_pub = deposit->denom_pub; + coin_info.denom_sig = deposit->ubsig; + + key_state = TALER_MINT_key_state_acquire (); + if (GNUNET_YES != + TALER_MINT_test_coin_valid (key_state, + &coin_info)) + { + TALER_MINT_key_state_release (key_state); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", "Coin is not valid"); + } + TALER_MINT_key_state_release (key_state); + + /* FIXME: verify coin signature! */ + /* + if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_DEPOSIT, + &deposit->purpose, + &deposit->coin_sig, + &deposit->coin_pub)) + { + resp = json_pack ("{s:s}", "error", "Signature verfication failed"); + resp_code = MHD_HTTP_NOT_FOUND; + goto EXITIF_exit; + } + */ + + return TALER_MINT_db_execute_deposit (connection, + deposit); } /** - * Handle a "/deposit" request + * Handle a "/deposit" request. This function parses the + * JSON information and then calls #verify_and_execute_deposit() + * to verify the data and execute the deposit. + * + * @param connection the MHD connection to handle + * @param root root of the posted JSON + * @param purpose is this a #TALER_SIGNATURE_DEPOSIT or + * #TALER_SIGNATURE_INCREMENTAL_DEPOSIT + * @param wire json describing the wire details (?) + * @return MHD result code + */ +static int +parse_and_handle_deposit_request (struct MHD_Connection *connection, + const json_t *root, + uint32_t purpose, + const json_t *wire) +{ + struct Deposit *deposit; + char *wire_enc; + size_t len; + int res; + + // FIXME: `struct Deposit` is clearly ill-defined, we should + // not have to do this... + if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT | JSON_SORT_KEYS))) + { + GNUNET_break_op (0); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", "Bad format"); + + } + len = strlen (wire_enc) + 1; + GNUNET_free (wire_enc); + + deposit = GNUNET_malloc (sizeof (struct Deposit) + len); + { + struct GNUNET_MINT_ParseFieldSpec spec[] = + { + TALER_MINT_PARSE_FIXED ("coin_pub", &deposit->coin_pub), + TALER_MINT_PARSE_FIXED ("denom_pub", &deposit->denom_pub), + TALER_MINT_PARSE_FIXED ("ubsig", &deposit->ubsig), + TALER_MINT_PARSE_FIXED ("merchant_pub", &deposit->merchant_pub), + TALER_MINT_PARSE_FIXED ("H_a", &deposit->h_contract), + TALER_MINT_PARSE_FIXED ("H_wire", &deposit->h_wire), + TALER_MINT_PARSE_FIXED ("csig", &deposit->coin_sig), + TALER_MINT_PARSE_FIXED ("transaction_id", &deposit->transaction_id), + TALER_MINT_PARSE_END + }; + res = TALER_MINT_parse_json_data (connection, + wire, /* FIXME: wire or root here? */ + spec); + if (GNUNET_SYSERR == res) + return MHD_NO; /* hard failure */ + if (GNUNET_NO == res) + return MHD_YES; /* failure */ + + deposit->purpose.purpose = htonl (purpose); + deposit->purpose.size = htonl (sizeof (struct Deposit) + - offsetof (struct Deposit, purpose)); + res = verify_and_execute_deposit (connection, + deposit); + TALER_MINT_release_parsed_data (spec); + } + GNUNET_free (deposit); + return res; +} + + +/** + * Handle a "/deposit" request. Parses the JSON in the post and, if + * successful, passes the JSON data to + * #parse_and_handle_deposit_request(). * * @param rh context of the handler * @param connection the MHD connection to handle @@ -76,18 +185,10 @@ TALER_MINT_handler_deposit (struct RequestHandler *rh, size_t *upload_data_size) { json_t *json; - struct Deposit *deposit; json_t *wire; - json_t *resp; - char *wire_enc = NULL; const char *deposit_type; - struct MintKeyState *key_state; - struct TALER_CoinPublicInfo coin_info; - struct TALER_RSA_Signature ubsig; - size_t len; - int resp_code; - PGconn *db_conn; int res; + uint32_t purpose; res = TALER_MINT_parse_post_json (connection, connection_cls, @@ -98,187 +199,37 @@ TALER_MINT_handler_deposit (struct RequestHandler *rh, return MHD_NO; if ( (GNUNET_NO == res) || (NULL == json) ) return MHD_YES; - if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) - { - /* FIXME: return error message to client via MHD! */ - GNUNET_break (0); - return MHD_NO; - } - - deposit = NULL; - wire = NULL; - resp = NULL; if (-1 == json_unpack (json, "{s:s, s:o}", "type", &deposit_type, "wire", &wire)) { GNUNET_break_op (0); - resp = json_pack ("{s:s}", "error", "Bad format"); - resp_code = MHD_HTTP_BAD_REQUEST; - goto EXITIF_exit; + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", "Bad format"); } - if (NULL == (wire_enc = json_dumps (wire, JSON_COMPACT | JSON_SORT_KEYS))) - { - GNUNET_break_op (0); - resp = json_pack ("{s:s}", "error", "Bad format"); - resp_code = MHD_HTTP_BAD_REQUEST; - goto EXITIF_exit; - } - len = strlen (wire_enc) + 1; - deposit = GNUNET_malloc (sizeof (struct Deposit) + len); -#define EXITIF(cond) \ - do { \ - if (cond) { GNUNET_break (0); goto EXITIF_exit; } \ - } while (0) - // FIXME: need to distinguish between _OK and _NO return values here, - // and never try to queue our own! -#define PARSE_DATA(field, addr) \ - EXITIF (GNUNET_OK != \ - GNUNET_MINT_parse_navigate_json \ - (connection, json, \ - JNAV_FIELD, field, JNAV_RET_DATA, addr, sizeof (*addr))) - PARSE_DATA ("coin_pub", &deposit->coin_pub); - PARSE_DATA ("denom_pub", &deposit->denom_pub); - PARSE_DATA ("ubsig", &ubsig); - PARSE_DATA ("merchant_pub", &deposit->merchant_pub); - PARSE_DATA ("H_a", &deposit->h_contract); - PARSE_DATA ("H_wire", &deposit->h_wire); - PARSE_DATA ("csig", &deposit->coin_sig); - PARSE_DATA ("transaction_id", &deposit->transaction_id); -#undef PARSE_DATA if (0 == strcmp ("DIRECT_DEPOSIT", deposit_type)) - deposit->purpose.purpose = htonl (TALER_SIGNATURE_DEPOSIT); + purpose = TALER_SIGNATURE_DEPOSIT; else if (0 == strcmp ("INCREMENTAL_DEPOSIT", deposit_type)) - deposit->purpose.purpose = htonl (TALER_SIGNATURE_INCREMENTAL_DEPOSIT); + purpose = TALER_SIGNATURE_INCREMENTAL_DEPOSIT; else { GNUNET_break_op (0); - resp = json_pack ("{s:s}", "error", "Bad format"); - resp_code = MHD_HTTP_BAD_REQUEST; - goto EXITIF_exit; - } - deposit->purpose.size = htonl (sizeof (struct Deposit) - - offsetof (struct Deposit, purpose)); - memcpy (&coin_info.coin_pub, - &deposit->coin_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)); - coin_info.denom_pub = deposit->denom_pub; - coin_info.denom_sig = ubsig; - key_state = TALER_MINT_key_state_acquire (); - if (GNUNET_YES != TALER_MINT_test_coin_valid (key_state, - &coin_info)) - { - TALER_MINT_key_state_release (key_state); - resp = json_pack ("{s:s}", "error", "Coin is not valid"); - resp_code = MHD_HTTP_NOT_FOUND; - goto EXITIF_exit; - } - TALER_MINT_key_state_release (key_state); - /* - if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_DEPOSIT, - &deposit->purpose, - &deposit->coin_sig, - &deposit->coin_pub)) - { - resp = json_pack ("{s:s}", "error", "Signature verfication failed"); - resp_code = MHD_HTTP_NOT_FOUND; - goto EXITIF_exit; - } - */ - - /* Check if we already received the same deposit permission, - * or the coin was already deposited */ - - { - struct Deposit *existing_deposit; - int res; - - res = TALER_MINT_DB_get_deposit (db_conn, - &deposit->coin_pub, - &existing_deposit); - if (GNUNET_YES == res) - { - // FIXME: memory leak - if (0 == memcmp (existing_deposit, deposit, sizeof (struct Deposit))) - return helper_deposit_send_response_success (connection, deposit); - // FIXME: in the future, check if there's enough credits - // left on the coin. For now: refuse - // FIXME: return more information here - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", - "double spending"); - } - - if (GNUNET_SYSERR == res) - { - GNUNET_break (0); - /* FIXME: return error message to client via MHD! */ - return MHD_NO; - } - } - - { - struct KnownCoin known_coin; - int res; - - res = TALER_MINT_DB_get_known_coin (db_conn, &coin_info.coin_pub, &known_coin); - if (GNUNET_YES == res) - { - // coin must have been refreshed - // FIXME: check - // FIXME: return more information here - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", "coin was refreshed"); - } - if (GNUNET_SYSERR == res) - { - GNUNET_break (0); - /* FIXME: return error message to client via MHD! */ - return MHD_NO; - } - - /* coin valid but not known => insert into DB */ - known_coin.is_refreshed = GNUNET_NO; - known_coin.expended_balance = TALER_amount_ntoh (deposit->amount); - known_coin.public_info = coin_info; - - if (GNUNET_OK != TALER_MINT_DB_insert_known_coin (db_conn, &known_coin)) - { - GNUNET_break (0); - /* FIXME: return error message to client via MHD! */ - return MHD_NO; - } - } - - if (GNUNET_OK != TALER_MINT_DB_insert_deposit (db_conn, deposit)) - { - GNUNET_break (0); - /* FIXME: return error message to client via MHD! */ - return MHD_NO; - } - return helper_deposit_send_response_success (connection, deposit); - - EXITIF_exit: - if (NULL != resp) - res = TALER_MINT_reply_json (connection, - resp, - resp_code); - else - res = MHD_NO; - if (NULL != wire) json_decref (wire); - if (NULL != deposit) - GNUNET_free (deposit); - if (NULL != wire_enc) - GNUNET_free (wire_enc); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", "Bad format"); + } + res = parse_and_handle_deposit_request (connection, + json, + purpose, + wire); + json_decref (wire); return res; -#undef EXITIF -#undef PARSE_DATA } + /* end of taler-mint-httpd_deposit.c */ diff --git a/src/mint/taler-mint-httpd_parsing.c b/src/mint/taler-mint-httpd_parsing.c index f61049b5e..56744c6b0 100644 --- a/src/mint/taler-mint-httpd_parsing.c +++ b/src/mint/taler-mint-httpd_parsing.c @@ -269,9 +269,10 @@ TALER_MINT_parse_post_cleanup_callback (void *con_cls) * @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 TALER_MINT_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, + * @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 @@ -461,6 +462,142 @@ GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection, } +/** + * Find a fixed-size field in the top-level of the JSON tree and store + * it in @a data. + * + * 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 field name of the field to navigate to + * @param data where to store the extracted data + * @param data_size size of the @a data field + * @param[IN|OUT] ret return value, function does nothing if @a ret is not #GNUNET_YES + * on entry; will set @a ret to: + * #GNUNET_YES if navigation was successful + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error + */ +static void +parse_fixed_json_data (struct MHD_Connection *connection, + const json_t *root, + const char *field, + void *data, + size_t data_size, + int *ret) +{ + if (GNUNET_YES != *ret) + return; + *ret = GNUNET_MINT_parse_navigate_json (connection, + root, + JNAV_FIELD, field, + JNAV_RET_DATA, data, data_size); +} + + +/** + * Find a variable-size field in the top-level of the JSON tree and store + * it in @a data. + * + * 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 field name of the field to navigate to + * @param data where to store a pointer to memory allocated for the extracted data + * @param[IN|OUT] ret return value, function does nothing if @a ret is not #GNUNET_YES + * on entry; will set @a ret to: + * #GNUNET_YES if navigation was successful + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error + */ +static void +parse_variable_json_data (struct MHD_Connection *connection, + const json_t *root, + const char *field, + void **data, + size_t *data_size, + int *ret) +{ + if (GNUNET_YES != *ret) + return; + *ret = GNUNET_MINT_parse_navigate_json (connection, + root, + JNAV_FIELD, field, + JNAV_RET_DATA_VAR, data, data_size); + +} + + +/** + * 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 + * #TALER_MINT_release_parsed_data() when done) + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error + */ +int +TALER_MINT_parse_json_data (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_MINT_ParseFieldSpec *spec) +{ + unsigned int i; + int ret; + + ret = GNUNET_YES; + for (i=0; NULL != spec[i].field_name; i++) + { + if (0 == spec[i].destination_size_in) + parse_variable_json_data (connection, root, + spec[i].field_name, + (void **) spec[i].destination, + &spec[i].destination_size_out, + &ret); + else + parse_fixed_json_data (connection, root, + spec[i].field_name, + spec[i].destination, + spec[i].destination_size_in, + &ret); + } + if (GNUNET_YES != ret) + TALER_MINT_release_parsed_data (spec); + return ret; +} + + +/** + * Release all memory allocated for the variable-size fields in + * the parser specification. + * + * @param spec specification to free + */ +void +TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec) +{ + unsigned int i; + + for (i=0; NULL != spec[i].field_name; i++) + if ( (0 == spec[i].destination_size_in) && + (0 != spec[i].destination_size_out) ) + { + GNUNET_free (spec[i].destination); + spec[i].destination = NULL; + spec[i].destination_size_out = 0; + } +} + + /** * Extract base32crockford encoded data from request. * @@ -472,9 +609,9 @@ GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection, * @param[out] out_data pointer to store the result * @param out_size expected size of data * @return - * GNUNET_YES if the the argument is present - * GNUNET_NO if the argument is absent or malformed - * GNUNET_SYSERR on internal error (error response could not be sent) + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) */ int TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection, diff --git a/src/mint/taler-mint-httpd_parsing.h b/src/mint/taler-mint-httpd_parsing.h index a278eb705..59c31f595 100644 --- a/src/mint/taler-mint-httpd_parsing.h +++ b/src/mint/taler-mint-httpd_parsing.h @@ -123,9 +123,10 @@ enum TALER_MINT_JsonNavigationCommand * @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 TALER_MINT_JsonNavigationCommand`) - * @return GNUNET_YES if navigation was successful - * GNUNET_NO if json is malformed, error response was generated - * GNUNET_SYSERR on internal error + * @return + * #GNUNET_YES if navigation was successful + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error */ int GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection, @@ -133,6 +134,89 @@ GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection, ...); +/** + * Specification for how to parse a JSON field. + */ +struct GNUNET_MINT_ParseFieldSpec +{ + /** + * Name of the field. NULL only to terminate array. + */ + const char *field_name; + + /** + * Where to store the result. Must have exactly + * @e destination_size bytes, except if @e destination_size is zero. + * NULL to skip assignment (but check presence of the value). + */ + void *destination; + + /** + * How big should the result be, 0 for variable size. In + * this case, @e destination must be a "void **", pointing + * to a location that is currently NULL and is to be allocated. + */ + size_t destination_size_in; + + /** + * @e destination_size_out will then be set to the size of the + * value that was stored in @e destination (useful for + * variable-size allocations). + */ + size_t destination_size_out; +}; + + +/** + * 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 + * #TALER_MINT_release_parsed_data() when done) + * #GNUNET_NO if json is malformed, error response was generated + * #GNUNET_SYSERR on internal error + */ +int +TALER_MINT_parse_json_data (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_MINT_ParseFieldSpec *spec); + + +/** + * Release all memory allocated for the variable-size fields in + * the parser specification. + * + * @param spec specification to free + */ +void +TALER_MINT_release_parsed_data (struct GNUNET_MINT_ParseFieldSpec *spec); + + +/** + * Generate line in parser specification for fixed-size value. + * + * @param field name of the field + * @param value where to store the value + */ +#define TALER_MINT_PARSE_FIXED(field,value) { field, value, sizeof (*value), 0 } + +/** + * Generate line in parser specification for variable-size value. + * + * @param field name of the field + * @param value where to store the value + */ +#define TALER_MINT_PARSE_VARIABLE(field,value) { field, &value, 0, 0 } + +/** + * Generate line in parser specification indicating the end of the spec. + */ +#define TALER_MINT_PARSE_END { NULL, NULL, 0, 0 } /** @@ -146,9 +230,9 @@ GNUNET_MINT_parse_navigate_json (struct MHD_Connection *connection, * @param[out] out_data pointer to store the result * @param out_size expected size of @a out_data * @return - * GNUNET_YES if the the argument is present - * GNUNET_NO if the argument is absent or malformed - * GNUNET_SYSERR on internal error (error response could not be sent) + * #GNUNET_YES if the the argument is present + * #GNUNET_NO if the argument is absent or malformed + * #GNUNET_SYSERR on internal error (error response could not be sent) */ int TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection, diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index bad4991da..d581e2623 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -204,8 +204,25 @@ TALER_MINT_reply_invalid_json (struct MHD_Connection *connection) } - - - +/** + * Send confirmation of deposit success to client. + * + * @param connection connection to the client + * @param deposit deposit request to confirm + * @return MHD result code + */ +int +TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, + const struct Deposit *deposit) +{ + // FIXME: return more information here, + // including in particular a signature over + // the deposit data from the mint! + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:s}", + "status", + "DEPOSIT_OK"); +} /* end of taler-mint-httpd_responses.c */ diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index be662319f..6dd8cda90 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -32,6 +32,7 @@ #include #include "taler-mint-httpd.h" #include "taler-mint-httpd_mhd.h" +#include "mint_db.h" /** @@ -122,6 +123,16 @@ int TALER_MINT_reply_invalid_json (struct MHD_Connection *connection); +/** + * Send confirmation of deposit success to client. + * + * @param connection connection to the client + * @param deposit deposit request to confirm + * @return MHD result code + */ +int +TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, + const struct Deposit *deposit);