/*
 *
 * Copyright 2018 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <grpc/support/port_platform.h>

#include "src/core/lib/security/credentials/tls/grpc_tls_credentials_options.h"

#include <stdlib.h>
#include <string.h>

#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>

/** -- gRPC TLS key materials config API implementation. -- **/
void grpc_tls_key_materials_config::set_key_materials(
    const char* pem_root_certs,
    const grpc_ssl_pem_key_cert_pair** pem_key_cert_pairs,
    size_t num_key_cert_pairs) {
  this->set_pem_root_certs(pem_root_certs);
  grpc_tls_key_materials_config::PemKeyCertPairList cert_pair_list;
  for (size_t i = 0; i < num_key_cert_pairs; i++) {
    auto current_pair = static_cast<grpc_ssl_pem_key_cert_pair*>(
        gpr_zalloc(sizeof(grpc_ssl_pem_key_cert_pair)));
    current_pair->cert_chain = gpr_strdup(pem_key_cert_pairs[i]->cert_chain);
    current_pair->private_key = gpr_strdup(pem_key_cert_pairs[i]->private_key);
    cert_pair_list.emplace_back(grpc_core::PemKeyCertPair(current_pair));
  }
  pem_key_cert_pair_list_ = std::move(cert_pair_list);
}

void grpc_tls_key_materials_config::set_key_materials(
    const char* pem_root_certs,
    const PemKeyCertPairList& pem_key_cert_pair_list) {
  this->set_pem_root_certs(pem_root_certs);
  grpc_tls_key_materials_config::PemKeyCertPairList dup_list(
      pem_key_cert_pair_list);
  pem_key_cert_pair_list_ = std::move(dup_list);
}

/** -- gRPC TLS credential reload config API implementation. -- **/
grpc_tls_credential_reload_config::grpc_tls_credential_reload_config(
    const void* config_user_data,
    int (*schedule)(void* config_user_data,
                    grpc_tls_credential_reload_arg* arg),
    void (*cancel)(void* config_user_data, grpc_tls_credential_reload_arg* arg),
    void (*destruct)(void* config_user_data))
    : config_user_data_(const_cast<void*>(config_user_data)),
      schedule_(schedule),
      cancel_(cancel),
      destruct_(destruct) {}

grpc_tls_credential_reload_config::~grpc_tls_credential_reload_config() {
  if (destruct_ != nullptr) {
    destruct_((void*)config_user_data_);
  }
}

/** -- gRPC TLS server authorization check API implementation. -- **/
grpc_tls_server_authorization_check_config::
    grpc_tls_server_authorization_check_config(
        const void* config_user_data,
        int (*schedule)(void* config_user_data,
                        grpc_tls_server_authorization_check_arg* arg),
        void (*cancel)(void* config_user_data,
                       grpc_tls_server_authorization_check_arg* arg),
        void (*destruct)(void* config_user_data))
    : config_user_data_(const_cast<void*>(config_user_data)),
      schedule_(schedule),
      cancel_(cancel),
      destruct_(destruct) {}

grpc_tls_server_authorization_check_config::
    ~grpc_tls_server_authorization_check_config() {
  if (destruct_ != nullptr) {
    destruct_((void*)config_user_data_);
  }
}

/** -- Wrapper APIs declared in grpc_security.h -- **/
grpc_tls_credentials_options* grpc_tls_credentials_options_create() {
  return new grpc_tls_credentials_options();
}

int grpc_tls_credentials_options_set_cert_request_type(
    grpc_tls_credentials_options* options,
    grpc_ssl_client_certificate_request_type type) {
  if (options == nullptr) {
    gpr_log(GPR_ERROR,
            "Invalid nullptr arguments to "
            "grpc_tls_credentials_options_set_cert_request_type()");
    return 0;
  }
  options->set_cert_request_type(type);
  return 1;
}

int grpc_tls_credentials_options_set_server_verification_option(
    grpc_tls_credentials_options* options,
    grpc_tls_server_verification_option server_verification_option) {
  if (options == nullptr) {
    gpr_log(GPR_ERROR,
            "Invalid nullptr arguments to "
            "grpc_tls_credentials_options_set_server_verification_option()");
    return 0;
  }
  if (server_verification_option != GRPC_TLS_SERVER_VERIFICATION &&
      options->server_authorization_check_config() == nullptr) {
    gpr_log(GPR_ERROR,
            "server_authorization_check_config needs to be specified when"
            "server_verification_option is not GRPC_TLS_SERVER_VERIFICATION");
    return 0;
  }
  options->set_server_verification_option(server_verification_option);
  return 1;
}

int grpc_tls_credentials_options_set_key_materials_config(
    grpc_tls_credentials_options* options,
    grpc_tls_key_materials_config* config) {
  if (options == nullptr || config == nullptr) {
    gpr_log(GPR_ERROR,
            "Invalid nullptr arguments to "
            "grpc_tls_credentials_options_set_key_materials_config()");
    return 0;
  }
  options->set_key_materials_config(config->Ref());
  return 1;
}

int grpc_tls_credentials_options_set_credential_reload_config(
    grpc_tls_credentials_options* options,
    grpc_tls_credential_reload_config* config) {
  if (options == nullptr || config == nullptr) {
    gpr_log(GPR_ERROR,
            "Invalid nullptr arguments to "
            "grpc_tls_credentials_options_set_credential_reload_config()");
    return 0;
  }
  options->set_credential_reload_config(config->Ref());
  return 1;
}

int grpc_tls_credentials_options_set_server_authorization_check_config(
    grpc_tls_credentials_options* options,
    grpc_tls_server_authorization_check_config* config) {
  if (options == nullptr || config == nullptr) {
    gpr_log(
        GPR_ERROR,
        "Invalid nullptr arguments to "
        "grpc_tls_credentials_options_set_server_authorization_check_config()");
    return 0;
  }
  options->set_server_authorization_check_config(config->Ref());
  return 1;
}

grpc_tls_key_materials_config* grpc_tls_key_materials_config_create() {
  return new grpc_tls_key_materials_config();
}

int grpc_tls_key_materials_config_set_key_materials(
    grpc_tls_key_materials_config* config, const char* root_certs,
    const grpc_ssl_pem_key_cert_pair** key_cert_pairs, size_t num) {
  if (config == nullptr || key_cert_pairs == nullptr || num == 0) {
    gpr_log(GPR_ERROR,
            "Invalid arguments to "
            "grpc_tls_key_materials_config_set_key_materials()");
    return 0;
  }
  config->set_key_materials(root_certs, key_cert_pairs, num);
  return 1;
}

int grpc_tls_key_materials_config_set_version(
    grpc_tls_key_materials_config* config, int version) {
  if (config == nullptr) {
    gpr_log(GPR_ERROR,
            "Invalid arguments to "
            "grpc_tls_key_materials_config_set_version()");
    return 0;
  }
  config->set_version(version);
  return 1;
}

int grpc_tls_key_materials_config_get_version(
    grpc_tls_key_materials_config* config) {
  if (config == nullptr) {
    gpr_log(GPR_ERROR,
            "Invalid arguments to "
            "grpc_tls_key_materials_config_get_version()");
    return -1;
  }
  return config->version();
}

grpc_tls_credential_reload_config* grpc_tls_credential_reload_config_create(
    const void* config_user_data,
    int (*schedule)(void* config_user_data,
                    grpc_tls_credential_reload_arg* arg),
    void (*cancel)(void* config_user_data, grpc_tls_credential_reload_arg* arg),
    void (*destruct)(void* config_user_data)) {
  if (schedule == nullptr) {
    gpr_log(
        GPR_ERROR,
        "Schedule API is nullptr in creating TLS credential reload config.");
    return nullptr;
  }
  return new grpc_tls_credential_reload_config(config_user_data, schedule,
                                               cancel, destruct);
}

grpc_tls_server_authorization_check_config*
grpc_tls_server_authorization_check_config_create(
    const void* config_user_data,
    int (*schedule)(void* config_user_data,
                    grpc_tls_server_authorization_check_arg* arg),
    void (*cancel)(void* config_user_data,
                   grpc_tls_server_authorization_check_arg* arg),
    void (*destruct)(void* config_user_data)) {
  if (schedule == nullptr) {
    gpr_log(GPR_ERROR,
            "Schedule API is nullptr in creating TLS server authorization "
            "check config.");
    return nullptr;
  }
  return new grpc_tls_server_authorization_check_config(
      config_user_data, schedule, cancel, destruct);
}
