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

  Challenger 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.

  Challenger 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
  Challenger; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
*/
/**
 * @file challenger-httpd_info.c
 * @brief functions to handle incoming requests for infos
 * @author Christian Grothoff
 */
#include "platform.h"
#include "challenger-httpd.h"
#include <gnunet/gnunet_util_lib.h>
#include "challenger-httpd_common.h"
#include "challenger-httpd_info.h"

/**
 * Prefix of a 'Bearer' token in an 'Authorization' HTTP header.
 */
#define BEARER_PREFIX "Bearer "


/**
 * Maximum number of retries for the database interaction.
 */
#define MAX_RETRIES 3

MHD_RESULT
CH_handler_info (struct CH_HandlerContext *hc,
                 const char *upload_data,
                 size_t *upload_data_size)
{
  const char *auth;
  const char *token;
  struct CHALLENGER_AccessTokenP grant;

  (void) upload_data;
  (void) upload_data_size;
  auth = MHD_lookup_connection_value (hc->connection,
                                      MHD_HEADER_KIND,
                                      MHD_HTTP_HEADER_AUTHORIZATION);
  if (NULL == auth)
  {
    GNUNET_break_op (0);
    return TALER_MHD_reply_with_error (hc->connection,
                                       MHD_HTTP_FORBIDDEN,
                                       TALER_EC_GENERIC_PARAMETER_MISSING,
                                       MHD_HTTP_HEADER_AUTHORIZATION);
  }
  if (0 != strncmp (auth,
                    BEARER_PREFIX,
                    strlen (BEARER_PREFIX)))
  {
    GNUNET_break_op (0);
    return TALER_MHD_reply_with_error (hc->connection,
                                       MHD_HTTP_FORBIDDEN,
                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
                                       MHD_HTTP_HEADER_AUTHORIZATION);
  }
  token = auth + strlen (BEARER_PREFIX);

  if (GNUNET_OK !=
      GNUNET_STRINGS_string_to_data (token,
                                     strlen (token),
                                     &grant,
                                     sizeof (grant)))
  {
    GNUNET_break_op (0);
    return TALER_MHD_reply_with_error (hc->connection,
                                       MHD_HTTP_FORBIDDEN,
                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
                                       MHD_HTTP_HEADER_AUTHORIZATION);
  }

  /* Check token is valid */
  for (unsigned int r = 0; r<MAX_RETRIES; r++)
  {
    uint64_t id;
    json_t *address;
    enum GNUNET_DB_QueryStatus qs;
    struct GNUNET_TIME_Timestamp address_expiration;

    qs = CH_db->info_get_token (CH_db->cls,
                                &grant,
                                &id,
                                &address,
                                &address_expiration);
    switch (qs)
    {
    case GNUNET_DB_STATUS_HARD_ERROR:
      GNUNET_break (0);
      return TALER_MHD_reply_with_error (hc->connection,
                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
                                         "info_get_token");
    case GNUNET_DB_STATUS_SOFT_ERROR:
      if (r < MAX_RETRIES - 1)
        continue;
      GNUNET_break (0);
      return TALER_MHD_reply_with_error (hc->connection,
                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
                                         "info_get_token");
    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
      return TALER_MHD_reply_with_error (hc->connection,
                                         MHD_HTTP_NOT_FOUND,
                                         TALER_EC_CHALLENGER_GRANT_UNKNOWN,
                                         "info_get_token");
    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
      break;
    }
    return TALER_MHD_REPLY_JSON_PACK (
      hc->connection,
      MHD_HTTP_OK,
      GNUNET_JSON_pack_uint64 ("id",
                               id),
      GNUNET_JSON_pack_object_steal ("address",
                                     address),
      GNUNET_JSON_pack_string ("address_type",
                               CH_address_type),
      GNUNET_JSON_pack_timestamp ("expires",
                                  address_expiration));
  }
  GNUNET_assert (0);
  return MHD_NO;
}
