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 /src/mint-lib | |
| parent | 5c780a7a972f4363e0b3c4521fe8c1fbb846723b (diff) | |
add generic json parser interpreter to simplify parsing logic (next)
Diffstat (limited to 'src/mint-lib')
| -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 0ea52479..ac538392 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 00000000..b2757f11 --- /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 00000000..87afbd44 --- /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 */ | 
