summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-11-23 11:02:34 +0100
committerChristian Grothoff <christian@grothoff.org>2019-11-23 11:02:34 +0100
commitfef8a57fae513259fd0b08e430b04d3b59d20210 (patch)
treed01aacc18695a23dd5afa1ae3a8adcf47c4107c8
parenta2a5f429159a5be2a453dcc7dacf30e7c0f9e963 (diff)
downloadexchange-fef8a57fae513259fd0b08e430b04d3b59d20210.tar.gz
exchange-fef8a57fae513259fd0b08e430b04d3b59d20210.tar.bz2
exchange-fef8a57fae513259fd0b08e430b04d3b59d20210.zip
add parsing API to new libtalermhd
-rw-r--r--src/include/taler_mhd_lib.h108
-rw-r--r--src/mhd/Makefile.am3
-rw-r--r--src/mhd/mhd_parsing.c294
3 files changed, 404 insertions, 1 deletions
diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h
index 55396090..d126d0ae 100644
--- a/src/include/taler_mhd_lib.h
+++ b/src/include/taler_mhd_lib.h
@@ -24,6 +24,7 @@
#ifndef TALER_MHD_LIB_H
#define TALER_MHD_LIB_H
#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
#include <jansson.h>
#include <microhttpd.h>
#include "taler_error_codes.h"
@@ -153,4 +154,111 @@ int
TALER_MHD_reply_request_too_large (struct MHD_Connection *connection);
+/**
+ * Process a POST request containing a JSON object. This
+ * function realizes an MHD POST processor that will
+ * (incrementally) process JSON data uploaded to the HTTP
+ * server. It will store the required state in the
+ * "connection_cls", which must be cleaned up using
+ * #TALER_MHD_post_cleanup_callback().
+ *
+ * @param connection the MHD connection
+ * @param con_cls the closure (points to a `struct Buffer *`)
+ * @param upload_data the POST data
+ * @param upload_data_size number of bytes in @a upload_data
+ * @param json the JSON object for a completed request
+ * @return
+ * #GNUNET_YES if json object was parsed or at least
+ * may be parsed in the future (call again);
+ * `*json` will be NULL if we need to be called again,
+ * and non-NULL if we are done.
+ * #GNUNET_NO is request incomplete or invalid
+ * (error message was generated)
+ * #GNUNET_SYSERR on internal error
+ * (we could not even queue an error message,
+ * close HTTP session with MHD_NO)
+ */
+int
+TALER_MHD_parse_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json);
+
+
+/**
+ * Function called whenever we are done with a request
+ * to clean up our state.
+ *
+ * @param con_cls value as it was left by
+ * #TALER_MHD_post_json(), to be cleaned up
+ */
+void
+TALER_MHD_parse_post_cleanup_callback (void *con_cls);
+
+
+/**
+ * Parse JSON object into components based on the given field
+ * specification.
+ *
+ * @param connection the connection to send an error response to
+ * @param root the JSON node to start the navigation at.
+ * @param spec field specification for the parser
+ * @return
+ * #GNUNET_YES if navigation was successful (caller is responsible
+ * for freeing allocated variable-size data using
+ * GNUNET_JSON_parse_free() when done)
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+int
+TALER_MHD_parse_json_data (struct MHD_Connection *connection,
+ const json_t *root,
+ struct GNUNET_JSON_Specification *spec);
+
+
+/**
+ * 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[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
+ * GNUNET_JSON_parse_free() when done)
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+int
+TALER_MHD_parse_json_array (struct MHD_Connection *connection,
+ const json_t *root,
+ struct GNUNET_JSON_Specification *spec,
+ ...);
+
+
+/**
+ * Extraxt fixed-size 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 @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
+TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
+ const char *param_name,
+ void *out_data,
+ size_t out_size);
+
+
#endif
diff --git a/src/mhd/Makefile.am b/src/mhd/Makefile.am
index 2c6d464f..ed0c8841 100644
--- a/src/mhd/Makefile.am
+++ b/src/mhd/Makefile.am
@@ -10,7 +10,8 @@ lib_LTLIBRARIES = \
libtalermhd.la
libtalermhd_la_SOURCES = \
- mhd_responses.c
+ mhd_parsing.c \
+ mhd_responses.c
libtalermhd_la_LDFLAGS = \
-version-info 0:0:0 \
-export-dynamic -no-undefined
diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c
new file mode 100644
index 00000000..0b070ffe
--- /dev/null
+++ b/src/mhd/mhd_parsing.c
@@ -0,0 +1,294 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014--2019 Taler Systems SA
+
+ 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, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mhd_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 <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+
+
+/**
+ * Maximum POST request size.
+ */
+#define REQUEST_BUFFER_MAX (1024 * 1024)
+
+
+/**
+ * Process a POST request containing a JSON object. This function
+ * realizes an MHD POST processor that will (incrementally) process
+ * JSON data uploaded to the HTTP server. It will store the required
+ * state in the @a con_cls, which must be cleaned up using
+ * #TALER_MHD_post_cleanup_callback().
+ *
+ * @param connection the MHD connection
+ * @param con_cls the closure (points to a `struct Buffer *`)
+ * @param upload_data the POST data
+ * @param upload_data_size number of bytes in @a upload_data
+ * @param json the JSON object for a completed request
+ * @return
+ * #GNUNET_YES if json object was parsed or at least
+ * may be parsed in the future (call again);
+ * `*json` will be NULL if we need to be called again,
+ * and non-NULL if we are done.
+ * #GNUNET_NO is request incomplete or invalid
+ * (error message was generated)
+ * #GNUNET_SYSERR on internal error
+ * (we could not even queue an error message,
+ * close HTTP session with MHD_NO)
+ */
+int
+TALER_MHD_parse_post_json (struct MHD_Connection *connection,
+ void **con_cls,
+ const char *upload_data,
+ size_t *upload_data_size,
+ json_t **json)
+{
+ enum GNUNET_JSON_PostResult pr;
+
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ con_cls,
+ upload_data,
+ upload_data_size,
+ json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ return (MHD_NO ==
+ TALER_MHD_reply_with_error
+ (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_PARSER_OUT_OF_MEMORY,
+ "out of memory")) ? GNUNET_SYSERR : GNUNET_NO;
+
+ case GNUNET_JSON_PR_CONTINUE:
+ return GNUNET_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ return (MHD_NO ==
+ TALER_MHD_reply_request_too_large
+ (connection)) ? GNUNET_SYSERR : GNUNET_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_JSON_INVALID,
+ "invalid JSON uploaded"))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ case GNUNET_JSON_PR_SUCCESS:
+ GNUNET_break (NULL != *json);
+ return GNUNET_YES;
+ }
+ /* this should never happen */
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Function called whenever we are done with a request
+ * to clean up our state.
+ *
+ * @param con_cls value as it was left by
+ * #TALER_MHD_post_json(), to be cleaned up
+ */
+void
+TALER_MHD_parse_post_cleanup_callback (void *con_cls)
+{
+ // FIXME: this should probably NOT be done with a 'void *' like this!
+ GNUNET_JSON_post_parser_cleanup (con_cls);
+}
+
+
+/**
+ * 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_MHD_parse_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_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PARAMETER_MISSING,
+ 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_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_PARAMETER_MALFORMED,
+ param_name))
+ ? GNUNET_SYSERR : GNUNET_NO;
+ return GNUNET_OK;
+}
+
+
+/**
+ * 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[in,out] spec field specification for the parser
+ * @return
+ * #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
+TALER_MHD_parse_json_data (struct MHD_Connection *connection,
+ const json_t *root,
+ struct GNUNET_JSON_Specification *spec)
+{
+ int 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)
+ {
+ if (NULL == error_json_name)
+ error_json_name = "<no field>";
+ ret = (MHD_YES ==
+ TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:I, s:s, s:I}",
+ "hint", "JSON parse error",
+ "code",
+ (json_int_t)
+ TALER_EC_JSON_INVALID_WITH_DETAILS,
+ "field", error_json_name,
+ "line", (json_int_t) error_line))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ return ret;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * 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[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
+ * GNUNET_JSON_parse_free() when done)
+ * #GNUNET_NO if json is malformed, error response was generated
+ * #GNUNET_SYSERR on internal error
+ */
+int
+TALER_MHD_parse_json_array (struct MHD_Connection *connection,
+ const json_t *root,
+ struct GNUNET_JSON_Specification *spec,
+ ...)
+{
+ int ret;
+ 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) )
+ {
+ dim++;
+ root = json_array_get (root, ret);
+ }
+ va_end (ap);
+ if (NULL == root)
+ {
+ ret = (MHD_YES ==
+ TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:I, s:I}",
+ "hint", "expected array",
+ "code",
+ (json_int_t)
+ TALER_EC_JSON_INVALID_WITH_DETAILS,
+ "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 = "<no field>";
+ ret = (MHD_YES ==
+ TALER_MHD_reply_json_pack (connection,
+ MHD_HTTP_BAD_REQUEST,
+ "{s:s, s:I, s:I}",
+ "hint", error_json_name,
+ "code",
+ (json_int_t)
+ TALER_EC_JSON_INVALID_WITH_DETAILS,
+ "line", (json_int_t) error_line))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ return ret;
+ }
+ return GNUNET_YES;
+}
+
+
+/* end of mhd_parsing.c */