/*
  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 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"
/**
 * 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 !=
          TALER_json_to_amount (pos,
                                spec[i].details.amount))
      {
        GNUNET_break_op (0);
        return i;
      }
      break;
    case MAJ_CMD_TIME_ABSOLUTE:
      if (GNUNET_OK !=
          TALER_json_to_abs (pos,
                             spec[i].details.abs_time))
      {
        GNUNET_break_op (0);
        return i;
      }
      break;
    case MAJ_CMD_STRING:
      {
        const char *str;
        str = json_string_value (pos);
        if (NULL == str)
        {
          GNUNET_break_op (0);
          return i;
        }
        *spec[i].details.strptr = str;
      }
      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_size);
        if (GNUNET_OK != res)
        {
          GNUNET_break_op (0);
          return i;
        }
      }
      break;
    case MAJ_CMD_BINARY_VARIABLE:
      {
        const char *str;
        size_t size;
        void *data;
        int res;
        str = json_string_value (pos);
        if (NULL == str)
        {
          GNUNET_break_op (0);
          return i;
        }
        size = (strlen (str) * 5) / 8;
        if (size >= 1024)
        {
          GNUNET_break_op (0);
          return i;
        }
        data = GNUNET_malloc (size);
        res = GNUNET_STRINGS_string_to_data (str,
                                             strlen (str),
                                             data,
                                             size);
        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_size_p = size;
      }
      break;
    case MAJ_CMD_RSA_PUBLIC_KEY:
      {
        size_t size;
        const char *str;
        int res;
        void *buf;
        str = json_string_value (pos);
        if (NULL == str)
        {
          GNUNET_break_op (0);
          return i;
        }
        size = (strlen (str) * 5) / 8;
        buf = GNUNET_malloc (size);
        res = GNUNET_STRINGS_string_to_data (str,
                                             strlen (str),
                                             buf,
                                             size);
        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,
                                                 size);
        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 size;
        const char *str;
        int res;
        void *buf;
        str = json_string_value (pos);
        if (NULL == str)
        {
          GNUNET_break_op (0);
          return i;
        }
        size = (strlen (str) * 5) / 8;
        buf = GNUNET_malloc (size);
        res = GNUNET_STRINGS_string_to_data (str,
                                             strlen (str),
                                             buf,
                                             size);
        if (GNUNET_OK != res)
        {
          GNUNET_free (buf);
          GNUNET_break_op (0);
          return i;
        }
        *spec[i].details.rsa_signature
          = GNUNET_CRYPTO_rsa_signature_decode (buf,
                                                size);
        GNUNET_free (buf);
        if (NULL == spec[i].details.rsa_signature)
          return i;
      }
      break;
    case MAJ_CMD_UINT16:
      {
        json_int_t val;
        if (! json_is_integer (pos))
        {
          GNUNET_break_op (0);
          return i;
        }
        val = json_integer_value (pos);
        if ( (0 > val) || (val > UINT16_MAX) )
        {
          GNUNET_break_op (0);
          return i;
        }
        *spec[i].details.u16 = (uint16_t) val;
      }
      break;
    case MAJ_CMD_JSON_OBJECT:
      {
        if (! (json_is_object (pos) || json_is_array (pos)) )
        {
          GNUNET_break_op (0);
          return i;
        }
        json_incref (pos);
        *spec[i].details.obj = pos;
      }
      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