/*
  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_EDDSA_SIGNATURE:
      {
        struct TALER_CoinSpendSignatureP sig;
        struct GNUNET_CRYPTO_EccSignaturePurpose *purpose;
        size_t size;
        struct MAJ_Specification sig_spec[] = {
          MAJ_spec_fixed_auto ("eddsa_sig", &sig),
          MAJ_spec_varsize ("eddsa_val", (void**) &purpose, &size),
          MAJ_spec_end
        };
        if (GNUNET_OK !=
            MAJ_parse_json (pos,
                            sig_spec))
        {
          GNUNET_break_op (0);
          MAJ_parse_free (sig_spec);
          return i;
        }
        if (size != ntohl (purpose->size))
        {
          GNUNET_break_op (0);
          MAJ_parse_free (sig_spec);
          return i;
        }
        if (GNUNET_OK !=
            GNUNET_CRYPTO_eddsa_verify (ntohl (purpose->purpose),
                                        purpose,
                                        &sig.eddsa_signature,
                                        spec[i].details.eddsa_signature.pub_key))
        {
          GNUNET_break_op (0);
          MAJ_parse_free (sig_spec);
          return i;
        }
        *spec[i].details.eddsa_signature.purpose_p = purpose;
      }
      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