From 0d1eced630f4ed05ad95fdbb4354fd428c9cdbf6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Mar 2016 15:23:11 +0100 Subject: first refactoring of JSON logic to address #4150 and #4237 --- src/include/taler_json_lib.h | 127 +++++-------------------------------------- 1 file changed, 15 insertions(+), 112 deletions(-) (limited to 'src/include/taler_json_lib.h') diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index d9fa0518..aee151b9 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. 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 @@ -17,14 +17,18 @@ * @file include/taler_json_lib.h * @brief helper functions for JSON processing using libjansson * @author Sree Harsha Totakura + * @author Christian Grothoff */ -#ifndef TALER_json_LIB_H_ -#define TALER_json_LIB_H_ +#ifndef TALER_JSON_LIB_H_ +#define TALER_JSON_LIB_H_ #include +#include +#include "taler_util.h" /** * Print JSON parsing related error information + * @deprecated */ #define TALER_json_warn(error) \ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \ @@ -39,106 +43,18 @@ * @return a json object describing the amount */ json_t * -TALER_json_from_amount (const struct TALER_Amount *amount); +TALER_JSON_from_amount (const struct TALER_Amount *amount); /** - * Convert absolute timestamp to a json string. + * Provide specification to parse given JSON object to an amount. * - * @param stamp the time stamp - * @return a json string with the timestamp in @a stamp - */ -json_t * -TALER_json_from_abs (struct GNUNET_TIME_Absolute stamp); - - -/** - * Convert RSA public key to JSON. - * - * @param pk public key to convert - * @return corresponding JSON encoding - */ -json_t * -TALER_json_from_rsa_public_key (struct GNUNET_CRYPTO_rsa_PublicKey *pk); - - -/** - * Convert RSA signature to JSON. - * - * @param sig signature to convert - * @return corresponding JSON encoding - */ -json_t * -TALER_json_from_rsa_signature (struct GNUNET_CRYPTO_rsa_Signature *sig); - - -/** - * Convert binary data to a JSON string - * with the base32crockford encoding. - * - * @param data binary data - * @param size size of @a data in bytes - * @return json string that encodes @a data - */ -json_t * -TALER_json_from_data (const void *data, - size_t size); - - -/** - * Parse given JSON object to Amount - * - * @param json the json object representing Amount + * @param name name of the amount field in the JSON * @param[out] r_amount where the amount has to be written - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -int -TALER_json_to_amount (json_t *json, - struct TALER_Amount *r_amount); - -/** - * Parse given JSON object to absolute time. - * - * @param json the json object representing absolute time in seconds - * @param[out] abs where the time has to be written - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -int -TALER_json_to_abs (json_t *json, - struct GNUNET_TIME_Absolute *abs); - -/** - * Parse given JSON object to data - * - * @param json the json object representing data - * @param out the pointer to hold the parsed data. - * @param out_size the size of @a out - * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error - */ -int -TALER_json_to_data (json_t *json, - void *out, - size_t out_size); - - -/** - * Convert JSON to RSA public key. - * - * @param json JSON encoding to convert - * @return corresponding public key */ -struct GNUNET_CRYPTO_rsa_PublicKey * -TALER_json_to_rsa_public_key (json_t *json); - - -/** - * Convert JSON to RSA signature. - * - * @param json JSON encoding to convert - * @return corresponding signature - */ -struct GNUNET_CRYPTO_rsa_Signature * -TALER_json_to_rsa_signature (json_t *json); +struct GNUNET_JSON_Specification +TALER_JSON_spec_amount (const char *name, + struct TALER_Amount *r_amount); /** @@ -149,22 +65,9 @@ TALER_json_to_rsa_signature (json_t *json); * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -TALER_hash_json (json_t *json, +TALER_JSON_hash (json_t *json, struct GNUNET_HashCode *hc); - -/** - * Check if the given wire format JSON object is correctly formatted - * - * @param allowed NULL-terminated array of allowed wire format types - * @param wire the JSON wire format object - * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not - */ -int -TALER_json_validate_wireformat (const char **allowed, - const json_t *wire); - - -#endif /* TALER_json_LIB_H_ */ +#endif /* TALER_JSON_LIB_H_ */ /* End of taler_json_lib.h */ -- cgit v1.2.3 From 737e3f4bf67a2048381785328206c595bffe632a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Mar 2016 19:16:35 +0100 Subject: refactor to eliminate duplicated JSON parsing logic (#4150) --- src/exchange/taler-exchange-httpd_admin.c | 16 +- src/exchange/taler-exchange-httpd_deposit.c | 48 +- src/exchange/taler-exchange-httpd_parsing.c | 891 +++------------------------ src/exchange/taler-exchange-httpd_parsing.h | 309 +--------- src/exchange/taler-exchange-httpd_refresh.c | 194 +++--- src/exchange/taler-exchange-httpd_reserve.c | 18 +- src/exchange/taler-exchange-httpd_test.c | 74 +-- src/exchange/taler-exchange-httpd_tracking.c | 18 +- src/include/taler_json_lib.h | 24 + src/json/json_helper.c | 32 + 10 files changed, 344 insertions(+), 1280 deletions(-) (limited to 'src/include/taler_json_lib.h') diff --git a/src/exchange/taler-exchange-httpd_admin.c b/src/exchange/taler-exchange-httpd_admin.c index 575df7bb..cc0245cd 100644 --- a/src/exchange/taler-exchange-httpd_admin.c +++ b/src/exchange/taler-exchange-httpd_admin.c @@ -113,12 +113,12 @@ TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh, struct GNUNET_TIME_Absolute at; json_t *wire; json_t *root; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_fixed ("reserve_pub", &reserve_pub), - TMH_PARSE_member_amount ("amount", &amount), - TMH_PARSE_member_time_abs ("execution_date", &at), - TMH_PARSE_member_object ("wire", &wire), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("reserve_pub", &reserve_pub), + TALER_JSON_spec_amount ("amount", &amount), + GNUNET_JSON_spec_absolute_time ("execution_date", &at), + GNUNET_JSON_spec_json ("wire", &wire), + GNUNET_JSON_spec_end () }; int res; @@ -148,7 +148,7 @@ TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh, TMH_json_validate_wireformat (wire)) { GNUNET_break_op (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_unknown (connection, "wire"); } @@ -157,7 +157,7 @@ TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh, &amount, at, wire); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return res; } diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 82dcf1a5..73e6463d 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -140,19 +140,19 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct TMH_KS_StateHandle *ks; struct GNUNET_HashCode my_h_wire; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_denomination_public_key ("denom_pub", &deposit.coin.denom_pub), - TMH_PARSE_member_denomination_signature ("ub_sig", &deposit.coin.denom_sig), - TMH_PARSE_member_fixed ("coin_pub", &deposit.coin.coin_pub), - TMH_PARSE_member_fixed ("merchant_pub", &deposit.merchant_pub), - TMH_PARSE_member_fixed ("H_contract", &deposit.h_contract), - TMH_PARSE_member_fixed ("H_wire", &deposit.h_wire), - TMH_PARSE_member_fixed ("coin_sig", &deposit.csig), - TMH_PARSE_member_uint64 ("transaction_id", &deposit.transaction_id), - TMH_PARSE_member_time_abs ("timestamp", &deposit.timestamp), - TMH_PARSE_member_time_abs ("refund_deadline", &deposit.refund_deadline), - TMH_PARSE_member_time_abs ("edate", &deposit.wire_deadline), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_denomination_public_key ("denom_pub", &deposit.coin.denom_pub), + TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig), + GNUNET_JSON_spec_fixed_auto ("coin_pub", &deposit.coin.coin_pub), + GNUNET_JSON_spec_fixed_auto ("merchant_pub", &deposit.merchant_pub), + GNUNET_JSON_spec_fixed_auto ("H_contract", &deposit.h_contract), + GNUNET_JSON_spec_fixed_auto ("H_wire", &deposit.h_wire), + GNUNET_JSON_spec_fixed_auto ("coin_sig", &deposit.csig), + GNUNET_JSON_spec_uint64 ("transaction_id", &deposit.transaction_id), + GNUNET_JSON_spec_absolute_time ("timestamp", &deposit.timestamp), + GNUNET_JSON_spec_absolute_time ("refund_deadline", &deposit.refund_deadline), + GNUNET_JSON_spec_absolute_time ("edate", &deposit.wire_deadline), + GNUNET_JSON_spec_end () }; memset (&deposit, 0, sizeof (deposit)); @@ -167,7 +167,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, if (GNUNET_YES != TMH_json_validate_wireformat (wire)) { - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_unknown (connection, "wire"); } @@ -176,7 +176,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, &my_h_wire)) { TALER_LOG_WARNING ("Failed to parse JSON wire format specification for /deposit request\n"); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_invalid (connection, "wire"); } @@ -185,7 +185,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, sizeof (struct GNUNET_HashCode))) { /* Client hashed contract differently than we did, reject */ - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_invalid (connection, "H_wire"); } @@ -196,7 +196,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, if (NULL == dki) { TMH_KS_release (ks); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_unknown (connection, "denom_pub"); } @@ -209,13 +209,13 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection, &deposit.deposit_fee)) { /* Total amount smaller than fee, invalid */ - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_invalid (connection, "f"); } res = verify_and_execute_deposit (connection, &deposit); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return res; } @@ -247,10 +247,10 @@ TMH_DEPOSIT_handler_deposit (struct TMH_RequestHandler *rh, json_t *wire; int res; struct TALER_Amount amount; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_object ("wire", &wire), - TMH_PARSE_member_amount ("f", &amount), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("wire", &wire), + TALER_JSON_spec_amount ("f", &amount), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -274,7 +274,7 @@ TMH_DEPOSIT_handler_deposit (struct TMH_RequestHandler *rh, json, &amount, wire); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); json_decref (json); return res; } diff --git a/src/exchange/taler-exchange-httpd_parsing.c b/src/exchange/taler-exchange-httpd_parsing.c index 3fd69ae5..f6e36781 100644 --- a/src/exchange/taler-exchange-httpd_parsing.c +++ b/src/exchange/taler-exchange-httpd_parsing.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -141,89 +141,6 @@ buffer_append (struct Buffer *buf, } -/** - * Release all memory allocated for the variable-size fields in - * the parser specification. - * - * @param spec specification to free - * @param spec_len number of items in @a spec to look at - */ -static void -release_data (struct TMH_PARSE_FieldSpecification *spec, - unsigned int spec_len) -{ - unsigned int i; - - for (i=0; i < spec_len; i++) - { - switch (spec[i].command) - { - case TMH_PARSE_JNC_FIELD: - GNUNET_break (0); - return; - case TMH_PARSE_JNC_INDEX: - GNUNET_break (0); - return; - case TMH_PARSE_JNC_RET_DATA: - break; - case TMH_PARSE_JNC_RET_DATA_VAR: - if (NULL != spec[i].destination) - { - GNUNET_free (* (void**) spec[i].destination); - *(void**) spec[i].destination = NULL; - *spec[i].destination_size_out = 0; - } - break; - case TMH_PARSE_JNC_RET_TYPED_JSON: - { - json_t *json; - - json = *(json_t **) spec[i].destination; - if (NULL != json) - { - json_decref (json); - *(json_t**) spec[i].destination = NULL; - } - } - break; - case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY: - { - struct TALER_DenominationPublicKey *pk; - - pk = spec[i].destination; - if (NULL != pk->rsa_public_key) - { - GNUNET_CRYPTO_rsa_public_key_free (pk->rsa_public_key); - pk->rsa_public_key = NULL; - } - } - break; - case TMH_PARSE_JNC_RET_RSA_SIGNATURE: - { - struct TALER_DenominationSignature *sig; - - sig = spec[i].destination; - if (NULL != sig->rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (sig->rsa_signature); - sig->rsa_signature = NULL; - } - } - break; - case TMH_PARSE_JNC_RET_AMOUNT: - memset (spec[i].destination, - 0, - sizeof (struct TALER_Amount)); - break; - case TMH_PARSE_JNC_RET_TIME_ABSOLUTE: - break; - case TMH_PARSE_JNC_RET_UINT64: - break; - } - } -} - - /** * Process a POST request containing a JSON object. This function * realizes an MHD POST processor that will (incrementally) process @@ -349,8 +266,8 @@ TMH_PARSE_post_cleanup_callback (void *con_cls) /** * Extract base32crockford encoded data from request. * - * Queues an error response to the connection if the parameter is missing or - * invalid. + * Queues an error response to the connection if the parameter is + * missing or invalid. * * @param connection the MHD connection * @param param_name the name of the parameter with the key @@ -391,758 +308,116 @@ TMH_PARSE_mhd_request_arg_data (struct MHD_Connection *connection, /** - * Extraxt variable-size base32crockford encoded data from request. - * - * Queues an error response to the connection if the parameter is missing - * or the encoding is invalid. - * - * @param connection the MHD connection - * @param param_name the name of the parameter with the key - * @param[out] out_data pointer to allocate buffer and store the result - * @param[out] out_size set to the size of the buffer allocated in @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) - */ -int -TMH_PARSE_mhd_request_var_arg_data (struct MHD_Connection *connection, - const char *param_name, - void **out_data, - size_t *out_size) -{ - const char *str; - size_t slen; - size_t olen; - void *out; - - str = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - param_name); - if (NULL == str) - { - return (MHD_NO == - TMH_RESPONSE_reply_arg_missing (connection, param_name)) - ? GNUNET_SYSERR : GNUNET_NO; - } - slen = strlen (str); - olen = (slen * 5) / 8; - out = GNUNET_malloc (olen); - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (str, - strlen (str), - out, - olen)) - { - GNUNET_free (out); - *out_size = 0; - return (MHD_NO == - TMH_RESPONSE_reply_arg_invalid (connection, param_name)) - ? GNUNET_SYSERR : GNUNET_NO; - } - *out_data = out; - *out_size = olen; - return GNUNET_OK; -} - - -/** - * Navigate through a JSON tree. - * - * Sends an error response if navigation is impossible (i.e. - * the JSON object is invalid) + * Parse JSON object into components based on the given field + * specification. Generates error response on parse errors. * * @param connection the connection to send an error response to * @param root the JSON node to start the navigation at. - * @param ... navigation specification (see `enum TMH_PARSE_JsonNavigationCommand`) + * @param[in,out] spec field specification for the parser * @return - * #GNUNET_YES if navigation was successful + * #GNUNET_YES if navigation was successful (caller is responsible + * for freeing allocated variable-size data using + * #GNUNET_JSON_parse_free() when done) * #GNUNET_NO if json is malformed, error response was generated - * #GNUNET_SYSERR on internal error (no response was generated, - * connection must be closed) + * #GNUNET_SYSERR on internal error */ int -TMH_PARSE_navigate_json (struct MHD_Connection *connection, - const json_t *root, - ...) +TMH_PARSE_json_data (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_JSON_Specification *spec) { - va_list argp; int ret; - json_t *path; /* what's our current path from 'root'? */ - - path = json_array (); - va_start (argp, root); - ret = 2; /* just not any of the valid return values */ - while (2 == ret) + const char *error_json_name; + unsigned int error_line; + + ret = GNUNET_JSON_parse (root, + spec, + &error_json_name, + &error_line); + if (GNUNET_SYSERR == ret) { - enum TMH_PARSE_JsonNavigationCommand command - = va_arg (argp, - enum TMH_PARSE_JsonNavigationCommand); - - switch (command) - { - case TMH_PARSE_JNC_FIELD: - { - const char *fname = va_arg(argp, const char *); - - json_array_append_new (path, - json_string (fname)); - root = json_object_get (root, - fname); - if (NULL == root) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:s, s:O}", - "error", "missing field in JSON", - "field", fname, - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - } - break; - - case TMH_PARSE_JNC_INDEX: - { - int fnum = va_arg(argp, int); - - json_array_append_new (path, - json_integer (fnum)); - root = json_array_get (root, - fnum); - if (NULL == root) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "missing index in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - } - break; - - case TMH_PARSE_JNC_RET_DATA: - { - void *where = va_arg (argp, void *); - size_t len = va_arg (argp, size_t); - const char *str; - int res; - - str = json_string_value (root); - if (NULL == str) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "string expected", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - res = GNUNET_STRINGS_string_to_data (str, strlen (str), - where, len); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "malformed binary data in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - ret = GNUNET_OK; - } - break; - - case TMH_PARSE_JNC_RET_DATA_VAR: - { - void **where = va_arg (argp, void **); - size_t *len = va_arg (argp, size_t *); - const char *str; - int res; - - str = json_string_value (root); - if (NULL == str) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_internal_error (connection, - "json_string_value() failed")) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - *len = (strlen (str) * 5) / 8; - if (NULL != where) - { - *where = GNUNET_malloc (*len); - res = GNUNET_STRINGS_string_to_data (str, - strlen (str), - *where, - *len); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - GNUNET_free (*where); - *where = NULL; - *len = 0; - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "malformed binary data in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - } - ret = GNUNET_OK; - } - break; - - case TMH_PARSE_JNC_RET_TYPED_JSON: - { - int typ = va_arg (argp, int); - const json_t **r_json = va_arg (argp, const json_t **); - - if ( (NULL == root) || - ( (-1 != typ) && - (json_typeof (root) != typ)) ) - { - GNUNET_break_op (0); - *r_json = NULL; - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:i, s:i, s:O}", - "error", "wrong JSON field type", - "type_expected", typ, - "type_actual", json_typeof (root), - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - *r_json = root; - json_incref ((json_t *) root); - ret = GNUNET_OK; - } - break; - - case TMH_PARSE_JNC_RET_UINT64: - { - uint64_t *r_u64 = va_arg (argp, uint64_t *); - - if (json_typeof (root) != JSON_INTEGER) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:s, s:i, s:O}", - "error", "wrong JSON field type", - "type_expected", "integer", - "type_actual", json_typeof (root), - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - *r_u64 = (uint64_t) json_integer_value (root); - ret = GNUNET_OK; - } - break; - - case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY: - { - struct TALER_DenominationPublicKey *where; - size_t len; - const char *str; - int res; - void *buf; - - where = va_arg (argp, - struct TALER_DenominationPublicKey *); - str = json_string_value (root); - if (NULL == str) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "string expected", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - len = (strlen (str) * 5) / 8; - buf = GNUNET_malloc (len); - res = GNUNET_STRINGS_string_to_data (str, - strlen (str), - buf, - len); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - GNUNET_free (buf); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "malformed binary data in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - where->rsa_public_key = GNUNET_CRYPTO_rsa_public_key_decode (buf, - len); - GNUNET_free (buf); - if (NULL == where->rsa_public_key) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "malformed RSA public key in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - ret = GNUNET_OK; - break; - } - - case TMH_PARSE_JNC_RET_RSA_SIGNATURE: - { - struct TALER_DenominationSignature *where; - size_t len; - const char *str; - int res; - void *buf; - - where = va_arg (argp, - struct TALER_DenominationSignature *); - str = json_string_value (root); - if (NULL == str) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "string expected", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - len = (strlen (str) * 5) / 8; - buf = GNUNET_malloc (len); - res = GNUNET_STRINGS_string_to_data (str, - strlen (str), - buf, - len); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - GNUNET_free (buf); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "malformed binary data in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - where->rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (buf, - len); - GNUNET_free (buf); - if (NULL == where->rsa_signature) - { - GNUNET_break_op (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "malformed RSA signature in JSON", - "path", path)) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } - ret = GNUNET_OK; - break; - } - - case TMH_PARSE_JNC_RET_AMOUNT: - { - struct TALER_Amount *where = va_arg (argp, void *); - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount (NULL, where), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse ((json_t *) root, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = (MHD_YES != - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O}", - "error", "Bad format", - "path", path)) - ? GNUNET_SYSERR : GNUNET_NO; - break; - } - if (0 != strcmp (where->currency, - TMH_exchange_currency_string)) - { - GNUNET_break_op (0); - ret = (MHD_YES != - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:O, s:s}", - "error", "Currency not supported", - "path", path, - "currency", where->currency)) - ? GNUNET_SYSERR : GNUNET_NO; - memset (where, 0, sizeof (struct TALER_Amount)); - break; - } - ret = GNUNET_OK; - break; - } - - case TMH_PARSE_JNC_RET_TIME_ABSOLUTE: - { - struct GNUNET_TIME_Absolute *where = va_arg (argp, void *); - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_absolute_time (NULL, where), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse ((json_t *) root, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ret = (MHD_YES != - TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:s, s:O}", - "error", "Bad format", - "hint", "expected absolute time", - "path", path)) - ? GNUNET_SYSERR : GNUNET_NO; - break; - } - ret = GNUNET_OK; - break; - } - - default: - GNUNET_break (0); - ret = (MHD_YES == - TMH_RESPONSE_reply_internal_error (connection, - "unhandled value in switch")) - ? GNUNET_NO : GNUNET_SYSERR; - break; - } + if (NULL == error_json_name) + error_json_name = ""; + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s, s:I}", + "error", "parse error", + "field", error_json_name, + "line", (json_int_t) error_line)) + ? GNUNET_NO : GNUNET_SYSERR; + return ret; } - va_end (argp); - json_decref (path); - return ret; + return GNUNET_YES; } /** - * Parse JSON object into components based on the given field - * specification. + * Parse JSON array into components based on the given field + * specification. Generates error response on parse errors. * * @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 + * @param[in,out] spec field specification for the parser + * @param ... -1-terminated list of array offsets of type 'int' * @return * #GNUNET_YES if navigation was successful (caller is responsible * for freeing allocated variable-size data using - * #TMH_PARSE_release_data() when done) + * #GNUNET_JSON_parse_free() when done) * #GNUNET_NO if json is malformed, error response was generated * #GNUNET_SYSERR on internal error */ int -TMH_PARSE_json_data (struct MHD_Connection *connection, - const json_t *root, - struct TMH_PARSE_FieldSpecification *spec) +TMH_PARSE_json_array (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_JSON_Specification *spec, + ...) { - unsigned int i; int ret; - - ret = GNUNET_YES; - for (i=0; NULL != spec[i].field_name; i++) + const char *error_json_name; + unsigned int error_line; + va_list ap; + json_int_t dim; + + va_start (ap, spec); + dim = 0; + while ( (-1 != (ret = va_arg (ap, int))) && + (NULL != root) ) { - if (GNUNET_YES != ret) - break; - switch (spec[i].command) - { - case TMH_PARSE_JNC_FIELD: - GNUNET_break (0); - return GNUNET_SYSERR; - case TMH_PARSE_JNC_INDEX: - GNUNET_break (0); - return GNUNET_SYSERR; - case TMH_PARSE_JNC_RET_DATA: - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_DATA, - spec[i].destination, - spec[i].destination_size_in); - break; - case TMH_PARSE_JNC_RET_DATA_VAR: - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_DATA_VAR, - (void **) spec[i].destination, - spec[i].destination_size_out); - break; - case TMH_PARSE_JNC_RET_TYPED_JSON: - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_TYPED_JSON, - spec[i].type, - spec[i].destination); - break; - case TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY: - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, - spec[i].destination); - break; - case TMH_PARSE_JNC_RET_RSA_SIGNATURE: - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_RSA_SIGNATURE, - spec[i].destination); - break; - case TMH_PARSE_JNC_RET_AMOUNT: - GNUNET_assert (sizeof (struct TALER_Amount) == - spec[i].destination_size_in); - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_AMOUNT, - spec[i].destination); - break; - case TMH_PARSE_JNC_RET_TIME_ABSOLUTE: - GNUNET_assert (sizeof (struct GNUNET_TIME_Absolute) == - spec[i].destination_size_in); - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_TIME_ABSOLUTE, - spec[i].destination); - break; - case TMH_PARSE_JNC_RET_UINT64: - GNUNET_assert (sizeof (uint64_t) == - spec[i].destination_size_in); - ret = TMH_PARSE_navigate_json (connection, - root, - TMH_PARSE_JNC_FIELD, - spec[i].field_name, - TMH_PARSE_JNC_RET_UINT64, - spec[i].destination); - break; - } + dim++; + root = json_array_get (root, ret); } - if (GNUNET_YES != ret) - release_data (spec, - i - 1); - return ret; -} - - -/** - * Release all memory allocated for the variable-size fields in - * the parser specification. - * - * @param spec specification to free - */ -void -TMH_PARSE_release_data (struct TMH_PARSE_FieldSpecification *spec) -{ - unsigned int i; - - for (i=0; NULL != spec[i].field_name; i++) ; - release_data (spec, i); -} - - -/** - * Generate line in parser specification for 64-bit integer - * given as an integer in JSON. - * - * @param field name of the field - * @param[out] u64 integer to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_uint64 (const char *field, - uint64_t *u64) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, (void *) u64, sizeof (uint64_t), NULL, TMH_PARSE_JNC_RET_UINT64, 0 }; - return ret; -} - - -/** - * Generate line in parser specification for JSON object value. - * - * @param field name of the field - * @param[out] jsonp address of pointer to JSON to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_object (const char *field, - json_t **jsonp) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_OBJECT }; - *jsonp = NULL; - return ret; -} - - -/** - * Generate line in parser specification for JSON array value. - * - * @param field name of the field - * @param[out] jsonp address of JSON pointer to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_array (const char *field, - json_t **jsonp) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, jsonp, 0, NULL, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_ARRAY }; - *jsonp = NULL; - return ret; -} - - -/** - * Generate line in parser specification for an absolute time. - * - * @param field name of the field - * @param[out] atime time to initialize - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_time_abs (const char *field, - struct GNUNET_TIME_Absolute *atime) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, atime, sizeof(struct GNUNET_TIME_Absolute), NULL, TMH_PARSE_JNC_RET_TIME_ABSOLUTE, 0 }; - return ret; -} - - -/** - * Generate line in parser specification for RSA public key. - * - * @param field name of the field - * @param[out] pk key to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_denomination_public_key (const char *field, - struct TALER_DenominationPublicKey *pk) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, pk, 0, NULL, TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, 0 }; - pk->rsa_public_key = NULL; - return ret; -} - - -/** - * Generate line in parser specification for RSA public key. - * - * @param field name of the field - * @param sig the signature to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_denomination_signature (const char *field, - struct TALER_DenominationSignature *sig) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, sig, 0, NULL, TMH_PARSE_JNC_RET_RSA_SIGNATURE, 0 }; - sig->rsa_signature = NULL; - return ret; -} - - -/** - * Generate line in parser specification for an amount. - * - * @param field name of the field - * @param amount a `struct TALER_Amount *` to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_amount (const char *field, - struct TALER_Amount *amount) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, amount, sizeof(struct TALER_Amount), NULL, TMH_PARSE_JNC_RET_AMOUNT, 0 }; - memset (amount, 0, sizeof (struct TALER_Amount)); - return ret; + if (NULL == root) + { + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:I}", + "error", "parse error", + "dimension", dim)) + ? GNUNET_NO : GNUNET_SYSERR; + return ret; + } + ret = GNUNET_JSON_parse (root, + spec, + &error_json_name, + &error_line); + if (GNUNET_SYSERR == ret) + { + if (NULL == error_json_name) + error_json_name = ""; + ret = (MHD_YES == + TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:s, s:I}", + "error", "parse error", + "field", error_json_name, + "line", (json_int_t) error_line)) + ? GNUNET_NO : GNUNET_SYSERR; + return ret; + } + return GNUNET_YES; } -/** - * Generate line in parser specification for variable-size value. - * - * @param field name of the field - * @param[out] ptr pointer to initialize - * @param[out] ptr_size size to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_variable (const char *field, - void **ptr, - size_t *ptr_size) -{ - struct TMH_PARSE_FieldSpecification ret = - { field, ptr, 0, ptr_size, TMH_PARSE_JNC_RET_DATA_VAR, 0 }; - *ptr = NULL; - return ret; -} - /* end of taler-exchange-httpd_parsing.c */ diff --git a/src/exchange/taler-exchange-httpd_parsing.h b/src/exchange/taler-exchange-httpd_parsing.h index a3923568..d09f5876 100644 --- a/src/exchange/taler-exchange-httpd_parsing.h +++ b/src/exchange/taler-exchange-httpd_parsing.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -26,6 +26,7 @@ #include #include #include "taler_util.h" +#include "taler_json_lib.h" /** @@ -72,291 +73,45 @@ TMH_PARSE_post_cleanup_callback (void *con_cls); /** - * Constants for JSON navigation description. - */ -enum TMH_PARSE_JsonNavigationCommand -{ - /** - * Access a field. - * Param: const char * - */ - TMH_PARSE_JNC_FIELD, - - /** - * Access an array index. - * Param: int - */ - TMH_PARSE_JNC_INDEX, - - /** - * Return base32crockford encoded data of - * constant size. - * Params: (void *, size_t) - */ - TMH_PARSE_JNC_RET_DATA, - - /** - * Return base32crockford encoded data of - * variable size. - * Params: (void **, size_t *) - */ - TMH_PARSE_JNC_RET_DATA_VAR, - - /** - * Return a json object, which must be - * of the given type (JSON_* type constants, - * or -1 for any type). - * Params: (int, json_t **) - */ - TMH_PARSE_JNC_RET_TYPED_JSON, - - /** - * Return a `struct GNUNET_CRYPTO_rsa_PublicKey` which was - * encoded as variable-size base32crockford encoded data. - */ - TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, - - /** - * Return a `struct GNUNET_CRYPTO_rsa_Signature` which was - * encoded as variable-size base32crockford encoded data. - */ - TMH_PARSE_JNC_RET_RSA_SIGNATURE, - - /** - * Return a `struct TALER_Amount` which was - * encoded within its own json object. - */ - TMH_PARSE_JNC_RET_AMOUNT, - - /** - * Return a `struct GNUNET_TIME_Absolute` which was - * encoded within its own json object. - * Param: struct GNUNET_TIME_Absolute * - */ - TMH_PARSE_JNC_RET_TIME_ABSOLUTE, - - /** - * Return a `uint64_t` which was - * encoded as a JSON integer. - * Param: uint64_t * - */ - TMH_PARSE_JNC_RET_UINT64 - -}; - - -/** - * Navigate through a JSON tree. - * - * Sends an error response if navigation is impossible (i.e. - * the JSON object is invalid) + * 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 ... navigation specification (see `enum TMH_PARSE_JsonNavigationCommand`) + * @param spec field specification for the parser * @return - * #GNUNET_YES if navigation was successful + * #GNUNET_YES if navigation was successful (caller is responsible + * for freeing allocated variable-size data using + * #GNUNET_JSON_parse_free() when done) * #GNUNET_NO if json is malformed, error response was generated * #GNUNET_SYSERR on internal error */ int -TMH_PARSE_navigate_json (struct MHD_Connection *connection, - const json_t *root, - ...); - - -/** - * @brief Specification for how to parse a JSON field. - */ -struct TMH_PARSE_FieldSpecification -{ - /** - * 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; - - /** - * Navigation command to use to extract the value. Note that - * #TMH_PARSE_JNC_RET_DATA or #TMH_PARSE_JNC_RET_DATA_VAR must be used for @e - * destination_size_in and @e destination_size_out to have a - * meaning. #TMH_PARSE_JNC_FIELD and #TMH_PARSE_JNC_INDEX must not be used here! - */ - enum TMH_PARSE_JsonNavigationCommand command; - - /** - * JSON type to use, only meaningful in connection with a @e command - * value of #TMH_PARSE_JNC_RET_TYPED_JSON. Typical values are - * #JSON_ARRAY and #JSON_OBJECT. - */ - int type; - -}; +TMH_PARSE_json_data (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_JSON_Specification *spec); /** - * Parse JSON object into components based on the given field - * specification. + * Parse JSON array into components based on the given field + * specification. Generates error response on parse errors. * * @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 + * @param[in,out] spec field specification for the parser + * @param ... -1-terminated list of array offsets of type 'int' * @return * #GNUNET_YES if navigation was successful (caller is responsible * for freeing allocated variable-size data using - * #TMH_PARSE_release_data() when done) + * #GNUNET_JSON_parse_free() when done) * #GNUNET_NO if json is malformed, error response was generated * #GNUNET_SYSERR on internal error */ int -TMH_PARSE_json_data (struct MHD_Connection *connection, - const json_t *root, - struct TMH_PARSE_FieldSpecification *spec); - - -/** - * Release all memory allocated for the variable-size fields in - * the parser specification. - * - * @param spec specification to free - */ -void -TMH_PARSE_release_data (struct TMH_PARSE_FieldSpecification *spec); - - -/** - * Generate line in parser specification for fixed-size value. - * - * @param field name of the field - * @param value where to store the value - */ -#define TMH_PARSE_member_fixed(field,value) { field, value, sizeof (*value), NULL, TMH_PARSE_JNC_RET_DATA, 0 } - - -/** - * Generate line in parser specification for variable-size value. - * - * @param field name of the field - * @param[out] ptr pointer to initialize - * @param[out] ptr_size size to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_variable (const char *field, - void **ptr, - size_t *ptr_size); - - -/** - * Generate line in parser specification for 64-bit integer - * given as an integer in JSON. - * - * @param field name of the field - * @param[out] u64 integer to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_uint64 (const char *field, - uint64_t *u64); - - -/** - * Generate line in parser specification for JSON array value. - * - * @param field name of the field - * @param[out] jsonp address of JSON pointer to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_array (const char *field, - json_t **jsonp); - - -/** - * Generate line in parser specification for JSON object value. - * - * @param field name of the field - * @param[out] jsonp address of pointer to JSON to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_object (const char *field, - json_t **jsonp); - - -/** - * Generate line in parser specification for RSA public key. - * - * @param field name of the field - * @param[out] pk key to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_denomination_public_key (const char *field, - struct TALER_DenominationPublicKey *pk); - - -/** - * Generate line in parser specification for RSA public key. - * - * @param field name of the field - * @param sig the signature to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_denomination_signature (const char *field, - struct TALER_DenominationSignature *sig); - - -/** - * Generate line in parser specification for an amount. - * - * @param field name of the field - * @param[out] amount a `struct TALER_Amount *` to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_amount (const char *field, - struct TALER_Amount *amount); - - -/** - * Generate line in parser specification for an absolute time. - * - * @param field name of the field - * @param[out] atime time to initialize - * @return corresponding field spec - */ -struct TMH_PARSE_FieldSpecification -TMH_PARSE_member_time_abs (const char *field, - struct GNUNET_TIME_Absolute *atime); - - - -/** - * Generate line in parser specification indicating the end of the spec. - */ -#define TMH_PARSE_MEMBER_END { NULL, NULL, 0, NULL, TMH_PARSE_JNC_FIELD, 0 } +TMH_PARSE_json_array (struct MHD_Connection *connection, + const json_t *root, + struct GNUNET_JSON_Specification *spec, + ...); /** @@ -381,28 +136,4 @@ TMH_PARSE_mhd_request_arg_data (struct MHD_Connection *connection, size_t out_size); -/** - * Extraxt variable-size base32crockford encoded data from request. - * - * Queues an error response to the connection if the parameter is missing - * or the encoding is invalid. - * - * @param connection the MHD connection - * @param param_name the name of the parameter with the key - * @param[out] out_data pointer to allocate buffer and store the result - * @param[out] out_size set to the size of the buffer allocated in @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) - */ -int -TMH_PARSE_mhd_request_var_arg_data (struct MHD_Connection *connection, - const char *param_name, - void **out_data, - size_t *out_size); - - - - #endif /* TALER_EXCHANGE_HTTPD_PARSING_H */ diff --git a/src/exchange/taler-exchange-httpd_refresh.c b/src/exchange/taler-exchange-httpd_refresh.c index 45bbf3d4..2349d90a 100644 --- a/src/exchange/taler-exchange-httpd_refresh.c +++ b/src/exchange/taler-exchange-httpd_refresh.c @@ -192,13 +192,13 @@ get_coin_public_info (struct MHD_Connection *connection, struct TALER_DenominationSignature sig; struct TALER_DenominationPublicKey pk; struct TALER_Amount amount; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_fixed ("coin_pub", &r_melt_detail->coin_info.coin_pub), - TMH_PARSE_member_denomination_signature ("denom_sig", &sig), - TMH_PARSE_member_denomination_public_key ("denom_pub", &pk), - TMH_PARSE_member_fixed ("confirm_sig", &melt_sig), - TMH_PARSE_member_amount ("value_with_fee", &amount), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("coin_pub", &r_melt_detail->coin_info.coin_pub), + TALER_JSON_spec_denomination_signature ("denom_sig", &sig), + TALER_JSON_spec_denomination_public_key ("denom_pub", &pk), + GNUNET_JSON_spec_fixed_auto ("confirm_sig", &melt_sig), + TALER_JSON_spec_amount ("value_with_fee", &amount), + GNUNET_JSON_spec_end () }; ret = TMH_PARSE_json_data (connection, @@ -216,7 +216,7 @@ get_coin_public_info (struct MHD_Connection *connection, TALER_test_coin_valid (&r_melt_detail->coin_info)) { GNUNET_break_op (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); r_melt_detail->coin_info.denom_sig.rsa_signature = NULL; r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL; return (MHD_YES == @@ -409,12 +409,16 @@ handle_refresh_melt_json (struct MHD_Connection *connection, { char *buf; size_t buf_size; - - res = TMH_PARSE_navigate_json (connection, - new_denoms, - TMH_PARSE_JNC_INDEX, (int) i, - TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, - &denom_pubs[i].rsa_public_key); + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_denomination_public_key (NULL, + &denom_pubs[i]), + GNUNET_JSON_spec_end () + }; + + res = TMH_PARSE_json_array (connection, + new_denoms, + spec, + i, -1); if (GNUNET_OK != res) { res = (GNUNET_NO == res) ? MHD_YES : MHD_NO; @@ -436,6 +440,7 @@ handle_refresh_melt_json (struct MHD_Connection *connection, /* decode JSON data on coin to melt */ struct TALER_AmountNBO melt_amount; + // FIXME: check json_array_get() return value for NULL! res = get_coin_public_info (connection, json_array_get (melt_coins, i), &coin_melt_details[i]); @@ -482,15 +487,23 @@ handle_refresh_melt_json (struct MHD_Connection *connection, char *link_enc; size_t link_enc_size; struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &commit_coin[i][j]; - - res = TMH_PARSE_navigate_json (connection, - coin_evs, - TMH_PARSE_JNC_INDEX, (int) i, - TMH_PARSE_JNC_INDEX, (int) j, - TMH_PARSE_JNC_RET_DATA_VAR, - &rcc->coin_ev, - &rcc->coin_ev_size); - + struct GNUNET_JSON_Specification coin_spec[] = { + GNUNET_JSON_spec_varsize (NULL, + (void **) &rcc->coin_ev, + &rcc->coin_ev_size), + GNUNET_JSON_spec_end () + }; + struct GNUNET_JSON_Specification link_spec[] = { + GNUNET_JSON_spec_varsize (NULL, + (void **) &link_enc, + &link_enc_size), + GNUNET_JSON_spec_end () + }; + + res = TMH_PARSE_json_array (connection, + coin_evs, + coin_spec, + i, j, -1); if (GNUNET_OK != res) { GNUNET_break_op (0); @@ -501,13 +514,10 @@ handle_refresh_melt_json (struct MHD_Connection *connection, GNUNET_CRYPTO_hash_context_read (hash_context, rcc->coin_ev, rcc->coin_ev_size); - res = TMH_PARSE_navigate_json (connection, - link_encs, - TMH_PARSE_JNC_INDEX, (int) i, - TMH_PARSE_JNC_INDEX, (int) j, - TMH_PARSE_JNC_RET_DATA_VAR, - &link_enc, - &link_enc_size); + res = TMH_PARSE_json_array (connection, + link_encs, + link_spec, + i, j, -1); if (GNUNET_OK != res) { GNUNET_break_op (0); @@ -520,7 +530,7 @@ handle_refresh_melt_json (struct MHD_Connection *connection, GNUNET_CRYPTO_hash_context_read (hash_context, link_enc, link_enc_size); - GNUNET_free (link_enc); + GNUNET_JSON_parse_free (link_spec); } } @@ -531,27 +541,29 @@ handle_refresh_melt_json (struct MHD_Connection *connection, for (j = 0; j < num_oldcoins; j++) { struct TALER_RefreshCommitLinkP *rcl = &commit_link[i][j]; - - res = TMH_PARSE_navigate_json (connection, - transfer_pubs, - TMH_PARSE_JNC_INDEX, (int) i, - TMH_PARSE_JNC_INDEX, (int) j, - TMH_PARSE_JNC_RET_DATA, - &rcl->transfer_pub, - sizeof (struct TALER_TransferPublicKeyP)); + struct GNUNET_JSON_Specification trans_spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &rcl->transfer_pub), + GNUNET_JSON_spec_end () + }; + struct GNUNET_JSON_Specification sec_spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &rcl->shared_secret_enc), + GNUNET_JSON_spec_end () + }; + + res = TMH_PARSE_json_array (connection, + transfer_pubs, + trans_spec, + i, j, -1); if (GNUNET_OK != res) { GNUNET_break_op (0); res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; goto cleanup; } - res = TMH_PARSE_navigate_json (connection, - secret_encs, - TMH_PARSE_JNC_INDEX, (int) i, - TMH_PARSE_JNC_INDEX, (int) j, - TMH_PARSE_JNC_RET_DATA, - &rcl->shared_secret_enc, - sizeof (struct TALER_EncryptedLinkSecretP)); + res = TMH_PARSE_json_array (connection, + secret_encs, + sec_spec, + i, j, -1); if (GNUNET_OK != res) { GNUNET_break_op (0); @@ -646,15 +658,16 @@ TMH_REFRESH_handler_refresh_melt (struct TMH_RequestHandler *rh, unsigned int num_oldcoins; unsigned int num_newcoins; json_t *coin_detail; + json_t *trans_detail; int res; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_array ("new_denoms", &new_denoms), - TMH_PARSE_member_array ("melt_coins", &melt_coins), - TMH_PARSE_member_array ("coin_evs", &coin_evs), - TMH_PARSE_member_array ("link_encs", &link_encs), - TMH_PARSE_member_array ("transfer_pubs", &transfer_pubs), - TMH_PARSE_member_array ("secret_encs", &secret_encs), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_json ("new_denoms", &new_denoms), + GNUNET_JSON_spec_json ("melt_coins", &melt_coins), + GNUNET_JSON_spec_json ("coin_evs", &coin_evs), + GNUNET_JSON_spec_json ("link_encs", &link_encs), + GNUNET_JSON_spec_json ("transfer_pubs", &transfer_pubs), + GNUNET_JSON_spec_json ("secret_encs", &secret_encs), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -678,43 +691,36 @@ TMH_REFRESH_handler_refresh_melt (struct TMH_RequestHandler *rh, if (TALER_CNC_KAPPA != json_array_size (coin_evs)) { GNUNET_break_op (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_invalid (connection, "coin_evs"); } if (TALER_CNC_KAPPA != json_array_size (transfer_pubs)) { GNUNET_break_op (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_arg_invalid (connection, "transfer_pubs"); } - res = TMH_PARSE_navigate_json (connection, - coin_evs, - TMH_PARSE_JNC_INDEX, (int) 0, - TMH_PARSE_JNC_RET_TYPED_JSON, - JSON_ARRAY, &coin_detail); - if (GNUNET_OK != res) + coin_detail = json_array_get (coin_evs, 0); + if (NULL == coin_detail) { + // FIXME: generate proper HTTP response! GNUNET_break_op (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } num_newcoins = json_array_size (coin_detail); - json_decref (coin_detail); - res = TMH_PARSE_navigate_json (connection, - transfer_pubs, - TMH_PARSE_JNC_INDEX, (int) 0, - TMH_PARSE_JNC_RET_TYPED_JSON, - JSON_ARRAY, &coin_detail); - if (GNUNET_OK != res) + + trans_detail = json_array_get (transfer_pubs, 0); + if (NULL == trans_detail) { + // FIXME: generate proper HTTP response! GNUNET_break_op (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } - num_oldcoins = json_array_size (coin_detail); - json_decref (coin_detail); + num_oldcoins = json_array_size (trans_detail); res = handle_refresh_melt_json (connection, new_denoms, melt_coins, @@ -724,7 +730,7 @@ TMH_REFRESH_handler_refresh_melt (struct TMH_RequestHandler *rh, num_newcoins, coin_evs, link_encs); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return res; } @@ -763,15 +769,16 @@ handle_refresh_reveal_json (struct MHD_Connection *connection, break; for (j = 0; j < num_oldcoins; j++) { + struct GNUNET_JSON_Specification tp_spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &transfer_privs[i][j]), + GNUNET_JSON_spec_end () + }; if (GNUNET_OK != res) break; - res = TMH_PARSE_navigate_json (connection, - tp_json, - TMH_PARSE_JNC_INDEX, (int) i, - TMH_PARSE_JNC_INDEX, (int) j, - TMH_PARSE_JNC_RET_DATA, - &transfer_privs[i][j], - sizeof (struct TALER_TransferPrivateKeyP)); + res = TMH_PARSE_json_array (connection, + tp_json, + tp_spec, + i, j, -1); GNUNET_break_op (GNUNET_OK == res); } } @@ -817,10 +824,10 @@ TMH_REFRESH_handler_refresh_reveal (struct TMH_RequestHandler *rh, json_t *reveal_detail; json_t *root; json_t *transfer_privs; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_fixed ("session_hash", &session_hash), - TMH_PARSE_member_array ("transfer_privs", &transfer_privs), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash), + GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -846,30 +853,25 @@ TMH_REFRESH_handler_refresh_reveal (struct TMH_RequestHandler *rh, /* Note we do +1 as 1 row (cut-and-choose!) is missing! */ if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1) { - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); GNUNET_break_op (0); return TMH_RESPONSE_reply_arg_invalid (connection, "transfer_privs"); } - res = TMH_PARSE_navigate_json (connection, - transfer_privs, - TMH_PARSE_JNC_INDEX, 0, - TMH_PARSE_JNC_RET_TYPED_JSON, - JSON_ARRAY, - &reveal_detail); - if (GNUNET_OK != res) + reveal_detail = json_array_get (transfer_privs, 0); + if (NULL == reveal_detail) { - TMH_PARSE_release_data (spec); + // FIXME: generate proper HTTP response! + GNUNET_JSON_parse_free (spec); GNUNET_break_op (0); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } num_oldcoins = json_array_size (reveal_detail); - json_decref (reveal_detail); res = handle_refresh_reveal_json (connection, &session_hash, num_oldcoins, transfer_privs); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return res; } diff --git a/src/exchange/taler-exchange-httpd_reserve.c b/src/exchange/taler-exchange-httpd_reserve.c index 45f07310..1763280e 100644 --- a/src/exchange/taler-exchange-httpd_reserve.c +++ b/src/exchange/taler-exchange-httpd_reserve.c @@ -101,17 +101,17 @@ TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct TMH_KS_StateHandle *ks; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_variable ("coin_ev", + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_varsize ("coin_ev", (void **) &blinded_msg, &blinded_msg_len), - TMH_PARSE_member_fixed ("reserve_pub", + GNUNET_JSON_spec_fixed_auto ("reserve_pub", &wsrd.reserve_pub), - TMH_PARSE_member_fixed ("reserve_sig", + GNUNET_JSON_spec_fixed_auto ("reserve_sig", &signature), - TMH_PARSE_member_denomination_public_key ("denom_pub", + TALER_JSON_spec_denomination_public_key ("denom_pub", &denomination_pub), - TMH_PARSE_MEMBER_END + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -135,7 +135,7 @@ TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, TMH_KS_DKU_WITHDRAW); if (NULL == dki) { - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); TMH_KS_release (ks); return TMH_RESPONSE_reply_arg_unknown (connection, "denom_pub"); @@ -169,7 +169,7 @@ TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, &wsrd.reserve_pub.eddsa_pub)) { TALER_LOG_WARNING ("Client supplied invalid signature for /reserve/withdraw request\n"); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_signature_invalid (connection, "reserve_sig"); } @@ -179,7 +179,7 @@ TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, blinded_msg, blinded_msg_len, &signature); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return res; } diff --git a/src/exchange/taler-exchange-httpd_test.c b/src/exchange/taler-exchange-httpd_test.c index 7b6438ca..0a4181ed 100644 --- a/src/exchange/taler-exchange-httpd_test.c +++ b/src/exchange/taler-exchange-httpd_test.c @@ -63,9 +63,9 @@ TMH_TEST_handler_test_base32 (struct TMH_RequestHandler *rh, void *in_ptr; size_t in_ptr_size; struct GNUNET_HashCode hc; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_variable ("input", &in_ptr, &in_ptr_size), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_varsize ("input", &in_ptr, &in_ptr_size), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -85,7 +85,7 @@ TMH_TEST_handler_test_base32 (struct TMH_RequestHandler *rh, GNUNET_CRYPTO_hash (in_ptr, in_ptr_size, &hc); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); json_decref (json); return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, @@ -126,10 +126,10 @@ TMH_TEST_handler_test_encrypt (struct TMH_RequestHandler *rh, struct GNUNET_CRYPTO_SymmetricSessionKey skey; void *in_ptr; size_t in_ptr_size; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_variable ("input", &in_ptr, &in_ptr_size), - TMH_PARSE_member_fixed ("key_hash", &key), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_varsize ("input", &in_ptr, &in_ptr_size), + GNUNET_JSON_spec_fixed_auto ("key_hash", &key), + GNUNET_JSON_spec_end () }; char *out; @@ -168,7 +168,7 @@ TMH_TEST_handler_test_encrypt (struct TMH_RequestHandler *rh, json = GNUNET_JSON_from_data (out, in_ptr_size); GNUNET_free (out); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, "{s:o}", @@ -206,9 +206,9 @@ TMH_TEST_handler_test_hkdf (struct TMH_RequestHandler *rh, struct GNUNET_HashCode hc; void *in_ptr; size_t in_ptr_size; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_variable ("input", &in_ptr, &in_ptr_size), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_varsize ("input", &in_ptr, &in_ptr_size), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -231,7 +231,7 @@ TMH_TEST_handler_test_hkdf (struct TMH_RequestHandler *rh, in_ptr, in_ptr_size, NULL, 0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); json = GNUNET_JSON_from_data (&hc, sizeof (struct GNUNET_HashCode)); return TMH_RESPONSE_reply_json_pack (connection, @@ -268,10 +268,10 @@ TMH_TEST_handler_test_ecdhe (struct TMH_RequestHandler *rh, struct GNUNET_CRYPTO_EcdhePublicKey pub; struct GNUNET_CRYPTO_EcdhePrivateKey priv; struct GNUNET_HashCode hc; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_fixed ("ecdhe_pub", &pub), - TMH_PARSE_member_fixed ("ecdhe_priv", &priv), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("ecdhe_pub", &pub), + GNUNET_JSON_spec_fixed_auto ("ecdhe_priv", &priv), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -294,11 +294,11 @@ TMH_TEST_handler_test_ecdhe (struct TMH_RequestHandler *rh, &pub, &hc)) { - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_internal_error (connection, "Failed to perform ECDH"); } - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, "{s:o}", @@ -335,10 +335,10 @@ TMH_TEST_handler_test_eddsa (struct TMH_RequestHandler *rh, struct GNUNET_CRYPTO_EddsaPublicKey pub; struct GNUNET_CRYPTO_EddsaSignature sig; struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_fixed ("eddsa_pub", &pub), - TMH_PARSE_member_fixed ("eddsa_sig", &sig), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("eddsa_pub", &pub), + GNUNET_JSON_spec_fixed_auto ("eddsa_sig", &sig), + GNUNET_JSON_spec_end () }; struct GNUNET_CRYPTO_EddsaPrivateKey *pk; @@ -365,11 +365,11 @@ TMH_TEST_handler_test_eddsa (struct TMH_RequestHandler *rh, &sig, &pub)) { - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_signature_invalid (connection, "eddsa_sig"); } - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); pk = GNUNET_CRYPTO_eddsa_key_create (); purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_TEST_EDDSA); if (GNUNET_OK != @@ -466,9 +466,9 @@ TMH_TEST_handler_test_rsa_sign (struct TMH_RequestHandler *rh, struct GNUNET_CRYPTO_rsa_Signature *sig; void *in_ptr; size_t in_ptr_size; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_variable ("blind_ev", &in_ptr, &in_ptr_size), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_varsize ("blind_ev", &in_ptr, &in_ptr_size), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -491,7 +491,7 @@ TMH_TEST_handler_test_rsa_sign (struct TMH_RequestHandler *rh, if (NULL == rsa_pk) { GNUNET_break (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_internal_error (connection, "Failed to create RSA key"); } @@ -501,11 +501,11 @@ TMH_TEST_handler_test_rsa_sign (struct TMH_RequestHandler *rh, if (NULL == sig) { GNUNET_break (0); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_internal_error (connection, "Failed to RSA-sign"); } - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); res = TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_OK, "{s:o}", @@ -542,11 +542,11 @@ TMH_TEST_handler_test_transfer (struct TMH_RequestHandler *rh, struct TALER_EncryptedLinkSecretP secret_enc; struct TALER_TransferPrivateKeyP trans_priv; struct TALER_CoinSpendPublicKeyP coin_pub; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_fixed ("secret_enc", &secret_enc), - TMH_PARSE_member_fixed ("trans_priv", &trans_priv), - TMH_PARSE_member_fixed ("coin_pub", &coin_pub), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("secret_enc", &secret_enc), + GNUNET_JSON_spec_fixed_auto ("trans_priv", &trans_priv), + GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub), + GNUNET_JSON_spec_end () }; struct TALER_LinkSecretP secret; @@ -571,7 +571,7 @@ TMH_TEST_handler_test_transfer (struct TMH_RequestHandler *rh, &coin_pub, &secret)) { - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); return TMH_RESPONSE_reply_internal_error (connection, "Failed to decrypt secret"); } diff --git a/src/exchange/taler-exchange-httpd_tracking.c b/src/exchange/taler-exchange-httpd_tracking.c index afb821cb..36782332 100644 --- a/src/exchange/taler-exchange-httpd_tracking.c +++ b/src/exchange/taler-exchange-httpd_tracking.c @@ -121,14 +121,14 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh, struct TALER_DepositTrackPS tps; uint64_t transaction_id; struct TALER_MerchantSignatureP merchant_sig; - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_fixed ("H_wire", &tps.h_wire), - TMH_PARSE_member_fixed ("H_contract", &tps.h_contract), - TMH_PARSE_member_fixed ("coin_pub", &tps.coin_pub), - TMH_PARSE_member_uint64 ("transaction_id", &transaction_id), - TMH_PARSE_member_fixed ("merchant_pub", &tps.merchant), - TMH_PARSE_member_fixed ("merchant_sig", &merchant_sig), - TMH_PARSE_MEMBER_END + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("H_wire", &tps.h_wire), + GNUNET_JSON_spec_fixed_auto ("H_contract", &tps.h_contract), + GNUNET_JSON_spec_fixed_auto ("coin_pub", &tps.coin_pub), + GNUNET_JSON_spec_uint64 ("transaction_id", &transaction_id), + GNUNET_JSON_spec_fixed_auto ("merchant_pub", &tps.merchant), + GNUNET_JSON_spec_fixed_auto ("merchant_sig", &merchant_sig), + GNUNET_JSON_spec_end () }; res = TMH_PARSE_post_json (connection, @@ -156,7 +156,7 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh, &tps.merchant, &merchant_sig, transaction_id); - TMH_PARSE_release_data (spec); + GNUNET_JSON_parse_free (spec); json_decref (json); return res; } diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index aee151b9..3dd661a9 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -57,6 +57,30 @@ TALER_JSON_spec_amount (const char *name, struct TALER_Amount *r_amount); +/** + * Generate line in parser specification for denomination public key. + * + * @param field name of the field + * @param[out] pk key to initialize + * @return corresponding field spec + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_denomination_public_key (const char *field, + struct TALER_DenominationPublicKey *pk); + + +/** + * Generate line in parser specification for denomination signature. + * + * @param field name of the field + * @param sig the signature to initialize + * @return corresponding field spec + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_denomination_signature (const char *field, + struct TALER_DenominationSignature *sig); + + /** * Hash a JSON for binary signing. * diff --git a/src/json/json_helper.c b/src/json/json_helper.c index b61fe21a..7eaaa441 100644 --- a/src/json/json_helper.c +++ b/src/json/json_helper.c @@ -143,4 +143,36 @@ TALER_JSON_spec_amount (const char *name, } +/** + * Generate line in parser specification for denomination public key. + * + * @param field name of the field + * @param[out] pk key to initialize + * @return corresponding field spec + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_denomination_public_key (const char *field, + struct TALER_DenominationPublicKey *pk) +{ + return GNUNET_JSON_spec_rsa_public_key (field, + &pk->rsa_public_key); +} + + +/** + * Generate line in parser specification for denomination signature. + * + * @param field name of the field + * @param sig the signature to initialize + * @return corresponding field spec + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_denomination_signature (const char *field, + struct TALER_DenominationSignature *sig) +{ + return GNUNET_JSON_spec_rsa_signature (field, + &sig->rsa_signature); +} + + /* end of json/json_helper.c */ -- cgit v1.2.3 From ad8351c912995a9ef0524822f0fdeef55e9d27a9 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 6 Apr 2016 10:22:09 +0200 Subject: fix iterate_matching_deposits(), LIMIT does not work with variables in Postgres (#4360) --- src/exchange/taler-exchange-aggregator.c | 5 +- src/exchangedb/Makefile.am | 1 + src/exchangedb/plugin_exchangedb_postgres.c | 26 ++++----- src/exchangedb/test_exchangedb.c | 81 ++++++++++++++++++++++++++++- src/include/taler_exchangedb_plugin.h | 15 +++++- src/include/taler_json_lib.h | 2 +- 6 files changed, 110 insertions(+), 20 deletions(-) (limited to 'src/include/taler_json_lib.h') diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index a6b7de21..cfc11a5f 100644 --- a/src/exchange/taler-exchange-aggregator.c +++ b/src/exchange/taler-exchange-aggregator.c @@ -78,9 +78,10 @@ static int test_mode; * of the smallest possible unit are aggregated, they do surpass the * "tiny" threshold beyond which we never trigger a wire transaction! * - * TODO: make configurable (via config file or command line option) + * Note: do not change here, Postgres requires us to hard-code the + * LIMIT in the prepared statement. */ -static unsigned int aggregation_limit = 10000; +static unsigned int aggregation_limit = TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT; /** diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 416d5688..0a7b0447 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -80,6 +80,7 @@ test_exchangedb_postgres_SOURCES = \ test_exchangedb.c test_exchangedb_postgres_LDADD = \ libtalerexchangedb.la \ + $(top_builddir)/src/json/libtalerjson.la \ $(top_srcdir)/src/util/libtalerutil.la \ $(top_srcdir)/src/pq/libtalerpq.la \ -lgnunetutil -ljansson diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 6807e756..c38c0827 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -952,7 +952,7 @@ postgres_prepare (PGconn *db_conn) " tiny=false AND" " done=false" " ORDER BY wire_deadline ASC" - " LIMIT 1;", + " LIMIT 1", 0, NULL); /* Used in #postgres_iterate_matching_deposits() */ @@ -975,8 +975,8 @@ postgres_prepare (PGconn *db_conn) " h_wire=$2 AND" " done=false" " ORDER BY wire_deadline ASC" - " LIMIT $3", - 3, NULL); + " LIMIT " TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR, + 2, NULL); /* Used in #postgres_mark_deposit_tiny() */ PREPARE ("mark_deposit_tiny", @@ -2336,7 +2336,6 @@ postgres_iterate_matching_deposits (void *cls, struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (merchant_pub), GNUNET_PQ_query_param_auto_from_type (h_wire), - GNUNET_PQ_query_param_uint32 (&limit), GNUNET_PQ_query_param_end }; PGresult *result; @@ -2344,8 +2343,8 @@ postgres_iterate_matching_deposits (void *cls, unsigned int n; result = GNUNET_PQ_exec_prepared (session->conn, - "deposits_iterate_matching", - params); + "deposits_iterate_matching", + params); if (PGRES_TUPLES_OK != PQresultStatus (result)) { @@ -2366,28 +2365,25 @@ postgres_iterate_matching_deposits (void *cls, struct TALER_Amount deposit_fee; struct GNUNET_TIME_Absolute wire_deadline; struct GNUNET_HashCode h_contract; - struct TALER_MerchantPublicKeyP merchant_pub; struct TALER_CoinSpendPublicKeyP coin_pub; uint64_t transaction_id; uint64_t serial_id; int ret; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("serial_id", - &serial_id), + &serial_id), GNUNET_PQ_result_spec_uint64 ("transaction_id", - &transaction_id), + &transaction_id), TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee), TALER_PQ_result_spec_amount ("deposit_fee", &deposit_fee), GNUNET_PQ_result_spec_absolute_time ("wire_deadline", - &wire_deadline), + &wire_deadline), GNUNET_PQ_result_spec_auto_from_type ("h_contract", - &h_contract), - GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", - &merchant_pub), + &h_contract), GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &coin_pub), + &coin_pub), GNUNET_PQ_result_spec_end }; if (GNUNET_OK != @@ -2399,7 +2395,7 @@ postgres_iterate_matching_deposits (void *cls, } ret = deposit_cb (deposit_cb_cls, serial_id, - &merchant_pub, + merchant_pub, &coin_pub, &amount_with_fee, &deposit_fee, diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index f33bb18c..8d06c007 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -20,6 +20,7 @@ */ #include "platform.h" #include "taler_exchangedb_lib.h" +#include "taler_json_lib.h" #include "taler_exchangedb_plugin.h" static int result; @@ -546,6 +547,70 @@ cb_wtid_check (void *cls, } +/** + * Function called with details about deposits that + * have been made. Called in the test on the + * deposit given in @a cls. + * + * @param cls closure a `struct TALER_EXCHANGEDB_Deposit *` + * @param rowid unique ID for the deposit in our DB, used for marking + * it as 'tiny' or 'done' + * @param merchant_pub public key of the merchant + * @param coin_pub public key of the coin + * @param amount_with_fee amount that was deposited including fee + * @param deposit_fee amount the exchange gets to keep as transaction fees + * @param transaction_id unique transaction ID chosen by the merchant + * @param h_contract hash of the contract between merchant and customer + * @param wire_deadline by which the merchant adviced that he would like the + * wire transfer to be executed + * @param wire wire details for the merchant, NULL from iterate_matching_deposits() + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR if deposit does + * not match our expectations + */ +static int +deposit_cb (void *cls, + unsigned long long rowid, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + uint64_t transaction_id, + const struct GNUNET_HashCode *h_contract, + struct GNUNET_TIME_Absolute wire_deadline, + const json_t *wire) +{ + struct TALER_EXCHANGEDB_Deposit *deposit = cls; + struct GNUNET_HashCode h_wire; + + if (NULL != wire) + TALER_JSON_hash (wire, &h_wire); + if ( (0 != memcmp (merchant_pub, + &deposit->merchant_pub, + sizeof (struct TALER_MerchantPublicKeyP))) || + (0 != TALER_amount_cmp (amount_with_fee, + &deposit->amount_with_fee)) || + (0 != TALER_amount_cmp (deposit_fee, + &deposit->deposit_fee)) || + (0 != memcmp (h_contract, + &deposit->h_contract, + sizeof (struct GNUNET_HashCode))) || + (0 != memcmp (coin_pub, + &deposit->coin.coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP))) || + (transaction_id != deposit->transaction_id) || + ( (NULL != wire) && + (0 != memcmp (&h_wire, + &deposit->h_wire, + sizeof (struct GNUNET_HashCode))) ) ) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + /** * Main function that will be run by the scheduler. * @@ -739,14 +804,16 @@ run (void *cls, RND_BLK (&deposit.csig); RND_BLK (&deposit.merchant_pub); RND_BLK (&deposit.h_contract); - RND_BLK (&deposit.h_wire); wire = json_loads (json_wire_str, 0, NULL); + TALER_JSON_hash (wire, + &deposit.h_wire); deposit.wire = wire; deposit.transaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); deposit.amount_with_fee = value; GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (CURRENCY, &deposit.deposit_fee)); + result = 8; FAILIF (GNUNET_OK != plugin->insert_deposit (plugin->cls, session, &deposit)); @@ -754,6 +821,15 @@ run (void *cls, plugin->have_deposit (plugin->cls, session, &deposit)); + result = 9; + FAILIF (1 != + plugin->iterate_matching_deposits (plugin->cls, + session, + &deposit.h_wire, + &deposit.merchant_pub, + &deposit_cb, &deposit, + 2)); + result = 10; deposit2 = deposit; deposit2.transaction_id++; /* should fail if transaction id is different */ FAILIF (GNUNET_NO != @@ -880,6 +956,9 @@ main (int argc, GNUNET_break (0); return -1; } + GNUNET_log_setup (argv[0], + "WARNING", + NULL); plugin_name++; (void) GNUNET_asprintf (&testname, "test-exchange-db-%s", plugin_name); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index cb1dcb34..3646981c 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -947,6 +947,17 @@ struct TALER_EXCHANGEDB_Plugin void *deposit_cb_cls); +/** + * Maximum number of results we return from iterate_matching_deposits(). + * + * Limit on the number of transactions we aggregate at once. Note + * that the limit must be big enough to ensure that when transactions + * of the smallest possible unit are aggregated, they do surpass the + * "tiny" threshold beyond which we never trigger a wire transaction! + */ +#define TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT 10000 +#define TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR "10000" + /** * Obtain information about other pending deposits for the same * destination. Those deposits must not already be "done". @@ -957,7 +968,9 @@ struct TALER_EXCHANGEDB_Plugin * @param merchant_pub public key of the merchant * @param deposit_cb function to call for each deposit * @param deposit_cb_cls closure for @a deposit_cb - * @param limit maximum number of matching deposits to return + * @param limit maximum number of matching deposits to return; should + * be #TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT, larger values + * are not supported, smaller values would be inefficient. * @return number of rows processed, 0 if none exist, * #GNUNET_SYSERR on error */ diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h index 3dd661a9..79589dba 100644 --- a/src/include/taler_json_lib.h +++ b/src/include/taler_json_lib.h @@ -89,7 +89,7 @@ TALER_JSON_spec_denomination_signature (const char *field, * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int -TALER_JSON_hash (json_t *json, +TALER_JSON_hash (const json_t *json, struct GNUNET_HashCode *hc); #endif /* TALER_JSON_LIB_H_ */ -- cgit v1.2.3