/************************************************************************
 *   Unreal Internet Relay Chat Daemon, include/modules.h
 *   (C) Carsten V. Munk 2000 <stskeeps@tspre.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *   $Id$
 */
#ifndef MODULES_H
#define MODULES_H
#include "types.h"
#define MAXCUSTOMHOOKS  30
#define MAXHOOKTYPES	200
#define MAXCALLBACKS	30
#define MAXEFUNCTIONS	192
#if defined(_WIN32)
 #define MOD_EXTENSION "dll"
 #define DLLFUNC	_declspec(dllexport)
 #define irc_dlopen(x,y) LoadLibrary(x)
 #define irc_dlclose FreeLibrary
 #define irc_dlsym(x,y,z) z = (void *)GetProcAddress(x,y)
 #define irc_dlerror our_dlerror
#else
 #define MOD_EXTENSION "so"
 #define irc_dlopen dlopen
 #define irc_dlclose dlclose
 #if defined(UNDERSCORE)
  #define irc_dlsym(x,y,z) z = obsd_dlsym(x,y)
 #else
  #define irc_dlsym(x,y,z) z = dlsym(x,y)
 #endif
 #define irc_dlerror dlerror
 #define DLLFUNC 
#endif

#define EVENT(x) void (x) (void *data)

/* Casts to int, void, void *, and char * function pointers */
#define TO_INTFUNC(x) (int (*)())(x)
#define TO_VOIDFUNC(x) (void (*)())(x)
#define TO_PVOIDFUNC(x) (void *(*)())(x)
#define TO_PCHARFUNC(x) (char *(*)())(x)

typedef struct Event Event;
typedef struct EventInfo EventInfo;
typedef struct Hook Hook;
typedef struct Hooktype Hooktype;
typedef struct Callback Callback;
typedef struct Efunction Efunction;
typedef enum EfunctionType EfunctionType;
typedef struct APICallback APICallback;

/*
 * Module header that every module must include, with the name of
 * mod_header
*/

typedef struct ModuleHeader {
	char *name;
	char *version;
	char *description;
	char *author;
	char *modversion;
} ModuleHeader;

typedef struct Module Module;

typedef struct ModuleChild
{
	struct ModuleChild *prev, *next; 
	Module *child; /* Aww. aint it cute? */
} ModuleChild;

typedef struct {
	int size;
	int module_load;
	Module *handle;
} ModuleInfo;


typedef enum ModuleObjectType {
	MOBJ_EVENT = 1,
	MOBJ_HOOK = 2,
	MOBJ_COMMAND = 3,
	MOBJ_HOOKTYPE = 4,
	MOBJ_VERSIONFLAG = 5,
	MOBJ_UMODE = 7,
	MOBJ_COMMANDOVERRIDE = 8,
	MOBJ_EXTBAN = 9,
	MOBJ_CALLBACK = 10,
	MOBJ_ISUPPORT = 11,
	MOBJ_EFUNCTION = 12,
	MOBJ_CMODE = 13,
	MOBJ_MODDATA = 14,
	MOBJ_VALIDATOR = 15,
	MOBJ_CLICAP = 16,
	MOBJ_MTAG = 17,
	MOBJ_HISTORY_BACKEND = 18,
	MOBJ_RPC = 19,
	MOBJ_API_CALLBACK = 20,
} ModuleObjectType;

typedef struct Umode Umode;
struct Umode {
	Umode *prev, *next;
	long mode; /**< Mode mask */
	char letter; /**< Mode character */
	int unset_on_deoper; /**< When set to 1 then this user mode will be unset on de-oper */
	int (*allowed)(Client *client, int what); /**< The 'is this user allowed to set this mode?' routine */
	char unloaded; /**< Internal flag to indicate module is being unloaded */
	Module *owner; /**< Module that owns this user mode */
};

typedef enum ModDataType {
	MODDATATYPE_LOCAL_VARIABLE	= 1,
	MODDATATYPE_GLOBAL_VARIABLE	= 2,
	MODDATATYPE_CLIENT		= 3,
	MODDATATYPE_LOCAL_CLIENT	= 4,
	MODDATATYPE_CHANNEL		= 5,
	MODDATATYPE_MEMBER		= 6,
	MODDATATYPE_MEMBERSHIP		= 7,
} ModDataType;

typedef enum ModDataSync {
	MODDATA_SYNC_NORMAL		= 1, /**< Sync normally via MD command */
	MODDATA_SYNC_EARLY		= 2, /**< Attempt to (also) sync early in the UID command */
} ModDataSync;

typedef struct ModDataInfo ModDataInfo;

struct ModDataInfo {
	ModDataInfo *prev, *next;
	char *name; /**< Name for this moddata */
	Module *owner; /**< Owner of this moddata */
	ModDataType type; /**< Type of module data (eg: for client, channel, etc..) */
	int slot; /**< Assigned slot */
	char unloaded; /**< Module being unloaded? */
	void (*free)(ModData *m); /**< Function will be called when the data needs to be freed (may be NULL if not using dynamic storage) */
	const char *(*serialize)(ModData *m); /**< Function which converts the data to a string. May return NULL if 'm' contains no data (since for example m->ptr may be NULL). */
	void (*unserialize)(const char *str, ModData *m); /**< Function which converts the string back to data */
	ModDataSync sync; /**< Send in netsynch (when servers connect) */
	int remote_write; /**< Allow remote servers to set/unset this moddata, even if it they target one of our own clients */
	int self_write; /**< Allow remote servers to set/unset moddata of their own server object (irc1.example.net writing the MD object of irc1.example.net) */
};

#define moddata_client(acptr, md)    acptr->moddata[md->slot]
#define moddata_local_client(acptr, md)    acptr->local->moddata[md->slot]
#define moddata_channel(channel, md)   channel->moddata[md->slot]
#define moddata_member(m, md)        m->moddata[md->slot]
#define moddata_membership(m, md)    m->moddata[md->slot]
#define moddata_local_variable(md)         local_variable_moddata[md->slot]
#define moddata_global_variable(md)        global_variable_moddata[md->slot]

/* Can bypass message restriction - Types */
typedef enum BypassChannelMessageRestrictionType {
	BYPASS_CHANMSG_EXTERNAL = 1,
	BYPASS_CHANMSG_MODERATED = 2,
	BYPASS_CHANMSG_COLOR = 3,
	BYPASS_CHANMSG_CENSOR = 4,
	BYPASS_CHANMSG_NOTICE = 5,
} BypassChannelMessageRestrictionType;

/** @defgroup ChannelModeAPI Channel mode API
 * @{
 */

#define EXCHK_ACCESS		0 /**< Check user access */
#define EXCHK_ACCESS_ERR	1 /**< Check user access and send error to user */
#define EXCHK_PARAM		2 /**< Check parameter */

/* return values for EXCHK_ACCESS*: */
#define EX_DENY			0  /**< MODE change disallowed, except in case of operoverride */
#define EX_ALLOW		1  /**< MODE change allowed */
#define EX_ALWAYS_DENY		-1 /**< MODE change disallowed, even in case of operoverride */

#define EXSJ_SAME		0 /**< SJOIN: Parameters are the same */
#define EXSJ_WEWON		1 /**< SJOIN: We won! w00t */
#define EXSJ_THEYWON		2 /**< SJOIN: They won :( */
#define EXSJ_MERGE		3 /**< SJOIN: Merging of modes, neither won nor lost */

/** Channel mode bit/value */
typedef unsigned long long Cmode_t;

typedef enum CmodeType {
	CMODE_NORMAL=0,
	CMODE_MEMBER=1,
} CmodeType;

#define RANK_CHANOWNER  4000
#define RANK_CHANADMIN  3000
#define RANK_CHANOP     2000
#define RANK_HALFOP     1000
#define RANK_VOICE        -1

/** Channel mode handler.
 * This struct contains all extended channel mode information,
 * like the flag, mode, their handler functions, etc.
 *
 * @note For a channel mode without parameters you only need to set 'flag'
 * and set the 'is_ok' function. All the rest is for parameter modes
 * or is optional.
 */
typedef struct Cmode Cmode;
struct Cmode {
	Cmode *prev, *next;

	/** mode character (like 'Z') */
	char		letter;

	CmodeType	type;

	/** If type is CMODE_NORMAL, then bitmask (eg: 0x10) that
	 * is used in channel->mode.mode
	 */
	Cmode_t		mode;

	/** If type is CMODE_MEMBER, then the prefix used in NAMES etc (eg @) */
	char		prefix;

	/** If type is CMODE_MEMBER, then the prefix used in SJOIN (eg @) */
	char		sjoin_prefix;

	/** If type is CMODE_MEMBER, then the rank of this prefix.
	 * Higher ranking = more rights.
	 * This is used, for example, in NAMES without NAMESX when we can only
	 * show one symbol but not all.
	 * For the shipped modules vhoaq we use the RANK_* values.
	 */
	int		rank;

	/** Number of parameters (1 or 0) */
	int		paracount;

	/** Check access or parameter of the channel mode.
	 * @param client	The client
	 * @param channel	The channel
	 * @param para		The parameter (NULL for paramless modes)
	 * @param checkt	Check type, see EXCHK_* macros
	 * @param what		MODE_ADD or MODE_DEL
	 * @returns EX_DENY, EX_ALLOW or EX_ALWAYS_DENY
	 */
	int (*is_ok)(Client *client, Channel *channel, char mode, const char *para, int checkt, int what);

	/** Store parameter in memory for channel.
	 * This function pointer is NULL (unused) for modes without parameters.
	 * @param list		The list, this usually points to channel->mode.mode_params.
	 * @param para		The parameter to store.
	 * @returns the head of the list, RTFS if you wonder why.
	 * @note IMPORTANT: only allocate a new paramstruct if you need to.
	 *       Search for any current one first! Eg: in case of mode +y 5 and then +y 6 later without -y.
	 */
	void *(*put_param)(void *list, const char *para);

	/** Get the stored parameter as a readable/printable string.
	 * This function pointer is NULL (unused) for modes without parameters.
	 * @param parastruct	The parameter struct
	 * @returns a pointer to the string (temporary storage)
	 */
	const char *(*get_param)(void *parastruct);

	/** Convert input parameter to output.
	 * This converts stuff like where a MODE +l "1aaa" becomes "1".
	 *
	 * This function pointer is NULL (unused) for modes without parameters.
	 * @param para		The input parameter.
	 * @param client	The client that the mode request came from (can be NULL!)
	 * @param channel	The channel that the mode request came from (can be NULL!)
	 * @returns pointer to output string (temporary storage)
	 * @note The 'client' field will be NULL if for example called for set::modes-on-join.
	 * @note You should probably not use 'client' or 'channel' in most cases.
	 *       In particular you MUST NOT SEND ERRORS to the client.
	 *       This should be done in is_ok() and not in conv_param().
	 */
	const char *(*conv_param)(const char *para, Client *client, Channel *channel);

	/** Free and remove parameter from list.
	 * This function pointer is NULL (unused) for modes without parameters.
	 * @param parastruct	The parameter struct
	 * @param soft		This is set to 1 if you may 'resist freeing'
	 *			(used by floodprot module to have active F profile even if -F).
	 * @returns Normally return 0, must return 1 if it 'resisted' freeing.
	 * @note In most cases you will just call safe_free() on 'list'
	 */
	int (*free_param)(void *parastruct, int soft);

	/** duplicate a struct and return a pointer to duplicate.
	 * This function pointer is NULL (unused) for modes without parameters.
	 * @param parastruct		The parameter struct
	 * @returns pointer to newly allocated struct.
	 * @note In most cases you will simply safe_alloc() and memcpy()
	 */
	void *(*dup_struct)(void *parastruct);

	/** Compares two parameters and decides who wins the SJOIN fight.
	 * When syncing channel modes (cmd_sjoin) a parameter conflict may occur, things like
	 * "+l 5" vs "+l 10". This function should determinate who wins the fight.
	 * This decision should, of course, not be random. It needs to decide according to
	 * the same principles on all servers on the IRC network. Examples of such
	 * comparisons are "highest wins" (+l) and a strcmp() check (+k/+L).
	 * 
	 * This function pointer is NULL (unused) for modes without parameters.
	 * @param channel	The channel that the fight is about
	 * @param our		Our parameter struct
	 * @param their		Their parameter struct
	 */
	int (*sjoin_check)(Channel *channel, void *our, void *their);

	/** Local channel mode? Prevents remote servers from setting/unsetting this */
	char local;
	
	/** Unsetting also eats/requires a parameter. Unusual, but possible. */
	char unset_with_param;

	/** Is this mode available for chanmode +f, and if so for which flood type?
	 * eg 'j' for join flood.
	 */
	char flood_type_action;

	/** Is this mode being unloaded?
	 * This is set to 1 if the chanmode module providing this mode is unloaded
	 * and we are waiting to see if in our new round of loads a "new" chanmode
	 * module will popup to take this mode. This only happens during a rehash,
	 * should never be 0 outside an internal rehash.
	 */
	char unloaded;

	/** Slot number - Can be used instead of GETPARAMSLOT() */
	int param_slot;
	
	/** Module owner */
        Module *owner;
};

/** The struct used to register a channel mode handler.
 * For documentation, see Cmode struct.
 */
typedef struct {
	char		letter;
	CmodeType	type;
	char		prefix;
	char		sjoin_prefix;
	int		rank;
	int		paracount;
	int		(*is_ok)(Client *,Channel *, char mode, const char *para, int, int);
	void *		(*put_param)(void *, const char *);
	const char *	(*get_param)(void *);
	const char *	(*conv_param)(const char *, Client *, Channel *);
	int		(*free_param)(void *, int);
	void *		(*dup_struct)(void *);
	int		(*sjoin_check)(Channel *, void *, void *);
	char		local;
	char		unset_with_param;
	char		flood_type_action;
} CmodeInfo;

/** Get a slot number for a param - eg GETPARAMSLOT('k') */
#define GETPARAMSLOT(x)	param_to_slot_mapping[x]

/** Get a cmode handler by slot - for example for [dont use this]: GETPARAMHANDLERBYSLOT(5)->get_param(channel) */
#define GETPARAMHANDLERBYSLOT(slotid)	ParamTable[slotid]

/** Same as GETPARAMHANDLERBYSLOT but then by letter - like [dont use this]: GETPARAMHANDLERBYSLOT('k')->get_param(channel) */
#define GETPARAMHANDLERBYLETTER(x)	ParamTable[GETPARAMSLOT(x)]

/** Get paramter data struct - for like: ((aModejEntry *)GETPARASTRUCT(channel, 'j'))->t */
#define GETPARASTRUCT(mychannel, mychar)	channel->mode.mode_params[GETPARAMSLOT(mychar)]

#define GETPARASTRUCTEX(v, mychar)	v[GETPARAMSLOT(mychar)]

/** @} */

#define CMP_GETSLOT(x) GETPARAMSLOT(x)
#define CMP_GETHANDLERBYSLOT(x) GETPARAMHANDLERBYSLOT(x)
#define CMP_GETHANDLERBYLETTER(x) GETPARAMHANDLERBYLETTER(x)
#define CMP_GETSTRUCT(x,y) GETPARASTRUCT(x,y)

/*** Extended bans ***/

typedef enum ExtbanCheck {
	EXBCHK_ACCESS=0,	/**< Check access */
	EXBCHK_ACCESS_ERR=1,	/**< Check access and send error */
	EXBCHK_PARAM=2		/**< Check if the parameter is valid */
} ExtbanCheck;

typedef enum ExtbanType {
	EXBTYPE_BAN=0,		/**< Ban (channel mode +b) */
	EXBTYPE_EXCEPT=1,	/**< Ban exception (channel mode +e) */
	EXBTYPE_INVEX=2,	/**< Invite exception (channel mode +I) */
	EXBTYPE_TKL=3		/**< TKL or other generic matcher outside banning routines */
} ExtbanType;

#define BCTX_CONV_OPTION_WRITE_LETTER_BANS	1 /* Always write letter extbans in output of conv_param */

#define EXTBANTABLESZ		32

typedef enum ExtbanOptions {
        EXTBOPT_CHSVSMODE=0x1,		/**< SVSMODE -b/-e/-I will clear this ban (UNUSED as of 6.0.1+) */
        EXTBOPT_ACTMODIFIER=0x2,	/**< Action modifier (not a matcher). These are extended bans like ~q/~n/~j. */
        EXTBOPT_NOSTACKCHILD=0x4,	/**< Disallow prefixing with another extban. Eg disallow ~n:~T:censor:xyz */
        EXTBOPT_INVEX=0x8,		/**< Available for use with +I too */
        EXTBOPT_TKL=0x10		/**< Available for use in TKL's too (eg: /GLINE ~a:account) */
} ExtbanOptions;

typedef struct {
	Client *client;		/**< Client to check, can be a remote client */
	Channel *channel;	/**< Channel to check */
	const char *banstr;	/**< Mask string (ban) */
	int ban_check_types;	/**< Ban types to check for, one or more of BANCHK_* OR'd together */
	const char *msg;	/**< Message, only for some BANCHK_* types (for censoring text) */
	const char *error_msg;	/**< Error message, can be NULL */
	int no_extbans;		/**< Set to 1 to disable extended bans checking - only nick!user@host allowed */
	int what;		/**< MODE_ADD or MODE_DEL (for is_ok) */
	ExtbanType ban_type;	/**< EXBTYPE_BAN or EXBTYPE_EXCEPT (for is_ok) */
	ExtbanCheck is_ok_check;/**< One of EXBCHK_* (for is_ok) */
	int conv_options;	/**< One of BCTX_CONV_OPTION_* (for conv_param) */
} BanContext;

typedef struct Extban Extban;

struct Extban {
	Extban *prev, *next;

	/** extended ban character */
	char letter;

	/** extended ban name */
	char *name;

	/** extban options */
	ExtbanOptions options;

	unsigned int is_banned_events;	/**< Which BANCHK_* events to listen on */

	int (*is_ok)(BanContext *b);

	/** Convert input parameter to output.
	 * like with normal bans '+b blah' gets '+b blah!*@*', and it allows
	 * you to limit the length of the ban too.
	 * return value: pointer to output string (temp. storage)
	 */
	const char *(*conv_param)(BanContext *b, Extban *handler);

	/** Checks if the user is affected by this ban [optional].
	 * This may be set to NULL if you have is_banned_events set to 0 (zero),
	 * this can be useful if you don't actually ban a user, eg for text bans.
	 * This function is called from is_banned() and two other places.
	 */
	int (*is_banned)(BanContext *b);

	/** extbans module */
	Module *owner;

	/** Set to 1 during rehash when module is unloading (which may be re-used, and then set to 0) */
	char unloaded;

	/** Set to 1 when it is preregistered in MOD_TEST already */
	char preregistered;
};

typedef struct {
	char letter;
	char *name;
	ExtbanOptions options;
	int (*is_ok)(BanContext *b);
	const char *(*conv_param)(BanContext *b, Extban *handler);
	int (*is_banned)(BanContext *b);
	unsigned int is_banned_events;
} ExtbanInfo;


typedef struct Command Command;
struct Command {
	Command *prev, *next;
	RealCommand *cmd;
};

typedef struct Versionflag Versionflag;
struct Versionflag {
	Versionflag *prev, *next;
	char flag;
	ModuleChild *parents;
};

/* This type needs a forward declaration: */
typedef struct MessageTagHandler MessageTagHandler;

#define CLICAP_FLAGS_NONE               0x0
#define CLICAP_FLAGS_AFFECTS_MTAGS	0x1	/**< Setting this clientcapability can have an effect on which message tags are displayed or their content */
#define CLICAP_FLAGS_ADVERTISE_ONLY     0x4

typedef struct ClientCapability ClientCapability;
struct ClientCapability {
	ClientCapability *prev, *next;
	char *name;                              /**< The name of the CAP */
	long cap;                                /**< The acptr->user->proto we should set (if any, can be 0, like for sts) */
	int flags;                               /**< A flag from CLICAP_FLAGS_* */
	int (*visible)(Client *);                /**< Should the capability be visible? Note: parameter may be NULL. [optional] */
	const char *(*parameter)(Client *);      /**< CAP parameters. Note: parameter may be NULL. [optional] */
	MessageTagHandler *mtag_handler;         /**< For reverse dependency */
	Module *owner;                           /**< Module introducing this CAP. */
	char unloaded;                           /**< Internal flag to indicate module is being unloaded */
};

typedef struct {
	char *name;
	int flags;
	int (*visible)(Client *);
	const char *(*parameter)(Client *);
} ClientCapabilityInfo;

/** @defgroup MessagetagAPI Message tag API
 * @{
 */

/** No special message-tag handler flags */
#define MTAG_HANDLER_FLAGS_NONE			0x0
/** This message-tag does not have a CAP REQ xx (eg: for "msgid") */
#define MTAG_HANDLER_FLAGS_NO_CAP_NEEDED	0x1

/** Message Tag Handler */
struct MessageTagHandler {
	MessageTagHandler *prev, *next;
	char *name;                                             /**< The name of the message-tag */
	int flags;                                              /**< A flag of MTAG_HANDLER_FLAGS_* */
	int (*is_ok)(Client *, const char *, const char *);     /**< Verify syntax and access rights */
	int (*should_send_to_client)(Client *);                 /**< Tag may be sent to this client (normally NULL!) */
	Module *owner;                                          /**< Module introducing this CAP. */
	ClientCapability *clicap_handler;                       /**< Client capability handler associated with this */
	char unloaded;                                          /**< Internal flag to indicate module is being unloaded */
};

/** The struct used to register a message tag handler.
 * For documentation, see the MessageTagHandler struct.
 */
typedef struct {
	char *name;
	int flags;
	int (*is_ok)(Client *, const char *, const char *);
	int (*should_send_to_client)(Client *);
	ClientCapability *clicap_handler;
} MessageTagHandlerInfo;

/** @} */

/** Filter for history: the command / type of the request */
typedef enum HistoryFilterCommand {
        HFC_SIMPLE=1,		/**< Simple history request for lines / unixtime */
        HFC_BEFORE=2,		/**< CHATHISTORY BEFORE */
        HFC_AFTER=3,		/**< CHATHISTORY AFTER */
        HFC_LATEST=4,		/**< CHATHISTORY LATEST */
        HFC_AROUND=5,		/**< CHATHISTORY AROUND */
        HFC_BETWEEN=6		/**< CHATHISTORY BETWEEN */
} HistoryFilterCommand;

/** Filter for history get requests */
typedef struct HistoryFilter HistoryFilter;
struct HistoryFilter {
        HistoryFilterCommand cmd;	/**< Filter command, one of HistoryFilterCommand */
        int last_lines;			/**< Used by HFC_SIMPLE */
        int last_seconds;		/**< Used by HFC_SIMPLE */
        char *timestamp_a;		/**< First parameter of HFC_* (either this or msgid_a) */
        char *msgid_a;			/**< First parameter of HFC_* (either this or timestamp_a) */
        char *timestamp_b;		/**< Second parameter of HFC_BETWEEN (either this or msgid_b) */
        char *msgid_b;			/**< Second parameter of HFC_BETWEEN (either this or timestamp_b) */
        char *account;			/**< (For deletion only) if not NULL, reject deletion of messages not from this account */
        int limit;			/**< Maximum number of lines to return */
};

/** History log lines, used by HistoryResult among others */
typedef struct HistoryLogLine HistoryLogLine;
struct HistoryLogLine {
	HistoryLogLine *prev, *next;
	time_t t;		/**< Rounded time on seconds, for quick access. */
	char *msgid;		/**< Pointer to 'msgid' mtag. Do NOT free this, it is freed by freeing 'mtags'. */
	char *time;		/**< Pointer to 'time' mtag. Do NOT free this, it is freed by freeing 'mtags'. */
	MessageTag *mtags;	/**< Message tags associated with this message */
	char line[1];		/**< The full (old-skool) IRC protocol line */
};

typedef struct HistoryResult HistoryResult;
struct HistoryResult {
        char *object;					/**< Name of the history object, eg '#test' */
        HistoryLogLine *log;				/**< The resulting log lines */
        HistoryLogLine *log_tail;			/**< Last entry in the log lines */
};

/** History Backend */
typedef struct HistoryBackend HistoryBackend;
struct HistoryBackend {
	HistoryBackend *prev, *next;
	char *name;                                   /**< The name of the history backend (eg: "mem") */
	int (*history_set_limit)(const char *object, int max_lines, long max_time); /**< Impose a limit on a history object */
	int (*history_add)(const char *object, MessageTag *mtags, const char *line); /**< Add to history */
	HistoryResult *(*history_request)(const char *object, HistoryFilter *filter);  /**< Request history */
	int (*history_delete)(const char *object, HistoryFilter *filter, int *rejected_deletes);  /**< Delete lines from the history. Returns the number of matching lines and sets rejected_deletes if not NULL */
	int (*history_destroy)(const char *object);  /**< Destroy history of this object completely */
	Module *owner;                                /**< Module introducing this */
	char unloaded;                                /**< Internal flag to indicate module is being unloaded */
};

/** The struct used to register a history backend.
 * For documentation, see the History Backend struct above.
 */
typedef struct {
	char *name;
	int (*history_set_limit)(const char *object, int max_lines, long max_time);
	int (*history_add)(const char *object, MessageTag *mtags, const char *line);
	HistoryResult *(*history_request)(const char *object, HistoryFilter *filter);
	int (*history_delete)(const char *object, HistoryFilter *filter, int *rejected_deletes);
	int (*history_destroy)(const char *object);
} HistoryBackendInfo;

/** @defgroup RPCAPI RPC API
 * @{
 */

/** No special flags set */
#define RPC_HANDLER_FLAGS_NONE			0x0
#define RPC_HANDLER_FLAGS_UNFILTERED		0x1	/**< Don't filter input (don't reject strings bigger than 510 in length or containing \r or \n) */

/** RPC Tag Handler */
typedef struct RPCHandler RPCHandler;
struct RPCHandler {
	RPCHandler *prev, *next;
	char *method;                                             /**< Name of the method handler, eg "client.get" */
	int flags;                                                /**< A flag of RPC_HANDLER_FLAG_* */
	LogLevel loglevel;                                        /**< Log level to use for this call: for example ULOG_DEBUG for .list calls, leave 0 for default */
	void (*call)(Client *, json_t *request, json_t *params);  /**< RPC call: use RPC_CALL_FUNC() ! */
	Module *owner;                                            /**< Module introducing this. */
	char unloaded;                                            /**< Internal flag to indicate module is being unloaded */
};

/** The struct used to register a RPC handler.
 * For documentation, see the RPCHandler struct.
 */
typedef struct {
	char *method;
	int flags;
	LogLevel loglevel;
	void (*call)(Client *, json_t *request, json_t *params);
} RPCHandlerInfo;

/** RPC function - used by all RPC call functions.
 * This is used in the code like <pre>RPC_CALL_FUNC(rpc_call_xyz)</pre> as a function definition.
 * It allows the UnrealIRCd devs to add or change parameters to the function without
 * (necessarily) breaking your code.
 * @param client      The client issueing the request
 * @param request     The full JSON-RPC request
 * @param params      Parameters of the JSON-RPC call
 * @note You are expected to call rpc_response() or rpc_error() on the request.
 */
#define RPC_CALL_FUNC(x) void (x) (Client *client, json_t *request, json_t *params)

/** @} */

struct Hook {
	Hook *prev, *next;
	int priority;
	int type;
	union {
		int (*intfunc)();
		void (*voidfunc)();
		char *(*stringfunc)();
		const char *(*conststringfunc)();
	} func;
	Module *owner;
};

struct Callback {
	Callback *prev, *next;
	short type;
	union {
		int (*intfunc)();
		void (*voidfunc)();
		void *(*pvoidfunc)();
		char *(*stringfunc)();
		const char *(*conststringfunc)();
	} func;
	Module *owner;
	char willberemoved; /* will be removed on next rehash? (eg the 'old'/'current' one) */
};

/* Definition of an efunction: a MANDATORY Extern Function (in a module),
 * for things like do_join, join_channel, etc.
 * The difference between callbacks and efunctions are:
 * - efunctions are (usually) mandatory, while callbacks can be optional
 * - efunctions are meant for internal usage, so 3rd party modules are
 *   not allowed to add them.
 * - all efunctions are declared as function pointers in modules.c
 */
struct Efunction {
	Efunction *prev, *next;
	short type;
	union {
		int (*intfunc)();
		void (*voidfunc)();
		void *(*pvoidfunc)();
		char *(*stringfunc)();
		const char *(*conststringfunc)();
	} func;
	Module *owner;
	char willberemoved; /* will be removed on next rehash? (eg the 'old'/'current' one) */
};

struct Hooktype {
	short id;
	char *string;
	ModuleChild *parents;
};

typedef struct ISupport ISupport;
struct ISupport {
	ISupport *prev, *next;
	char *token;
	char *value;
	Module *owner;
};

typedef struct ModuleObject {
	struct ModuleObject *prev, *next;
	ModuleObjectType type;
	union {
		Event *event;
		Hook *hook;
		Command *command;
		Hooktype *hooktype;
		Versionflag *versionflag;
		Umode *umode;
		CommandOverride *cmdoverride;
		Extban *extban;
		Callback *callback;
		Efunction *efunction;
		ISupport *isupport;
		Cmode *cmode;
		ModDataInfo *moddata;
		OperClassValidator *validator;
		ClientCapability *clicap;
		MessageTagHandler *mtag;
		HistoryBackend *history_backend;
		RPCHandler *rpc;
		APICallback *apicallback;
	} object;
} ModuleObject;

/*
 * What we use to keep track internally of the modules
*/

#define MODERR_NOERROR 0
#define MODERR_EXISTS  1
#define MODERR_NOSPACE 2
#define MODERR_INVALID 3
#define MODERR_NOTFOUND 4

extern unsigned int ModuleGetError(Module *module);
extern const char *ModuleGetErrorStr(Module *module);
extern unsigned int ModuleGetOptions(Module *module);
extern void ModuleSetOptions(Module *module, unsigned int options, int action);

struct Module
{
	struct Module *prev, *next;
	int priority;
	ModuleHeader    *header; /* The module's header */
#ifdef _WIN32
	HMODULE dll;		/* Return value of LoadLibrary */
#else
	void	*dll;		/* Return value of dlopen */
#endif	
	unsigned char flags;    /* 8-bits for flags .. [<- this is misleading, there's mod->flags = .. everywhere] */
	ModuleChild *children;
	ModuleObject *objects;
	ModuleInfo modinfo; /* Used to store handle info for module */
	unsigned char options;
	unsigned char errorcode;
	char *tmp_file;
	char *relpath;
	unsigned long mod_sys_version;
	unsigned int compiler_version;
};
/*
 * Symbol table
*/

#define MOD_OPT_PERM		0x0001 /* Permanent module (not unloadable) */
#define MOD_OPT_OFFICIAL	0x0002 /* Official module, do not set "tainted" */
#define MOD_OPT_PERM_RELOADABLE	0x0004 /* Module is semi-permanent: it can be re-loaded but not un-loaded */
#define MOD_OPT_GLOBAL		0x0008 /* Module is required to be loaded globally (i.e. across the entire network) */
#define MOD_OPT_PRIORITY	0x1000 /* Module wants a higher or lower priority for unloading, init, load, etc */
#define MOD_OPT_UNLOAD_PRIORITY	0x1000 /* Alias for MOD_OPT_PRIORITY */
#define MOD_Dep(name, container,module) {#name, (vFP *) &container, module}

/** Websocket module should init 'first' because it handles sockets */
#define WEBSOCKET_MODULE_PRIORITY_INIT		-1000000000
/** Websocket module should unload 'last' because it handles sockets */
#define WEBSOCKET_MODULE_PRIORITY_UNLOAD	1000000000

/** Event structs */
struct Event {
	Event		*prev;		/**< Previous event (linked list) */
	Event		*next;		/**< Next event (linked list) */
	char		*name;		/**< Name of the event */
	long		every_msec;	/**< How often we should run this event */
	long		count;		/**< How many times this event should run (0 = infinite) */
	vFP		event;		/**< Actual function to call */
	void		*data;		/**< The data to pass in the function call */
	struct timeval	last_run;	/**< Last time this event ran */
	char		deleted;	/**< Set to 1 if this event is marked for deletion */
	Module		*owner;		/**< To which module this event belongs */
};

#define EMOD_EVERY 0x0001
#define EMOD_HOWMANY 0x0002
#define EMOD_NAME 0x0004
#define EMOD_EVENT 0x0008
#define EMOD_DATA 0x0010

/** event struct information, for EventMod() only - see Event for documentation */
struct EventInfo {
	int flags;
	long count;
	time_t every_msec;
	char *name;
	vFP event;
	void *data;
};

typedef enum APICallbackType {
	API_CALLBACK_WEB_RESPONSE = 1,
	API_CALLBACK_RESOLVER_HOST = 11,
} APICallbackType;

struct APICallback {
	APICallback *prev, *next;
	char *name; /**< Name of the api callback */
	Module *owner; /**< To which module this object belongs */
	char unloaded; /**< Set to 1 if this object is marked for deletion */
	APICallbackType callback_type;
	union {
		void (*web_response)(OutgoingWebRequest *request, OutgoingWebResponse *response);
		ares_host_callback resolver_host;
	} callback; /**< The callback itself, obviously chosen by .callback_type */
};

extern MODVAR Hook		*Hooks[MAXHOOKTYPES];
extern MODVAR Hooktype		Hooktypes[MAXCUSTOMHOOKS];
extern MODVAR Callback *Callbacks[MAXCALLBACKS], *RCallbacks[MAXCALLBACKS];
extern MODVAR ClientCapability *clicaps;
extern MODVAR long clicaps_affecting_mtag;

extern Event *EventAdd(Module *module, const char *name, vFP event, void *data, long every_msec, int count);
extern void   EventDel(Event *event);
extern Event *EventMarkDel(Event *event);
extern Event *EventFind(const char *name);
extern int EventMod(Event *event, EventInfo *mods);
extern void DoEvents(void);
extern void EventStatus(Client *client);
extern void SetupEvents(void);


extern void Module_Init(void);
extern const char *Module_Create(const char *path);
extern const char *Module_TransformPath(const char *path_);
extern void Init_all_testing_modules(void);
extern void Unload_all_loaded_modules(void);
extern void Unload_all_testing_modules(void);
extern int Module_Unload(const char *name);
extern vFP Module_Sym(const char *name);
extern vFP Module_SymX(const char *name, Module **mptr);
extern int Module_free(Module *mod);
#ifdef __OpenBSD__
extern void *obsd_dlsym(void *handle, const char *symbol);
#endif
#ifdef _WIN32
extern const char *our_dlerror(void);
#endif

extern Versionflag *VersionflagAdd(Module *module, char flag);
extern void VersionflagDel(Versionflag *vflag, Module *module);

extern ISupport *ISupportAdd(Module *module, const char *token, const char *value);
extern void ISupportSetValue(ISupport *isupport, const char *value);
extern void ISupportDel(ISupport *isupport);
extern ISupport *ISupportFind(const char *token);
extern void ISupportSet(Module *module, const char *name, const char *value);
extern void ISupportSetFmt(Module *module, const char *name, FORMAT_STRING(const char *pattern), ...) __attribute__((format(printf,3,4)));
extern void ISupportDelByName(const char *name);

extern ClientCapability *ClientCapabilityFind(const char *token, Client *client);
extern ClientCapability *ClientCapabilityFindReal(const char *token);
extern ClientCapability *ClientCapabilityAdd(Module *module, ClientCapabilityInfo *clicap_request, long *cap);
extern void ClientCapabilityDel(ClientCapability *clicap);

extern MessageTagHandler *MessageTagHandlerFind(const char *token);
extern MessageTagHandler *MessageTagHandlerAdd(Module *module, MessageTagHandlerInfo *mreq);
extern void MessageTagHandlerDel(MessageTagHandler *m);

extern HistoryBackend *HistoryBackendFind(const char *name);
extern HistoryBackend *HistoryBackendAdd(Module *module, HistoryBackendInfo *mreq);
extern void HistoryBackendDel(HistoryBackend *m);

extern RPCHandler *RPCHandlerFind(const char *method);
extern RPCHandler *RPCHandlerAdd(Module *module, RPCHandlerInfo *mreq);
extern void RPCHandlerDel(RPCHandler *m);

#ifndef GCC_TYPECHECKING
#define HookAdd(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, func, NULL, NULL, NULL)
#define HookAddVoid(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, func, NULL, NULL)
#define HookAddString(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, NULL, func, NULL)
#define HookAddConstString(module, hooktype, priority, func) HookAddMain(module, hooktype, priority, NULL, NULL, NULL, func)
#else
#define HookAdd(module, hooktype, priority, func) \
__extension__ ({ \
	ValidateHooks(hooktype, func); \
    HookAddMain(module, hooktype, priority, func, NULL, NULL, NULL); \
})

#define HookAddVoid(module, hooktype, priority, func) \
__extension__ ({ \
	ValidateHooks(hooktype, func); \
    HookAddMain(module, hooktype, priority, NULL, func, NULL, NULL); \
})

#define HookAddString(module, hooktype, priority, func) \
__extension__ ({ \
	ValidateHooks(hooktype, func); \
    HookAddMain(module, hooktype, priority, NULL, NULL, func, NULL); \
})
#define HookAddConstString(module, hooktype, priority, func) \
__extension__ ({ \
	ValidateHooks(hooktype, func); \
    HookAddMain(module, hooktype, priority, NULL, NULL, NULL, func); \
})
#endif /* GCC_TYPCHECKING */

extern Hook	*HookAddMain(Module *module, int hooktype, int priority, int (*intfunc)(), void (*voidfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)());
extern Hook	*HookDel(Hook *hook);

extern Hooktype *HooktypeAdd(Module *module, const char *string, int *type);
extern void HooktypeDel(Hooktype *hooktype, Module *module);

#define RunHook(hooktype,...) do { Hook *h; for (h = Hooks[hooktype]; h; h = h->next) (*(h->func.intfunc))(__VA_ARGS__); } while(0)
#define RunHookReturn(hooktype,retchk,...) \
{ \
 int retval; \
 Hook *h; \
 for (h = Hooks[hooktype]; h; h = h->next) \
 { \
  retval = (*(h->func.intfunc))(__VA_ARGS__); \
  if (retval retchk) return; \
 } \
}
#define RunHookReturnInt(hooktype,retchk,...) \
{ \
 int retval; \
 Hook *h; \
 for (h = Hooks[hooktype]; h; h = h->next) \
 { \
  retval = (*(h->func.intfunc))(__VA_ARGS__); \
  if (retval retchk) return retval; \
 } \
}

#define CallbackAdd(module, cbtype, func) CallbackAddMain(module, cbtype, func, NULL, NULL, NULL, NULL)
#define CallbackAddVoid(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, func, NULL, NULL, NULL)
#define CallbackAddPVoid(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, NULL, func, NULL, NULL)
#define CallbackAddString(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, NULL, NULL, func, NULL)
#define CallbackAddConstString(module, cbtype, func) CallbackAddMain(module, cbtype, NULL, NULL, NULL, NULL, func)

extern Callback *CallbackAddMain(Module *module, int cbtype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)());
extern Callback	*CallbackDel(Callback *cb);

#define EfunctionAdd(module, cbtype, func) EfunctionAddMain(module, cbtype, func, NULL, NULL, NULL, NULL)
#define EfunctionAddVoid(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, func, NULL, NULL, NULL)
#define EfunctionAddPVoid(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, func, NULL, NULL)
#define EfunctionAddString(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, NULL, func, NULL)
#define EfunctionAddConstString(module, cbtype, func) EfunctionAddMain(module, cbtype, NULL, NULL, NULL, NULL, func)

extern Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*intfunc)(), void (*voidfunc)(), void *(*pvoidfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)());
extern Efunction *EfunctionDel(Efunction *cb);

extern Command *CommandAdd(Module *module, const char *cmd, CmdFunc func, unsigned char params, int flags);
extern Command *AliasAdd(Module *module, const char *cmd, AliasCmdFunc aliasfunc, unsigned char params, int flags);
extern void CommandDel(Command *command);
extern void CommandDelX(Command *command, RealCommand *cmd);
extern int CommandExists(const char *name);
extern CommandOverride *CommandOverrideAdd(Module *module, const char *name, int priority, OverrideCmdFunc func);
extern void CommandOverrideDel(CommandOverride *ovr);
extern void CallCommandOverride(CommandOverride *ovr, Client *client, MessageTag *mtags, int parc, const char *parv[]);
/** Call next command override function - easy way to do it.
 * This way you don't have to call CallCommandOverride() with the right arguments.
 * Which is nice because command (override) arguments may change in future UnrealIRCd versions.
 */
#define CALL_NEXT_COMMAND_OVERRIDE()	CallCommandOverride(ovr, client, recv_mtags, parc, parv)

extern void moddata_free_client(Client *acptr);
extern void moddata_free_local_client(Client *acptr);
extern void moddata_free_channel(Channel *channel);
extern void moddata_free_member(Member *m);
extern void moddata_free_membership(Membership *m);
extern ModDataInfo *findmoddata_byname(const char *name, ModDataType type);
extern int moddata_client_set(Client *acptr, const char *varname, const char *value);
extern const char *moddata_client_get(Client *acptr, const char *varname);
extern ModData *moddata_client_get_raw(Client *client, const char *varname);
extern int moddata_local_client_set(Client *acptr, const char *varname, const char *value);
extern const char *moddata_local_client_get(Client *acptr, const char *varname);

extern void LoadPersistentPointerX(ModuleInfo *modinfo, const char *varshortname, void **var, void (*free_variable)(ModData *m));
#define LoadPersistentPointer(modinfo, var, free_variable) LoadPersistentPointerX(modinfo, #var, (void **)&var, free_variable)
extern void SavePersistentPointerX(ModuleInfo *modinfo, const char *varshortname, void *var);
#define SavePersistentPointer(modinfo, var) SavePersistentPointerX(modinfo, #var, var)

extern void LoadPersistentIntX(ModuleInfo *modinfo, const char *varshortname, int *var);
#define LoadPersistentInt(modinfo, var) LoadPersistentIntX(modinfo, #var, &var)
extern void SavePersistentIntX(ModuleInfo *modinfo, const char *varshortname, int var);
#define SavePersistentInt(modinfo, var) SavePersistentIntX(modinfo, #var, var)

extern void LoadPersistentLongX(ModuleInfo *modinfo, const char *varshortname, long *var);
#define LoadPersistentLong(modinfo, var) LoadPersistentLongX(modinfo, #var, &var)
extern void SavePersistentLongX(ModuleInfo *modinfo, const char *varshortname, long var);
#define SavePersistentLong(modinfo, var) SavePersistentLongX(modinfo, #var, var)

extern void LoadPersistentLongLongX(ModuleInfo *modinfo, const char *varshortname, long long *var);
#define LoadPersistentLongLong(modinfo, var) LoadPersistentLongLongX(modinfo, #var, &var)
extern void SavePersistentLongLongX(ModuleInfo *modinfo, const char *varshortname, long long var);
#define SavePersistentLongLong(modinfo, var) SavePersistentLongLongX(modinfo, #var, var)

extern APICallback *APICallbackFind(const char *method, APICallbackType callback_type);
extern void APICallbackDel(APICallback *m);
extern APICallback *APICallbackAdd(Module *module, APICallback *mreq);

#define RegisterApiCallback(modhandle, api_callback_type, api_callback_function, api_name, api_func) \
	do { \
		APICallback req; \
		memset(&req, 0, sizeof(req)); \
		req.name = api_name; \
		req.callback_type = api_callback_type; \
		req.callback.api_callback_function = api_func; \
		APICallbackAdd(modhandle, &req); \
	} while(0)

#define RegisterApiCallbackWebResponse(modhandle, api_name, api_func) \
	RegisterApiCallback(modhandle, API_CALLBACK_WEB_RESPONSE, web_response, api_name, api_func)

#define RegisterApiCallbackResolverHost(modhandle, api_name, api_func) \
	RegisterApiCallback(modhandle, API_CALLBACK_RESOLVER_HOST, resolver_host, api_name, api_func)

/** Hooks trigger on "events", such as a new user connecting or joining a channel,
 * see https://www.unrealircd.org/docs/Dev:Hook_API for background info.
 * You are suggested to use CTRL+F on this page to search for any useful hook,
 * see also the example session on how to find and use a hook at
 * https://www.unrealircd.org/docs/Dev:Hook_API#Example_session_finding_and_using_a_hook
 *
 * @defgroup HookAPI Hook API
 * @{
 */

/* Hook types */
/** See hooktype_pre_local_connect() */
#define HOOKTYPE_PRE_LOCAL_CONNECT	1
/** See hooktype_local_connect() */
#define HOOKTYPE_LOCAL_CONNECT	2
/** See hooktype_remote_connect() */
#define HOOKTYPE_REMOTE_CONNECT	3
/** See hooktype_pre_local_quit() */
#define HOOKTYPE_PRE_LOCAL_QUIT	4
/** See hooktype_local_quit() */
#define HOOKTYPE_LOCAL_QUIT	5
/** See hooktype_remote_quit() */
#define HOOKTYPE_REMOTE_QUIT	6
/** See hooktype_unkuser_quit() */
#define HOOKTYPE_UNKUSER_QUIT	7
/** See hooktype_server_connect() */
#define HOOKTYPE_SERVER_CONNECT	8
/** See hooktype_server_handshake_out() */
#define HOOKTYPE_SERVER_HANDSHAKE_OUT	9
/** See hooktype_server_sync() */
#define HOOKTYPE_SERVER_SYNC	10
/** See hooktype_post_server_connect() */
#define HOOKTYPE_POST_SERVER_CONNECT	11
/** See hooktype_server_synced() */
#define HOOKTYPE_SERVER_SYNCED	12
/** See hooktype_server_quit() */
#define HOOKTYPE_SERVER_QUIT	13
/** See hooktype_local_nickchange() */
#define HOOKTYPE_LOCAL_NICKCHANGE	14
/** See hooktype_remote_nickchange() */
#define HOOKTYPE_REMOTE_NICKCHANGE	15
/** See hooktype_can_join() */
#define HOOKTYPE_CAN_JOIN	16
/** See hooktype_pre_local_join() */
#define HOOKTYPE_PRE_LOCAL_JOIN	17
/** See hooktype_local_join() */
#define HOOKTYPE_LOCAL_JOIN	18
/** See hooktype_remote_join() */
#define HOOKTYPE_REMOTE_JOIN	19
/** See hooktype_pre_local_part() */
#define HOOKTYPE_PRE_LOCAL_PART	20
/** See hooktype_local_part() */
#define HOOKTYPE_LOCAL_PART	21
/** See hooktype_remote_part() */
#define HOOKTYPE_REMOTE_PART	22
/** See hooktype_pre_local_kick() */
#define HOOKTYPE_PRE_LOCAL_KICK	23
/** See hooktype_can_kick() */
#define HOOKTYPE_CAN_KICK	24
/** See hooktype_local_kick() */
#define HOOKTYPE_LOCAL_KICK	25
/** See hooktype_remote_kick() */
#define HOOKTYPE_REMOTE_KICK	26
/** See hooktype_pre_chanmsg() */
#define HOOKTYPE_PRE_CHANMSG	28
/** See hooktype_can_send_to_user() */
#define HOOKTYPE_CAN_SEND_TO_USER	29
/** See hooktype_can_send_to_channel() */
#define HOOKTYPE_CAN_SEND_TO_CHANNEL	30
/** See hooktype_usermsg() */
#define HOOKTYPE_USERMSG	31
/** See hooktype_chanmsg() */
#define HOOKTYPE_CHANMSG	32
/** See hooktype_pre_local_topic() */
#define HOOKTYPE_PRE_LOCAL_TOPIC	33
/** See hooktype_topic() */
#define HOOKTYPE_TOPIC	35
/** See hooktype_pre_local_chanmode() */
#define HOOKTYPE_PRE_LOCAL_CHANMODE	36
/** See hooktype_pre_remote_chanmode() */
#define HOOKTYPE_PRE_REMOTE_CHANMODE	37
/** See hooktype_local_chanmode() */
#define HOOKTYPE_LOCAL_CHANMODE	38
/** See hooktype_remote_chanmode() */
#define HOOKTYPE_REMOTE_CHANMODE	39
/** See hooktype_modechar_del() */
#define HOOKTYPE_MODECHAR_DEL	40
/** See hooktype_modechar_add() */
#define HOOKTYPE_MODECHAR_ADD	41
/** See hooktype_away() */
#define HOOKTYPE_AWAY	42
/** See hooktype_pre_invite() */
#define HOOKTYPE_PRE_INVITE	43
/** See hooktype_invite() */
#define HOOKTYPE_INVITE	44
/** See hooktype_pre_knock() */
#define HOOKTYPE_PRE_KNOCK	45
/** See hooktype_knock() */
#define HOOKTYPE_KNOCK	46
/** See hooktype_whois() */
#define HOOKTYPE_WHOIS	47
/** See hooktype_who_status() */
#define HOOKTYPE_WHO_STATUS	48
/** See hooktype_pre_kill() */
#define HOOKTYPE_PRE_KILL	49
/** See hooktype_local_kill() */
#define HOOKTYPE_LOCAL_KILL	50
/** See hooktype_rehashflag() */
#define HOOKTYPE_REHASHFLAG	51
/** See hooktype_configposttest() */
#define HOOKTYPE_CONFIGPOSTTEST	52
/** See hooktype_rehash() */
#define HOOKTYPE_REHASH	53
/** See hooktype_rehash_complete() */
#define HOOKTYPE_REHASH_COMPLETE	54
/** See hooktype_configtest() */
#define HOOKTYPE_CONFIGTEST	55
/** See hooktype_configrun() */
#define HOOKTYPE_CONFIGRUN	56
/** See hooktype_configrun_ex() */
#define HOOKTYPE_CONFIGRUN_EX	57
/** See hooktype_stats() */
#define HOOKTYPE_STATS	58
/** See hooktype_local_oper() */
#define HOOKTYPE_LOCAL_OPER	59
/** See hooktype_local_pass() */
#define HOOKTYPE_LOCAL_PASS	60
/** See hooktype_channel_create() */
#define HOOKTYPE_CHANNEL_CREATE	61
/** See hooktype_channel_destroy() */
#define HOOKTYPE_CHANNEL_DESTROY	62
/** See hooktype_tkl_except() */
#define HOOKTYPE_TKL_EXCEPT	63
/** See hooktype_umode_change() */
#define HOOKTYPE_UMODE_CHANGE	64
/** See hooktype_tkl_add() */
#define HOOKTYPE_TKL_ADD	65
/** See hooktype_tkl_del() */
#define HOOKTYPE_TKL_DEL	66
/** See hooktype_log() */
#define HOOKTYPE_LOG	67
/** See hooktype_local_spamfilter() */
#define HOOKTYPE_LOCAL_SPAMFILTER	68
/** See hooktype_silenced() */
#define HOOKTYPE_SILENCED	69
/** See hooktype_rawpacket_in() */
#define HOOKTYPE_RAWPACKET_IN	70
/** See hooktype_packet() */
#define HOOKTYPE_PACKET	71
/** See hooktype_handshake() */
#define HOOKTYPE_HANDSHAKE	72
/** See hooktype_free_client() */
#define HOOKTYPE_FREE_CLIENT	73
/** See hooktype_free_user() */
#define HOOKTYPE_FREE_USER	74
/** See hooktype_can_join_limitexceeded() */
#define HOOKTYPE_CAN_JOIN_LIMITEXCEEDED	75
/** See hooktype_visible_in_channel() */
#define HOOKTYPE_VISIBLE_IN_CHANNEL	76
/** See hooktype_see_channel_in_whois() */
#define HOOKTYPE_SEE_CHANNEL_IN_WHOIS	77
/** See hooktype_join_data() */
#define HOOKTYPE_JOIN_DATA	78
/** See hooktype_invite_bypass() */
#define HOOKTYPE_INVITE_BYPASS	79
/** See hooktype_view_topic_outside_channel() */
#define HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL	80
/** See hooktype_chan_permit_nick_change() */
#define HOOKTYPE_CHAN_PERMIT_NICK_CHANGE	81
/** See hooktype_is_channel_secure() */
#define HOOKTYPE_IS_CHANNEL_SECURE	82
/** See hooktype_channel_synced() */
#define HOOKTYPE_CHANNEL_SYNCED	83
/** See hooktype_can_sajoin() */
#define HOOKTYPE_CAN_SAJOIN	84
/** See hooktype_mode_deop() */
#define HOOKTYPE_MODE_DEOP	86
/** See hooktype_dcc_denied() */
#define HOOKTYPE_DCC_DENIED	87
/** See hooktype_secure_connect() */
#define HOOKTYPE_SECURE_CONNECT	88
/** See hooktype_can_bypass_channel_message_restriction() */
#define HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION	89
/** See hooktype_sasl_continuation() */
#define HOOKTYPE_SASL_CONTINUATION	91
/** See hooktype_sasl_result() */
#define HOOKTYPE_SASL_RESULT	92
/** See hooktype_take_action() */
#define HOOKTYPE_TAKE_ACTION	93
/** See hooktype_find_tkline_match() */
#define HOOKTYPE_FIND_TKLINE_MATCH	94
/** See hooktype_welcome() */
#define HOOKTYPE_WELCOME	95
/** See hooktype_pre_command() */
#define HOOKTYPE_PRE_COMMAND	96
/** See hooktype_post_command() */
#define HOOKTYPE_POST_COMMAND	97
/** See hooktype_new_message() */
#define HOOKTYPE_NEW_MESSAGE	98
/** See hooktype_is_handshake_finished() */
#define HOOKTYPE_IS_HANDSHAKE_FINISHED	99
/** See hooktype_pre_local_quit_chan() */
#define HOOKTYPE_PRE_LOCAL_QUIT_CHAN	100
/** See hooktype_ident_lookup() */
#define HOOKTYPE_IDENT_LOOKUP	101
/** See hooktype_account_login() */
#define HOOKTYPE_ACCOUNT_LOGIN	102
/** See hooktype_close_connection() */
#define HOOKTYPE_CLOSE_CONNECTION	103
/** See hooktype_connect_extinfo() */
#define HOOKTYPE_CONNECT_EXTINFO	104
/** See hooktype_is_invited() */
#define HOOKTYPE_IS_INVITED	105
/** See hooktype_post_local_nickchange() */
#define HOOKTYPE_POST_LOCAL_NICKCHANGE	106
/** See hooktype_post_remote_nickchange() */
#define HOOKTYPE_POST_REMOTE_NICKCHANGE	107
/** See hooktype_userhost_change() */
#define HOOKTYPE_USERHOST_CHANGE 108
/** See hooktype_realname_change() */
#define HOOKTYPE_REALNAME_CHANGE 109
/** See hooktype_can_set_topic() */
#define HOOKTYPE_CAN_SET_TOPIC	110
/** See hooktype_ip_change() */
#define HOOKTYPE_IP_CHANGE	111
/** See hooktype_json_expand_client() */
#define HOOKTYPE_JSON_EXPAND_CLIENT	112
/** See hooktype_json_expand_client() */
#define HOOKTYPE_JSON_EXPAND_CLIENT_USER	113
/** See hooktype_json_expand_client() */
#define HOOKTYPE_JSON_EXPAND_CLIENT_SERVER	114
/** See hooktype_json_expand_channel() */
#define HOOKTYPE_JSON_EXPAND_CHANNEL	115
/** See hooktype_accept() */
#define HOOKTYPE_ACCEPT		116
/** See hooktype_pre_local_handshake_timeout */
#define HOOKTYPE_PRE_LOCAL_HANDSHAKE_TIMEOUT	117
/** See hooktype_rehash_log */
#define HOOKTYPE_REHASH_LOG	118
/** See hooktype_dns_finished */
#define HOOKTYPE_DNS_FINISHED	119
/** See hooktype_reconfigure_web_listener */
#define HOOKTYPE_CONFIG_LISTENER	120
/** See hooktype_watch_add */
#define HOOKTYPE_WATCH_ADD	121
/** See hooktype_watch_del */
#define HOOKTYPE_WATCH_DEL	122
/** See hooktype_monitor_notification */
#define HOOKTYPE_MONITOR_NOTIFICATION	123
/** See hooktype_sasl_authenticate */
#define HOOKTYPE_SASL_AUTHENTICATE	124
/** See hooktype_sasl_mechs */
#define HOOKTYPE_SASL_MECHS		125

/* Adding a new hook here?
 * 1) Add the #define HOOKTYPE_.... with a new number
 * 2) Add a hook prototype (see below)
 * 3) Add type checking (even more below)
 */

/* Hook prototypes */
/** Called when a local user connects, allows pausing or rejecting the user (function prototype for HOOKTYPE_PRE_LOCAL_CONNECT).
 * @param client		The client
 * @retval HOOK_DENY		Stop the connection (hold/pause it).
 * @retval HOOK_ALLOW		Allow the connection (stop processing other modules)
 * @retval HOOK_CONTINUE	Allow the connection, unless another module blocks it.
 */
int hooktype_pre_local_connect(Client *client);

/** Called when a local user connects (function prototype for HOOKTYPE_LOCAL_CONNECT).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_connect(Client *client);

/** Called when a remote user connects (function prototype for HOOKTYPE_REMOTE_CONNECT).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_remote_connect(Client *client);

/** Called when a local user disconnects, allows changing the quit/disconnect reason (function prototype for HOOKTYPE_PRE_LOCAL_QUIT).
 * @param client		The client
 * @param client		The quit/disconnect reason
 * @return The quit reason (you may also return 'comment' if it should be unchanged) or NULL for an empty reason.
 */
const char *hooktype_pre_local_quit(Client *client, const char *comment);

/** Called when a local user quits or otherwise disconnects (function prototype for HOOKTYPE_PRE_LOCAL_QUIT).
 * @param client		The client
 * @param mtags         	Message tags associated with the quit
 * @param comment       	The quit/exit reason
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_quit(Client *client, MessageTag *mtags, const char *comment);

/** Called when a remote user qutis or otherwise disconnects (function prototype for HOOKTYPE_REMOTE_QUIT).
 * @param client		The client
 * @param mtags         	Message tags associated with the quit
 * @param comment       	The quit/exit reason
 * @return The return value is ignored (use return 0)
 */
int hooktype_remote_quit(Client *client, MessageTag *mtags, const char *comment);

/** Called when an unregistered user disconnects, so before the user was fully online (function prototype for HOOKTYPE_UNKUSER_QUIT).
 * @param client		The client
 * @param mtags         	Message tags associated with the quit
 * @param comment       	The quit/exit reason
 * @return The return value is ignored (use return 0)
 */
int hooktype_unkuser_quit(Client *client, MessageTag *mtags, const char *comment);

/** Called when a local or remote server connects / links in (function prototype for HOOKTYPE_SERVER_CONNECT).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_server_connect(Client *client);

/** Called very early when doing an outgoing server connect (function prototype for HOOKTYPE_SERVER_HANDSHAKE_OUT).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_server_handshake_out(Client *client);

/** Called on new locally connected server, in or out, after all users/channels/TKLs/etc have been synced, but before EOS (function prototype for HOOKTYPE_SERVER_SYNC).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_server_sync(Client *client);

/** Called when a local or remote server connects / links in, but only after EOS (End Of Sync) has been received or sent (function prototype for HOOKTYPE_POST_SERVER_CONNECT).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_post_server_connect(Client *client);

/** Called when a local or remote server is linked in and fully synced, after EOS / End Of Sync (function prototype for HOOKTYPE_SERVER_SYNCED).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_server_synced(Client *client);

/** Called when a local or remote server disconnects (function prototype for HOOKTYPE_SERVER_QUIT).
 * @param client		The client
 * @param mtags         	Message tags associated with the disconnect
 * @return The return value is ignored (use return 0)
 */
int hooktype_server_quit(Client *client, MessageTag *mtags);

/** Called when a local user changes the nick name (function prototype for HOOKTYPE_LOCAL_NICKCHANGE).
 * @param client		The client
 * @param mtags         	Message tags associated with the event
 * @param newnick		The new nick name
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_nickchange(Client *client, MessageTag *mtags, const char *newnick);

/** Called when a remote user changes the nick name (function prototype for HOOKTYPE_REMOTE_NICKCHANGE).
 * @param client		The client
 * @param mtags         	Message tags associated with the event
 * @param newnick		The new nick name
 * @return The return value is ignored (use return 0)
 */
int hooktype_remote_nickchange(Client *client, MessageTag *mtags, const char *newnick);

/** Called when a user wants to join a channel, may the user join? (function prototype for HOOKTYPE_CAN_JOIN).
 * @param client		The client
 * @param channel		The channel the user wants to join
 * @param key			The key supplied by the client
 * @return Return 0 to allow the user, any other value should be an IRC numeric (eg: ERR_BANNEDFROMCHAN).
 */
int hooktype_can_join(Client *client, Channel *channel, const char *key, char **errmsg);

/** Called when a user wants to join a channel (function prototype for HOOKTYPE_PRE_LOCAL_JOIN).
 * IMPORTANT: Generally you want to use HOOKTYPE_CAN_JOIN / hooktype_can_join() instead!!
 * @param client		The client
 * @param channel		The channel the user wants to join
 * @param key			Channel key (can be NULL)
 * @retval HOOK_DENY		Deny the join.
 * @retval HOOK_ALLOW		Allow the join (stop processing other modules)
 * @retval HOOK_CONTINUE	Allow the join, unless another module blocks it.
 */
int hooktype_pre_local_join(Client *client, Channel *channel, const char *key);

/** Called when a local user joins a channel (function prototype for HOOKTYPE_LOCAL_JOIN).
 * @param client		The client
 * @param channel		The channel the user wants to join
 * @param mtags         	Message tags associated with the event
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_join(Client *client, Channel *channel, MessageTag *mtags);

/** Called when a remote user joins a channel (function prototype for HOOKTYPE_REMOTE_JOIN).
 * @param client		The client
 * @param channel		The channel the user wants to join
 * @param mtags         	Message tags associated with the event
 * @return The return value is ignored (use return 0)
 */
int hooktype_remote_join(Client *client, Channel *channel, MessageTag *mtags);

/** Called when a local user wants to part a channel (function prototype for HOOKTYPE_PRE_LOCAL_PART).
 * @param client		The client
 * @param channel		The channel the user wants to part
 * @param comment		The PART reason, this may be NULL.
 * @return The part reason (you may also return 'comment' if it should be unchanged) or NULL for an empty reason.
 */
const char *hooktype_pre_local_part(Client *client, Channel *channel, const char *comment);

/** Called when a local user parts a channel (function prototype for HOOKTYPE_LOCAL_PART).
 * @param client		The client
 * @param channel		The channel the user is leaving
 * @param mtags         	Message tags associated with the event
 * @param comment		The PART reason, this may be NULL.
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment);

/** Called when a remote user parts a channel (function prototype for HOOKTYPE_REMOTE_PART).
 * @param client		The client
 * @param channel		The channel the user is leaving
 * @param mtags         	Message tags associated with the event
 * @param comment		The PART reason, this may be NULL.
 * @return The return value is ignored (use return 0)
 */
int hooktype_remote_part(Client *client, Channel *channel, MessageTag *mtags, const char *comment);

/** Do not use this function, use hooktype_can_kick() instead!
 */
const char *hooktype_pre_local_kick(Client *client, Client *victim, Channel *channel, const char *comment);

/** Called when a local user wants to kick another user from a channel (function prototype for HOOKTYPE_CAN_KICK).
 * @param client		The client issuing the command
 * @param victim		The victim that should be kicked
 * @param channel		The channel the user should be kicked from
 * @param comment		The KICK reason, this may be NULL.
 * @param client_member_modes	The member modes of 'client' (eg "o"), never NULL but can be empty.
 * @param victim_member_modes	The member modes of 'victim' (eg "v"), never NULL but can be empty.
 * @param errmsg		The error message that should be shown to the user (full IRC protocol line).
 * @retval EX_DENY		Deny the KICK (unless IRCOp with sufficient override rights).
 * @retval EX_ALWAYS_DENY	Deny the KICK always (even if IRCOp).
 * @retval EX_ALLOW		Allow the kick, unless another module blocks it.
 */
int hooktype_can_kick(Client *client, Client *victim, Channel *channel, const char *comment, const char *client_member_modes, const char *victim_member_modes, const char **errmsg);

/** Called when a local user is kicked (function prototype for HOOKTYPE_LOCAL_KICK).
 * @param client		The client issuing the command
 * @param victim		The victim that should be kicked
 * @param channel		The channel the user should be kicked from
 * @param mtags         	Message tags associated with the event
 * @param comment		The KICK reason, this may be NULL.
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, const char *comment);

/** Called when a remote user is kicked (function prototype for HOOKTYPE_REMOTE_KICK).
 * @param client		The client issuing the command
 * @param victim		The victim that should be kicked
 * @param channel		The channel the user should be kicked from
 * @param mtags         	Message tags associated with the event
 * @param comment		The KICK reason, this may be NULL.
 * @return The return value is ignored (use return 0)
 */
int hooktype_remote_kick(Client *client, Client *victim, Channel *channel, MessageTag *mtags, const char *comment);

/** Called right before a message is sent to the channel (function prototype for HOOKTYPE_PRE_CHANMSG).
 * This function is only used by delayjoin. It cannot block a message. See hooktype_can_send_to_user() instead!
 * @param client		The client
 * @param channel		The channel
 * @param mtags         	Message tags associated with the event (pointer-to-pointer)
 * @param text			The text that will be sent
 * @return The return value is ignored (use return 0)
 */
int hooktype_pre_chanmsg(Client *client, Channel *channel, MessageTag **mtags, const char *text, SendType sendtype);

/** Called when a user wants to send a message to another user (function prototype for HOOKTYPE_CAN_SEND_TO_USER).
 * @param client		The sender
 * @param target		The recipient
 * @param text			The text to be sent (double pointer!)
 * @param errmsg		The error message. If you block the message (HOOK_DENY) then you MUST set this!
 * @param sendtype		The message type, for example SEND_TYPE_PRIVMSG.
 * @retval HOOK_DENY		Deny the message. The 'errmsg' will be sent to the user.
 * @retval HOOK_CONTINUE	Allow the message, unless other modules block it.
 */
int hooktype_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype);

/** Called when a user wants to send a message to a channel (function prototype for HOOKTYPE_CAN_SEND_TO_CHANNEL).
 * @param client		The sender
 * @param channel		The channel to send to
 * @param member		The membership struct, so you can see for example op status.
 * @param text			The text to be sent (double pointer!)
 * @param errmsg		The error message. If you block the message (HOOK_DENY) then you MUST set this!
 * @param sendtype		The message type, for example SEND_TYPE_PRIVMSG.
 * @retval HOOK_DENY		Deny the message. The 'errmsg' will be sent to the user.
 * @retval HOOK_CONTINUE	Allow the message, unless other modules block it.
 */
int hooktype_can_send_to_channel(Client *client, Channel *channel, Membership *member, const char **text, const char **errmsg, SendType sendtype);

/** Called when a message is sent from one user to another user (function prototype for HOOKTYPE_USERMSG).
 * @param client		The sender
 * @param to			The recipient
 * @param mtags         	Message tags associated with the event
 * @param text			The text
 * @param sendtype		The message type, for example SEND_TYPE_PRIVMSG.
 * @return The return value is ignored (use return 0)
 */
int hooktype_usermsg(Client *client, Client *to, MessageTag *mtags, const char *text, SendType sendtype);

/** Called when a message is sent to a channel (function prototype for HOOKTYPE_CHANMSG).
 * @param client		The sender
 * @param channel		The channel
 * @param sendflags		One of SEND_* (eg SEND_ALL, SKIP_DEAF).
 * @param member_modes		Either NULL, or a member mode like "h", "o", etc.
 * @param target		Target string, usually this is "#channel", but it can also contain prefixes like "@#channel"
 * @param mtags         	Message tags associated with the event
 * @param text			The text
 * @param sendtype		The message type, for example SEND_TYPE_PRIVMSG.
 * @return The return value is ignored (use return 0)
 */
int hooktype_chanmsg(Client *client, Channel *channel, int sendflags, const char *member_modes, const char *target, MessageTag *mtags, const char *text, SendType sendtype);

/** Called when a user wants to set the topic (function prototype for HOOKTYPE_CAN_SET_TOPIC).
 * @param client		The client issuing the command
 * @param channel		The channel the topic should be set for
 * @param topic			The topic that should be set, this may be NULL for unset.
 * @param errmsg		The error message that should be shown to the user (full IRC protocol line).
 * @retval EX_DENY		Deny the TOPIC (unless IRCOp with sufficient override rights).
 * @retval EX_ALWAYS_DENY	Deny the TOPIC always (even if IRCOp).
 * @retval EX_ALLOW		Allow the TOPIC, unless another module blocks it.
 */
int hooktype_can_set_topic(Client *client, Channel *channel, const char *topic, const char **errmsg);

/** Called when a local user wants to change the channel topic (function prototype for HOOKTYPE_PRE_LOCAL_TOPIC).
 * @param client		The client
 * @param channel		The channel
 * @param topic			The new requested topic
 * @return The new topic (you may also return 'topic'), or NULL if the topic change request should be rejected.
 */
const char *hooktype_pre_local_topic(Client *client, Channel *channel, const char *topic);

/** Called when the channel topic is changed (function prototype for HOOKTYPE_TOPIC).
 * @param client		The client
 * @param channel		The channel
 * @param mtags         	Message tags associated with the event
 * @param topic			The new topic
 * @return The return value is ignored (use return 0)
 */
int hooktype_topic(Client *client, Channel *channel, MessageTag *mtags, const char *topic);

/** Called when a local user changes channel modes, called early (function prototype for HOOKTYPE_PRE_LOCAL_CHANMODE).
 * WARNING: This does not allow you to stop or reject the channel modes. It only allows you to do stuff -before- the
 * mode is changed. It is currently only used by the delayjoin module.
 * @param client		The client
 * @param channel		The channel
 * @param mtags         	Message tags associated with the event
 * @param modebuf		The mode buffer, for example "+o"
 * @param parabuf		The parameter buffer, for example "NiceOp"
 * @param sendts		Send timestamp
 * @param samode		Is this an SAMODE?
 * @return The return value is ignored (use return 0)
 */
int hooktype_pre_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode);

/** Called when a remote user changes channel modes, called early (function prototype for HOOKTYPE_PRE_REMOTE_CHANMODE).
 * WARNING: This does not allow you to stop or reject the channel modes. It only allows you to do stuff -before- the
 * mode is changed. It is currently only used by the delayjoin module.
 * @param client		The client
 * @param channel		The channel
 * @param mtags         	Message tags associated with the event
 * @param modebuf		The mode buffer, for example "+o"
 * @param parabuf		The parameter buffer, for example "NiceOp"
 * @param sendts		Send timestamp
 * @param samode		Is this an SAMODE?
 * @return The return value is ignored (use return 0)
 */
int hooktype_pre_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode);

/** Called when a local user changes channel modes (function prototype for HOOKTYPE_LOCAL_CHANMODE).
 * @param client		The client
 * @param channel		The channel
 * @param mtags         	Message tags associated with the event
 * @param modebuf		The mode buffer, for example "+o"
 * @param parabuf		The parameter buffer, for example "NiceOp"
 * @param sendts		Send timestamp
 * @param samode		Is this an SAMODE?
 * @param destroy_channel	Module can set this to 1 to indicate 'channel' was destroyed
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel);

/** Called when a remote user changes channel modes (function prototype for HOOKTYPE_REMOTE_CHANMODE).
 * @param client		The client
 * @param channel		The channel
 * @param mtags         	Message tags associated with the event
 * @param modebuf		The mode buffer, for example "+o"
 * @param parabuf		The parameter buffer, for example "NiceOp"
 * @param sendts		Send timestamp
 * @param samode		Is this an SAMODE?
 * @param destroy_channel	Module can set this to 1 to indicate 'channel' was destroyed
 * @return The return value is ignored (use return 0)
 */
int hooktype_remote_chanmode(Client *client, Channel *channel, MessageTag *mtags, const char *modebuf, const char *parabuf, time_t sendts, int samode, int *destroy_channel);

/** Called when a channel mode is removed by a local or remote user (function prototype for HOOKTYPE_MODECHAR_DEL).
 * NOTE: This is currently not terribly useful for most modules. It is used by by the floodprot and noknock modules.
 * @param channel		The channel
 * @param modechar		The mode character, eg 'k'
 * @return The return value is ignored (use return 0)
 */
int hooktype_modechar_del(Channel *channel, int modechar);

/** Called when a channel mode is set by a local or remote user (function prototype for HOOKTYPE_MODECHAR_ADD).
 * NOTE: This is currently not terribly useful for most modules. It is used by by the floodprot and noknock modules.
 * @param channel		The channel
 * @param modechar		The mode character, eg 'k'
 * @return The return value is ignored (use return 0)
 */
int hooktype_modechar_add(Channel *channel, int modechar);

/** Called when a user sets away status or unsets away status (function prototype for HOOKTYPE_AWAY).
 * @param client		The client
 * @param mtags         	Message tags associated with the event
 * @param reason		The away reason, or NULL if away is unset.
 * @param already_as_away	Set to 1 if the user only changed their away reason.
 * @return The return value is ignored (use return 0)
 */
int hooktype_away(Client *client, MessageTag *mtags, const char *reason, int already_as_away);

/** Called when a user wants to invite another user to a channel (function prototype for HOOKTYPE_PRE_INVITE).
 * @param client		The client
 * @param acptr			The user who is invited (victim)
 * @param channel		The channel the user is invited to
 * @param override		If this was an override (1) or not. Note: pointer to an int!
 * @retval HOOK_DENY		Deny the invite.
 * @retval HOOK_ALLOW		Allow the invite (stop processing other modules)
 * @retval HOOK_CONTINUE	Allow the invite, unless another module blocks it.
 */
int hooktype_pre_invite(Client *client, Client *acptr, Channel *channel, int *override);

/** Called when a user invites another user to a channel (function prototype for HOOKTYPE_INVITE).
 * @param client		The client
 * @param acptr			The user who is invited (victim)
 * @param channel		The channel the user is invited to
 * @param mtags			Message tags associated with the event
 * @return The return value is ignored (use return 0)
 */
int hooktype_invite(Client *client, Client *acptr, Channel *channel, MessageTag *mtags);

/** Called when a user wants to knock on a channel (function prototype for HOOKTYPE_PRE_KNOCK).
 * @param client		The client
 * @param channel		The channel to knock on
 * @param reason		Knock reason (can be replaced if needed)
 * @retval HOOK_DENY		Deny the knock.
 * @retval HOOK_ALLOW		Allow the knock (stop processing other modules)
 * @retval HOOK_CONTINUE	Allow the knock, unless another module blocks it.
 */
int hooktype_pre_knock(Client *client, Channel *channel, const char **reason);

/** Called when a user knocks on a channel (function prototype for HOOKTYPE_KNOCK).
 * @param client		The client
 * @param channel		The channel to knock on
 * @param mtags			Message tags associated with the event
 * @param comment		The knock reason
 * @return The return value is ignored (use return 0)
 */
int hooktype_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment);

/** Called when a user whoises someone (function prototype for HOOKTYPE_WHOIS).
 * @param client		The client issuing the command
 * @param target		The user who is the target of the /WHOIS.
 * @param list			The name/value/prio list that you can add information to
 *				that will be sent to the user as the WHOIS response.
 * @return The return value is ignored (use return 0)
 */
int hooktype_whois(Client *client, Client *target, NameValuePrioList **list);

/** Called to add letters to the WHO status column (function prototype for HOOKTYPE_WHO_STATUS).
 * If a user does a /WHO request, then WHO will show a number of status flags
 * such as B to show the user is a bot (see "HELPOP WHO" for the full list).
 * @param client		The client
 * @param target		The target, the user for which we should display WHO status flags
 * @param channel		The channel if a channel WHO, or NULL
 * @param member		The membership information, or NULL
 * @param status		The current status flags, so far
 * @param cansee		If 'client' can see 'target' (eg: in same channel or -i)
 * @return Return 0 if no WHO status flags need to be added, otherwise return the ascii character (eg: return 'B').
 */
int hooktype_who_status(Client *client, Client *target, Channel *channel, Member *member, const char *status, int cansee);

/** Called when an IRCOp wants to kill another user (function prototype for HOOKTYPE_PRE_KILL).
 * @param client		The client
 * @param victim		The user who should be killed
 * @param reason		The kill reason
 * @retval EX_DENY		Deny the KICK (unless IRCOp with sufficient override rights).
 * @retval EX_ALWAYS_DENY	Deny the KICK always (even if IRCOp).
 * @retval EX_ALLOW		Allow the kick, unless another module blocks it.
 */
int hooktype_pre_kill(Client *client, Client *victim, const char *reason);

/** Called when a local user kills another user (function prototype for HOOKTYPE_LOCAL_KILL).
 * Note that kills from remote IRCOps will show up as regular quits, so use hooktype_remote_quit() and hooktype_local_quit().
 * @param client		The client
 * @param victim		The victim
 * @param comment		The kill reason
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_kill(Client *client, Client *victim, const char *comment);

/** Called when an IRCOp calls /REHASH with a -parameter (function prototype for HOOKTYPE_REHASHFLAG).
 * @param client		The client issuing the command, or NULL if rehashing due to system signal.
 * @param str			The rehash flag (eg: "-all")
 * @return The return value is ignored (use return 0)
 */
int hooktype_rehashflag(Client *client, const char *str);

/** Called when the server is rehashing (function prototype for HOOKTYPE_REHASH).
 * @return The return value is ignored (use return 0)
 */
int hooktype_rehash(void);

/** Called when the server has completed rehashing (function prototype for HOOKTYPE_REHASH_COMPLETE).
 * @return The return value is ignored (use return 0)
 */
int hooktype_rehash_complete(void);

/** Called when searching for a test function for a specific configuration item (function prototype for HOOKTYPE_CONFIGTEST).
 * This is part of the configuration API, which is better documented at the
 * wiki at https://www.unrealircd.org/docs/Dev:Configuration_API
 * @param cfptr			Configuration file
 * @param ce			Configuration entry
 * @param section		One of CONFIG_*, eg: CONFIG_MAIN.
 * @param errors		Counter for errors
 * @retval 0			This entry is not for us, we don't know anything about it.
 * @retval -1			Errors encountered (the number of errors is stored in *errors)
 * @retval 1			This entry is handled and is without any errors.
 */
int hooktype_configtest(ConfigFile *cfptr, ConfigEntry *ce, int section, int *errors);

/** Called after all hooktype_configtest() have run, to check for missing config items (function prototype for HOOKTYPE_CONFIGPOSTTEST).
 * @param errors		The number of errors
 * @returns In case of errors, return -1.
 */
int hooktype_configposttest(int *errors);

/** Called to run/do the active configuration for this configuration item (function prototype for HOOKTYPE_CONFIGRUN).
 * This is part of the configuration API, which is better documented at the
 * wiki at https://www.unrealircd.org/docs/Dev:Configuration_API
 * @param cfptr			Configuration file
 * @param ce			Configuration entry
 * @param section		One of CONFIG_*, eg: CONFIG_MAIN.
 * @retval 0			This entry is not for us, we don't know anything about it.
 * @retval 1			This entry is for us, it is now handled, don't call any other modules for it anymore.
 */
int hooktype_configrun(ConfigFile *cfptr, ConfigEntry *ce, int section);

/** Called to run/do the active configuration for this configuration item - extended version (function prototype for HOOKTYPE_CONFIGRUN_EX).
 * This particular "extended version" is only used for extending listen { } options, so you probably don't need this one.
 * Use hooktype_configrun() instead!
 * @param cfptr			Configuration file
 * @param ce			Configuration entry
 * @param section		One of CONFIG_*, eg: CONFIG_MAIN.
 * @param ptr			Pointer to something
 * @retval 0			This entry is not for us, we don't know anything about it.
 * @retval 1			This entry is for us, it is now handled, don't call any other modules for it anymore.
 */
int hooktype_configrun_ex(ConfigFile *cfptr, ConfigEntry *ce, int section, void *ptr);

/** Called when a user types /STATS <something> (function prototype for HOOKTYPE_STATS).
 * This way a module can add a new STATS item, eg 'STATS something'
 * @param client		The client issuing the command
 * @param str			The parameter to the STATS command, eg 'something'.
 * @return The return value is ignored (use return 0)
 */
int hooktype_stats(Client *client, const char *str);

/** Called when a user becomes IRCOp or is no longer an IRCOp (function prototype for HOOKTYPE_LOCAL_OPER).
 * @param client		The client
 * @param add			1 if the user becomes IRCOp, 0 if the user is no longer IRCOp
 * @param oper_block		The name of the oper block used to oper up
 * @param operclass		The name of the operclass
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_oper(Client *client, int add, const char *oper_block, const char *operclass);

/** Called when a client sends a PASS command (function prototype for HOOKTYPE_LOCAL_PASS).
 * @param client		The client
 * @param password		The password supplied by the client
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_pass(Client *client, const char *password);

/** Called when a channel is created (function prototype for HOOKTYPE_CHANNEL_CREATE).
 * @param channel		The channel that just got created
 * @note This function is not used much, use hooktype_local_join() and hooktype_remote_join() instead.
 * @return The return value is ignored (use return 0)
 */
int hooktype_channel_create(Channel *channel);

/** Called when a channel is completely destroyed (function prototype for HOOKTYPE_CHANNEL_DESTROY).
 * @param channel		The channel that is about to be destroyed
 * @param should_destroy	Module can set this to 1 to prevent destriction
 * @note A channel is usually destroyed due to the last user leaving. But in some cases
 *       a channel is created and then immediately destroyed within nanoseconds. Just so you know.
 * @return The return value is ignored (use return 0)
 */
int hooktype_channel_destroy(Channel *channel, int *should_destroy);

/** Called when a user matches a TKL and is pending to be killed (function prototype for HOOKTYPE_TKL_EXCEPT).
 * @param client		The client
 * @param ban_type		The TKL type, one of TKL_*. For example TKL_GLOBAL|TKL_KILL for a gline.
 * @retval 0 Ban/kill the user.
 * @retval 1 User is exempt, do NOT kill or ban.
 */
int hooktype_tkl_except(Client *client, int ban_type);

/** Called when the user modes of a user change (function prototype for HOOKTYPE_UMODE_CHANGE).
 * @param client		The client
 * @param setflags		The current user modes
 * @param newflags		The new user modes
 * @note The user mode can be changed due to a MODE by the user itself, by a server, or by SVSMODE/SVS2MODE from Services.
 * @return The return value is ignored (use return 0)
 */
int hooktype_umode_change(Client *client, long setflags, long newflags);

/** Called when a new TKL is added (function prototype for HOOKTYPE_TKL_ADD).
 * @param client		The client adding the TKL (this can be &me)
 * @param tkl			The TKL entry
 * @return The return value is ignored (use return 0)
 */
int hooktype_tkl_add(Client *client, TKL *tkl);

/** Called when removing an existing TKL (function prototype for HOOKTYPE_TKL_DEL).
 * @param client		The client removing the TKL (this can be &me)
 * @param tkl			The TKL entry
 * @return The return value is ignored (use return 0)
 */
int hooktype_tkl_del(Client *client, TKL *tkl);

/** Called when something is logged via the unreal_log() function (function prototype for HOOKTYPE_LOG).
 * @param loglevel		Loglevel (eg ULOG_INFO)
 * @param subsystem		Subsystem (eg "operoverride")
 * @param event_id		Event ID (eg "SAJOIN_COMMAND")
 * @param msg			Message(s) in text form
 * @param json			The JSON log entry
 * @param json_serialized	The serialized JSON log entry (as a string)
 * @param timebuf		The [xxxx] time buffer, for convenience
 * @return The return value is ignored (use return 0)
 */
int hooktype_log(LogLevel loglevel, const char *subsystem, const char *event_id, MultiLine *msg, json_t *json, const char *json_serialized, const char *timebuf);

/** Called when a local user matches a spamfilter (function prototype for HOOKTYPE_LOCAL_SPAMFILTER).
 * @param client		The client
 * @param str			The text that matched, this may be stripped from color and control codes.
 * @param str_in		The original text
 * @param target		The spamfilter type, one of SPAMF_*, such as SPAMF_CHANMSG.
 * @param destination		The destination, such as the name of another client or channel
 * @param tkl			The spamfilter TKL entry that matched
 * @return The return value is ignored (use return 0)
 */
int hooktype_local_spamfilter(Client *client, const char *str, const char *str_in, int type, const char *target, TKL *tkl);

/** Called when a user sends something to a user that has the sender silenced (function prototype for HOOKTYPE_SILENCED).
 * UnrealIRCd support a SILENCE list. If the target user has added someone on the silence list, eg via SILENCE +BadUser,
 * and then 'BadUser' tries to send a message to this user, this hook will be triggered.
 * @param client		The client trying to send a message/notice
 * @param target		The intended recipient of the message
 * @param sendtype		Indicating if it is a PRIVMSG, NOTICE or something else.
 * @note This function is rarely used.
 * @return The return value is ignored (use return 0)
 */
int hooktype_silenced(Client *client, Client *target, SendType sendtype);

/** Called on every incoming packet (function prototype for HOOKTYPE_RAWPACKET_IN).
 * This is quite invasive, so only use this if you cannot do the same via some other means (eg overrides or hooks).
 * The typical use cases are things like: handling an entirely different protocol (eg: websocket module),
 * or old stuff like codepage conversions, basically: things that work on entire packets.
 * @param client		The client
 * @param readbuf		The buffer
 * @param length		The length of the buffer
 * @note If you want to alter the buffer contents then replace 'readbuf' with your own buffer and set 'length' appropriately.
 * @return The return value is ignored (use return 0)
 */
int hooktype_rawpacket_in(Client *client, const char *readbuf, int *length);

/** Called when a packet is received or sent (function prototype for HOOKTYPE_PACKET).
 * @param client		The locally connected sender, this can be &me
 * @param to			The locally connected recipient, this can be &me
 * @param intended_to		The originally intended recipient, this could be a remote user
 * @param msg			The buffer
 * @param length		The length of the buffer
 * @note When reading a packet, 'client' will indicate the locally connected user and 'to' will be &me.
 *       When sending a pcket, 'client' will be &me and 'to' will be the locally connected user.
 *       If you want to alter the buffer contents then replace 'msg' with your own buffer and set 'length' appropriately.
 * @return The return value is ignored (use return 0)
 */
int hooktype_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length);

/** Called very early when a client connects (function prototype for HOOKTYPE_HANDSHAKE).
 * This is called as soon as the socket is connected and the client is being set up,
 * so before the client has sent any application data, and certainly before it is
 * known whether this client will become a user or a server.
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_handshake(Client *client);

/** Called very early when a client connects (function prototype for HOOKTYPE_ACCEPT).
 * Module coders: have a look at hooktype_handshake() instead of this one!
 * HOOKTYPE_ACCEPT is called even before HOOKTYPE_HANDSHAKE, as soon as the socket
 * is connected and during the client is being set up, before the SSL/TLS handshake.
 * It is only used for connection flood detection and checking (G)Z-lines.
 * Note that this connection is also called for *NIX domain socket connections,
 * HTTP(S) requests, and so on.
 * @param client		The client
 * @return One of HOOK_*. Use HOOK_DENY to reject the client.
 */
int hooktype_accept(Client *client);

/** Called when a client structure is freed (function prototype for HOOKTYPE_FREE_CLIENT).
 * @param client		The client
 * @note Normally you use hooktype_local_quit(), hooktype_remote_quit() and hooktype_unkuser_quit() for this.
 * @return The return value is ignored (use return 0)
 */
int hooktype_free_client(Client *client);

/** Called when the user structure, client->user, is being freed (function prototype for HOOKTYPE_FREE_USER).
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_free_user(Client *client);

/** Called when +l limit is exceeded when joining (function prototype for HOOKTYPE_CAN_JOIN_LIMITEXCEEDED).
 * @param client		The client
 * @param channel		The channel
 * @param key			The channel key
 * @note I don't think this works?
 * @return Unclear..
 */
int hooktype_can_join_limitexceeded(Client *client, Channel *channel, const char *key, char **errmsg);

/** Called to check if the user is visible in the channel (function prototype for HOOKTYPE_VISIBLE_IN_CHANNEL).
 * For example, the delayjoin module (+d/+D) will 'return 0' here if the user is hidden due to delayed join.
 * @param client		The client
 * @param channel		The channel
 * @param client_member		The client Member * struct in the channel
 * @retval 0 The user is NOT visible
 * @retval 1 The user is visible
 */
int hooktype_visible_in_channel(Client *client, Channel *channel, Member *client_member);

/** Called to check if the channel of a user should be shown in WHOIS/WHO (function prototype for HOOKTYPE_SEE_CHANNEL_IN_WHOIS).
 * @param client		The client ASKING, eg doing the /WHOIS.
 * @param target		The client who is being interrogated
 * @param channel		The channel that 'client' is in
 * @retval 0 The channel should NOT be visible
 * @retval 1 Show the channel
 */
int hooktype_see_channel_in_whois(Client *client, Client *target, Channel *channel);

/** Called when a user is added to a channel (function prototype for HOOKTYPE_JOIN_DATA).
 * Note that normally you use hooktype_local_join() and hooktype_remote_join() for this.
 * This function only exists so it is easy to work with dynamic data, and even
 * that is an old idea now that we have the moddata system.
 * @param client		The client joining
 * @param channel		The channel the client joined to
 * @return The return value is ignored (use return 0)
 */
int hooktype_join_data(Client *who, Channel *channel);

/** Should the user be able to bypass channel restrictions because they are invited? (function prototype for HOOKTYPE_INVITE_BYPASS).
 * @param client		The client
 * @param channel		The channel
 * @retval HOOK_DENY		Don't allow the user to bypass channel restrictions when they are invited
 * @retval HOOK_CONTINUE	Obey the normal rules
 * @note Usually you want a user to be able to bypass channel restrictions such as +l or +b when they are /INVITEd by another user
 *       or have invited themselves (OperOverride). But, there may be special cases where you don't want this.
 *       For example, this hook is used by +O to still not allow ircops to join +O channels even if they have OperOverride capability.
 */
int hooktype_invite_bypass(Client *client, Channel *channel);

/** Should a user be able to view the topic when not in the channel? (function prototype for HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL).
 * @param client		The client requesting the topic
 * @param channel		The channel
 * @note This visibility check is only partially implemented. Do not count on it.
 * @retval HOOK_DENY		Deny the topic request
 * @retval HOOK_CONTINUE	Obey the normal rules
 */
int hooktype_view_topic_outside_channel(Client *client, Channel *channel);

/** Is a user permitted to change its nickname? (function prototype for HOOKTYPE_CHAN_PERMIT_NICK_CHANGE).
 * This is called for each channel the user is in. This is used by the +N (nonickchange) channel mode.
 * @param client		The client
 * @param channel		The channel the user is in
 * @retval HOOK_DENY		Deny the nick change
 * @retval HOOK_CONTINUE	Obey the normal rules (allow it, unless denied by something else)
 */
int hooktype_chan_permit_nick_change(Client *client, Channel *channel);

/** Is the channel considered "secure"? (function prototype for HOOKTYPE_IS_CHANNEL_SECURE).
 * This is used by the +z/+Z modules.
 * @param channel		The channel
 * @retval 0			No, the channel is not secure
 * @retval 1			Yes, the channel is secure
 */
int hooktype_is_channel_secure(Channel *channel);

/** Called after a channel is synced due to netmerge (function prototype for HOOKTYPE_CHANNEL_SYNCED).
 * When a server connects channel status is exchanged in order to synchronize the two sides of channels.
 * After each SJOIN command this function is called to check if anything special
 * needs to be join. At the moment this function is only used by channel mode +z
 * which will kick out any insecure users if we are the "loosing" side of a split.
 * @param channel		The channel
 * @param merge			Set to 1 if merging due to equal timestamps on both sides, 0 otherwise
 * @param removetheirs		Set to 1 if the other side is the loosing side and we are the winning side.
 * @param nomode		Set to 1 if this is a SJOIN without modes (rare? services?)
 * @retval HOOK_DENY		Deny the channel merge. Important: only return this after you have destroyed the channel!
 * @retval HOOK_CONTINUE	Continue normally
 */
int hooktype_channel_synced(Channel *channel, int merge, int removetheirs, int nomode);

/** Can the target client be SAJOIN'ed to a particular channel? (function prototype for HOOKTYPE_CAN_SAJOIN).
 * @param target		The client that should be joined
 * @param channel		The channel that the client should be joined to
 * @param client		The client issuing the request (usually IRCOp)
 * @retval HOOK_DENY		Deny the SAJOIN
 * @retval HOOK_CONTINUE	Allow the SAJOIN, unless blocked by something else
 */
int hooktype_can_sajoin(Client *target, Channel *channel, Client *client);

/** May the target user be deoped? (function prototype for HOOKTYPE_MODE_DEOP).
 * This is for example used by the +S (Services bot) user mode to block deop requests to services bots.
 * @param client		The client issuing the command
 * @param victim		The victim that should be deoped (MODE -o)
 * @param channel		The channel
 * @param what			Always MODE_DEL at the moment
 * @param modechar		The mode character: q/a/o/h/v
 * @param client_access		Channel member modes of 'client', eg "o", never NULL but can be empty.
 * @param target_access		Channel member modes of 'client', eg "h", never NULL but can be empty.
 * @param reject_reason		The error string that should be sent to the client
 * @retval HOOK_CONTINUE	Proceed normally (allow it)
 * @retval HOOK_DENY		Reject the mode change
 * @retval HOOK_ALWAYS_DENY	Reject the mode change, even if IRCOp/Services/..
 */
int hooktype_mode_deop(Client *client, Client *victim, Channel *channel, u_int what, int modechar, const char *client_access, const char *target_access, const char **reject_reason);

/** Called when a DCC request was denied by the IRCd (function prototype for HOOKTYPE_DCC_DENIED).
 * @param client		The client who tried to send a file
 * @param target		The intended recipient
 * @param realfile		The original file name, may contain strange characters or be very long
 * @param displayfile		The file name for displaying purposes, properly filtered.
 * @param denydcc		The deny dcc { ] rule that triggered.
 * @return The return value is ignored (use return 0)
 */
int hooktype_dcc_denied(Client *client, const char *target, const char *realfile, const char *displayfile, ConfigItem_deny_dcc *denydcc);

/** Called in the user accept procedure, when setting the +z user mode (function prototype for HOOKTYPE_SECURE_CONNECT).
 * This is only meant to be used by the WEBIRC module, so it can do -z for fake secure users.
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_secure_connect(Client *client);

/** Can the user bypass a particular channel message restriction? (function prototype for HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION).
 * This is for example used to bypass +S (stripcolor) via ~m:color:*!*@a.b.c.d if the user matches that extban.
 * @param client		The client (sender)
 * @param channel		The channel
 * @param bypass_type		The restriction to bypass, for example BYPASS_CHANMSG_COLOR
 * @retval HOOK_ALLOW		Allow to bypass the restriction
 * @retval HOOK_CONTINUE	Continue as normal, obey normal rules, deny bypassing the restriction.
 */
int hooktype_can_bypass_channel_message_restriction(Client *client, Channel *channel, BypassChannelMessageRestrictionType bypass_type);

/** Called when a SASL continuation response is received (function prototype for HOOKTYPE_SASL_CONTINUATION).
 * This is only used by the authprompt module, it unlikely that you need it.
 * @param client		The client for which the SASL authentication is taking place
 * @param buf			The AUTHENTICATE buffer
 * @retval HOOK_CONTINUE	Continue as normal
 * @retval HOOK_DENY		Do not handle the SASL request, or at least don't show the response to the client.
 */
int hooktype_sasl_continuation(Client *client, const char *buf);

/** Called when a SASL result response is received (function prototype for HOOKTYPE_SASL_RESULT).
 * This is only used by the authprompt module.
 * @param client		The client for which the SASL authentication is taking place
 * @param successs		Whether the SASL authentication was successful (1) or not (0)
 * @retval HOOK_CONTINUE	Continue as normal
 * @retval HOOK_DENY		Do not handle the SASL response, or at least don't show the response to the client.
 */
int hooktype_sasl_result(Client *client, int success);

/** Called when a TKL ban should be added on the host (function prototype for HOOKTYPE_TAKE_ACTION).
 * This is called for automated bans such as spamfilter hits, flooding, etc.
 * This hook can be used to prevent the ban, or as used by the authprompt to delay it.
 * @param client		The client that should be banned
 * @param action		The actions to take, one of BAN_ACT_*
 * @param reason		The ban reason
 * @param duration		The duration of the ban, 0 for permanent ban
 * @return The magic value 99 is used to exempt the user (=do not ban!), otherwise the ban is added.
 */
int hooktype_take_action(Client *client, BanActionValue action, const char *reason, long duration);

/** Called when a TKL ban is hit by this user (function prototype for HOOKTYPE_FIND_TKLINE_MATCH).
 * This is called when an existing TKL entry is hit by the user.
 * To prevent an automated ban to be added on a host/ip, see hooktype_take_action().
 * @param client		The client
 * @param tkl			The TKL entry
 * @return The magic value 99 is used to exempt the user (=do not kill!), otherwise the ban is executed.
 */
int hooktype_find_tkline_match(Client *client, TKL *tk);

/** Called when the user connects for each welcome numeric (function prototype for HOOKTYPE_WELCOME).
 * This can be used to send some additional notice or data to the user at a step of your choosing.
 * This is called before all numerics with 'after_numeric' set to 0, and then after numeric
 * 001, 002, 003, 005, 396, 266, 376. In the last call, 'after_numeric' is 999 when all initial
 * numerics have been sent but before the user is auto-joined to channels (if any).
 * @param client		The client
 * @param after_numeric		Which numeric has just been sent
 * @return The return value is ignored (use return 0)
 */
int hooktype_welcome(Client *client, int after_numeric);

/** Called right before parsing a line and client command (function prototype for HOOKTYPE_PRE_COMMAND).
 * This is only used by labeled-reponse. If you think this hook is useful then you
 * should probably use the CommandOverride API instead!
 * @param client		The direct local client connection from which the line is received.
 * @param mtags			Message tags, if any.
 * @param buf			The buffer (without message tags)
 * @return The return value is ignored (use return 0)
 */
int hooktype_pre_command(Client *from, MessageTag *mtags, const char *buf);

/** Called right after finishing a client command (function prototype for HOOKTYPE_POST_COMMAND).
 * This is only used by labeled-reponse. If you think this hook is useful then you
 * should probably use the CommandOverride API instead!
 * @param client		The direct local client connection from which the line is received.
 * @param mtags			Message tags, if any.
 * @param buf			The buffer (without message tags)
 * @return The return value is ignored (use return 0)
 */
int hooktype_post_command(Client *from, MessageTag *mtags, const char *buf);

/** Called when new_message() is executed (function prototype for HOOKTYPE_NEW_MESSAGE).
 * When a new message with message tags is prepared, code in UnrealIRCd
 * and in modules will call new_message(). From that function this hook
 * is also called. The purpose of this hook is so you can add additional
 * message tags that belong the user. For example it is used
 * by the account-tag module to add account=xyz information, see that module for a good example.
 * @param sender		The client from which the message will be sent
 * @param recv_mtags		The message tags as originally received before, or NULL if completely new.
 * @param mtag_list		The newly created message tag list that we are building
 * @param signature		Special signature when used through new_message_special()
 * @return The return value is ignored (use return 0)
 */
void hooktype_new_message(Client *sender, MessageTag *recv_mtags, MessageTag **mtag_list, const char *signature);

/** Is the client handshake finished? (function prototype for HOOKTYPE_IS_HANDSHAKE_FINISHED).
 * This is called by the is_handshake_finished() function to check if the user
 * can be accepted on IRC, or if there are still other checks/input pending.
 * This can be used to "hold" a user temporarily until something happens, such
 * as the user typing a password or waiting for a remote access check to return a result.
 * For an example usage, see the cap module, which uses it to "hold" the connection
 * if a "CAP LS" has been sent and no "CAP END" has been received yet.
 * @param client		The client
 * @retval 1			Yes, the handshake is finished, as far as we are concerned.
 * @retval 0			No, the handshake is not yet finished, do not allow the user in yet.
 */
int hooktype_is_handshake_finished(Client *client);

/** Called upon a local client quit, allows altering the quit message on a per-channel basis (function prototype for HOOKTYPE_PRE_LOCAL_QUIT_CHAN).
 * If you don't need to change the quit message on a per-channel basis, but want to change it regardless of channels, then use hooktype_pre_local_quit().
 * If you don't need to change the quit message at all, then use hooktype_local_quit() and hooktype_remote_quit() instead.
 * @param client		The client
 * @param channel		The channel
 * @param comment		The quit message
 * @return The original quit message (comment), the new quit message (pointing to your own static buffer), or NULL (no quit message)
 */
const char *hooktype_pre_local_quit_chan(Client *client, Channel *channel, const char *comment);

/** Called when an ident lookup should be made (function prototype for HOOKTYPE_IDENT_LOOKUP).
 * This is used by the ident_lookup module.
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_ident_lookup(Client *client);

/** Called when someone logs in/out a services account (function prototype for HOOKTYPE_ACCOUNT_LOGIN).
 * The account name can be found in client->user->account. It will be the string "0" if the user is logged out.
 * @param client		The client
 * @param mtags         	Message tags associated with the event
 * @return The return value is ignored (use return 0)
 */
int hooktype_account_login(Client *client, MessageTag *mtags);

/** Called when closing the connection of a local user (function prototype for HOOKTYPE_CLOSE_CONNECTION).
 * This is called from close_connection(). Note that a lot of client information
 * has already been freed, so normally you should use the quit/exit functions instead:
 * hooktype_local_quit(), hooktype_remote_quit() and hooktype_unkuser_quit().
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_close_connection(Client *client);

/** Called when a user connects to add extra information (function prototype for HOOKTYPE_CONNECT_EXTINFO).
 * If you want to use this, then use the nvplist_add() or nvplist_add_fmt() function
 * to add the information to the list. See also get_connect_extinfo() for inspiration.
 * @param client		The client
 * @param list			The name/value/prio list that you can add information to
 * @return The return value is ignored (use return 0)
 */
int hooktype_connect_extinfo(Client *client, NameValuePrioList **list);

/** Called when a user wants to join a channel that require invitation.
 * Use hook priorities to enforce a specific policy, especially denying the invitation.
 * @param client		The client
 * @param channel		The channel client is willing to join
 * @param invited		Set to 0 for user who should not be invited, set to 1 if the user is invited.
 * @return The return value is ignored (use return 0)
 */
int hooktype_is_invited(Client *client, Channel *channel, int *invited);

/** Called after a local user has changed the nick name (function prototype for HOOKTYPE_POST_LOCAL_NICKCHANGE).
 * @param client		The client
 * @param mtags         	Message tags associated with the event
 * @param oldnick		The nick name before the nick change
 * @return The return value is ignored (use return 0)
 */
int hooktype_post_local_nickchange(Client *client, MessageTag *mtags, const char *oldnick);

/** Called after a remote user has changed the nick name (function prototype for HOOKTYPE_POST_REMOTE_NICKCHANGE).
 * @param client		The client
 * @param mtags         	Message tags associated with the event
 * @param oldnick		The nick name before the nick change
 * @return The return value is ignored (use return 0)
 */
int hooktype_post_remote_nickchange(Client *client, MessageTag *mtags, const char *oldnick);

/** Called when user name or user host has changed.
 * @param client		The client whose user@host has changed
 * @param olduser		Old username of the client
 * @param oldhost		Old hostname of the client
 * @return The return value is ignored (use return 0)
 */
int hooktype_userhost_change(Client *client, const char *olduser, const char *oldhost);

/** Called when user realname has changed.
 * @param client		The client whose realname has changed
 * @param oldinfo		Old realname of the client
 * @return The return value is ignored (use return 0)
 */
int hooktype_realname_change(Client *client, const char *oldinfo);

/** Called when changing IP (eg due to PROXY/WEBIRC/etc).
 * @param client		The client whose IP has changed
 * @param oldip			Old IP of the client
 * @returns If you reject the user then use dead_link() and return HOOK_DENY
 *          (DO NOT USE exit_client(), only dead_link()!),
 *          otherwise use 'return 0' to proceed normally.
 */
int hooktype_ip_change(Client *client, const char *oldip);

/** Called when json_expand_client() is called.
 * Used for expanding information about 'client' in logging routines.
 * @param client		The client that should be expanded
 * @param detail		The amount of detail to provide (always 0 at the moment)
 * @param j			The JSON object
 * @return The return value is ignored (use return 0)
 */
int hooktype_json_expand_client(Client *client, int detail, json_t *j);

/** Called when json_expand_client_user() is called.
 * Used for expanding information about 'client' in logging routines
 * when the client is a USER.
 * @param client		The client that should be expanded
 * @param detail		The amount of detail to provide (always 0 at the moment)
 * @param j			The JSON object - root
 * @param child			The JSON object - "user" child item
 * @return The return value is ignored (use return 0)
 */
int hooktype_json_expand_client_user(Client *client, int detail, json_t *j, json_t *child);

/** Called when json_expand_client_server() is called.
 * Used for expanding information about 'client' in logging routines
 * when the client is a SERVER.
 * @param client		The client that should be expanded
 * @param detail		The amount of detail to provide (always 0 at the moment)
 * @param j			The JSON object - root
 * @param child			The JSON object - "server" child item
 * @return The return value is ignored (use return 0)
 */
int hooktype_json_expand_client_server(Client *client, int detail, json_t *j, json_t *child);

/** Called when json_expand_channel() is called.
 * Used for expanding information about 'channel' in logging routines.
 * @param channel		The channel that should be expanded
 * @param detail		The amount of detail to provide (always 0 at the moment)
 * @param j			The JSON object
 * @return The return value is ignored (use return 0)
 */
int hooktype_json_expand_channel(Channel *channel, int detail, json_t *j);

/** Called when a local user is about to be disconnected due to a registration timeout,
 * allows changing the disconnect reason (function prototype for HOOKTYPE_PRE_LOCAL_HANDSHAKE_TIMEOUT).
 * This is used by the authprompt module.
 * @param client		The client
 * @param comment		The quit/disconnect reason (can be changed by you)
 * @retval HOOK_CONTINUE	Continue as normal
 * @retval HOOK_ALLOW		Do not exit the user due to a handshake timeout
 */
int hooktype_pre_local_handshake_timeout(Client *client, const char **comment);

/** Called when a REHASH completed (either succesfully or with a failure).
 * This gives the full rehash log. Used by the JSON-RPC interface. (function prototype for HOOKTYPE_REHASH_LOG)
 * @param failure		Set to 1 if the rehash failed, otherwise 0.
 * @param t			The JSON object containing the rehash log and other information.
 * @return The return value is ignored (use return 0)
 */
int hooktype_rehash_log(int failure, json_t *rehash_log);

/** Called when DNS has been done for a client (or has not been done because it was skipped).
 * (function prototype for HOOKTYPE_DNS_FINISHED)
 * @param client		The client
 * @return The return value is ignored (use return 0)
 */
int hooktype_dns_finished(Client *client);

/** Called after an listener block is processed
 * (function prototype for HOOKTYPE_CONFIG_LISTENER)
 * @param listener		The listener
 * @return The return value is ignored (use return 0)
 */
int hooktype_config_listener(ConfigItem_listen *listener);

/** Called after an entry is added to a WATCH (or MONITOR) list.
 * @param nick	Name of the new entry (watched user's nick)
 * @param client	Owner of the watch list
 * @param flags	Flags for the entry (WATCH_FLAG_TYPE_*)
 * @return The return value is ignored (use return 0)
 */
int hooktype_watch_add(char *nick, Client *client, int flags);

/** Called after an entry is removed from a WATCH (or MONITOR) list.
 * @param nick	Name of the entry (watched user's nick) to be deleted
 * @param client	Owner of the watch list
 * @param flags	Flags for the entry (WATCH_FLAG_TYPE_*) to be deleted
 * @return The return value is ignored (use return 0)
 */
int hooktype_watch_del(char *nick, Client *client, int flags);

/** Called when an user is notified about a MONITORed nick coming off- or online.
 * @param watcher	The user being notified
 * @param client	The user (dis)appearing
 * @param online	1 if it's coming online, 0 otherwise
 * @return The return value is ignored (use return 0)
 */
int hooktype_monitor_notification(Client *watcher, Client *client, int online);

/** Called when an AUTHENTICATE command is sent by the client, for SASL authentication.
 * This can be used by authentication modules.
 * @param client		The client (user)
 * @param first			Set to 1 if this is the first AUTHENTICATE, set to 0 if it is a continuation.
 * @param param			The AUTHENTICATE parameter (max 400 chars)
 * @return The return value is ignored (use return 0)
 */
int hooktype_sasl_authenticate(Client *client, int first, const char *param);

/** Called for showing SASL mechanisms eg in sasl=xxx via "CAP LS 302"
 * @param client		The client
 * @return The saslmechlist
 */
const char *hooktype_sasl_mechs(Client *client);
/** @} */

#ifdef GCC_TYPECHECKING
#define ValidateHook(validatefunc, func) __builtin_types_compatible_p(__typeof__(func), __typeof__(validatefunc))

_UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check arguments and return type of function.")

#define ValidateHooks(hooktype, func) \
    if (((hooktype == HOOKTYPE_LOCAL_QUIT) && !ValidateHook(hooktype_local_quit, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_NICKCHANGE) && !ValidateHook(hooktype_local_nickchange, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_CONNECT) && !ValidateHook(hooktype_local_connect, func)) || \
        ((hooktype == HOOKTYPE_REHASHFLAG) && !ValidateHook(hooktype_rehashflag, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_PART) && !ValidateHook(hooktype_pre_local_part, func)) || \
        ((hooktype == HOOKTYPE_CONFIGPOSTTEST) && !ValidateHook(hooktype_configposttest, func)) || \
        ((hooktype == HOOKTYPE_REHASH) && !ValidateHook(hooktype_rehash, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_CONNECT) && !ValidateHook(hooktype_pre_local_connect, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_QUIT) && !ValidateHook(hooktype_pre_local_quit, func)) || \
        ((hooktype == HOOKTYPE_SERVER_CONNECT) && !ValidateHook(hooktype_server_connect, func)) || \
        ((hooktype == HOOKTYPE_SERVER_SYNC) && !ValidateHook(hooktype_server_sync, func)) || \
        ((hooktype == HOOKTYPE_SERVER_QUIT) && !ValidateHook(hooktype_server_quit, func)) || \
        ((hooktype == HOOKTYPE_STATS) && !ValidateHook(hooktype_stats, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_JOIN) && !ValidateHook(hooktype_local_join, func)) || \
        ((hooktype == HOOKTYPE_CONFIGTEST) && !ValidateHook(hooktype_configtest, func)) || \
        ((hooktype == HOOKTYPE_CONFIGRUN) && !ValidateHook(hooktype_configrun, func)) || \
        ((hooktype == HOOKTYPE_USERMSG) && !ValidateHook(hooktype_usermsg, func)) || \
        ((hooktype == HOOKTYPE_CHANMSG) && !ValidateHook(hooktype_chanmsg, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_PART) && !ValidateHook(hooktype_local_part, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_KICK) && !ValidateHook(hooktype_local_kick, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_CHANMODE) && !ValidateHook(hooktype_local_chanmode, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_OPER) && !ValidateHook(hooktype_local_oper, func)) || \
        ((hooktype == HOOKTYPE_UNKUSER_QUIT) && !ValidateHook(hooktype_unkuser_quit, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_PASS) && !ValidateHook(hooktype_local_pass, func)) || \
        ((hooktype == HOOKTYPE_REMOTE_CONNECT) && !ValidateHook(hooktype_remote_connect, func)) || \
        ((hooktype == HOOKTYPE_REMOTE_QUIT) && !ValidateHook(hooktype_remote_quit, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_JOIN) && !ValidateHook(hooktype_pre_local_join, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_KICK) && !ValidateHook(hooktype_pre_local_kick, func)) || \
        ((hooktype == HOOKTYPE_CAN_SET_TOPIC) && !ValidateHook(hooktype_can_set_topic, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_TOPIC) && !ValidateHook(hooktype_pre_local_topic, func)) || \
        ((hooktype == HOOKTYPE_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_remote_nickchange, func)) || \
        ((hooktype == HOOKTYPE_CHANNEL_CREATE) && !ValidateHook(hooktype_channel_create, func)) || \
        ((hooktype == HOOKTYPE_CHANNEL_DESTROY) && !ValidateHook(hooktype_channel_destroy, func)) || \
        ((hooktype == HOOKTYPE_REMOTE_CHANMODE) && !ValidateHook(hooktype_remote_chanmode, func)) || \
        ((hooktype == HOOKTYPE_TKL_EXCEPT) && !ValidateHook(hooktype_tkl_except, func)) || \
        ((hooktype == HOOKTYPE_UMODE_CHANGE) && !ValidateHook(hooktype_umode_change, func)) || \
        ((hooktype == HOOKTYPE_TOPIC) && !ValidateHook(hooktype_topic, func)) || \
        ((hooktype == HOOKTYPE_REHASH_COMPLETE) && !ValidateHook(hooktype_rehash_complete, func)) || \
        ((hooktype == HOOKTYPE_TKL_ADD) && !ValidateHook(hooktype_tkl_add, func)) || \
        ((hooktype == HOOKTYPE_TKL_DEL) && !ValidateHook(hooktype_tkl_del, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_KILL) && !ValidateHook(hooktype_local_kill, func)) || \
        ((hooktype == HOOKTYPE_LOG) && !ValidateHook(hooktype_log, func)) || \
        ((hooktype == HOOKTYPE_REMOTE_JOIN) && !ValidateHook(hooktype_remote_join, func)) || \
        ((hooktype == HOOKTYPE_REMOTE_PART) && !ValidateHook(hooktype_remote_part, func)) || \
        ((hooktype == HOOKTYPE_REMOTE_KICK) && !ValidateHook(hooktype_remote_kick, func)) || \
        ((hooktype == HOOKTYPE_LOCAL_SPAMFILTER) && !ValidateHook(hooktype_local_spamfilter, func)) || \
        ((hooktype == HOOKTYPE_SILENCED) && !ValidateHook(hooktype_silenced, func)) || \
        ((hooktype == HOOKTYPE_POST_SERVER_CONNECT) && !ValidateHook(hooktype_post_server_connect, func)) || \
        ((hooktype == HOOKTYPE_RAWPACKET_IN) && !ValidateHook(hooktype_rawpacket_in, func)) || \
        ((hooktype == HOOKTYPE_PACKET) && !ValidateHook(hooktype_packet, func)) || \
        ((hooktype == HOOKTYPE_HANDSHAKE) && !ValidateHook(hooktype_handshake, func)) || \
        ((hooktype == HOOKTYPE_AWAY) && !ValidateHook(hooktype_away, func)) || \
        ((hooktype == HOOKTYPE_INVITE) && !ValidateHook(hooktype_invite, func)) || \
        ((hooktype == HOOKTYPE_CAN_JOIN) && !ValidateHook(hooktype_can_join, func)) || \
        ((hooktype == HOOKTYPE_CAN_SEND_TO_CHANNEL) && !ValidateHook(hooktype_can_send_to_channel, func)) || \
        ((hooktype == HOOKTYPE_CAN_SEND_TO_USER) && !ValidateHook(hooktype_can_send_to_user, func)) || \
        ((hooktype == HOOKTYPE_CAN_KICK) && !ValidateHook(hooktype_can_kick, func)) || \
        ((hooktype == HOOKTYPE_FREE_CLIENT) && !ValidateHook(hooktype_free_client, func)) || \
        ((hooktype == HOOKTYPE_FREE_USER) && !ValidateHook(hooktype_free_user, func)) || \
        ((hooktype == HOOKTYPE_PRE_CHANMSG) && !ValidateHook(hooktype_pre_chanmsg, func)) || \
        ((hooktype == HOOKTYPE_KNOCK) && !ValidateHook(hooktype_knock, func)) || \
        ((hooktype == HOOKTYPE_MODECHAR_ADD) && !ValidateHook(hooktype_modechar_add, func)) || \
        ((hooktype == HOOKTYPE_MODECHAR_DEL) && !ValidateHook(hooktype_modechar_del, func)) || \
        ((hooktype == HOOKTYPE_CAN_JOIN_LIMITEXCEEDED) && !ValidateHook(hooktype_can_join_limitexceeded, func)) || \
        ((hooktype == HOOKTYPE_VISIBLE_IN_CHANNEL) && !ValidateHook(hooktype_visible_in_channel, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_CHANMODE) && !ValidateHook(hooktype_pre_local_chanmode, func)) || \
        ((hooktype == HOOKTYPE_PRE_REMOTE_CHANMODE) && !ValidateHook(hooktype_pre_remote_chanmode, func)) || \
        ((hooktype == HOOKTYPE_JOIN_DATA) && !ValidateHook(hooktype_join_data, func)) || \
        ((hooktype == HOOKTYPE_PRE_KNOCK) && !ValidateHook(hooktype_pre_knock, func)) || \
        ((hooktype == HOOKTYPE_PRE_INVITE) && !ValidateHook(hooktype_pre_invite, func)) || \
        ((hooktype == HOOKTYPE_INVITE_BYPASS) && !ValidateHook(hooktype_invite_bypass, func)) || \
        ((hooktype == HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL) && !ValidateHook(hooktype_view_topic_outside_channel, func)) || \
        ((hooktype == HOOKTYPE_CHAN_PERMIT_NICK_CHANGE) && !ValidateHook(hooktype_chan_permit_nick_change, func)) || \
        ((hooktype == HOOKTYPE_IS_CHANNEL_SECURE) && !ValidateHook(hooktype_is_channel_secure, func)) || \
        ((hooktype == HOOKTYPE_CHANNEL_SYNCED) && !ValidateHook(hooktype_channel_synced, func)) || \
        ((hooktype == HOOKTYPE_CAN_SAJOIN) && !ValidateHook(hooktype_can_sajoin, func)) || \
        ((hooktype == HOOKTYPE_WHOIS) && !ValidateHook(hooktype_whois, func)) || \
        ((hooktype == HOOKTYPE_WHO_STATUS) && !ValidateHook(hooktype_who_status, func)) || \
        ((hooktype == HOOKTYPE_MODE_DEOP) && !ValidateHook(hooktype_mode_deop, func)) || \
        ((hooktype == HOOKTYPE_PRE_KILL) && !ValidateHook(hooktype_pre_kill, func)) || \
        ((hooktype == HOOKTYPE_SEE_CHANNEL_IN_WHOIS) && !ValidateHook(hooktype_see_channel_in_whois, func)) || \
        ((hooktype == HOOKTYPE_DCC_DENIED) && !ValidateHook(hooktype_dcc_denied, func)) || \
        ((hooktype == HOOKTYPE_SERVER_HANDSHAKE_OUT) && !ValidateHook(hooktype_server_handshake_out, func)) || \
        ((hooktype == HOOKTYPE_SERVER_SYNCED) && !ValidateHook(hooktype_server_synced, func)) || \
        ((hooktype == HOOKTYPE_SECURE_CONNECT) && !ValidateHook(hooktype_secure_connect, func)) || \
        ((hooktype == HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION) && !ValidateHook(hooktype_can_bypass_channel_message_restriction, func)) || \
        ((hooktype == HOOKTYPE_SASL_CONTINUATION) && !ValidateHook(hooktype_sasl_continuation, func)) || \
        ((hooktype == HOOKTYPE_SASL_RESULT) && !ValidateHook(hooktype_sasl_result, func)) || \
        ((hooktype == HOOKTYPE_TAKE_ACTION) && !ValidateHook(hooktype_take_action, func)) || \
        ((hooktype == HOOKTYPE_FIND_TKLINE_MATCH) && !ValidateHook(hooktype_find_tkline_match, func)) || \
        ((hooktype == HOOKTYPE_WELCOME) && !ValidateHook(hooktype_welcome, func)) || \
        ((hooktype == HOOKTYPE_PRE_COMMAND) && !ValidateHook(hooktype_pre_command, func)) || \
        ((hooktype == HOOKTYPE_POST_COMMAND) && !ValidateHook(hooktype_post_command, func)) || \
        ((hooktype == HOOKTYPE_NEW_MESSAGE) && !ValidateHook(hooktype_new_message, func)) || \
        ((hooktype == HOOKTYPE_IS_HANDSHAKE_FINISHED) && !ValidateHook(hooktype_is_handshake_finished, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_QUIT_CHAN) && !ValidateHook(hooktype_pre_local_quit_chan, func)) || \
        ((hooktype == HOOKTYPE_IDENT_LOOKUP) && !ValidateHook(hooktype_ident_lookup, func)) || \
        ((hooktype == HOOKTYPE_CONFIGRUN_EX) && !ValidateHook(hooktype_configrun_ex, func)) || \
        ((hooktype == HOOKTYPE_ACCOUNT_LOGIN) && !ValidateHook(hooktype_account_login, func)) || \
        ((hooktype == HOOKTYPE_CLOSE_CONNECTION) && !ValidateHook(hooktype_close_connection, func)) || \
        ((hooktype == HOOKTYPE_CONNECT_EXTINFO) && !ValidateHook(hooktype_connect_extinfo, func)) || \
        ((hooktype == HOOKTYPE_IS_INVITED) && !ValidateHook(hooktype_is_invited, func)) || \
        ((hooktype == HOOKTYPE_POST_LOCAL_NICKCHANGE) && !ValidateHook(hooktype_post_local_nickchange, func)) || \
        ((hooktype == HOOKTYPE_POST_REMOTE_NICKCHANGE) && !ValidateHook(hooktype_post_remote_nickchange, func)) || \
        ((hooktype == HOOKTYPE_USERHOST_CHANGE) && !ValidateHook(hooktype_userhost_change, func)) || \
        ((hooktype == HOOKTYPE_REALNAME_CHANGE) && !ValidateHook(hooktype_realname_change, func)) || \
        ((hooktype == HOOKTYPE_IP_CHANGE) && !ValidateHook(hooktype_ip_change, func)) || \
        ((hooktype == HOOKTYPE_JSON_EXPAND_CLIENT) && !ValidateHook(hooktype_json_expand_client, func)) || \
        ((hooktype == HOOKTYPE_JSON_EXPAND_CLIENT_USER) && !ValidateHook(hooktype_json_expand_client_user, func)) || \
        ((hooktype == HOOKTYPE_JSON_EXPAND_CLIENT_SERVER) && !ValidateHook(hooktype_json_expand_client_server, func)) || \
        ((hooktype == HOOKTYPE_JSON_EXPAND_CHANNEL) && !ValidateHook(hooktype_json_expand_channel, func)) || \
        ((hooktype == HOOKTYPE_PRE_LOCAL_HANDSHAKE_TIMEOUT) && !ValidateHook(hooktype_pre_local_handshake_timeout, func)) || \
        ((hooktype == HOOKTYPE_REHASH_LOG) && !ValidateHook(hooktype_rehash_log, func)) || \
        ((hooktype == HOOKTYPE_DNS_FINISHED) && !ValidateHook(hooktype_dns_finished, func)) || \
        ((hooktype == HOOKTYPE_CONFIG_LISTENER) && !ValidateHook(hooktype_config_listener, func)) || \
        ((hooktype == HOOKTYPE_WATCH_ADD) && !ValidateHook(hooktype_watch_add, func)) || \
        ((hooktype == HOOKTYPE_WATCH_DEL) && !ValidateHook(hooktype_watch_del, func)) || \
        ((hooktype == HOOKTYPE_MONITOR_NOTIFICATION) && !ValidateHook(hooktype_monitor_notification, func)) || \
        ((hooktype == HOOKTYPE_SASL_AUTHENTICATE) && !ValidateHook(hooktype_sasl_authenticate, func)) || \
        ((hooktype == HOOKTYPE_SASL_MECHS) && !ValidateHook(hooktype_sasl_mechs, func))) \
        _hook_error_incompatible();
#endif /* GCC_TYPECHECKING */

/* Hook return values */
#define HOOK_CONTINUE 0
#define HOOK_ALLOW -1
#define HOOK_DENY 1

/* Callback types */
#define CALLBACKTYPE_CLOAK 1
#define CALLBACKTYPE_CLOAK_KEY_CHECKSUM 2
#define CALLBACKTYPE_CLOAK_EX 3
#define CALLBACKTYPE_BLACKLIST_CHECK 4
#define CALLBACKTYPE_REPUTATION_STARTTIME 5
#define CALLBACKTYPE_GEOIP_LOOKUP 6

/* To add a new efunction, only if you are an UnrealIRCd coder:
 * 1) Add a new entry here
 * 2) Add the function in src/api-efunctions.c
 * 3) Add the initalization in src/api-efunctions.c
 * 4) Add the extern entry in include/h.h in the
 *    section marked "Efuncs"
 */
/** Efunction types. */
enum EfunctionType {
	EFUNC_DO_JOIN=1,
	EFUNC_JOIN_CHANNEL,
	EFUNC_CAN_JOIN,
	EFUNC_DO_MODE,
	EFUNC_SET_MODE,
	EFUNC_SET_CHANNEL_MODE,
	EFUNC_SET_CHANNEL_TOPIC,
	EFUNC_CMD_UMODE,
	EFUNC_REGISTER_USER,
	EFUNC_TKL_HASH,
	EFUNC_TKL_TYPETOCHAR,
	EFUNC_TKL_ADD_SERVERBAN,
	EFUNC_TKL_DEL_LINE,
	EFUNC_TKL_CHECK_LOCAL_REMOVE_SHUN,
	EFUNC_TKL_EXPIRE,
	EFUNC_TKL_CHECK_EXPIRE,
	EFUNC_FIND_TKLINE_MATCH,
	EFUNC_FIND_SHUN,
	EFUNC_FIND_SPAMFILTER_USER,
	EFUNC_FIND_QLINE,
	EFUNC_FIND_TKLINE_MATCH_ZAP,
	EFUNC_TKL_STATS,
	EFUNC_TKL_SYNCH,
	EFUNC_CMD_TKL,
	EFUNC_TAKE_ACTION,
	EFUNC_MATCH_SPAMFILTER,
	EFUNC_MATCH_SPAMFILTER_MTAGS,
	EFUNC_JOIN_VIRUSCHAN,
	EFUNC_FIND_TKLINE_MATCH_ZAP_EX,
	EFUNC_SEND_LIST,
	EFUNC_STRIPCOLORS,
	EFUNC_SPAMFILTER_BUILD_USER_STRING,
	EFUNC_SEND_PROTOCTL_SERVERS,
	EFUNC_VERIFY_LINK,
	EFUNC_SEND_SERVER_MESSAGE,
	EFUNC_BROADCAST_MD_CLIENT,
	EFUNC_BROADCAST_MD_CHANNEL,
	EFUNC_BROADCAST_MD_MEMBER,
	EFUNC_BROADCAST_MD_MEMBERSHIP,
	EFUNC_INTRODUCE_USER,
	EFUNC_CHECK_DENY_VERSION,
	EFUNC_BROADCAST_MD_CLIENT_CMD,
	EFUNC_BROADCAST_MD_CHANNEL_CMD,
	EFUNC_BROADCAST_MD_MEMBER_CMD,
	EFUNC_BROADCAST_MD_MEMBERSHIP_CMD,
	EFUNC_MODDATA_ADD_S2S_MTAGS,
	EFUNC_MODDATA_EXTRACT_S2S_MTAGS,
	EFUNC_SEND_MODDATA_CLIENT,
	EFUNC_SEND_MODDATA_CHANNEL,
	EFUNC_SEND_MODDATA_MEMBERS,
	EFUNC_BROADCAST_MODDATA_CLIENT,
	EFUNC_MATCH_USER,
	EFUNC_USERHOST_SAVE_CURRENT,
	EFUNC_USERHOST_CHANGED,
	EFUNC_SEND_JOIN_TO_LOCAL_USERS,
	EFUNC_DO_NICK_NAME,
	EFUNC_DO_REMOTE_NICK_NAME,
	EFUNC_CHARSYS_GET_CURRENT_LANGUAGES,
	EFUNC_BROADCAST_SINFO,
	EFUNC_CONNECT_SERVER,
	EFUNC_IS_SERVICES_BUT_NOT_ULINED,
	EFUNC_PARSE_MESSAGE_TAGS,
	EFUNC_MTAGS_TO_STRING,
	EFUNC_TKL_CHARTOTYPE,
	EFUNC_TKL_CONFIGTYPETOCHAR,
	EFUNC_TKL_TYPE_STRING,
	EFUNC_TKL_TYPE_CONFIG_STRING,
	EFUNC_CAN_SEND_TO_CHANNEL,
	EFUNC_CAN_SEND_TO_USER,
	EFUNC_BROADCAST_MD_GLOBALVAR,
	EFUNC_BROADCAST_MD_GLOBALVAR_CMD,
	EFUNC_TKL_IP_HASH,
	EFUNC_TKL_IP_HASH_TYPE,
	EFUNC_TKL_ADD_BANEXCEPTION,
	EFUNC_TKL_ADD_NAMEBAN,
	EFUNC_TKL_ADD_SPAMFILTER,
	EFUNC_SENDNOTICE_TKL_ADD,
	EFUNC_SENDNOTICE_TKL_DEL,
	EFUNC_FREE_TKL,
	EFUNC_FIND_TKL_SERVERBAN,
	EFUNC_FIND_TKL_BANEXCEPTION,
	EFUNC_FIND_TKL_NAMEBAN,
	EFUNC_FIND_TKL_SPAMFILTER,
	EFUNC_FIND_TKL_EXCEPTION,
	EFUNC_SERVER_BAN_PARSE_MASK,
	EFUNC_SERVER_BAN_EXCEPTION_PARSE_MASK,
	EFUNC_TKL_ADDED,
	EFUNC_ADD_SILENCE,
	EFUNC_DEL_SILENCE,
	EFUNC_IS_SILENCED,
	EFUNC_LABELED_RESPONSE_SAVE_CONTEXT,
	EFUNC_LABELED_RESPONSE_SET_CONTEXT,
	EFUNC_LABELED_RESPONSE_FORCE_END,
	EFUNC_KICK_USER,
	EFUNC_WATCH_ADD,
	EFUNC_WATCH_DEL,
	EFUNC_WATCH_DEL_LIST,
	EFUNC_WATCH_GET,
	EFUNC_WATCH_CHECK,
	EFUNC_TKL_UHOST,
	EFUNC_DO_UNREAL_LOG_REMOTE_DELIVER,
	EFUNC_GET_CHMODES_FOR_USER,
	EFUNC_WHOIS_GET_POLICY,
	EFUNC_MAKE_OPER,
	EFUNC_UNREAL_MATCH_IPLIST,
	EFUNC_WEBSERVER_SEND_RESPONSE,
	EFUNC_WEBSERVER_CLOSE_CLIENT,
	EFUNC_WEBSERVER_HANDLE_BODY,
	EFUNC_RPC_RESPONSE,
	EFUNC_RPC_ERROR,
	EFUNC_RPC_ERROR_FMT,
	EFUNC_RPC_SEND_REQUEST_TO_REMOTE,
	EFUNC_RPC_SEND_RESPONSE_TO_REMOTE,
	EFUNC_RRPC_SUPPORTED,
	EFUNC_RRPC_SUPPORTED_SIMPLE,
	EFUNC_WEBSOCKET_HANDLE_WEBSOCKET,
	EFUNC_WEBSOCKET_CREATE_PACKET,
	EFUNC_WEBSOCKET_CREATE_PACKET_EX,
	EFUNC_WEBSOCKET_CREATE_PACKET_SIMPLE,
	EFUNC_CHECK_DENY_LINK,
	EFUNC_MTAG_GENERATE_ISSUED_BY_IRC,
	EFUNC_CANCEL_IDENT_LOOKUP,
	EFUNC_SPAMREPORT,
	EFUNC_CRULE_TEST,
	EFUNC_CRULE_PARSE,
	EFUNC_CRULE_EVAL,
	EFUNC_CRULE_FREE,
	EFUNC_CRULE_ERRSTRING,
	EFUNC_BAN_ACT_SET_REPUTATION,
	EFUNC_GET_CENTRAL_API_KEY,
	EFUNC_CENTRAL_SPAMREPORT,
	EFUNC_CENTRAL_SPAMREPORT_ENABLED,
	EFUNC_SASL_SUCCEEDED,
	EFUNC_SASL_FAILED,
	EFUNC_DECODE_AUTHENTICATE_PLAIN,
	EFUNC_EXIT_CLIENT,
	EFUNC_EXIT_CLIENT_FMT,
	EFUNC_EXIT_CLIENT_EX,
	EFUNC_BANNED_CLIENT,
	EFUNC_UNREAL_EXPAND_STRING,
};

/* Module flags */
#define MODFLAG_NONE	0x0000
#define MODFLAG_LOADED	0x0001 /* Fully loaded */
#define MODFLAG_TESTING 0x0002 /* Not yet initialized */
#define MODFLAG_INIT	0x0004 /* Initialized */
#define MODFLAG_DELAYED 0x0008 /* Delayed unload */

/* Module function return values */
#define MOD_SUCCESS 0
#define MOD_FAILED -1
#define MOD_DELAY 2

#define CONFIG_MAIN 1
#define CONFIG_SET 2
#define CONFIG_BAN 3
#define CONFIG_EXCEPT 4
#define CONFIG_DENY 5
#define CONFIG_ALLOW 6
#define CONFIG_CLOAKKEYS 7
#define CONFIG_SET_ANTI_FLOOD 8
#define CONFIG_REQUIRE 9
#define CONFIG_LISTEN 10
#define CONFIG_LISTEN_OPTIONS 11
#define CONFIG_SET_HISTORY_CHANNEL 12

#define MOD_HEADER Mod_Header
#define MOD_TEST() DLLFUNC int Mod_Test(ModuleInfo *modinfo)
#define MOD_INIT() DLLFUNC int Mod_Init(ModuleInfo *modinfo)
#define MOD_LOAD() DLLFUNC int Mod_Load(ModuleInfo *modinfo)
#define MOD_UNLOAD() DLLFUNC int Mod_Unload(ModuleInfo *modinfo)

#define CLOAK_KEY_CHECKSUM	RCallbacks[CALLBACKTYPE_CLOAK_KEY_CHECKSUM] != NULL ? RCallbacks[CALLBACKTYPE_CLOAK_KEY_CHECKSUM]->func.stringfunc() : "nil"

#ifdef DYNAMIC_LINKING
 #include "modversion.h"
#endif

#endif

