/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
#ifndef tls_cert_h
#define tls_cert_h

#include "tls_util.h"

/**
 * The PEM data of a certificate and its key.
 */
typedef struct {
    tls_data_t cert_pem;
    tls_data_t pkey_pem;
} tls_cert_pem_t;

/**
 * Specify a certificate via files or PEM data.
 */
typedef struct {
    const char *cert_file; /* file path, relative to ap_root */
    const char *pkey_file; /* file path, relative to ap_root */
    const char *cert_pem;  /* NUL-terminated PEM string */
    const char *pkey_pem;  /* NUL-terminated PEM string */
} tls_cert_spec_t;

/**
 * Load the PEM data for a certificate file and key file as given in `cert`.
 */
apr_status_t tls_cert_load_pem(
    apr_pool_t *p, const tls_cert_spec_t *cert, tls_cert_pem_t **ppem);

apr_status_t tls_cert_to_pem(const char **ppem, apr_pool_t *p, const rustls_certificate *cert);

/**
 * Load a rustls certified key from a certificate specification.
 * The returned `rustls_certified_key` is owned by the caller.
 * @param p the memory pool to use
 * @param spec the specification for the certificate (file or PEM data)
 * @param cert_pem return the PEM data used for loading the certificates, optional
 * @param pckey the loaded certified key on return
 */
apr_status_t tls_cert_load_cert_key(
    apr_pool_t *p, const tls_cert_spec_t *spec,
    const char **pcert_pem, const rustls_certified_key **pckey);

/**
 * A registry of rustls_certified_key* by identifier.
 */
typedef struct tls_cert_reg_t tls_cert_reg_t;
struct  tls_cert_reg_t{
    apr_pool_t *pool;
    apr_hash_t *id2entry;
    apr_hash_t *key2entry;
};

/**
 * Create a new registry with lifetime based on the memory pool.
 * The registry will take care of its memory and allocated keys when
 * the pool is destroyed.
 */
tls_cert_reg_t *tls_cert_reg_make(apr_pool_t *p);

/**
 * Return the number of certified keys in the registry.
 */
apr_size_t tls_cert_reg_count(tls_cert_reg_t *reg);

/**
 * Get a the `rustls_certified_key` identified by `spec` from the registry.
 * This will load the key the first time it is requested.
 * The returned `rustls_certified_key` is owned by the registry.
 * @param reg the certified key registry
 * @param s the server_rec this is loaded into, useful for error logging
 * @param spec the specification of the certified key
 * @param pckey the certified key instance on return
 */
apr_status_t tls_cert_reg_get_certified_key(
    tls_cert_reg_t *reg, server_rec *s, const tls_cert_spec_t *spec, const rustls_certified_key **pckey);

/**
 * Visit all certified keys in the registry.
 * The callback may return 0 to abort the iteration.
 * @param userdata supplied by the visit invocation
 * @param s the server_rec the certified was load into first
 * @param id internal identifier of the certified key
 * @param cert_pem the PEM data of the certificate and its chain
 * @param certified_key the key instance itself
 */
typedef int tls_cert_reg_visitor(
    void *userdata, server_rec *s,
    const char *id, const char *cert_pem, const rustls_certified_key *certified_key);

/**
 * Visit all certified_key entries in the registry.
 * @param visitor callback invoked on each entry until it returns 0.
 * @param userdata passed to callback
 * @param reg the registry to iterate over
 */
void tls_cert_reg_do(
    tls_cert_reg_visitor *visitor, void *userdata, tls_cert_reg_t *reg);

/**
 * Get the identity assigned to a loaded, certified key. Returns NULL, if the
 * key is not part of the registry. The returned bytes are owned by the registry
 * entry.
 * @param reg the registry to look in.
 * @param certified_key the key to get the identifier for
 */
const char *tls_cert_reg_get_id(tls_cert_reg_t *reg, const rustls_certified_key *certified_key);

/**
 * Load all root certificates from a PEM file into a rustls_root_cert_store.
 * @param p the memory pool to use
 * @param store_file the (server relative) path of the PEM file
 * @param pstore the loaded root store on success
 */
apr_status_t tls_cert_load_root_store(
    apr_pool_t *p, const char *store_file, const rustls_root_cert_store **pstore);

typedef struct tls_cert_root_stores_t tls_cert_root_stores_t;
struct tls_cert_root_stores_t {
    apr_pool_t *pool;
    apr_hash_t *file2store;
};

/**
 * Create a new root stores registry with lifetime based on the memory pool.
 * The registry will take care of its memory and allocated stores when
 * the pool is destroyed.
 */
tls_cert_root_stores_t *tls_cert_root_stores_make(apr_pool_t *p);

/**
 * Clear the root stores registry, freeing all stores.
 */
void tls_cert_root_stores_clear(tls_cert_root_stores_t *stores);

/**
 * Load all root certificates from a PEM file into a rustls_root_cert_store.
 * @param p the memory pool to use
 * @param store_file the (server relative) path of the PEM file
 * @param pstore the loaded root store on success
 */
apr_status_t tls_cert_root_stores_get(
    tls_cert_root_stores_t *stores,
    const char *store_file,
    const rustls_root_cert_store **pstore);

typedef struct tls_cert_verifiers_t tls_cert_verifiers_t;
struct tls_cert_verifiers_t {
    apr_pool_t *pool;
    tls_cert_root_stores_t *stores;
    apr_hash_t *file2verifier;
};

/**
 * Create a new registry for certificate verifiers with lifetime based on the memory pool.
 * The registry will take care of its memory and allocated verifiers when
 * the pool is destroyed.
 * @param p the memory pool to use
 * @param stores the store registry for lookups
 */
tls_cert_verifiers_t *tls_cert_verifiers_make(
    apr_pool_t *p, tls_cert_root_stores_t *stores);

/**
 * Clear the verifiers registry, freeing all verifiers.
 */
void tls_cert_verifiers_clear(
    tls_cert_verifiers_t *verifiers);

/**
 * Get the mandatory client certificate verifier for the
 * root certificate store in `store_file`. Will create
 * the verifier if not already known.
 * @param verifiers the registry of certificate verifiers
 * @param store_file the (server relative) path of the PEM file with certificates
 * @param pverifiers the verifier on success
 */
apr_status_t tls_cert_client_verifiers_get(
    tls_cert_verifiers_t *verifiers,
    const char *store_file,
    const rustls_client_cert_verifier **pverifier);

/**
 * Get the optional client certificate verifier for the
 * root certificate store in `store_file`. Will create
 * the verifier if not already known.
 * @param verifiers the registry of certificate verifiers
 * @param store_file the (server relative) path of the PEM file with certificates
 * @param pverifiers the verifier on success
 */
apr_status_t tls_cert_client_verifiers_get_optional(
    tls_cert_verifiers_t *verifiers,
    const char *store_file,
    const rustls_client_cert_verifier **pverifier);

#endif /* tls_cert_h */
