/* This file is part of TALER Copyright (C) 2014, 2015 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 data_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 */ 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; } /** * 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; void *ptr; 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 (0 != spec[i].destination_size_out) { GNUNET_free (spec[i].destination); spec[i].destination = NULL; spec[i].destination_size_out = 0; } break; case TMH_PARSE_JNC_RET_TYPED_JSON: ptr = *(void **) spec[i].destination; if (NULL != ptr) { json_decref (ptr); *(void**) 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 * JSON data uploaded to the HTTP server. It will store the required * state in the @a con_cls, which must be cleaned up using * #TMH_PARSE_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 TMH_PARSE_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; *json = NULL; 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 (MHD_NO == TMH_RESPONSE_reply_internal_error (connection, "out of memory")) ? GNUNET_SYSERR : GNUNET_NO; } /* everything OK, wait for more POST data */ fprintf (stderr, "Init %p\n", r); *upload_data_size = 0; *con_cls = r; return GNUNET_YES; } 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 */ *con_cls = NULL; buffer_deinit (r); GNUNET_free (r); return (MHD_NO == TMH_RESPONSE_reply_request_too_large (connection)) ? GNUNET_SYSERR : GNUNET_NO; } /* everything OK, wait for more POST data */ *upload_data_size = 0; return GNUNET_YES; } /* We have seen the whole request. */ *json = json_loadb (r->data, r->fill, 0, NULL); if (NULL == *json) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to parse JSON request body\n"); return (MHD_YES == TMH_RESPONSE_reply_invalid_json (connection)) ? GNUNET_NO : GNUNET_SYSERR; } fprintf (stderr, "Deinit %p\n", r); buffer_deinit (r); GNUNET_free (r); *con_cls = NULL; return GNUNET_YES; } /** * Function called whenever we are done with a request * to clean up our state. * * @param con_cls value as it was left by * #TMH_PARSE_post_json(), to be cleaned up */ void TMH_PARSE_post_cleanup_callback (void *con_cls) { struct Buffer *r = con_cls; if (NULL != r) { buffer_deinit (r); GNUNET_free (r); } } /** * 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 TMH_PARSE_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 == TMH_RESPONSE_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 == TMH_RESPONSE_reply_arg_invalid (connection, param_name)) ? GNUNET_SYSERR : GNUNET_NO; return GNUNET_OK; } /** * 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) * * @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`) * @return * #GNUNET_YES if navigation was successful * #GNUNET_NO if json is malformed, error response was generated * #GNUNET_SYSERR on internal error (no response was generated, * connection must be closed) */ int TMH_PARSE_navigate_json (struct MHD_Connection *connection, const json_t *root, ...) { 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) { 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) { 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) { 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; // FIXME: avoidable code duplication here... str = json_string_value (root); if (NULL == str) { 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) { 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; // FIXME: avoidable code duplication here... str = json_string_value (root); if (NULL == str) { 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) { int res; *where = GNUNET_malloc (*len); res = GNUNET_STRINGS_string_to_data (str, strlen (str), *where, *len); if (GNUNET_OK != res) { 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 ( (-1 != typ) && (json_typeof (root) != typ)) { *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) { 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) { 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_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) { 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) { 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_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) { 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 *); if (GNUNET_OK != TALER_json_to_amount ((json_t *) root, where)) { if (MHD_YES != TMH_RESPONSE_reply_json_pack (connection, MHD_HTTP_BAD_REQUEST, "{s:s, s:o}", "error", "Bad format", "path", path)) return GNUNET_SYSERR; return GNUNET_NO; } if (0 != strcmp (where->currency, TMH_mint_currency_string)) { if (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)) { memset (where, 0, sizeof (struct TALER_Amount)); return GNUNET_SYSERR; } memset (where, 0, sizeof (struct TALER_Amount)); return GNUNET_NO; } ret = GNUNET_OK; break; } case TMH_PARSE_JNC_RET_TIME_ABSOLUTE: { struct GNUNET_TIME_Absolute *where = va_arg (argp, void *); if (GNUNET_OK != TALER_json_to_abs ((json_t *) root, where)) { if (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)) return GNUNET_SYSERR; return GNUNET_NO; } 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; } } va_end (argp); json_decref (path); return ret; } /** * 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 * #TMH_PARSE_release_data() 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) { unsigned int i; int ret; void *ptr; ret = GNUNET_YES; for (i=0; NULL != spec[i].field_name; i++) { 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: ptr = NULL; ret = TMH_PARSE_navigate_json (connection, root, TMH_PARSE_JNC_FIELD, spec[i].field_name, TMH_PARSE_JNC_RET_DATA_VAR, &ptr, &spec[i].destination_size_out); spec[i].destination = ptr; break; case TMH_PARSE_JNC_RET_TYPED_JSON: ptr = NULL; ret = TMH_PARSE_navigate_json (connection, root, TMH_PARSE_JNC_FIELD, spec[i].field_name, TMH_PARSE_JNC_RET_TYPED_JSON, spec[i].type, &ptr); *((void**)spec[i].destination) = ptr; 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; } } if (GNUNET_YES != ret) release_data (spec, i); 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), 0, TMH_PARSE_JNC_RET_UINT64, 0 }; return ret; } /** * Generate line in parser specification for JSON object value. * * @param field name of the field * @param 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, (void **) jsonp, 0, 0, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_OBJECT }; return ret; } /** * Generate line in parser specification for JSON array value. * * @param field name of the field * @param 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, 0, TMH_PARSE_JNC_RET_TYPED_JSON, JSON_ARRAY }; 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), 0, 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, 0, TMH_PARSE_JNC_RET_RSA_PUBLIC_KEY, 0 }; 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, 0, TMH_PARSE_JNC_RET_RSA_SIGNATURE, 0 }; 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), 0, TMH_PARSE_JNC_RET_AMOUNT, 0 }; return ret; } /* end of taler-mint-httpd_parsing.c */