Dev:Hook API

What are hooks?
When you write an UnrealIRCd module you usually want your code to be called after a specific event has been triggered, such as a new user connecting, a user joining a channel, etc. The Hook API allows a module to "hook" into UnrealIRCd's core and modules. It basically informs UnrealIRCd that your function wants be called when XYZ happens.

UnrealIRCd has more than 80 of these hooks, so chances are high that we have one you are looking for!

How hooks are called
Here's some little explanation of how hooks are called "by us" from the core and our own modules. It may not interest you if you just want to "hook in" (for that, skip this and see next section):

When we want to run a hook we have a few functions available. The most basic ones are RunHook/RunHookX: RunHook(HOOKTYPE_XYZ); RunHook1(HOOKTYPE_XYZ, arg1); RunHook2(HOOKTYPE_XYZ, arg1, arg2); RunHook3(HOOKTYPE_XYZ, arg1, arg2, arg3); etc..

Sometimes modules want to have the option to 'stop processing', for this we have: RunHookReturnInt(hooktype,x,retchk); etc... (^^ improve)

How to have your module "hook in"
If you found the hook you are looking for (see next section) then you should use our HookAddXXX functions to make it so your function will be called by the hook system. Your HookAddXXX function goes in MOD_INIT, with the exception of two or three hooks which go in MOD_TEST.

We have different HookAddXX functions, the ones you need depend on the return type of the hook.

For hooks returning integers (the majority): HookAdd(module, hooktype, priority, func)

For hooks returning nothing (void): HookAddVoid(module, hooktype, priority, func)

For hooks returning a string (char *): HookAddPChar(module, hooktype, priority, func)

Example usage: int MOD_INIT(mymod)(ModuleInfo *modinfo) {   HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, mymod_connect); HookAddPChar(modinfo->handle, HOOKTYPE_PRE_USERMSG, 0, mymod_usermsg);

A note on priority
You may have noticed the priority argument, which we set to 0 in the above example. You can use 0 if it doesn't matter to you if your hook is called before or after hooks from other modules. Use a negative value such as -100 to be called BEFORE the rest, or a positive value such as 100 to be at the end of the hook callback list.

What is NOT safe to do in a hook
It is not safe to kill the user in a hook. That is, if your hook is called for 'sptr' then do not use exit_client(sptr....). Instead you should use dead_link(sptr, "reason here").

The reason for this is that the caller of the hook does not expect the user object to disappear.

The only exception to this rule is the PRE_LOCAL_CONNECT hook. You can safely return exit_client there.

HOOKTYPE_CONFIGTEST
This hook gets called when the configuration file 'test' runs. If you add your own configuration block or variable then you need this.

See Dev:Configuration API for the full configuration API, and Testing configuration there specifically.

HOOKTYPE_CONFIGPOSTTEST
This hook is called AFTER all HOOKTYPE_CONFIGTEST hooks have been called. If you require a specific configuration block or variable then this allows you (with the help of HOOKTYPE_CONFIGTEST earlier) to see if some option was never specified.

See Dev:Configuration API for the full configuration API, and Checking for missing configuration items there specifically.

HOOKTYPE_CONFIGRUN
Once the configuration file has passed testing (see previous two hooks) UnrealIRCd will "run" the configuration file. At this point you can no longer "cancel" processing of the configuration file so any options specified are assumed to be valid at this point and you must deal with it.

See Dev:Configuration API for the full configuration API, and Running the configuration there specifically.

HOOKTYPE_LOCAL_CONNECT
Called when a user connects to OUR server. When this hook is called the user is fully "registered" in IRC terms, that means "USER" and "NICK" have been received and the user is now "online".

NOTE 1: If you want to take some sort of reject/kill action on the user (eg: for access control) then you must use HOOKTYPE_PRE_LOCAL_CONNECT instead.

NOTE 2: If you want to send a message to the user and want precise control over when the message should be sent, eg after numeric 001, after 005, after initial LUSERS or MOTD, etc... then use HOOKTYPE_WELCOME.

Add the hook from MOD_INIT: HookAdd(modinfo->handle, HOOKTYPE_LOCAL_CONNECT, 0, mymod_connect);

Function syntax and example: int mymod_connect(aClient *sptr) {   sendnotice(sptr, "Hi %s, welcome on-board!!", sptr->name); return 0; }

HOOKTYPE_PRE_LOCAL_CONNECT
Called after "USER" and "NICK" but before the user is fully registered / online. In contrast to HOOKTYPE_LOCAL_CONNECT this function allows you to reject/kill the user.

This function is normally only used if your module provides some sort of access control functionality. If you just want to take some sort of other action, like send a message or log something, then use HOOKTYPE_LOCAL_CONNECT or HOOKTYPE_WELCOME instead.

Add the hook from MOD_INIT: HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, 0, mymod_preconnect);

Function syntax and example: int mymod_preconnect(aClient *sptr) {   if (!strcmp(sptr->name, "lazyuser")) return exit_client(sptr, sptr, sptr, "We do not allow lazy people on this server, sorry");

return 0; }

HOOKTYPE_REMOTE_CONNECT
Called when a user connects to a remote server.

HOOKTYPE_LOCAL_QUIT
Called when a user gets disconnected, either via /QUIT or any other reason including but not limited to: ping timeout, being killed, network errors, etc.

If your module manages it's own per-user struct then this function allows you to free it from here (you may also be interested in HOOKTYPE_UNKUSER_QUIT then). Naturally it can also be used for logging or any other action you think is useful on user disconnection.

Hook from MOD_INIT: HookAdd(modinfo->handle, HOOKTYPE_LOCAL_QUIT, 0, mymod_quit);

Function syntax & example: int mymod_quit(aClient *sptr, char *comment) {   SomeDynamicInfo *e = find_some_dynamic_info(sptr); if (e) free_some_dynamic_info_and_remove_from_list(e);

return 0; }

HOOKTYPE_PRE_LOCAL_QUIT
Similar to HOOKTYPE_LOCAL_QUIT but this one is called a little earlier and is only meant when you want to change the quit reason. An example use case would be a censorship module.

Hook from MOD_INIT: HookAddPChar(modinfo->handle, HOOKTYPE_PRE_LOCAL_QUIT, 0, mymod_prequit);

Function syntax & example: char *mymod_prequit(aClient *acptr, char *comment) {   if (strstr(comment, "shit")) return NULL; // we could return a string here (which will then be used), or we can return NULL which means: no quit reason

return comment; }

HOOKTYPE_UNKUSER_QUIT
Called when an "unknown" user disconnects. Unknown here means: any client who is not online yet. This hook is for example triggered if you connect to the IRC port and then disconnect without sending any USER or NICK. It could be used for logging or to deallocate any custom made structures if necessary (not too common and HOOKTYPE_FREE_CLIENT is arguably more suitable for that).

The syntax and usage is exactly the same as HOOKTYPE_LOCAL_QUIT. Note, though, that sptr->name may or may not be filled in (as NICK may or may not have been received). Additionally, even if sptr->name contains a nickname it has not yet been fully "approved" and since it's not really online the other servers know nothing about this user.

HOOKTYPE_REMOTE_QUIT
Called when a user disconnects or gets killed on a remote server.

HOOKTYPE_LOCAL_NICKCHANGE
int hooktype_local_nickchange(aClient *sptr, char *newnick);

HOOKTYPE_REMOTE_NICKCHANGE
int hooktype_remote_nickchange(aClient *cptr, aClient *sptr, char *newnick);

HOOKTYPE_REHASHFLAG
int hooktype_rehashflag(aClient *cptr, aClient *sptr, char *str);

HOOKTYPE_PRE_LOCAL_PART
char *hooktype_pre_local_part(aClient *sptr, aChannel *chptr, char *comment);

HOOKTYPE_REHASH
int hooktype_rehash(void);

HOOKTYPE_SERVER_CONNECT
int hooktype_server_connect(aClient *sptr);

HOOKTYPE_SERVER_QUIT
int hooktype_server_quit(aClient *sptr);

HOOKTYPE_STATS
int hooktype_stats(aClient *sptr, char *str);

HOOKTYPE_LOCAL_JOIN
int hooktype_local_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *parv[]);

HOOKTYPE_USERMSG
int hooktype_usermsg(aClient *sptr, aClient *to, char *text, int notice);

HOOKTYPE_CHANMSG
int hooktype_chanmsg(aClient *sptr, aChannel *chptr, char *text, int notice);

HOOKTYPE_LOCAL_PART
int hooktype_local_part(aClient *cptr, aClient *sptr, aChannel *chptr, char *comment);

HOOKTYPE_LOCAL_KICK
int hooktype_local_kick(aClient *cptr, aClient *sptr, aClient *victim, aChannel *chptr, char *comment);

HOOKTYPE_LOCAL_CHANMODE
int hooktype_local_chanmode(aClient *cptr, aClient *sptr, aChannel *chptr, char *modebuf, char *parabuf, time_t sendts, int samode);

HOOKTYPE_LOCAL_TOPIC
int hooktype_local_topic(aClient *cptr, aClient *sptr, aChannel *chptr, char *topic);

HOOKTYPE_LOCAL_OPER
int hooktype_local_oper(aClient *sptr, int add);

HOOKTYPE_LOCAL_PASS
int hooktype_local_pass(aClient *sptr, char *password);

HOOKTYPE_PRE_LOCAL_JOIN
This function allows you, unlike HOOKTYPE_LOCAL_JOIN, to "reject" a local join. Useful if your module provides some sort of channel access restriction.

int hooktype_pre_local_join(aClient *sptr, aChannel *chptr, char *parv[]);

HOOKTYPE_PRE_LOCAL_KICK
This function allows you, unlike HOOKTYPE_LOCAL_KICK, to "reject" a KICK request from a local client. For example if your module creates some special class of users that may not be kicked.

char *hooktype_pre_local_kick(aClient *sptr, aClient *victim, aChannel *chptr, char *comment);

Note: see also HOOKTYPE_CAN_KICK

HOOKTYPE_PRE_LOCAL_TOPIC
This function allows you, unlike HOOKTYPE_LOCAL_TOPIC, to "reject" a topic change request from a local user.

char *hooktype_pre_local_topic(aClient *cptr, aClient *sptr, aChannel *chptr, char *topic);

HOOKTYPE_CHANNEL_CREATE
int hooktype_channel_create(aClient *sptr, aChannel *chptr);

HOOKTYPE_CHANNEL_DESTROY
int hooktype_channel_destroy(aChannel *chptr, int *should_destroy);

HOOKTYPE_REMOTE_CHANMODE
int hooktype_remote_chanmode(aClient *cptr, aClient *sptr, aChannel *chptr, char *modebuf, char *parabuf, time_t sendts, int samode);

HOOKTYPE_TKL_EXCEPT
int hooktype_tkl_except(aClient *cptr, aTKline *tkl);

HOOKTYPE_FIND_TKLINE_MATCH
int hooktype_find_tkline_match(aClient *sptr, aTKline *tkl);

HOOKTYPE_UMODE_CHANGE
int hooktype_umode_change(aClient *sptr, long setflags, long newflags);

HOOKTYPE_TOPIC
int hooktype_topic(aClient *cptr, aClient *sptr, aChannel *chptr, char *topic);

HOOKTYPE_REHASH_COMPLETE
int hooktype_rehash_complete(void);

HOOKTYPE_TKL_ADD
int hooktype_tkl_add(aClient *cptr, aClient *sptr, aTKline *tkl, int parc, char *parv[]);

HOOKTYPE_TKL_DEL
int hooktype_tkl_del(aClient *cptr, aClient *sptr, aTKline *tkl, int parc, char *parv[]);

HOOKTYPE_LOCAL_KILL
int hooktype_local_kill(aClient *sptr, aClient *victim, char *comment);

HOOKTYPE_LOG
int hooktype_log(int flags, char *timebuf, char *buf);

HOOKTYPE_REMOTE_JOIN
int hooktype_remote_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *parv[]);

HOOKTYPE_REMOTE_PART
int hooktype_remote_part(aClient *cptr, aClient *sptr, aChannel *chptr, char *comment);

HOOKTYPE_REMOTE_KICK
int hooktype_remote_kick(aClient *cptr, aClient *sptr, aClient *victim, aChannel *chptr, char *comment);

HOOKTYPE_LOCAL_SPAMFILTER
int hooktype_local_spamfilter(aClient *acptr, char *str, char *str_in, int type, char *target, aTKline *tkl);

HOOKTYPE_SILENCED
int hooktype_silenced(aClient *cptr, aClient *sptr, aClient *to, int notice);

HOOKTYPE_POST_SERVER_CONNECT
int hooktype_post_server_connect(aClient *sptr);

HOOKTYPE_RAWPACKET_IN
int hooktype_rawpacket_in(aClient *sptr, char *readbuf, int *length);

HOOKTYPE_LOCAL_NICKPASS
int hooktype_local_nickpass(aClient *sptr, aClient *nickserv);

HOOKTYPE_PACKET
int hooktype_packet(aClient *from, aClient *to, char **msg, int length);

HOOKTYPE_HANDSHAKE
int hooktype_handshake(aClient *sptr);

HOOKTYPE_AWAY
int hooktype_away(aClient *sptr, char *reason);

HOOKTYPE_INVITE
int hooktype_invite(aClient *from, aClient *to, aChannel *chptr);

HOOKTYPE_CAN_SEND
int hooktype_can_send(aClient *sptr, aChannel *chptr, char *text, Membership *member, int notice);

HOOKTYPE_CAN_SEND_SECURE
int hooktype_can_send_secure(aClient *sptr, aChannel *chptr);

HOOKTYPE_CAN_JOIN
int hooktype_can_join(aClient *sptr, aChannel *chptr, char *key, char *parv[]);

HOOKTYPE_CAN_JOIN_LIMITEXCEEDED
int hooktype_can_join_limitexceeded(aClient *sptr, aChannel *chptr, char *key, char *parv[]);

HOOKTYPE_CAN_SAJOIN
int hooktype_can_sajoin(aClient *target, aChannel *chptr, aClient *sptr);

HOOKTYPE_CAN_KICK
int hooktype_can_kick(aClient *sptr, aClient *victim, aChannel *chptr, char *comment, long sptr_flags, long victim_flags, char **error);

HOOKTYPE_FREE_CLIENT
int hooktype_free_client(aClient *acptr);

HOOKTYPE_FREE_USER
int hooktype_free_user(anUser *user, aClient *acptr);

HOOKTYPE_PRE_CHANMSG
Similar to HOOKTYPE_CHANMSG, this one is called when a user wants to send a channel message. This hook, however, allows you to actually change the channel message (or reject/block it entirely). It used by for example censorship modules.

char *hooktype_pre_chanmsg(aClient *sptr, aChannel *chptr, char *text, int notice);

Note: see also HOOKTYPE_CAN_SEND ??

HOOKTYPE_PRE_USERMSG
Similar to HOOKTYPE_USERMSG, this one is called when a user wants to send a private message to another user. This hook, however, allows you to actually change the message (or reject/block it entirely). It used by for example censorship modules.

char *hooktype_pre_usermsg(aClient *sptr, aClient *to, char *text, int notice);

HOOKTYPE_KNOCK
int hooktype_knock(aClient *sptr, aChannel *chptr);

HOOKTYPE_MODECHAR_ADD
int hooktype_modechar_add(aChannel *chptr, int modechar);

HOOKTYPE_MODECHAR_DEL
int hooktype_modechar_del(aChannel *chptr, int modechar);

HOOKTYPE_EXIT_ONE_CLIENT
int hooktype_exit_one_client(aClient *sptr);

HOOKTYPE_VISIBLE_IN_CHANNEL
int hooktype_visible_in_channel(aClient *sptr, aChannel *chptr);

HOOKTYPE_PRE_LOCAL_CHANMODE
Is called a little earlier than HOOKTYPE_LOCAL_CHANMODE ? (does not allow rejection right?)

HOOKTYPE_PRE_REMOTE_CHANMODE
Is called a little earlier than HOOKTYPE_REMOTE_CHANMODE ? (does not allow rejection)

HOOKTYPE_JOIN_DATA
int hooktype_join_data(aClient *who, aChannel *chptr);

HOOKTYPE_PRE_KNOCK
Similar to HOOKTYPE_KNOCK this is called when a user issues a /KNOCK request. Unlike HOOKTYPE_KNOCK, this function actually allows you to reject/block the /KNOCK.

HOOKTYPE_PRE_INVITE
Similar to HOOKTYPE_INVITE this is called when a user issues an /INVITE request. Unlike HOOKTYPE_INVITE, this function actually allows you to reject/block the /INVITE.

HOOKTYPE_OPER_INVITE_BAN
int hooktype_oper_invite_ban(aClient *sptr, aChannel *chptr);

HOOKTYPE_VIEW_TOPIC_OUTSIDE_CHANNEL
int hooktype_view_topic_outside_channel(aClient *sptr, aChannel *chptr);

HOOKTYPE_CHAN_PERMIT_NICK_CHANGE
int hooktype_chan_permit_nick_change(aClient *sptr, aChannel *chptr);

HOOKTYPE_IS_CHANNEL_SECURE
int hooktype_is_channel_secure(aChannel *chptr);

HOOKTYPE_CHANNEL_SYNCED
void hooktype_channel_synced(aChannel *chptr, unsigned short merge, unsigned short removetheirs, unsigned short nomode);

HOOKTYPE_WHOIS
int hooktype_whois(aClient *sptr, aClient *target);

HOOKTYPE_CHECK_INIT
int hooktype_check_init(aClient *cptr, char *sockname, size_t size);

HOOKTYPE_WHO_STATUS
int hooktype_who_status(aClient *sptr, aClient *target, aChannel *chptr, Member *member, char *status, int cansee);

HOOKTYPE_MODE_DEOP
int hooktype_mode_deop(aClient *sptr, aClient *victim, aChannel *chptr, u_int what, char modechar, long my_access, char **badmode);

HOOKTYPE_PRE_KILL
int hooktype_pre_kill(aClient *sptr, aClient *victim, char *killpath);

HOOKTYPE_PLACE_HOST_BAN
int hooktype_place_host_ban(aClient *sptr, int action, char *reason, long duration);

HOOKTYPE_SEE_CHANNEL_IN_WHOIS
int hooktype_see_channel_in_whois(aClient *sptr, aClient *target, aChannel *chptr);

HOOKTYPE_DCC_DENIED
int hooktype_dcc_denied(aClient *sptr, aClient *target, char *realfile, char *displayfile, ConfigItem_deny_dcc *denydcc);

HOOKTYPE_SERVER_HANDSHAKE_OUT
int hooktype_server_handshake_out(aClient *sptr);

HOOKTYPE_SERVER_SYNCHED
Called when the server is synchronized. That is: the server has linked in and all the channel/user state synchronization and such has been done. int hooktype_server_synched(aClient *sptr);

HOOKTYPE_SECURE_CONNECT
Called when a secure user connects. Used internally so the secure flag can be stripped. Not really meant for 3rd party modules. int hooktype_secure_connect(aClient *sptr);

HOOKTYPE_CAN_BYPASS_CHANNEL_MESSAGE_RESTRICTION
Called to check if a user can bypass a channel message/notice restriction. int hooktype_can_bypass_channel_message_restriction(aClient *sptr, aChannel *chptr, BypassMessageRestrictionType bypass_type); Naturally, sptr is the client pointer and chptr the channel. The bypass_type is one of: This hook is used by the extended ban exception +e ~m:type:mask
 * BYPASS_MSG_EXTERNAL: bypass +n
 * BYPASS_MSG_MODERATED: bypass +m
 * BYPASS_MSG_COLOR: bypass +c/+S
 * BYPASS_MSG_CENSOR: bypass +G
 * BYPASS_MSG_NOTICE: bypass +T

HOOKTYPE_WELCOME
Available since UnrealIRCd 4.2.2

When the user connects, this hook is called after each welcome numeric is sent. This way you can insert text at any place. int hooktype_welcome(aClient *sptr, int after_numeric);

At the time of writing this documentation (Jan 2019) the hook is called with:
 * after_numeric=0: before any welcome numerics are sent (before 001)
 * after_numeric=1: after numeric 001 (RPL_WELCOME)
 * after_numeric=2: after numeric 002 (RPL_YOURHOST)
 * after_numeric=3: after numeric 003 (RPL_CREATED)
 * after_numeric=4: after numeric 003 (RPL_MYINFO)
 * after_numeric=5: after numeric 005 (RPL_ISUPPORT)
 * after_numeric=396: after numeric 396 (RPL_HOSTHIDDEN), if any
 * after_numeric=266: after numeric 266 (LUSERS)
 * after_numeric=376: after numeric 376 (MOTD)
 * after_numeric=999: After all the initial numerics were sent, before (auto-)joining any channels