/**

  @section license License

  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.
 */

#pragma once

#if __has_include(<openssl/opensslconf.h>)
#include <openssl/opensslconf.h>
#endif
#include <openssl/ssl.h>

#include <memory>
#include <mutex>
#include <tsutil/TsSharedMutex.h>

/** A class for handling TLS secrets logging. */
class TLSKeyLogger
{
public:
  TLSKeyLogger(const TLSKeyLogger &)            = delete;
  TLSKeyLogger &operator=(const TLSKeyLogger &) = delete;

  ~TLSKeyLogger()
  {
    std::unique_lock lock{_mutex};
    close_keylog_file();
  }

  /** A callback for TLS secret key logging.
   *
   * This is the callback registered with OpenSSL's SSL_CTX_set_keylog_callback
   * to log TLS secrets if the user enabled that feature. For more information
   * about this callback, see OpenSSL's documentation of
   * SSL_CTX_set_keylog_callback.
   *
   * @param[in] ssl The SSL object associated with the connection.
   * @param[in] line The line to place in the keylog file.
   */
  static void
  ssl_keylog_cb(const SSL * /* ssl ATS_UNUSED */, const char *line)
  {
    instance().log(line);
  }

  /** Return whether TLS key logging is enabled.
   *
   * @return True if TLS session key logging is enabled, false otherwise.
   */
  static bool
  is_enabled()
  {
    return instance()._fd >= 0;
  }

  /** Enable keylogging.
   *
   * @param[in] keylog_file The path to the file to log TLS secrets to.
   */
  static void
  enable_keylogging(const char *keylog_file)
  {
    instance().enable_keylogging_internal(keylog_file);
  }

  /** Disable TLS secrets logging. */
  static void
  disable_keylogging()
  {
    instance().disable_keylogging_internal();
  }

private:
  TLSKeyLogger() = default;

  /** Return the TLSKeyLogger singleton.
   *
   * We use a getter rather than a class static singleton member so that the
   * construction of the singleton delayed until after TLS configuration is
   * processed.
   */
  static TLSKeyLogger &
  instance()
  {
    static TLSKeyLogger instance;
    return instance;
  }

  /** Close the file descriptor for the key log file.
   *
   * @note This assumes that a unique lock has been acquired for _mutex.
   */
  void close_keylog_file();

  /** A TLS secret line to log to the keylog file.
   *
   * @param[in] line A line to log to the keylog file.
   */
  void log(const char *line);

  /** Enable TLS keylogging in the instance singleton. */
  void enable_keylogging_internal(const char *keylog_file);

  /** Disable TLS keylogging in the instance singleton. */
  void disable_keylogging_internal();

private:
  /** A file descriptor for the log file receiving the TLS secrets. */
  int _fd = -1;

  /** A mutex to coordinate dynamically changing TLS logging config changes and
   * logging to the TLS log file. */
  ts::shared_mutex _mutex;
};
