From f58fb4fc21be694096ae223fe0990ab93a52e815 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 16 Jan 2015 13:50:07 +0100 Subject: starting to separate out response generation API and parsing APIs --- src/mint/Makefile.am | 3 +- src/mint/taler-mint-httpd.c | 3 +- src/mint/taler-mint-httpd_deposit.c | 29 ++- src/mint/taler-mint-httpd_json.c | 417 ------------------------------- src/mint/taler-mint-httpd_json.h | 119 --------- src/mint/taler-mint-httpd_keys.c | 2 +- src/mint/taler-mint-httpd_mhd.c | 167 +------------ src/mint/taler-mint-httpd_mhd.h | 23 +- src/mint/taler-mint-httpd_parsing.c | 453 ++++++++++++++++++++++++++++++++++ src/mint/taler-mint-httpd_parsing.h | 136 ++++++++++ src/mint/taler-mint-httpd_refresh.c | 137 +++++----- src/mint/taler-mint-httpd_responses.c | 146 +++++++++++ src/mint/taler-mint-httpd_responses.h | 93 +++++++ src/mint/taler-mint-httpd_withdraw.c | 71 +++--- 14 files changed, 980 insertions(+), 819 deletions(-) delete mode 100644 src/mint/taler-mint-httpd_json.c delete mode 100644 src/mint/taler-mint-httpd_json.h create mode 100644 src/mint/taler-mint-httpd_parsing.c create mode 100644 src/mint/taler-mint-httpd_parsing.h create mode 100644 src/mint/taler-mint-httpd_responses.c create mode 100644 src/mint/taler-mint-httpd_responses.h (limited to 'src') diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index eff126a07..4a2e775da 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -61,8 +61,9 @@ taler_mint_reservemod_LDFLAGS = \ 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_mhd.c \ - taler-mint-httpd_json.c taler-mint-httpd_json.h \ taler-mint-httpd_keys.c \ taler-mint-httpd_deposit.c \ taler-mint-httpd_withdraw.c \ diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index 14dfa657c..61d0bbdce 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -32,7 +32,7 @@ #include "taler_signatures.h" #include "taler_rsa.h" #include "taler_json_lib.h" -#include "taler-mint-httpd_json.h" +#include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_mhd.h" #include "taler-mint-httpd_keys.h" #include "taler-mint-httpd_deposit.h" @@ -372,4 +372,3 @@ main (int argc, char *const *argv) MHD_stop_daemon (mydaemon); return (GNUNET_OK == ret) ? 0 : 1; } - diff --git a/src/mint/taler-mint-httpd_deposit.c b/src/mint/taler-mint-httpd_deposit.c index b8bcc87db..8c040b691 100644 --- a/src/mint/taler-mint-httpd_deposit.c +++ b/src/mint/taler-mint-httpd_deposit.c @@ -31,9 +31,10 @@ #include "taler_signatures.h" #include "taler_rsa.h" #include "taler_json_lib.h" -#include "taler-mint-httpd_json.h" +#include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_keys.h" #include "taler-mint-httpd_deposit.h" +#include "taler-mint-httpd_responses.h" /** @@ -48,8 +49,11 @@ helper_deposit_send_response_success (struct MHD_Connection *connection, struct Deposit *deposit) { // FIXME: return more information here - return request_send_json_pack (connection, MHD_HTTP_OK, - "{s:s}", "status", "DEPOSIT_OK"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:s}", + "status", + "DEPOSIT_OK"); } @@ -199,9 +203,11 @@ TALER_MINT_handler_deposit (struct RequestHandler *rh, // FIXME: in the future, check if there's enough credits // left on the coin. For now: refuse // FIXME: return more information here - return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", "double spending"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_FORBIDDEN, + "{s:s}", + "error", + "double spending"); } if (GNUNET_SYSERR == res) @@ -221,9 +227,10 @@ TALER_MINT_handler_deposit (struct RequestHandler *rh, // coin must have been refreshed // FIXME: check // FIXME: return more information here - return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", "coin was refreshed"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_FORBIDDEN, + "{s:s}", + "error", "coin was refreshed"); } if (GNUNET_SYSERR == res) { @@ -252,7 +259,9 @@ TALER_MINT_handler_deposit (struct RequestHandler *rh, EXITIF_exit: if (NULL != resp) - res = send_response_json (connection, resp, resp_code); + res = TALER_MINT_reply_json (connection, + resp, + resp_code); else res = MHD_NO; if (NULL != wire) diff --git a/src/mint/taler-mint-httpd_json.c b/src/mint/taler-mint-httpd_json.c deleted file mode 100644 index e9183073f..000000000 --- a/src/mint/taler-mint-httpd_json.c +++ /dev/null @@ -1,417 +0,0 @@ -#include "platform.h" -#include -#include "taler-mint-httpd_json.h" - - - -/** - * Initial size for POST - * request buffer. - */ -#define REQUEST_BUFFER_INITIAL 1024 - -/** - * Maximum POST request size - */ -#define REQUEST_BUFFER_MAX (1024*1024) - - -/** - * Buffer for POST requests. - */ -struct Buffer -{ - /** - * Allocated memory - */ - char *data; - - /** - * Number of valid bytes in buffer. - */ - size_t fill; - - /** - * Number of allocated bytes in buffer. - */ - size_t alloc; -}; - - -/** - * Initialize a buffer. - * - * @param buf the buffer to initialize - * @param data the initial data - * @param data_size size of the initial data - * @param alloc_size size of the buffer - * @param max_size maximum size that the buffer can grow to - * @return a GNUnet result code - */ -static int -buffer_init (struct Buffer *buf, const void *data, size_t data_size, size_t alloc_size, size_t max_size) -{ - if (data_size > max_size || alloc_size > max_size) - return GNUNET_SYSERR; - if (data_size > alloc_size) - alloc_size = data_size; - buf->data = GNUNET_malloc (alloc_size); - memcpy (buf->data, data, data_size); - return GNUNET_OK; -} - - -/** - * Free the data in a buffer. Does *not* free - * the buffer object itself. - * - * @param buf buffer to de-initialize - */ -static void -buffer_deinit (struct Buffer *buf) -{ - GNUNET_free (buf->data); - buf->data = NULL; -} - - -/** - * Append data to a buffer, growing the buffer if necessary. - * - * @param buf the buffer to append to - * @param data the data to append - * @param size the size of @a data - * @param max_size maximum size that the buffer can grow to - * @return GNUNET_OK on success, - * GNUNET_NO if the buffer can't accomodate for the new data - * GNUNET_SYSERR on fatal error (out of memory?) - */ -static int -buffer_append (struct Buffer *buf, const void *data, size_t data_size, size_t max_size) -{ - if (buf->fill + data_size > max_size) - return GNUNET_NO; - if (data_size + buf->fill > buf->alloc) - { - char *new_buf; - size_t new_size = buf->alloc; - while (new_size < buf->fill + data_size) - new_size += 2; - if (new_size > max_size) - return GNUNET_NO; - new_buf = GNUNET_malloc (new_size); - memcpy (new_buf, buf->data, buf->fill); - buf->data = new_buf; - buf->alloc = new_size; - } - memcpy (buf->data + buf->fill, data, data_size); - buf->fill += data_size; - return GNUNET_OK; -} - - -/** - * Send JSON object as response. Decreases the reference count of the - * JSON object. - * - * @param connection the MHD connection - * @param json the json object - * @param status_code the http status code - * @return MHD result code - */ -int -send_response_json (struct MHD_Connection *connection, - json_t *json, - unsigned int status_code) -{ - struct MHD_Response *resp; - char *json_str; - - json_str = json_dumps (json, JSON_INDENT(2)); - json_decref (json); - resp = MHD_create_response_from_buffer (strlen (json_str), json_str, - MHD_RESPMEM_MUST_FREE); - if (NULL == resp) - return MHD_NO; - return MHD_queue_response (connection, status_code, resp); -} - - -/** - * Send a JSON object via an MHD connection, - * specified with the JANSSON pack syntax (see json_pack). - * - * @param connection connection to send the JSON over - * @param http_code HTTP status for the response - * @param fmt format string for pack - * @param ... varargs - * @return MHD_YES on success or MHD_NO on error - */ -int -request_send_json_pack (struct MHD_Connection *connection, - unsigned int http_code, - const char *fmt, ...) -{ - json_t *msg; - va_list argp; - int ret; - - va_start(argp, fmt); - msg = json_vpack_ex (NULL, 0, fmt, argp); - va_end(argp); - if (NULL == msg) - return MHD_NO; - ret = send_response_json (connection, msg, http_code); - json_decref (msg); - return ret; -} - - -/** - * Process a POST request containing a JSON object. - * - * @param connection the MHD connection - * @param con_cs the closure (contains a 'struct Buffer *') - * @param upload_data the POST data - * @param upload_data_size the POST data size - * @param json the JSON object for a completed request - * - * @returns - * GNUNET_YES if json object was parsed - * GNUNET_NO is request incomplete or invalid - * GNUNET_SYSERR on internal error - */ -int -process_post_json (struct MHD_Connection *connection, - void **con_cls, - const char *upload_data, - size_t *upload_data_size, - json_t **json) -{ - struct Buffer *r = *con_cls; - - if (NULL == *con_cls) - { - /* We are seeing a fresh POST request. */ - - r = GNUNET_new (struct Buffer); - if (GNUNET_OK != buffer_init (r, upload_data, *upload_data_size, - REQUEST_BUFFER_INITIAL, REQUEST_BUFFER_MAX)) - { - *con_cls = NULL; - buffer_deinit (r); - GNUNET_free (r); - return GNUNET_SYSERR; - } - *upload_data_size = 0; - *con_cls = r; - return GNUNET_NO; - } - if (0 != *upload_data_size) - { - /* We are seeing an old request with more data available. */ - - if (GNUNET_OK != buffer_append (r, upload_data, *upload_data_size, - REQUEST_BUFFER_MAX)) - { - /* Request too long or we're out of memory. */ - - *con_cls = NULL; - buffer_deinit (r); - GNUNET_free (r); - return GNUNET_SYSERR; - } - *upload_data_size = 0; - return GNUNET_NO; - } - - /* We have seen the whole request. */ - - *json = json_loadb (r->data, r->fill, 0, NULL); - buffer_deinit (r); - GNUNET_free (r); - if (NULL == *json) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Can't parse JSON request body\n"); - return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - GNUNET_NO, GNUNET_SYSERR, - "{s:s}", - "error", "invalid json"); - } - *con_cls = NULL; - - return GNUNET_YES; -} - - -/** - * Navigate through a JSON tree. - * - * 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 ... navigation specification (see JNAV_*) - * @return GNUNET_YES if navigation was successful - * GNUNET_NO if json is malformed, error response was generated - * GNUNET_SYSERR on internal error - */ -int -request_json_require_nav (struct MHD_Connection *connection, - const json_t *root, ...) -{ - va_list argp; - int ignore = GNUNET_NO; - // what's our current path from 'root'? - json_t *path; - - path = json_array (); - - va_start(argp, root); - - while (1) - { - int command = va_arg(argp, int); - switch (command) - { - case JNAV_FIELD: - { - const char *fname = va_arg(argp, const char *); - if (GNUNET_YES == ignore) - break; - json_array_append_new (path, json_string (fname)); - root = json_object_get (root, fname); - if (NULL == root) - { - - (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - 0, 0, - "{s:s,s:o}", - "error", "missing field in JSON", - "path", path); - ignore = GNUNET_YES; - break; - } - } - break; - case JNAV_INDEX: - { - int fnum = va_arg(argp, int); - if (GNUNET_YES == ignore) - break; - json_array_append_new (path, json_integer (fnum)); - root = json_array_get (root, fnum); - if (NULL == root) - { - (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - 0, 0, - "{s:s, s:o}", - "error", "missing index in JSON", - "path", path); - ignore = GNUNET_YES; - break; - } - } - break; - case JNAV_RET_DATA: - { - void *where = va_arg (argp, void *); - size_t len = va_arg (argp, size_t); - const char *str; - int res; - - va_end(argp); - if (GNUNET_YES == ignore) - return GNUNET_NO; - str = json_string_value (root); - if (NULL == str) - { - (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - 0, 0, - "{s:s, s:o}", - "error", "string expected", - "path", path); - return GNUNET_NO; - } - res = GNUNET_STRINGS_string_to_data (str, strlen (str), - where, len); - if (GNUNET_OK != res) - { - (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - 0, 0, - "{s:s,s:o}", - "error", "malformed binary data in JSON", - "path", path); - return GNUNET_NO; - } - return GNUNET_YES; - } - break; - case JNAV_RET_DATA_VAR: - { - void **where = va_arg (argp, void **); - size_t *len = va_arg (argp, size_t *); - const char *str; - - va_end(argp); - if (GNUNET_YES == ignore) - return GNUNET_NO; - str = json_string_value (root); - if (NULL == str) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - *len = (strlen (str) * 5) / 8; - if (where != NULL) - { - int res; - *where = GNUNET_malloc (*len); - res = GNUNET_STRINGS_string_to_data (str, strlen (str), - *where, *len); - if (GNUNET_OK != res) - { - (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - 0, 0, - "{s:s, s:o}", - "error", "malformed binary data in JSON", - "path", path); - return GNUNET_NO; - } - } - return GNUNET_OK; - } - break; - case JNAV_RET_TYPED_JSON: - { - int typ = va_arg (argp, int); - const json_t **r_json = va_arg (argp, const json_t **); - - va_end(argp); - if (GNUNET_YES == ignore) - return GNUNET_NO; - if (typ != -1 && json_typeof (root) != typ) - { - (void) request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - 0, 0, - "{s:s, s:i, s:i s:o}", - "error", "wrong JSON field type", - "type_expected", typ, - "type_actual", json_typeof (root), - "path", path); - return GNUNET_NO; - } - *r_json = root; - return GNUNET_OK; - } - break; - default: - GNUNET_assert (0); - } - } - GNUNET_assert (0); -} - - - diff --git a/src/mint/taler-mint-httpd_json.h b/src/mint/taler-mint-httpd_json.h deleted file mode 100644 index da601401f..000000000 --- a/src/mint/taler-mint-httpd_json.h +++ /dev/null @@ -1,119 +0,0 @@ - - -#ifndef TALER_MICROHTTPD_LIB_H_ -#define TALER_MICROHTTPD_LIB_H_ - - -#include -#include - - -/** - * Constants for JSON navigation description. - */ -enum -{ - /** - * Access a field. - * Param: const char * - */ - JNAV_FIELD, - /** - * Access an array index. - * Param: int - */ - JNAV_INDEX, - /** - * Return base32crockford encoded data of - * constant size. - * Params: (void *, size_t) - */ - JNAV_RET_DATA, - /** - * Return base32crockford encoded data of - * variable size. - * Params: (void **, size_t *) - */ - JNAV_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 **) - */ - JNAV_RET_TYPED_JSON -}; - - - -/** - * Send JSON object as response. Decreases - * the reference count of the JSON object. - * - * @param connection the MHD connection - * @param json the json object - * @param status_code the http status code - * @return MHD result code (MHD_YES on success) - */ -int -send_response_json (struct MHD_Connection *connection, - json_t *json, - unsigned int status_code); - - -/** - * Send a JSON object via an MHD connection, - * specified with the JANSSON pack syntax (see json_pack). - * - * @param connection connection to send the JSON over - * @param http_code HTTP status for the response - * @param fmt format string for pack - * @param ... varargs - * @return MHD_YES on success or MHD_NO on error - */ -int -request_send_json_pack (struct MHD_Connection *connection, - unsigned int http_code, - const char *fmt, ...); - - -/** - * Process a POST request containing a JSON object. - * - * @param connection the MHD connection - * @param con_cs the closure (contains a 'struct Buffer *') - * @param upload_data the POST data - * @param upload_data_size the POST data size - * @param json the JSON object for a completed request - * - * @returns - * GNUNET_YES if json object was parsed - * GNUNET_NO is request incomplete or invalid - * GNUNET_SYSERR on internal error - */ -int -process_post_json (struct MHD_Connection *connection, - void **con_cls, - const char *upload_data, - size_t *upload_data_size, - json_t **json); - - -/** - * Navigate through a JSON tree. - * - * 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 ... navigation specification (see JNAV_*) - * @return GNUNET_YES if navigation was successful - * GNUNET_NO if json is malformed, error response was generated - * GNUNET_SYSERR on internal error - */ -int -request_json_require_nav (struct MHD_Connection *connection, - const json_t *root, ...); - -#endif /* TALER_MICROHTTPD_LIB_H_ */ diff --git a/src/mint/taler-mint-httpd_keys.c b/src/mint/taler-mint-httpd_keys.c index 6d6e9468d..149e44d3f 100644 --- a/src/mint/taler-mint-httpd_keys.c +++ b/src/mint/taler-mint-httpd_keys.c @@ -31,7 +31,7 @@ #include "taler_signatures.h" #include "taler_rsa.h" #include "taler_json_lib.h" -#include "taler-mint-httpd_json.h" +#include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_keys.h" diff --git a/src/mint/taler-mint-httpd_mhd.c b/src/mint/taler-mint-httpd_mhd.c index d78f36d95..ed8a6eeda 100644 --- a/src/mint/taler-mint-httpd_mhd.c +++ b/src/mint/taler-mint-httpd_mhd.c @@ -16,7 +16,9 @@ /** * @file taler-mint-httpd_mhd.c - * @brief helpers for MHD interaction + * @brief helpers for MHD interaction; these are TALER_MINT_handler_ functions + * that generate simple MHD replies that do not require any real operations + * to be performed (error handling, static pages, etc.) * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff @@ -27,9 +29,10 @@ #include #include #include -#include "taler-mint-httpd_json.h" +#include "taler-mint-httpd_responses.h" #include "taler-mint-httpd.h" #include "taler-mint-httpd_mhd.h" +#include "taler-mint-httpd_responses.h" /** @@ -121,63 +124,6 @@ TALER_MINT_handler_agpl_redirect (struct RequestHandler *rh, } -/** - * Function to call to handle the request by building a JSON - * reply from varargs. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[IN|OUT] connection_cls the connection's closure (can be updated) - * @param response_code HTTP response code to use - * @param do_cache can the response be cached? (0: no, 1: yes) - * @param fmt format string for pack - * @param ... varargs - * @return MHD result code - */ -int -TALER_MINT_helper_send_json_pack (struct RequestHandler *rh, - struct MHD_Connection *connection, - void *connection_cls, - int response_code, - int do_cache, - const char *fmt, - ...) -{ - int ret; - json_t *json; - va_list argp; - char *json_str; - struct MHD_Response *response; - - va_start (argp, fmt); - json = json_vpack_ex (NULL, 0, fmt, argp); - va_end (argp); - if (NULL == json) - return MHD_NO; - json_str = json_dumps (json, JSON_INDENT(2)); - json_decref (json); - if (NULL == json_str) - return MHD_NO; - response = MHD_create_response_from_buffer (strlen (json_str), - json_str, - MHD_RESPMEM_MUST_FREE); - if (NULL == response) - { - free (json_str); - return MHD_NO; - } - if (NULL != rh->mime_type) - (void) MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - rh->mime_type); - ret = MHD_queue_response (connection, - response_code, - response); - MHD_destroy_response (response); - return ret; -} - - /** * Function to call to handle the request by building a JSON * reply with an error message from @a rh. @@ -196,105 +142,12 @@ TALER_MINT_handler_send_json_pack_error (struct RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - return TALER_MINT_helper_send_json_pack (rh, - connection, - connection_cls, - 1, /* caching enabled */ - rh->response_code, - "{s:s}", - "error", - rh->data); + return TALER_MINT_reply_json_pack (connection, + rh->response_code, + "{s:s}", + "error", + rh->data); } -/** - * Send a response for an invalid argument. - * - * @param connection the MHD connection to use - * @param param_name the parameter that is missing - * @return a GNUnet result code - */ -static int -request_arg_invalid (struct MHD_Connection *connection, - const char *param_name) -{ - json_t *json; - json = json_pack ("{ s:s, s:s }", - "error", "invalid parameter", - "parameter", param_name); - if (MHD_YES != send_response_json (connection, json, MHD_HTTP_BAD_REQUEST)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_NO; -} - - -/** - * Get a GET paramater that is a string, - * or send an error response if the parameter is missing. - * - * @param connection the connection to get the parameter from / - * send the error response to - * @param param_name the parameter name - * @param str pointer to store the parameter string, - * must be freed by the caller - * @return GNUNET_YES if the parameter is present and valid, - * GNUNET_NO if the parameter is missing - * GNUNET_SYSERR on internal error - */ -static int -request_arg_require_string (struct MHD_Connection *connection, - const char *param_name, - const char **str) -{ - *str = MHD_lookup_connection_value (connection, MHD_GET_ARGUMENT_KIND, param_name); - if (NULL == *str) - { - if (MHD_NO == - request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - "{ s:s, s:s }", - "error", "missing parameter", - "parameter", param_name)) - return GNUNET_SYSERR; - return GNUNET_NO; - } - return GNUNET_OK; -} - - -/** - * Extraxt base32crockford encoded data from request. - * - * 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 - * @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) - */ -int -TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection, - const char *param_name, - void *out_data, - size_t out_size) -{ - const char *str; - int ret; - - if (GNUNET_OK != (ret = request_arg_require_string (connection, param_name, &str))) - return ret; - if (GNUNET_OK != GNUNET_STRINGS_string_to_data (str, strlen (str), out_data, out_size)) - return request_arg_invalid (connection, param_name); - return GNUNET_OK; -} - - - /* end of taler-mint-httpd_mhd.c */ diff --git a/src/mint/taler-mint-httpd_mhd.h b/src/mint/taler-mint-httpd_mhd.h index 29ab7f64b..1390a2753 100644 --- a/src/mint/taler-mint-httpd_mhd.h +++ b/src/mint/taler-mint-httpd_mhd.h @@ -16,7 +16,7 @@ /** * @file taler-mint-httpd_mhd.h - * @brief helpers for MHD interaction + * @brief helpers for MHD interaction, used to generate simple responses * @author Florian Dold * @author Benedikt Mueller * @author Christian Grothoff @@ -108,25 +108,4 @@ TALER_MINT_handler_send_json_pack_error (struct RequestHandler *rh, size_t *upload_data_size); -/** - * Extraxt base32crockford encoded data from request. - * - * 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 - * @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) - */ -int -TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection, - const char *param_name, - void *out_data, - size_t out_size); - #endif diff --git a/src/mint/taler-mint-httpd_parsing.c b/src/mint/taler-mint-httpd_parsing.c new file mode 100644 index 000000000..a976c0c06 --- /dev/null +++ b/src/mint/taler-mint-httpd_parsing.c @@ -0,0 +1,453 @@ +/* + This file is part of TALER + (C) 2014 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 + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_parsing.c + * @brief functions to parse incoming requests (MHD arguments and JSON snippets) + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ + +#include "platform.h" +#include +#include "taler-mint-httpd_parsing.h" +#include "taler-mint-httpd_responses.h" + + +/** + * Initial size for POST + * request buffer. + */ +#define REQUEST_BUFFER_INITIAL 1024 + +/** + * Maximum POST request size + */ +#define REQUEST_BUFFER_MAX (1024*1024) + + +/** + * Buffer for POST requests. + */ +struct Buffer +{ + /** + * Allocated memory + */ + char *data; + + /** + * Number of valid bytes in buffer. + */ + size_t fill; + + /** + * Number of allocated bytes in buffer. + */ + size_t alloc; +}; + + +/** + * Initialize a buffer. + * + * @param buf the buffer to initialize + * @param data the initial data + * @param data_size size of the initial data + * @param alloc_size size of the buffer + * @param max_size maximum size that the buffer can grow to + * @return a GNUnet result code + */ +static int +buffer_init (struct Buffer *buf, + const void *data, + size_t data_size, + size_t alloc_size, + size_t max_size) +{ + if (data_size > max_size || alloc_size > max_size) + return GNUNET_SYSERR; + if (data_size > alloc_size) + alloc_size = data_size; + buf->data = GNUNET_malloc (alloc_size); + memcpy (buf->data, data, data_size); + return GNUNET_OK; +} + + +/** + * Free the data in a buffer. Does *not* free + * the buffer object itself. + * + * @param buf buffer to de-initialize + */ +static void +buffer_deinit (struct Buffer *buf) +{ + GNUNET_free (buf->data); + buf->data = NULL; +} + + +/** + * Append data to a buffer, growing the buffer if necessary. + * + * @param buf the buffer to append to + * @param data the data to append + * @param size the size of @a data + * @param max_size maximum size that the buffer can grow to + * @return GNUNET_OK on success, + * GNUNET_NO if the buffer can't accomodate for the new data + * GNUNET_SYSERR on fatal error (out of memory?) + */ +static int +buffer_append (struct Buffer *buf, + const void *data, + size_t data_size, + size_t max_size) +{ + if (buf->fill + data_size > max_size) + return GNUNET_NO; + if (data_size + buf->fill > buf->alloc) + { + char *new_buf; + size_t new_size = buf->alloc; + while (new_size < buf->fill + data_size) + new_size += 2; + if (new_size > max_size) + return GNUNET_NO; + new_buf = GNUNET_malloc (new_size); + memcpy (new_buf, buf->data, buf->fill); + buf->data = new_buf; + buf->alloc = new_size; + } + memcpy (buf->data + buf->fill, data, data_size); + buf->fill += data_size; + return GNUNET_OK; +} + + +/** + * Process a POST request containing a JSON object. + * + * @param connection the MHD connection + * @param con_cs the closure (contains a 'struct Buffer *') + * @param upload_data the POST data + * @param upload_data_size the POST data size + * @param json the JSON object for a completed request + * + * @returns + * GNUNET_YES if json object was parsed + * GNUNET_NO is request incomplete or invalid + * GNUNET_SYSERR on internal error + */ +int +process_post_json (struct MHD_Connection *connection, + void **con_cls, + const char *upload_data, + size_t *upload_data_size, + json_t **json) +{ + struct Buffer *r = *con_cls; + + if (NULL == *con_cls) + { + /* We are seeing a fresh POST request. */ + + r = GNUNET_new (struct Buffer); + if (GNUNET_OK != + buffer_init (r, + upload_data, + *upload_data_size, + REQUEST_BUFFER_INITIAL, + REQUEST_BUFFER_MAX)) + { + *con_cls = NULL; + buffer_deinit (r); + GNUNET_free (r); + return GNUNET_SYSERR; + } + *upload_data_size = 0; + *con_cls = r; + return GNUNET_NO; + } + if (0 != *upload_data_size) + { + /* We are seeing an old request with more data available. */ + + if (GNUNET_OK != + buffer_append (r, + upload_data, + *upload_data_size, + REQUEST_BUFFER_MAX)) + { + /* Request too long or we're out of memory. */ + + *con_cls = NULL; + buffer_deinit (r); + GNUNET_free (r); + return GNUNET_SYSERR; + } + *upload_data_size = 0; + return GNUNET_NO; + } + + /* We have seen the whole request. */ + + *json = json_loadb (r->data, r->fill, 0, NULL); + buffer_deinit (r); + GNUNET_free (r); + if (NULL == *json) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Can't parse JSON request body\n"); + return (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", "invalid json")) + ? GNUNET_NO : GNUNET_SYSERR; + } + *con_cls = NULL; + + return GNUNET_YES; +} + + +/** + * Navigate through a JSON tree. + * + * 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 ... navigation specification (see JNAV_*) + * @return GNUNET_YES if navigation was successful + * GNUNET_NO if json is malformed, error response was generated + * GNUNET_SYSERR on internal error + */ +int +request_json_require_nav (struct MHD_Connection *connection, + const json_t *root, ...) +{ + va_list argp; + int ignore = GNUNET_NO; + // what's our current path from 'root'? + json_t *path; + + path = json_array (); + + va_start(argp, root); + + while (1) + { + int command = va_arg(argp, int); + switch (command) + { + case JNAV_FIELD: + { + const char *fname = va_arg(argp, const char *); + if (GNUNET_YES == ignore) + break; + json_array_append_new (path, json_string (fname)); + root = json_object_get (root, fname); + if (NULL == root) + { + /* FIXME: can't IGNORE this return value! */ + (void) TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s,s:o}", + "error", + "missing field in JSON", + "path", + path); + ignore = GNUNET_YES; + break; + } + } + break; + case JNAV_INDEX: + { + int fnum = va_arg(argp, int); + if (GNUNET_YES == ignore) + break; + json_array_append_new (path, json_integer (fnum)); + root = json_array_get (root, fnum); + if (NULL == root) + { + /* FIXME: can't IGNORE this return value! */ + (void) TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", "missing index in JSON", + "path", path); + ignore = GNUNET_YES; + break; + } + } + break; + case JNAV_RET_DATA: + { + void *where = va_arg (argp, void *); + size_t len = va_arg (argp, size_t); + const char *str; + int res; + + va_end(argp); + if (GNUNET_YES == ignore) + return GNUNET_NO; + str = json_string_value (root); + if (NULL == str) + { + /* FIXME: can't IGNORE this return value! */ + (void) TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "string expected", + "path", + path); + return GNUNET_NO; + } + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + where, len); + if (GNUNET_OK != res) + { + /* FIXME: can't IGNORE this return value! */ + (void) TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s,s:o}", + "error", + "malformed binary data in JSON", + "path", path); + return GNUNET_NO; + } + return GNUNET_YES; + } + break; + case JNAV_RET_DATA_VAR: + { + void **where = va_arg (argp, void **); + size_t *len = va_arg (argp, size_t *); + const char *str; + + va_end(argp); + if (GNUNET_YES == ignore) + return GNUNET_NO; + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + *len = (strlen (str) * 5) / 8; + if (where != NULL) + { + int res; + *where = GNUNET_malloc (*len); + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + *where, *len); + if (GNUNET_OK != res) + { + /* FIXME: can't IGNORE this return value! */ + (void) TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s, s:o}", + "error", + "malformed binary data in JSON", + "path", path); + return GNUNET_NO; + } + } + return GNUNET_OK; + } + break; + case JNAV_RET_TYPED_JSON: + { + int typ = va_arg (argp, int); + const json_t **r_json = va_arg (argp, const json_t **); + + va_end(argp); + if (GNUNET_YES == ignore) + return GNUNET_NO; + if (typ != -1 && json_typeof (root) != typ) + { + /* FIXME: can't IGNORE this return value! */ + (void) TALER_MINT_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); + return GNUNET_NO; + } + *r_json = root; + return GNUNET_OK; + } + break; + default: + GNUNET_assert (0); + } + } + GNUNET_assert (0); +} + + +/** + * Extract base32crockford encoded data from request. + * + * 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 + * @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) + */ +int +TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection, + const char *param_name, + void *out_data, + size_t out_size) +{ + const char *str; + + str = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + param_name); + if (NULL == str) + { + return (MHD_NO == + TALER_MINT_reply_arg_missing (connection, param_name)) + ? GNUNET_SYSERR : GNUNET_NO; + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (str, + strlen (str), + out_data, + out_size)) + return (MHD_NO == + TALER_MINT_reply_arg_invalid (connection, param_name)) + ? GNUNET_SYSERR : GNUNET_NO; + return GNUNET_OK; +} diff --git a/src/mint/taler-mint-httpd_parsing.h b/src/mint/taler-mint-httpd_parsing.h new file mode 100644 index 000000000..2c6bb073c --- /dev/null +++ b/src/mint/taler-mint-httpd_parsing.h @@ -0,0 +1,136 @@ +/* + This file is part of TALER + (C) 2014 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 + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_parsing.h + * @brief functions to parse incoming requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ + +#ifndef TALER_MICROHTTPD_LIB_H_ +#define TALER_MICROHTTPD_LIB_H_ + + +#include +#include + + +/** + * Constants for JSON navigation description. + */ +enum +{ + /** + * Access a field. + * Param: const char * + */ + JNAV_FIELD, + /** + * Access an array index. + * Param: int + */ + JNAV_INDEX, + /** + * Return base32crockford encoded data of + * constant size. + * Params: (void *, size_t) + */ + JNAV_RET_DATA, + /** + * Return base32crockford encoded data of + * variable size. + * Params: (void **, size_t *) + */ + JNAV_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 **) + */ + JNAV_RET_TYPED_JSON +}; + + +/** + * Process a POST request containing a JSON object. + * + * @param connection the MHD connection + * @param con_cs the closure (contains a 'struct Buffer *') + * @param upload_data the POST data + * @param upload_data_size the POST data size + * @param json the JSON object for a completed request + * + * @returns + * GNUNET_YES if json object was parsed + * GNUNET_NO is request incomplete or invalid + * GNUNET_SYSERR on internal error + */ +int +process_post_json (struct MHD_Connection *connection, + void **con_cls, + const char *upload_data, + size_t *upload_data_size, + json_t **json); + + +/** + * Navigate through a JSON tree. + * + * 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 ... navigation specification (see JNAV_*) + * @return GNUNET_YES if navigation was successful + * GNUNET_NO if json is malformed, error response was generated + * GNUNET_SYSERR on internal error + */ +int +request_json_require_nav (struct MHD_Connection *connection, + const json_t *root, ...); + + + + +/** + * Extraxt base32crockford encoded data from request. + * + * 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 + * @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) + */ +int +TALER_MINT_mhd_request_arg_data (struct MHD_Connection *connection, + const char *param_name, + void *out_data, + size_t out_size); + + + + +#endif /* TALER_MICROHTTPD_LIB_H_ */ diff --git a/src/mint/taler-mint-httpd_refresh.c b/src/mint/taler-mint-httpd_refresh.c index 888e7de3b..73b474194 100644 --- a/src/mint/taler-mint-httpd_refresh.c +++ b/src/mint/taler-mint-httpd_refresh.c @@ -31,10 +31,11 @@ #include "taler_signatures.h" #include "taler_rsa.h" #include "taler_json_lib.h" -#include "taler-mint-httpd_json.h" +#include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_keys.h" #include "taler-mint-httpd_mhd.h" #include "taler-mint-httpd_refresh.h" +#include "taler-mint-httpd_responses.h" /** @@ -228,10 +229,10 @@ check_confirm_signature (struct MHD_Connection *connection, coin_pub)) { if (MHD_YES != - request_send_json_pack (connection, - MHD_HTTP_UNAUTHORIZED, - "{s:s}", - "error", "signature invalid")) + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_UNAUTHORIZED, + "{s:s}", + "error", "signature invalid")) return GNUNET_SYSERR; return GNUNET_NO; } @@ -356,15 +357,19 @@ refresh_accept_melts (struct MHD_Connection *connection, dki = &(TALER_MINT_get_denom_key (key_state, &coin_public_info.denom_pub)->issue); if (NULL == dki) - return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "denom not found")) + return (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", "denom not found")) ? GNUNET_NO : GNUNET_SYSERR; if (GNUNET_OK != TALER_MINT_test_coin_valid (key_state, &coin_public_info)) - return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "coin invalid")) + return (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", "coin invalid")) ? GNUNET_NO : GNUNET_SYSERR; res = TALER_MINT_DB_get_known_coin (db_conn, &coin_public_info.coin_pub, @@ -379,9 +384,13 @@ refresh_accept_melts (struct MHD_Connection *connection, if (GNUNET_YES == res) { if (GNUNET_YES == known_coin.is_refreshed) - return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "coin already refreshed")) ? GNUNET_NO : GNUNET_SYSERR; + return (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "coin already refreshed")) + ? GNUNET_NO : GNUNET_SYSERR; } else { @@ -413,9 +422,11 @@ refresh_accept_melts (struct MHD_Connection *connection, * pay the refreshing fees of the coin. */ if (TALER_amount_cmp (coin_gain, TALER_amount_ntoh (dki->fee_refresh)) < 0) - return (MHD_YES == request_send_json_pack (connection, MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "depleted")) ? GNUNET_NO : GNUNET_SYSERR; + return (MHD_YES == + TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", "depleted")) ? GNUNET_NO : GNUNET_SYSERR; coin_gain = TALER_amount_subtract (coin_gain, TALER_amount_ntoh (dki->fee_refresh)); @@ -472,9 +483,9 @@ helper_refresh_send_melt_response (struct MHD_Connection *connection, json_object_set (root, "signature", sig_json); } - return send_response_json (connection, - root, - MHD_HTTP_OK); + return TALER_MINT_reply_json (connection, + root, + MHD_HTTP_OK); } @@ -524,9 +535,10 @@ request_json_check_signature (struct MHD_Connection *connection, if (purpose_num != ntohl (purpose->purpose)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (purpose wrong)\n"); - return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", "signature invalid (purpose)"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", "signature invalid (purpose)"); } res = request_json_require_nav (connection, root, @@ -543,10 +555,11 @@ request_json_check_signature (struct MHD_Connection *connection, if (size != ntohl (purpose->size)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (size wrong)\n"); - return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - GNUNET_NO, GNUNET_SYSERR, - "{s:s}", - "error", "signature invalid (size)"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + GNUNET_NO, GNUNET_SYSERR, + "{s:s}", + "error", "signature invalid (size)"); } if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (purpose_num, @@ -555,9 +568,11 @@ request_json_check_signature (struct MHD_Connection *connection, pub)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "signature invalid (did not verify)\n"); - return request_send_json_pack (connection, MHD_HTTP_UNAUTHORIZED, - "{s:s}", - "error", "invalid signature (verification)"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_UNAUTHORIZED, + "{s:s}", + "error", + "invalid signature (verification)"); } return GNUNET_OK; @@ -705,10 +720,11 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh, if (NULL == melt_sig_json) { GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn)); - return request_send_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", "melt_signature missing"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", + "melt_signature missing"); } body.melt_hash = melt_hash; @@ -734,9 +750,11 @@ TALER_MINT_handler_refresh_melt (struct RequestHandler *rh, { GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn)); - return request_send_json_pack (connection, MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", "not enough coins melted"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_FORBIDDEN, + "{s:s}", + "error", + "not enough coins melted"); } if (GNUNET_OK != TALER_MINT_DB_commit (db_conn)) @@ -771,10 +789,11 @@ refresh_send_commit_response (struct MHD_Connection *connection, body.noreveal_index = htons (refresh_session->noreveal_index); sig_json = sign_as_json (&body.purpose); GNUNET_assert (NULL != sig_json); - return request_send_json_pack (connection, MHD_HTTP_OK, - "{s:i, s:o}", - "noreveal_index", (int) refresh_session->noreveal_index, - "signature", sig_json); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_OK, + "{s:i, s:o}", + "noreveal_index", (int) refresh_session->noreveal_index, + "signature", sig_json); } @@ -1022,9 +1041,11 @@ TALER_MINT_handler_refresh_commit (struct RequestHandler *rh, if (NULL == commit_sig_json) { GNUNET_break (GNUNET_OK == TALER_MINT_DB_rollback (db_conn)); - return request_send_json_pack (connection, MHD_HTTP_BAD_REQUEST, - "{s:s}", - "error", "commit_signature missing"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_BAD_REQUEST, + "{s:s}", + "error", + "commit_signature missing"); } body.commit_hash = commit_hash; @@ -1106,9 +1127,9 @@ helper_refresh_reveal_send_response (struct MHD_Connection *connection, TALER_JSON_from_data (&ev_sig, sizeof (struct TALER_RSA_Signature))); } - return send_response_json (connection, - root, - MHD_HTTP_OK); + return TALER_MINT_reply_json (connection, + root, + MHD_HTTP_OK); } @@ -1471,10 +1492,11 @@ TALER_MINT_handler_refresh_link (struct RequestHandler *rh, } if (GNUNET_NO == res) { - return request_send_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "link data not found (transfer)"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "link data not found (transfer)"); } GNUNET_assert (GNUNET_OK == res); @@ -1486,10 +1508,11 @@ TALER_MINT_handler_refresh_link (struct RequestHandler *rh, } if (GNUNET_NO == res) { - return request_send_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "link data not found (link)"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "link data not found (link)"); } GNUNET_assert (GNUNET_OK == res); json_object_set_new (root, "transfer_pub", @@ -1498,7 +1521,9 @@ TALER_MINT_handler_refresh_link (struct RequestHandler *rh, json_object_set_new (root, "secret_enc", TALER_JSON_from_data (&shared_secret_enc, sizeof (struct SharedSecretEnc))); - return send_response_json (connection, root, MHD_HTTP_OK); + return TALER_MINT_reply_json (connection, + root, + MHD_HTTP_OK); } diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c new file mode 100644 index 000000000..8f886c3d9 --- /dev/null +++ b/src/mint/taler-mint-httpd_responses.c @@ -0,0 +1,146 @@ +/* + This file is part of TALER + (C) 2014 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 + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_responses.c + * @brief API for generating the various replies of the mint; these + * functions are called TALER_MINT_reply_ and they generate + * and queue MHD response objects for a given connection. + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler-mint-httpd_responses.h" + + +/** + * Send JSON object as response. Decreases the reference count of the + * JSON object. + * + * @param connection the MHD connection + * @param json the json object + * @param response_code the http response code + * @return MHD result code + */ +int +TALER_MINT_reply_json (struct MHD_Connection *connection, + json_t *json, + unsigned int response_code) +{ + struct MHD_Response *resp; + char *json_str; + int ret; + + json_str = json_dumps (json, JSON_INDENT(2)); + json_decref (json); + resp = MHD_create_response_from_buffer (strlen (json_str), json_str, + MHD_RESPMEM_MUST_FREE); + if (NULL == resp) + return MHD_NO; + (void) MHD_add_response_header (resp, + MHD_HTTP_HEADER_CONTENT_TYPE, + "application/json"); + ret = MHD_queue_response (connection, response_code, resp); + MHD_destroy_response (resp); + return ret; +} + + +/** + * Function to call to handle the request by building a JSON + * reply from a format string and varargs. + * + * @param connection the MHD connection to handle + * @param response_code HTTP response code to use + * @param fmt format string for pack + * @param ... varargs + * @return MHD result code + */ +int +TALER_MINT_reply_json_pack (struct MHD_Connection *connection, + unsigned int response_code, + const char *fmt, + ...) +{ + json_t *json; + va_list argp; + + va_start (argp, fmt); + json = json_vpack_ex (NULL, 0, fmt, argp); + va_end (argp); + if (NULL == json) + return MHD_NO; + return TALER_MINT_reply_json (connection, + json, + response_code); +} + + +/** + * Send a response indicating an invalid argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is invalid + * @return a MHD result code + */ +int +TALER_MINT_reply_arg_invalid (struct MHD_Connection *connection, + const char *param_name) +{ + json_t *json; + + json = json_pack ("{ s:s, s:s }", + "error", + "invalid parameter", + "parameter", + param_name); + return TALER_MINT_reply_json (connection, + json, + MHD_HTTP_BAD_REQUEST); +} + + +/** + * Send a response indicating a missing argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is missing + * @return a MHD result code + */ +int +TALER_MINT_reply_arg_missing (struct MHD_Connection *connection, + const char *param_name) +{ + json_t *json; + + json = json_pack ("{ s:s, s:s }", + "error", + "missing parameter", + "parameter", + param_name); + return TALER_MINT_reply_json (connection, + json, + MHD_HTTP_BAD_REQUEST); +} + + + + + + + +/* 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 new file mode 100644 index 000000000..0e48341fd --- /dev/null +++ b/src/mint/taler-mint-httpd_responses.h @@ -0,0 +1,93 @@ +/* + This file is part of TALER + (C) 2014 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 + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ + +/** + * @file taler-mint-httpd_responses.h + * @brief API for generating the various replies of the mint; these + * functions are called TALER_MINT_reply_ and they generate + * and queue MHD response objects for a given connection. + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_MINT_HTTPD_RESPONSES_H +#define TALER_MINT_HTTPD_RESPONSES_H +#include +#include +#include +#include +#include +#include "taler-mint-httpd.h" +#include "taler-mint-httpd_mhd.h" + + +/** + * Send JSON object as response. Decreases the reference count of the + * JSON object. + * + * @param connection the MHD connection + * @param json the json object + * @param response_code the http response code + * @return MHD result code + */ +int +TALER_MINT_reply_json (struct MHD_Connection *connection, + json_t *json, + unsigned int response_code); + + +/** + * Function to call to handle the request by building a JSON + * reply from a format string and varargs. + * + * @param connection the MHD connection to handle + * @param response_code HTTP response code to use + * @param fmt format string for pack + * @param ... varargs + * @return MHD result code + */ +int +TALER_MINT_reply_json_pack (struct MHD_Connection *connection, + unsigned int response_code, + const char *fmt, + ...); + + +/** + * Send a response indicating an invalid argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is invalid + * @return MHD result code + */ +int +TALER_MINT_reply_arg_invalid (struct MHD_Connection *connection, + const char *param_name); + + +/** + * Send a response indicating a missing argument. + * + * @param connection the MHD connection to use + * @param param_name the parameter that is missing + * @return a MHD result code + */ +int +TALER_MINT_reply_arg_missing (struct MHD_Connection *connection, + const char *param_name); + + +#endif diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c index 22024e80e..cc0de7f6f 100644 --- a/src/mint/taler-mint-httpd_withdraw.c +++ b/src/mint/taler-mint-httpd_withdraw.c @@ -31,10 +31,11 @@ #include "taler_signatures.h" #include "taler_rsa.h" #include "taler_json_lib.h" -#include "taler-mint-httpd_json.h" +#include "taler-mint-httpd_parsing.h" #include "taler-mint-httpd_keys.h" #include "taler-mint-httpd_mhd.h" #include "taler-mint-httpd_withdraw.h" +#include "taler-mint-httpd_responses.h" /** @@ -135,14 +136,11 @@ TALER_MINT_handler_withdraw_status (struct RequestHandler *rh, &reserve_pub, &reserve); if (GNUNET_SYSERR == res) - return TALER_MINT_helper_send_json_pack (rh, - connection, - connection_cls, - 0 /* no caching */, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", - "Reserve not found"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "Reserve not found"); if (GNUNET_OK != res) { // FIXME: return 'internal error'? @@ -178,9 +176,9 @@ TALER_MINT_handler_withdraw_status (struct RequestHandler *rh, sig_to_json (&reserve.status_sig_purpose, &reserve.status_sig)); - return send_response_json (connection, - json, - MHD_HTTP_OK); + return TALER_MINT_reply_json (connection, + json, + MHD_HTTP_OK); } @@ -200,9 +198,9 @@ helper_withdraw_sign_send_reply (struct MHD_Connection *connection, json_object_set_new (root, "ev_sig", TALER_JSON_from_data (&collectable->ev_sig, sizeof (struct TALER_RSA_Signature))); - return send_response_json (connection, - root, - MHD_HTTP_OK); + return TALER_MINT_reply_json (connection, + root, + MHD_HTTP_OK); } @@ -317,10 +315,11 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh, return MHD_NO; } if (GNUNET_NO == res) - return request_send_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "Reserve not found"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "Reserve not found"); // fill out all the missing info in the request before // we can check the signature on the request @@ -334,19 +333,21 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh, &wsrd.purpose, &wsrd.sig, &wsrd.reserve_pub)) - return request_send_json_pack (connection, - MHD_HTTP_UNAUTHORIZED, - "{s:s}", - "error", "Invalid Signature"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_UNAUTHORIZED, + "{s:s}", + "error", "Invalid Signature"); key_state = TALER_MINT_key_state_acquire (); dki = TALER_MINT_get_denom_key (key_state, &wsrd.denomination_pub); TALER_MINT_key_state_release (key_state); if (NULL == dki) - return request_send_json_pack (connection, MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", "Denomination not found"); + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "Denomination not found"); amount_required = TALER_amount_ntoh (dki->issue.value); amount_required = TALER_amount_add (amount_required, @@ -354,14 +355,16 @@ TALER_MINT_handler_withdraw_sign (struct RequestHandler *rh, if (0 < TALER_amount_cmp (amount_required, TALER_amount_ntoh (reserve.balance))) - return request_send_json_pack (connection, - MHD_HTTP_PAYMENT_REQUIRED, - "{s:s}", - "error", "Insufficient funds"); - if (GNUNET_OK != TALER_RSA_sign (dki->denom_priv, - &wsrd.coin_envelope, - sizeof (struct TALER_RSA_BlindedSignaturePurpose), - &ev_sig)) + return TALER_MINT_reply_json_pack (connection, + MHD_HTTP_PAYMENT_REQUIRED, + "{s:s}", + "error", + "Insufficient funds"); + if (GNUNET_OK != + TALER_RSA_sign (dki->denom_priv, + &wsrd.coin_envelope, + sizeof (struct TALER_RSA_BlindedSignaturePurpose), + &ev_sig)) { // FIXME: return 'internal error' GNUNET_break (0); -- cgit v1.2.3