/*
  This file is part of Taler
  Copyright (C) 2018-2023 Taler Systems SA

  Taler is free software; you can redistribute it and/or modify it under the
  terms of the GNU 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 General Public License for more details.

  You should have received a copy of the GNU General Public License along with
  Taler; see the file COPYING.  If not, see
  <http://www.gnu.org/licenses/>
*/
/**
 * @file bank/mb_parse.c
 * @brief Convenience function to parse authentication configuration
 * @author Christian Grothoff
 */
#include "platform.h"
#include "taler_merchant_bank_lib.h"
#include <gnunet/gnunet_json_lib.h>


/**
 * Names of authentication methods available.
 */
static const struct
{
  const char *m;
  enum TALER_MERCHANT_BANK_AuthenticationMethod e;
} methods[] = {
  { "NONE",   TALER_MERCHANT_BANK_AUTH_NONE   },
  { "BASIC",  TALER_MERCHANT_BANK_AUTH_BASIC  },
  { "BEARER", TALER_MERCHANT_BANK_AUTH_BEARER },
  { NULL, TALER_MERCHANT_BANK_AUTH_NONE     }
};


enum GNUNET_GenericReturnValue
TALER_MERCHANT_BANK_auth_parse_cfg (
  const struct GNUNET_CONFIGURATION_Handle *cfg,
  const char *section,
  struct TALER_MERCHANT_BANK_AuthenticationData *auth)
{
  char *method;

  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_string (cfg,
                                             section,
                                             "WIRE_GATEWAY_URL",
                                             &auth->wire_gateway_url))
  {
    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                               section,
                               "WIRE_GATEWAY_URL");
    return GNUNET_SYSERR;
  }

  if (GNUNET_OK !=
      GNUNET_CONFIGURATION_get_value_string (cfg,
                                             section,
                                             "WIRE_GATEWAY_AUTH_METHOD",
                                             &method))
  {
    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                               section,
                               "WIRE_GATEWAY_AUTH_METHOD");
    GNUNET_free (auth->wire_gateway_url);
    return GNUNET_SYSERR;
  }
  for (unsigned int i = 0; NULL != methods[i].m; i++)
  {
    if (0 == strcasecmp (method,
                         methods[i].m))
    {
      switch (methods[i].e)
      {
      case TALER_MERCHANT_BANK_AUTH_NONE:
        auth->method = TALER_MERCHANT_BANK_AUTH_NONE;
        GNUNET_free (method);
        return GNUNET_OK;
      case TALER_MERCHANT_BANK_AUTH_BASIC:
        if (GNUNET_OK !=
            GNUNET_CONFIGURATION_get_value_string (cfg,
                                                   section,
                                                   "USERNAME",
                                                   &auth->details.basic.username))
        {
          GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                     section,
                                     "USERNAME");
          GNUNET_free (method);
          GNUNET_free (auth->wire_gateway_url);
          return GNUNET_SYSERR;
        }
        if (GNUNET_OK !=
            GNUNET_CONFIGURATION_get_value_string (cfg,
                                                   section,
                                                   "PASSWORD",
                                                   &auth->details.basic.password))
        {
          GNUNET_free (auth->details.basic.username);
          auth->details.basic.username = NULL;
          GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                     section,
                                     "PASSWORD");
          GNUNET_free (method);
          GNUNET_free (auth->wire_gateway_url);
          return GNUNET_SYSERR;
        }
        auth->method = TALER_MERCHANT_BANK_AUTH_BASIC;
        GNUNET_free (method);
        return GNUNET_OK;
      case TALER_MERCHANT_BANK_AUTH_BEARER:
        if (GNUNET_OK !=
            GNUNET_CONFIGURATION_get_value_string (cfg,
                                                   section,
                                                   "TOKEN",
                                                   &auth->details.bearer.token))
        {
          GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                     section,
                                     "TOKEN");
          GNUNET_free (method);
          GNUNET_free (auth->wire_gateway_url);
          return GNUNET_SYSERR;
        }
        auth->method = TALER_MERCHANT_BANK_AUTH_BEARER;
        GNUNET_free (method);
        return GNUNET_OK;
      }
    }
  }
  GNUNET_free (method);
  return GNUNET_SYSERR;
}


enum GNUNET_GenericReturnValue
TALER_MERCHANT_BANK_auth_parse_json (
  const json_t *cred,
  const char *backend_url,
  struct TALER_MERCHANT_BANK_AuthenticationData *auth)
{
  const char *method;

  if (NULL == backend_url)
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  if ( (0 == strlen (backend_url)) ||
       ('/' != backend_url[strlen (backend_url) - 1]) )
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  auth->wire_gateway_url = GNUNET_strdup (backend_url);
  method = json_string_value (json_object_get (cred,
                                               "type"));
  if (NULL == method)
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  for (unsigned int i = 0; NULL != methods[i].m; i++)
  {
    if (0 == strcasecmp (method,
                         methods[i].m))
    {
      switch (methods[i].e)
      {
      case TALER_MERCHANT_BANK_AUTH_NONE:
        auth->method = TALER_MERCHANT_BANK_AUTH_NONE;
        return GNUNET_OK;
      case TALER_MERCHANT_BANK_AUTH_BASIC:
        {
          const char *username;
          const char *password;
          struct GNUNET_JSON_Specification spec[] = {
            GNUNET_JSON_spec_string ("username",
                                     &username),
            GNUNET_JSON_spec_string ("password",
                                     &password),
            GNUNET_JSON_spec_end ()
          };
          enum GNUNET_GenericReturnValue res;
          const char *err;
          unsigned int eline;

          res = GNUNET_JSON_parse (cred,
                                   spec,
                                   &err,
                                   &eline);
          if (GNUNET_OK != res)
          {
            GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                        "Credentials malformed: %s (%u)\n",
                        err,
                        eline);
            GNUNET_free (auth->wire_gateway_url);
            return GNUNET_SYSERR;
          }
          auth->details.basic.username = GNUNET_strdup (username);
          auth->details.basic.password = GNUNET_strdup (password);
        }
        auth->method = TALER_MERCHANT_BANK_AUTH_BASIC;
        return GNUNET_OK;
      case TALER_MERCHANT_BANK_AUTH_BEARER:
        {
          const char *token;
          struct GNUNET_JSON_Specification spec[] = {
            GNUNET_JSON_spec_string ("token",
                                     &token),
            GNUNET_JSON_spec_end ()
          };
          enum GNUNET_GenericReturnValue res;
          const char *err;
          unsigned int eline;

          res = GNUNET_JSON_parse (cred,
                                   spec,
                                   &err,
                                   &eline);
          if (GNUNET_OK != res)
          {
            GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                        "Credentials malformed: %s (%u)\n",
                        err,
                        eline);
            GNUNET_free (auth->wire_gateway_url);
            return GNUNET_SYSERR;
          }
          auth->details.bearer.token = GNUNET_strdup (token);
        }
        auth->method = TALER_MERCHANT_BANK_AUTH_BEARER;
        return GNUNET_OK;
      }
    }
  }
  return GNUNET_SYSERR;
}


void
TALER_MERCHANT_BANK_auth_free (
  struct TALER_MERCHANT_BANK_AuthenticationData *auth)
{
  switch (auth->method)
  {
  case TALER_MERCHANT_BANK_AUTH_NONE:
    break;
  case TALER_MERCHANT_BANK_AUTH_BASIC:
    GNUNET_free (auth->details.basic.username);
    GNUNET_free (auth->details.basic.password);
    break;
  case TALER_MERCHANT_BANK_AUTH_BEARER:
    GNUNET_free (auth->details.bearer.token);
    break;
  }
  GNUNET_free (auth->wire_gateway_url);
}


/* end of mb_parse.c */
