diff options
author | Christian Grothoff <christian@grothoff.org> | 2015-06-20 17:40:28 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2015-06-20 17:40:28 +0200 |
commit | 605058f774a2852e2a0d2ca2b201a889c0edbd09 (patch) | |
tree | c1c9757ea2b0ee3a38a654e5d6ee6b995261ce0b | |
parent | 5c780a7a972f4363e0b3c4521fe8c1fbb846723b (diff) | |
download | exchange-605058f774a2852e2a0d2ca2b201a889c0edbd09.tar.xz |
add generic json parser interpreter to simplify parsing logic (next)
-rw-r--r-- | src/mint-lib/Makefile.am | 1 | ||||
-rw-r--r-- | src/mint-lib/mint_api_json.c | 404 | ||||
-rw-r--r-- | src/mint-lib/mint_api_json.h | 190 |
3 files changed, 595 insertions, 0 deletions
diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index 0ea52479e..ac538392f 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -15,6 +15,7 @@ libtalermint_la_LDFLAGS = \ libtalermint_la_SOURCES = \ mint_api_context.c mint_api_context.h \ + mint_api_json.c mint_api_json.h \ mint_api_handle.c mint_api_handle.h libtalermint_la_LIBADD = \ diff --git a/src/mint-lib/mint_api_json.c b/src/mint-lib/mint_api_json.c new file mode 100644 index 000000000..b2757f115 --- /dev/null +++ b/src/mint-lib/mint_api_json.c @@ -0,0 +1,404 @@ +/* + 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file mint-lib/mint_api_json.c + * @brief functions to parse incoming requests (JSON snippets) + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include "mint_api_json.h" + + +/** + * Parse absolute time specified in JSON format. The JSON format is + * "/TIMEVAL/" where TIMEVAL is in milliseconds. Additionally, we + * support "/forever/" to represent the end of time. + * + * @param f json specification of the amount + * @param[out] time set to the time specified in @a f + * @return + * #GNUNET_YES if parsing was successful + * #GNUNET_SYSERR on errors + */ +static int +parse_time_abs (json_t *f, + struct GNUNET_TIME_Absolute *time) +{ + const char *val; + size_t slen; + unsigned long long int tval; + char *endp; + + val = json_string_value (f); + if (NULL == val) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + slen = strlen (val); + if ( (slen <= 2) || + ('/' != val[0]) || + ('/' != val[slen - 1]) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (0 == strcasecmp (val, + "/forever/")) + { + *time = GNUNET_TIME_UNIT_FOREVER_ABS; + return GNUNET_OK; + } + tval = strtoull (&val[1], + &endp, + 10); + if (&val[slen - 1] != endp) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* Time is in 'ms' in JSON, but in microseconds in GNUNET_TIME_Absolute */ + time->abs_value_us = tval * 1000LL; + if ( (time->abs_value_us) / 1000LL != tval) + { + /* Integer overflow */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Parse amount specified in JSON format. + * + * @param f json specification of the amount + * @param[out] amount set to the amount specified in @a f + * @return + * #GNUNET_OK if parsing was successful + * #GNUNET_SYSERR on error + */ +static int +parse_amount (json_t *f, + struct TALER_Amount *amount) +{ + json_int_t value; + json_int_t fraction; + const char *currency; + + memset (amount, + 0, + sizeof (struct TALER_Amount)); + if (-1 == json_unpack (f, + "{s:I, s:I, s:s}", + "value", &value, + "fraction", &fraction, + "currency", ¤cy)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (value < 0) || + (fraction < 0) || + (value > UINT64_MAX) || + (fraction > UINT32_MAX) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (strlen (currency) >= TALER_CURRENCY_LEN) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + amount->value = (uint64_t) value; + amount->fraction = (uint32_t) fraction; + strcpy (amount->currency, currency); + (void) TALER_amount_normalize (amount); + return GNUNET_OK; +} + + +/** + * Navigate and parse data in a JSON tree. + * + * @param root the JSON node to start the navigation at. + * @param spec parse specification array + * @return offset in @a spec where parsing failed, -1 on success (!) + */ +static int +parse_json (json_t *root, + struct MAJ_Specification *spec) +{ + int i; + json_t *pos; /* what's our current position? */ + + pos = root; + for (i=0;MAJ_CMD_END != spec[i].cmd;i++) + { + pos = json_object_get (root, + spec[i].field); + if (NULL == pos) + { + GNUNET_break_op (0); + return i; + } + switch (spec[i].cmd) + { + case MAJ_CMD_END: + GNUNET_assert (0); + return i; + case MAJ_CMD_AMOUNT: + if (GNUNET_OK != + parse_amount (pos, + spec[i].details.amount)) + return i; + break; + case MAJ_CMD_TIME_ABSOLUTE: + if (GNUNET_OK != + parse_time_abs (pos, + spec[i].details.abs_time)) + return i; + break; + + case MAJ_CMD_BINARY_FIXED: + { + const char *str; + int res; + + str = json_string_value (pos); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + spec[i].details.fixed_data.dest, + spec[i].details.fixed_data.dest_len); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + return i; + } + } + break; + + case MAJ_CMD_BINARY_VARIABLE: + { + const char *str; + size_t len; + void *data; + int res; + + str = json_string_value (pos); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + len = (strlen (str) * 5) / 8; + if (len >= 1024) + { + GNUNET_break_op (0); + return i; + } + data = GNUNET_malloc (len); + res = GNUNET_STRINGS_string_to_data (str, strlen (str), + data, + len); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + GNUNET_free (data); + return i; + } + *spec[i].details.variable_data.dest_p = data; + *spec[i].details.variable_data.dest_len_p = len; + } + break; + + case MAJ_CMD_RSA_PUBLIC_KEY: + { + size_t len; + const char *str; + int res; + void *buf; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + 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); + GNUNET_break_op (0); + return i; + } + *spec[i].details.rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_decode (buf, + len); + GNUNET_free (buf); + if (NULL == spec[i].details.rsa_public_key) + { + GNUNET_break_op (0); + return i; + } + } + break; + + case MAJ_CMD_RSA_SIGNATURE: + { + size_t len; + const char *str; + int res; + void *buf; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return i; + } + 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); + GNUNET_break_op (0); + return i; + } + *spec[i].details.rsa_signature + = GNUNET_CRYPTO_rsa_signature_decode (buf, + len); + GNUNET_free (buf); + if (NULL == spec[i].details.rsa_signature) + return i; + } + break; + + default: + GNUNET_break (0); + return i; + } + } + return -1; /* all OK! */ +} + + +/** + * Free all elements allocated during a + * #MAJ_parse_json() operation. + * + * @param spec specification of the parse operation + * @param end number of elements in @a spec to process + */ +static void +parse_free (struct MAJ_Specification *spec, + int end) +{ + int i; + + for (i=0;i<end;i++) + { + switch (spec[i].cmd) + { + case MAJ_CMD_END: + GNUNET_assert (0); + return; + case MAJ_CMD_AMOUNT: + break; + case MAJ_CMD_TIME_ABSOLUTE: + break; + case MAJ_CMD_BINARY_FIXED: + break; + case MAJ_CMD_BINARY_VARIABLE: + GNUNET_free (*spec[i].details.variable_data.dest_p); + *spec[i].details.variable_data.dest_p = NULL; + *spec[i].details.variable_data.dest_len_p = 0; + break; + case MAJ_CMD_RSA_PUBLIC_KEY: + GNUNET_CRYPTO_rsa_public_key_free (*spec[i].details.rsa_public_key); + *spec[i].details.rsa_public_key = NULL; + break; + case MAJ_CMD_RSA_SIGNATURE: + GNUNET_CRYPTO_rsa_signature_free (*spec[i].details.rsa_signature); + *spec[i].details.rsa_signature = NULL; + break; + default: + GNUNET_break (0); + break; + } + } +} + + +/** + * Navigate and parse data in a JSON tree. + * + * @param root the JSON node to start the navigation at. + * @param spec parse specification array + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +MAJ_parse_json (const json_t *root, + struct MAJ_Specification *spec) +{ + int ret; + + ret = parse_json ((json_t *) root, + spec); + if (-1 == ret) + return GNUNET_OK; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "JSON field `%s` had unexpected value\n", + spec[ret].field); + parse_free (spec, ret); + return GNUNET_SYSERR; +} + + +/** + * Free all elements allocated during a + * #MAJ_parse_json() operation. + * + * @param spec specification of the parse operation + */ +void +MAJ_parse_free (struct MAJ_Specification *spec) +{ + int i; + + for (i=0;MAJ_CMD_END != spec[i].cmd;i++) ; + parse_free (spec, i); +} + + + +/* end of mint_api_json.c */ diff --git a/src/mint-lib/mint_api_json.h b/src/mint-lib/mint_api_json.h new file mode 100644 index 000000000..87afbd44b --- /dev/null +++ b/src/mint-lib/mint_api_json.h @@ -0,0 +1,190 @@ +/* + 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file mint-lib/mint_api_json.h + * @brief functions to parse incoming requests (JSON snippets) + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_util.h" +#include <jansson.h> + + +/** + * Enumeration with the various commands for the + * #MAJ_parse_json interpreter. + */ +enum MAJ_Command +{ + + /** + * End of command list. + */ + MAJ_CMD_END, + + /** + * Parse amount at current position. + */ + MAJ_CMD_AMOUNT, + + /** + * Parse absolute time at current position. + */ + MAJ_CMD_TIME_ABSOLUTE, + + /** + * Parse fixed binary value at current position. + */ + MAJ_CMD_BINARY_FIXED, + + /** + * Parse variable-size binary value at current position. + */ + MAJ_CMD_BINARY_VARIABLE, + + /** + * Parse RSA public key at current position. + */ + MAJ_CMD_RSA_PUBLIC_KEY, + + /** + * Parse RSA signature at current position. + */ + MAJ_CMD_RSA_SIGNATURE, + + /** + * Parse at current position. + */ + MAJ_CMD_A, + + /** + * Parse at current position. + */ + MAJ_CMD_B, + + /** + * Parse at current position. + */ + MAJ_CMD_C + +}; + + +/** + * Entry in parser specification for #MAJ_parse_json. + */ +struct MAJ_Specification +{ + + /** + * Command to execute. + */ + enum MAJ_Command cmd; + + /** + * Name of the field to access. + */ + const char *field; + + /** + * Further details for the command. + */ + union { + + /** + * Where to store amount for #MAJ_CMD_AMOUNT. + */ + struct TALER_Amount *amount; + + /** + * Where to store time, for #MAJ_CMD_TIME_ABSOLUTE. + */ + struct GNUNET_TIME_Absolute *abs_time; + + /** + * Where to write binary data, for #MAJ_CMD_BINARY_FIXED. + */ + struct { + /** + * Where to write the data. + */ + void *dest; + + /** + * How many bytes to write to @e dest. + */ + size_t dest_len; + + } fixed_data; + + /** + * Where to write binary data, for #MAJ_CMD_BINARY_VARIABLE. + */ + struct { + /** + * Where to store the pointer with the data (is allocated). + */ + void **dest_p; + + /** + * Where to store the number of bytes allocated at `*dest`. + */ + size_t *dest_len_p; + + } variable_data; + + /** + * Where to store the RSA public key for #MAJ_CMD_RSA_PUBLIC_KEY + */ + struct GNUNET_CRYPTO_rsa_PublicKey **rsa_public_key; + + /** + * Where to store the RSA signature for #MAJ_CMD_RSA_SIGNATURE + */ + struct GNUNET_CRYPTO_rsa_Signature **rsa_signature; + + } details; + +}; + + + +/** + * Navigate and parse data in a JSON tree. + * + * @param root the JSON node to start the navigation at. + * @param spec parse specification array + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +MAJ_parse_json (const json_t *root, + struct MAJ_Specification *spec); + + +/** + * Free all elements allocated during a + * #MAJ_parse_json() operation. + * + * @param spec specification of the parse operation + */ +void +MAJ_parse_free (struct MAJ_Specification *spec); + + +/* end of mint_api_json.h */ |