// See the file "COPYING" in the main distribution directory for copyright.

#pragma once

#include <functional>
#include <list>
#include <optional>
#include <string>
#include <utility>

#include "zeek/ZeekArgs.h"
#include "zeek/logging/WriterBackend.h"

// Avoid ccache busting of Plugin.h for internal plugins by
// only including zeek/zeek-version.h if we're building an
// external plugin. The define gets set in the CMakeLists.txt file
// for the Zeek::Internal target, which only exists when
// building Zeek itself.
#ifndef ZEEK_PLUGIN_SKIP_VERSION_CHECK
#include "zeek/zeek-version.h"
#define ZEEK_PLUGIN_ZEEK_VERSION ZEEK_VERSION_FUNCTION
#endif

namespace zeek::threading {
struct Field;
}

namespace zeek {

#ifdef _MSC_VER
#undef VOID
#endif

// Increase this when making incompatible changes to the plugin API. Note
// that the constant is never used in C code. It's picked up on by CMake.
constexpr int PLUGIN_API_VERSION = 7;

class ODesc;
class Event;
class Func;
class Obj;
class Packet;

template<class T>
class IntrusivePtr;
using ValPtr = IntrusivePtr<Val>;

namespace threading {
struct Field;
}
namespace detail {
class Frame;
}

namespace cluster {
class Backend;
class Event;
} // namespace cluster

namespace plugin {

class Manager;
class Component;
class Plugin;

/**
 * Hook types that a plugin may define. Each label maps to the corresponding
 * virtual method in \a Plugin.
 */
enum HookType : uint8_t {
    // Note: when changing this table, update hook_name() in Plugin.cc.
    HOOK_LOAD_FILE,           //< Activates Plugin::HookLoadFile().
    HOOK_LOAD_FILE_EXT,       //< Activates Plugin::HookLoadFileExtended().
    HOOK_CALL_FUNCTION,       //< Activates Plugin::HookCallFunction().
    HOOK_QUEUE_EVENT,         //< Activates Plugin::HookQueueEvent().
    HOOK_DRAIN_EVENTS,        //< Activates Plugin::HookDrainEvents().
    HOOK_UPDATE_NETWORK_TIME, //< Activates Plugin::HookUpdateNetworkTime().
    HOOK_SETUP_ANALYZER_TREE, //< Activates Plugin::HookAddToAnalyzerTree().
    HOOK_LOG_INIT,            //< Activates Plugin::HookLogInit().
    HOOK_LOG_WRITE,           //< Activates Plugin::HookLogWrite().
    HOOK_REPORTER,            //< Activates Plugin::HookReporter().
    HOOK_UNPROCESSED_PACKET,  //<Activates Plugin::HookUnprocessedPacket().
    HOOK_OBJ_DTOR,            //< Activates Plugin::HookObjDtor().
    HOOK_PUBLISH_EVENT,       //< Activates Plugin::HookPublishEvent().

    // Meta hooks.
    META_HOOK_PRE,  //< Activates Plugin::MetaHookPre().
    META_HOOK_POST, //< Activates Plugin::MetaHookPost().

    // End marker.
    NUM_HOOKS,
};

/**
 * Converts a hook type into a readable hook name.
 */
extern const char* hook_name(HookType h);

/**
 * Helper class to capture a plugin's version.
 * */
struct VersionNumber {
    int major = -1; //< Major version number.
    int minor = -1; //< Minor version number.
    int patch = 0;  //< Patch version number (available since Zeek 3.0).

    /**
     *  Returns true if the version is set to a non-negative value.
     */
    explicit operator bool() const { return major >= 0 && minor >= 0 && patch >= 0; }
};

/**
 * A class defining a plugin's static configuration parameters.
 */
class Configuration {
public:
    std::string name = "";        //< The plugin's name, including a namespace. Mandatory.
    std::string description = ""; //< A short textual description of the plugin. Mandatory.
    VersionNumber version;        //< THe plugin's version. Optional.

    // We force this to inline so that the API version gets hardcoded
    // into the external plugin. (Technically, it's not a "force", just a
    // strong hint.). The attribute seems generally available.
    inline Configuration() __attribute__((always_inline)) {
// Only bake in a ZEEK_PLUGIN_ZEEK_VERSION reference into external plugins. The
// internal ones are in the same binary so the runtime link check shouldn't be
// needed and we can avoid ccache busting. The define gets set in the
// CMakeLists.txt file for the Zeek::Internal target, which only exists when
// building Zeek itself.
#ifndef ZEEK_PLUGIN_SKIP_VERSION_CHECK
        zeek_version = ZEEK_PLUGIN_ZEEK_VERSION;
#endif
    }

    Configuration(Configuration&& c) noexcept {
        zeek_version = std::move(c.zeek_version);

        name = std::move(c.name);
        description = std::move(c.description);
        version = c.version;
    }

    Configuration(const Configuration& c) {
        zeek_version = c.zeek_version;

        name = c.name;
        description = c.description;
        version = c.version;
    }

    Configuration& operator=(Configuration&& c) noexcept {
        zeek_version = std::move(c.zeek_version);

        name = std::move(c.name);
        description = std::move(c.description);
        version = c.version;

        return *this;
    }

    Configuration& operator=(const Configuration& c) {
        zeek_version = c.zeek_version;

        name = c.name;
        description = c.description;
        version = c.version;

        return *this;
    }

    ~Configuration() {}

    /**
     * One can assign ZEEK_PLUGIN_ZEEK_VERSION to this to catch
     * version mismatches at link(!) time.
     */
    std::function<const char*()> zeek_version;

private:
    friend class Plugin;
};

/**
 * A class describing an item defined in \c *.bif file.
 */
class BifItem final {
public:
    /**
     * Type of the item.
     */
    enum Type : uint8_t {
        FUNCTION = 1,
        EVENT = 2,
        CONSTANT = 3,
        GLOBAL = 4,
        TYPE = 5,
    };

    /**
     * Constructor.
     *
     * @param id The script-level name of the item. This should be fully
     * qualified.
     *
     * @param type The type of the item.
     */
    BifItem(const std::string& id, Type type);

    /**
     * Copy constructor.
     */
    BifItem(const BifItem& other);

    /**
     * Assignment operator.
     */
    BifItem& operator=(const BifItem& other);

    /**
     * Destructor.
     */
    ~BifItem() = default;

    /**
     * Returns the script-level ID as passed into the constructor.
     */
    const std::string& GetID() const { return id; }

    /**
     * Returns the type as passed into the constructor.
     */
    Type GetType() const { return type; }

private:
    std::string id;
    Type type;
};

/**
 * A class encapsulating an event argument to then pass along with a meta hook.
 */
class HookArgument {
public:
    /**
     * Type of the argument.
     */
    enum Type : uint8_t {
        BOOL,
        DOUBLE,
        EVENT,
        FRAME,
        FUNC,
        FUNC_RESULT,
        INT,
        STRING,
        VAL,
        VAL_LIST,
        VOID,
        VOIDP,
        WRITER_INFO,
        CONN,
        THREAD_FIELDS,
        LOCATION,
        ARG_LIST,
        INPUT_FILE,
        PACKET,
        CLUSTER_BACKEND,
        CLUSTER_EVENT,
    };

    /**
     * Default constructor initialized the argument with type VOID.
     */
    HookArgument() { type = VOID; }

    /**
     * Constructor with a boolean argument.
     */
    explicit HookArgument(bool a) {
        type = BOOL;
        arg.bool_ = a;
    }

    /**
     * Constructor with a double argument.
     */
    explicit HookArgument(double a) {
        type = DOUBLE;
        arg.double_ = a;
    }

    /**
     * Constructor with an event argument.
     */
    explicit HookArgument(const Event* a) {
        type = EVENT;
        arg.event = a;
    }

    /**
     * Constructor with an connection argument.
     */
    explicit HookArgument(const Connection* c) {
        type = CONN;
        arg.conn = c;
    }

    /**
     * Constructor with a function argument.
     */
    explicit HookArgument(const Func* a) {
        type = FUNC;
        arg.func = a;
    }

    /**
     * Constructor with an integer  argument.
     */
    explicit HookArgument(int a) {
        type = INT;
        arg.int_ = a;
    }

    /**
     * Constructor with a string argument.
     */
    explicit HookArgument(const std::string& a) {
        type = STRING;
        arg_string = a;
    }

    /**
     * Constructor with a Zeek value argument.
     */
    explicit HookArgument(const Val* a) {
        type = VAL;
        arg.val = a;
    }

    /**
     * Constructor with a list of Zeek values argument.
     */
    explicit HookArgument(const ValPList* a) {
        type = VAL_LIST;
        arg.vals = a;
    }

    /**
     * Constructor with a void pointer argument.
     */
    explicit HookArgument(void* p) {
        type = VOIDP;
        arg.voidp = p;
    }

    /**
     * Constructor with a function result argument.
     */
    explicit HookArgument(std::pair<bool, Val*> fresult) {
        type = FUNC_RESULT;
        func_result = fresult;
    }

    /**
     * Constructor with a Frame argument.
     */
    explicit HookArgument(zeek::detail::Frame* f) {
        type = FRAME;
        arg.frame = f;
    }

    /**
     * Constructor with a WriterInfo argument.
     */
    explicit HookArgument(const logging::WriterBackend::WriterInfo* i) {
        type = WRITER_INFO;
        arg.winfo = i;
    }

    /**
     * Constructor with a threading field argument.
     */
    explicit HookArgument(const std::pair<int, const threading::Field* const*> fpair) {
        type = THREAD_FIELDS;
        tfields = fpair;
    }

    /**
     * Constructor with a location argument.
     */
    explicit HookArgument(const zeek::detail::Location* location) {
        type = LOCATION;
        arg.loc = location;
    }

    /**
     * Constructor with a zeek::Args argument.
     */
    explicit HookArgument(const Args* args) {
        type = ARG_LIST;
        arg.args = args;
    }

    /**
     * Constructor with HookLoadFileExtended result describing an input file.
     */
    explicit HookArgument(std::pair<int, std::optional<std::string>> file) {
        type = INPUT_FILE;
        input_file = std::move(file);
    }

    /**
     * Returns the value for a zeek::Packet* argument. The argument's type must
     * Constructor with a zeek::Packet* argument.
     */
    explicit HookArgument(const Packet* packet) {
        type = PACKET;
        arg.packet = packet;
    }

    /**
     * Constructor with cluster backend argument.
     */
    explicit HookArgument(zeek::cluster::Backend* backend) {
        type = CLUSTER_BACKEND;
        arg.cluster_backend = backend;
    }

    /**
     * Constructor with cluster event argument.
     */
    explicit HookArgument(zeek::cluster::Event* event) {
        type = CLUSTER_EVENT;
        arg.cluster_event = event;
    }

    /**
     * Returns the value for a boolean argument. The argument's type must
     * match accordingly.
     */
    bool AsBool() const {
        assert(type == BOOL);
        return arg.bool_;
    }

    /**
     * Returns the value for a double argument. The argument's type must
     * match accordingly.
     */
    double AsDouble() const {
        assert(type == DOUBLE);
        return arg.double_;
    }

    /**
     * Returns the value for an event argument. The argument's type must
     * match accordingly.
     */
    const Event* AsEvent() const {
        assert(type == EVENT);
        return arg.event;
    }

    /**
     * Returns the value for an connection argument. The argument's type must
     * match accordingly.
     */
    const Connection* AsConnection() const {
        assert(type == CONN);
        return arg.conn;
    }

    /**
     * Returns the value for a function argument. The argument's type must
     * match accordingly.
     */
    const Func* AsFunc() const {
        assert(type == FUNC);
        return arg.func;
    }

    /**
     * Returns the value for an integer argument. The argument's type must
     * match accordingly.
     */
    double AsInt() const {
        assert(type == INT);
        return arg.int_;
    }

    /**
     * Returns the value for a string argument. The argument's type must
     * match accordingly.
     */
    const std::string& AsString() const {
        assert(type == STRING);
        return arg_string;
    }

    /**
     * Returns the value for a Zeek value argument. The argument's type must
     * match accordingly.
     */
    const Val* AsVal() const {
        assert(type == VAL);
        return arg.val;
    }

    /**
     * Returns the value for a Zeek wrapped value argument.  The argument's type must
     * match accordingly.
     */
    const std::pair<bool, Val*> AsFuncResult() const {
        assert(type == FUNC_RESULT);
        return func_result;
    }

    /**
     * Returns the value for a Zeek frame argument.  The argument's type must
     * match accordingly.
     */
    const zeek::detail::Frame* AsFrame() const {
        assert(type == FRAME);
        return arg.frame;
    }

    /**
     * Returns the value for a logging WriterInfo argument.  The argument's type must
     * match accordingly.
     */
    const logging::WriterBackend::WriterInfo* AsWriterInfo() const {
        assert(type == WRITER_INFO);
        return arg.winfo;
    }

    /**
     * Returns the value for a threading fields argument.  The argument's type must
     * match accordingly.
     */
    const std::pair<int, const threading::Field* const*> AsThreadFields() const {
        assert(type == THREAD_FIELDS);
        return tfields;
    }

    /**
     * Returns the value for a list of Zeek values argument. The argument's type must
     * match accordingly.
     */
    const ValPList* AsValList() const {
        assert(type == VAL_LIST);
        return arg.vals;
    }

    /**
     * Returns the value as a Args.
     */
    const Args* AsArgList() const {
        assert(type == ARG_LIST);
        return arg.args;
    }

    /**
     * Returns the value for a void pointer argument. The argument's type
     * must match accordingly.
     */
    const void* AsVoidPtr() const {
        assert(type == VOIDP);
        return arg.voidp;
    }

    /**
     * Returns the value for a Packet pointer argument. The argument's type
     * must match accordingly.
     */
    const Packet* AsPacket() const {
        assert(type == PACKET);
        return arg.packet;
    }

    /**
     * Returns the value for a cluster backend argument.
     */
    const zeek::cluster::Backend* AsClusterBackend() const {
        assert(type == CLUSTER_EVENT);
        return arg.cluster_backend;
    }

    /**
     * Returns the value for a cluster event argument.
     */
    const zeek::cluster::Event* AsClusterEvent() const {
        assert(type == CLUSTER_EVENT);
        return arg.cluster_event;
    }

    /**
     * Returns the argument's type.
     */
    Type GetType() const { return type; }

    /**
     * Returns a textual representation of the argument.
     *
     * @param d Description object to use for rendering.
     */
    void Describe(ODesc* d) const;

private:
    Type type;
    union {
        bool bool_;
        double double_;
        const Event* event;
        const Connection* conn;
        const Func* func;
        const zeek::detail::Frame* frame;
        int int_;
        const Val* val;
        const ValPList* vals;
        const Args* args;
        const void* voidp;
        const logging::WriterBackend::WriterInfo* winfo;
        const zeek::detail::Location* loc;
        const Packet* packet;
        const cluster::Backend* cluster_backend;
        const cluster::Event* cluster_event;
    } arg;

    // Outside union because these have dtors.
    std::pair<bool, Val*> func_result;
    std::pair<int, const threading::Field* const*> tfields;
    std::string arg_string;
    std::pair<int, std::optional<std::string>> input_file;
};

using HookArgumentList = std::list<HookArgument>;

/**
 * Base class for all plugins.
 *
 * Plugins encapsulate functionality that extends one or more of Zeek's major
 * subsystems, such as analysis of a specific protocol, or logging output in
 * a particular format. A plugin acts a logical container that can provide a
 * set of functionality. Specifically, it may:
 *
 * - Provide one or more \a components implementing functionality. For
 *   example, a RPC plugin could provide analyzer for set of related
 *   protocols (RPC, NFS, etc.), each of which would be a separate component.
 *   Likewise, an SQLite plugin could provide both a writer and reader
 *   component.
 *
 * - Provide BiF elements (functions, events, types, globals). Typically
 *   they'll be defined in *.bif files, but a plugin can also create them
 *   internally.
 *
 * - Provide hooks (aka callbacks) into Zeek's core processing to inject
 *   and/or alter functionality.
 *
 * A plugin needs to explicitly register all the functionality it provides.
 * For components, it needs to call AddComponent(); for BiFs AddBifItem();
 * and for hooks EnableHook() and then also implement the corresponding
 * virtual methods.
 *
 */
class Plugin {
public:
    using component_list = std::list<Component*>;
    using bif_item_list = std::list<BifItem>;
    using hook_list = std::list<std::pair<HookType, int>>;

    /**
     * The different types of @loads supported by HookLoadFile.
     */
    enum LoadType : uint8_t { SCRIPT, SIGNATURES, PLUGIN };

    /**
     * Constructor.
     */
    Plugin();

    /**
     * Destructor.
     */
    virtual ~Plugin();

    /**
     * Returns the name of the plugin.
     */
    const std::string& Name() const;

    /**
     * Returns a short textual description of the plugin, if provided.
     */
    const std::string& Description() const;

    /**
     * Returns the version of the plugin. Versions are only meaningful
     * for dynamically compiled plugins; for statically compiled ones,
     * this will always return 0.
     */
    VersionNumber Version() const;

    /**
     * Returns true if this is a dynamically linked plugin.
     */
    bool DynamicPlugin() const;

    /**
     * For dynamic plugins, returns the base directory from which it was
     * loaded. For static plugins, returns an empty string.
     **/
    const std::string& PluginDirectory() const;

    /**
     * For dynamic plugins, returns the full path to the shared library
     * from which it was loaded. For static plugins, returns an empty
     * string.
     **/
    const std::string& PluginPath() const;

    /**
     * Returns a list of all components the plugin provides.
     */
    component_list Components() const;

    /**
     * Returns a list of all BiF items that the plugin provides. This
     * must be called only after InitBif() has been executed.
     */
    bif_item_list BifItems() const;

    /**
     * Returns a textual description of the plugin.
     *
     * @param d Description object to use for rendering. If "short mode"
     * is disabled, the rendering will include a list of all components
     * and BiF items.
     */
    void Describe(ODesc* d) const;

    /**
     * Registers an individual BiF that the plugin defines. The
     * information is for informational purposes only and will show up in
     * the result of BifItems() as well as in the Describe() output.
     * Another way to add this information is via overriding
     * CustomBifItems().
     *
     * Note that this method is rarely the right one to use. As it's for
     * informational purposes only, the plugin still needs to register
     * the BiF items themselves with the corresponding Zeek parts. Doing
     * so can be tricky, and it's recommend to instead define BiF items
     * in separate *.bif files that the plugin then pulls in. If defined
     * there, one does *not* need to call this method.
     *
     * @param name The name of the BiF item.
     *
     * @param type The item's type.
     */
    void AddBifItem(const std::string& name, BifItem::Type type);

    /**
     * Adds a file to the list of files that Zeek loads at startup. This
     * will normally be a Zeek script, but it passes through the plugin
     * system as well to load files with other extensions as supported by
     * any of the current plugins. In other words, calling this method is
     * similar to giving a file on the command line. Note that the file
     * may be only queued for now, and actually loaded later.
     *
     * This method must not be called after InitPostScript().
     *
     * @param file The file to load. It will be searched along the
     * standard paths.
     *
     * @return True if successful (which however may only mean
     * "successfully queued").
     */
    bool LoadZeekFile(const std::string& file);

protected:
    friend class Manager;

    /**
     * First-stage initialization of the plugin called early during Zeek's
     * startup, before scripts are parsed. This can be overridden by
     * derived classes; they must however call the parent's
     * implementation.
     */
    virtual void InitPreScript();

    /**
     * Second-stage initialization of the plugin called late during Zeek's
     * startup, after scripts are parsed. This can be overridden by
     * derived classes; they must however call the parent's
     * implementation.
     */
    virtual void InitPostScript();

    /**
     * Third-stage initialization of the plugin called just before enqueueing
     * zeek_init(), after script analysis and optimization completed.
     * This can be overridden by derived classes; they must however call the
     * parent's implementation.
     */
    virtual void InitPreExecution();

    /**
     * Finalizer method that derived classes can override for performing
     * custom tasks at shutdown.  This can be overridden by derived
     * classes; they must however call the parent's implementation.
     */
    virtual void Done();

    /**
     * Registers a component.
     *
     * @param c The component. The method takes ownership.
     */
    void AddComponent(Component* c);

    /**
     * Calls the Initialize() function of all components.
     */
    void InitializeComponents();

    /**
     * Enables a hook. The corresponding virtual method will now be
     * called as Zeek's processing proceeds. Note that enabling hooks can
     * have performance impact as many trigger frequently inside Zeek's
     * main processing path.
     *
     * Note that while hooks may be enabled/disabled dynamically at any
     * time, the output of Zeek's \c -NN option will only reflect their
     * state at startup time. Usually one should call this method for a
     * plugin's hooks in either the plugin's constructor or in
     * InitPreScript().
     *
     * @param hook The hook to enable.
     *
     * @param priority If multiple plugins enable the same hook, their
     * priorities determine the order in which they'll be executed, from
     * highest to lowest. If two plugins specify the same priority, order
     * is undefined.
     */
    void EnableHook(HookType hook, int priority = 0);

    /**
     * Disables a hook. Zeek will no longer call the corresponding virtual
     * method.
     *
     * @param hook The hook to disable.
     */
    void DisableHook(HookType hook);

    /**
     * Returns a list of hooks that are currently enabled for the plugin,
     * along with their priorities.
     */
    hook_list EnabledHooks() const;

    /**
     * Registers interest in an event, even if there's no handler for it.
     * Normally a plugin receives events through HookQueueEvent() only if Zeek
     * actually has code to execute for it. By calling this method, the
     * plugin tells Zeek to raise the event even if there's no corresponding
     * handler; it will then go into HookQueueEvent() just as any other.
     *
     * @param handler The event handler being interested in.
     */
    void RequestEvent(EventHandlerPtr handler);

    /**
     * Registers interest in the destruction of a Obj instance. When
     * Zeek's reference counting triggers the objects destructor to run,
     * \a HookObjDtor will be called.
     *
     * Note that this can get expensive if triggered for many objects.
     *
     * @param obj The object being interested in.
     */
    void RequestObjDtor(Obj* obj);

    // Hook functions.

    /**
     * Hook into loading input files. This method will be called between
     * InitPreScript() and InitPostScript(), but with no further order or
     * timing guaranteed. It will be called once for each input file Zeek
     * is about to load, either given on the command line or via @load
     * script directives. The hook can take over the file, in which case
     * Zeek will not further process it otherwise.
     *
     * @param type The type of load encountered: script load, signatures load,
     *             or plugin load.
     *
     * @param file The filename that was passed to @load. Only includes
     *             an extension if it was given in @load.
     *
     * @param resolved The file or directory name Zeek resolved from
     *                 the given path and is going to load. Empty string
     *                 if Zeek was not able to resolve a path.
     *
     * @return 1 if the plugin took over the file and loaded it
     * successfully; 0 if the plugin took over the file but had trouble
     * loading it (Zeek will abort in this case, and the plugin should
     * have printed an error message); and -1 if the plugin wasn't
     * interested in the file at all.
     */
    virtual int HookLoadFile(const LoadType type, const std::string& file, const std::string& resolved);

    /**
     * Hook into loading input files, with extended capabilities. This method
     * will be called between InitPreScript() and InitPostScript(), but with no
     * further order or timing guaranteed. It will be called once for each
     * input file Zeek is about to load, either given on the command line or via
     * @load script directives. The hook can take over the file, in which case
     * Zeek will not further process it otherwise. It can, alternatively, also
     * provide the file content as a string, which Zeek will then process just
     * as if it had read it from a file.
     *
     * @param type The type of load encountered: script load, signatures load,
     *             or plugin load.
     *
     * @param file The filename that was passed to @load. Only includes
     *             an extension if it was given in @load.
     *
     * @param resolved The file or directory name Zeek resolved from
     *                 the given path and is going to load. Empty string
     *                 if Zeek was not able to resolve a path.
     *
     * @return tuple of an integer and an optional string, where: the integer
     * must be 1 if the plugin takes over loading the file (see below); 0 if
     * the plugin wanted to take over the file but had trouble loading it
     * (processing will abort in this case, and the plugin should have printed
     * an error message); and -1 if the plugin wants Zeek to proceed processing
     * the file normally. If the plugins takes over by returning 1, there are
     * two cases: if the second tuple element remains unset, the plugin handled
     * the loading completely internally; Zeek will not do anything further with
     * it. Alternatively, the plugin may optionally return the actual content
     * to use for the file as a string through the tuple's second element. If
     * so, Zeek will ignore the file on disk and use that provided content
     * instead (including when there's actually no physical file in place on
     * disk at all, and loading would have hence failed otherwise).
     */
    virtual std::pair<int, std::optional<std::string>> HookLoadFileExtended(const LoadType type,
                                                                            const std::string& file,
                                                                            const std::string& resolved);

    /**
     * Hook into executing a script-level function/event/hook. Whenever
     * the script interpreter is about to execution a function, it first
     * gives all plugins with this hook enabled a chance to handle the
     * call (in the order of their priorities). A plugin can either just
     * inspect the call, or replace it (i.e., prevent the interpreter
     * from executing it). In the latter case it must provide a matching
     * return value.
     *
     * The default implementation never handles the call in any way.
     *
     * @param func The function being called.
     *
     * @param args The function arguments. The method can modify the list
     * in place as long as it ensures matching types and correct reference
     * counting.
     *
     * @return If the plugin handled the call, a pair with the first member
     * set to true, and a Val representing the result value to pass back to the
     * interpreter. If the plugin did not handle the call, it must return a
     * pair with the first member set to 'false' and null result value.
     */
    virtual std::pair<bool, ValPtr> HookFunctionCall(const Func* func, zeek::detail::Frame* parent, Args* args);

    /**
     * Hook into raising events. Whenever the script interpreter is about
     * to queue an event for later execution, it first gives all plugins
     * with this hook enabled a chance to handle the queuing otherwise
     * (in the order of their priorities). A plugin can either just
     * inspect the event, or take it over (i.e., prevent the interpreter
     * from queuing it itself).
     *
     * The default implementation never handles the queuing in any way.
     *
     * @param event The event to be queued. The method can modify it in
     * place as long as it ensures matching types and correct reference
     * counting.
     *
     * @return True if the plugin took charge of the event; in that case
     * it must have assumed ownership of the event and the interpreter will
     * not do anything further with it. False otherwise.
     */
    virtual bool HookQueueEvent(Event* event);

    /**
     * Hook into event queue draining. This method will be called
     * whenever the event manager is draining its queue.
     */
    virtual void HookDrainEvents();

    /**
     * Hook for updates to network time. This method will be called
     * whenever network time is advanced.
     *
     * @param network_time The new network time.
     */
    virtual void HookUpdateNetworkTime(double network_time);

    /**
     * Hook that executes when a connection's initial analyzer tree
     * has been fully set up. The hook can manipulate the tree at this time,
     * for example by adding further analyzers.
     *
     * @param conn The connection.
     */
    virtual void HookSetupAnalyzerTree(Connection* conn);

    /**
     * Hook for destruction of objects registered with
     * RequestObjDtor(). When Zeek's reference counting triggers the
     * objects destructor to run, this method will be run. It may also
     * run for other objects that this plugin has not registered for.
     *
     * @param obj A pointer to the object being destroyed. Note that the
     * object is already considered invalid and the pointer must not be
     * dereferenced.
     */
    virtual void HookObjDtor(void* obj);

    /**
     * Hook into log initialization. This method will be called when a
     * logging writer is created. A writer represents a single logging
     * filter. The method is called in the main thread, on the node that
     * causes a log line to be written. It will _not_ be called on the logger
     * node. The function will be called each for every instantiated writer.
     *
     * @param writer The name of the writer being instantiated.
     *
     * @param instantiating_filter Name of the filter causing the
     *        writer instantiation.
     *
     * @param local True if the filter is logging locally (writer
     *              thread will be located in same process).
     *
     * @param remote True if filter is logging remotely (writer thread
     *               will be located in different thread, typically
     *               in manager or logger node).
     *
     * @param info WriterBackend::WriterInfo with information about the writer.
     *
     * @param num_fields number of fields in the record being written.
     *
     * @param fields threading::Field description of the fields being logged.
     */
    virtual void HookLogInit(const std::string& writer, const std::string& instantiating_filter, bool local,
                             bool remote, const logging::WriterBackend::WriterInfo& info, int num_fields,
                             const threading::Field* const* fields);

    /**
     * Hook into log writing. This method will be called for each log line
     * being written by each writer. Each writer represents a single logging
     * filter. The method is called in the main thread, on the node that
     * causes a log line to be written. It will _not_ be called on the logger
     * node.
     * This function allows plugins to modify or skip logging of information.
     * Note - once a log line is skipped (by returning false), it will not
     * passed on to hooks that have not yet been called.
     *
     * @param writer The name of the writer.
     *
     * @param filter Name of the filter being written to.
     *
     * @param info WriterBackend::WriterInfo with information about the writer.
     *
     * @param num_fields number of fields in the record being written.
     *
     * @param fields threading::Field description of the fields being logged.
     *
     * @param vals threading::Values containing the values being written. Values
     *             can be modified in the Hook.
     *
     * @return true if log line should be written, false if log line should be
     *         skipped and not passed on to the writer.
     */
    virtual bool HookLogWrite(const std::string& writer, const std::string& filter,
                              const logging::WriterBackend::WriterInfo& info, int num_fields,
                              const threading::Field* const* fields, threading::Value** vals);

    /**
     * Hook into reporting. This method will be called for each reporter call
     * made; this includes weirds. The method cannot manipulate the data at
     * the current time; however it is possible to prevent script-side events
     * from being called by returning false.
     *
     * @param prefix The prefix passed by the reporter framework
     *
     * @param event The event to be called
     *
     * @param conn The associated connection
     *
     * @param addl Additional Zeek values; typically will be passed to the event
     *             by the reporter framework.
     *
     * @param location True if event expects location information
     *
     * @param location1 First location
     *
     * @param location2 Second location
     *
     * @param time True if event expects time information
     *
     * @param message Message supplied by the reporter framework
     *
     * @return true if event should be called by the reporter framework, false
     *         if the event call should be skipped
     */
    virtual bool HookReporter(const std::string& prefix, const EventHandlerPtr event, const Connection* conn,
                              const ValPList* addl, bool location, const zeek::detail::Location* location1,
                              const zeek::detail::Location* location2, bool time, const std::string& message);

    /**
     * Hook for packets that are considered unprocessed by an Analyzer. This
     * typically means that a packet has not had a log entry written for it by
     * the time analysis finishes.
     *
     * @param packet The data for an unprocessed packet
     */
    virtual void HookUnprocessedPacket(const Packet* packet);

    /**
     * Hook for intercepting remote event publish operations.
     *
     * This hook is invoked when Cluster::publish(), Cluster::publish_hrw() or
     * Cluster::publish_rr() is used in the scripting layer to publish a remote
     * event to a topic. It is also invoked when calling PublishEvent() on the
     * active cluster backend directly from C++ plugins. This hook can be used
     * for metrics collection, modifying or redirecting events to a different
     * topic. It's event possible to translate an event to another one. A plugin
     * should return false if it took responsibility of publishing the event, or
     * the verdict is to skip the publish operation.
     *
     * @param backend The backend publishing this event
     * @param topic The topic to which to publish this event
     * @param event The event itself
     *
     * @return true if event should be published, false if the publish
     *         operation should be skipped.
     */
    virtual bool HookPublishEvent(zeek::cluster::Backend& backend, const std::string& topic,
                                  zeek::cluster::Event& event);

    // Meta hooks.
    virtual void MetaHookPre(HookType hook, const HookArgumentList& args);
    virtual void MetaHookPost(HookType hook, const HookArgumentList& args, HookArgument result);

private:
    /**
     * A function called when the plugin is instantiated to query basic
     * configuration parameters.
     *
     * The plugin must override this method and return a suitably
     * initialized configuration object.
     *
     * @return A configuration describing the plugin.
     */
    virtual Configuration Configure() = 0;

    /**
     * Initializes the plugin's internal configuration. Called by the
     * manager before anything else.
     */
    void DoConfigure();

    /**
     * Sets the base directory and shared library path from which the
     * plugin was loaded.
     *
     * This is called by the manager.
     *
     * @param dir The plugin directory. The functions makes an internal
     * copy of string.
     *
     * @param sopath The full path the shared library loaded. The
     * functions makes an internal copy of string.
     */
    void SetPluginLocation(const std::string& dir, const std::string& sopath);

    /**
     * Marks the plugin as dynamically loaded.
     *
     * This is called by the manager.
     *
     * @param is_dynamic True if it's a dynamically loaded module.
     */
    void SetDynamic(bool is_dynamic);

    Configuration config;

    std::string base_dir; // The plugin's base directory.
    std::string sopath;   // For dynamic plugins, the full path to the shared library.
    bool dynamic;         // True if a dynamic plugin.

    component_list components; // Components the plugin provides.
    bif_item_list bif_items;   // BiF items the plugin provides.
};

} // namespace plugin
} // namespace zeek
