[READ-ONLY] Mirror of https://github.com/VibeDevelopers/Comet.
Comet doc technical hooks.md
60 kB · 1326 lines
Markdown
at dev

Hook documentation #

Hooks allow modules to execute logic during predefined spots of other module code or core code. Each function attached to a hook is passed a data pointer containing contextual information to assist with any logic that function may need to perform. For examples on how to register a hook function with a module or define a new hook in a module, see extensions/example_module.c.

Hook list #

Module Hook name Description
Core after_client_exit A user has left the server
Core burst_channel A channel was sent to a remote server during a netjoin
Core burst_client A user was sent to a remote server during a netjoin
Core burst_finished We finished bursting users/channels during a netjoin
Core can_join A local user is about to join a channel
Core can_kick A user is about to be kicked from a channel
Core can_send Called when checking if a user can talk on a channel
Core cap_change A user's client capabilities changed
Core client_exit A user is leaving the server
Core conf_read_end Called after reading conf files on start or rehash
Core conf_read_start Called before reading conf files on start or rehash
Core get_channel_access Called when checking a user's privileges on a channel
Core introduce_client A user has been introduced to the rest of the network
Core message_handler Determine the handler used to process an incoming message
Core message_tag Called for each tag on incoming messages
Core new_local_user A user connected locally, before introducing to network
Core new_remote_user A remote user was introduced, before propagating onward
Core outbound_msgbuf A message is about to be sent to one or more targets
Core parse_end We have finished processing an incoming message
Core priv_change An oper's privset changed
Core privmsg_channel A message or PART is about to be sent to a channel
Core privmsg_user A message is about to be sent to a user
Core rehash The server finished rehashing its conf files
Core server_introduced A server has been introduced to the network (pre-burst)
Core server_eob A server has finished processing a netjoin burst from us
Core umode_changed The modes for a user have changed
Core user_welcome Called during the registration burst for a new local user
m_info doing_info_conf Conf entries have been sent to an oper using INFO
m_invite can_invite A local user is about to invite another user to a channel
m_invite invite A local user is about to be invited to a channel
m_join can_create_channel A user is about to create a channel
m_join channel_join A user has finished joining a channel
m_join channel_lowerts A remote server gave us a lower TS for a channel
m_kill can_kill A local oper is about to kill a user
m_monitor new_monitor A user has added a new entry to their MONITOR list
m_nick local_nick_change A local user has changed nicknames
m_nick remote_nick_change A remote user has changed nicknames
m_quit client_quit A user has quit from the network
m_services account_change A user's services account has changed
m_stats doing_stats A user has run STATS
m_stats doing_stats_show_idle Called to determine if send/recv data is shown in STATS l
m_trace doing_trace_show_idle Called to determine if timestamp data is shown in TRACE
m_version doing_version_confopts Called before sending confopts in VERSION
m_who doing_who_show_idle Called to determine if idle time is shown in WHOX
m_whois doing_whois A local user has run WHOIS
m_whois doing_whois_channel_visibility Called to determine which channels to display in WHOIS
m_whois doing_whois_global A user has run a remote WHOIS targeting this server
m_whois doing_whois_show_idle Called to determine if idle time is shown in WHOIS

Registering hook functions #

To register a function to be called when a hook is executed, you will need to create an mapi_hfn_list_av1[] in your module. Each element of the array contains the following, in order:

  1. The name of the hook as a string. This hook name does not need to exist at the time of registration. If the hook doesn't exist and is later defined, the hook function will still be called.
  2. The function to execute. The function takes a single void * parameter and has a void return value.
  3. Optionally, a hook priority. There are six priority levels defined: HOOK_LOWEST, HOOK_LOW, HOOK_NORMAL, HOOK_HIGH, HOOK_HIGHEST, and HOOK_MONITOR. Hooks are executed in order of lowest to highest priority with HOOK_MONITOR being executed last. If unspecified, normal priority is used.

The final array element must contain a NULL hook name, to indicate the end of the array. This array is then passed into the DECLARE_MODULE_AV2 macro.

Registering new hooks #

A module can state that it is registering additional hooks that it will call internally. To do so, you will need to create an mapi_hlist_av1[] in your module. Each element of the array contains the following, in order:

  1. The name of the hook as a string. It is possible to register the same hook name multiple times across different modules without error. Should this happen, all modules registering that hook will share the same registration and hook function list.
  2. An int * to store the hook registration number. This pointer should be declared as a static int at the file scope of the module.

The final array element must contain a NULL hook name, to indicate the end of the array. This array is then passed into the DECLARE_MODULE_AV2 macro.

Calling hooks #

If a module wishes to call a hook, it uses the call_hook() function declared in include/hook.h. It passes the integer registration number of the hook to this function along with the data pointer to be passed to hook functions.

When calling an existing hook, check the documentation below to ensure that the data pointer you are passing is compatible with the documented usage elsewhere, to avoid crashing other modules with unexpected data.

Example:

/* File scope (DECLARE_MODULE_AV2 omitted for brevity): */
static int h_doing_whois;
mapi_hlist_av1[] my_module_hlist = {
    { "doing_whois", &h_doing_whois },
    { NULL, NULL }
};

/* Inside of some relevant function: */
hook_data_client hdata;
hdata.client = source_p;
hdata.target = target_p;
call_hook(h_doing_whois, &hdata);

Hook data structures #

Most of the time, the data pointer passed to hook functions is one of the various structures defined in include/hook.h. This is not always the case, so consult the documentation for the specific hook to find out what it passes for its data pointer, how it populates its fields, and whether data can flow back to the calling module.

When defining a new hook, pick one of the predefined structures to use for your data whenever possible. Think about the interaction model you want between hooked functions and your module, such as if you want other modules to be able to pass data back to you by modifying any of the field values.

Hook details #

account_change #

This hook is called after a user's services user (account name) has changed. The user may be either local or remote. This hook is not called when a user's services account changes during registration.

Hook data: hook_cdata *

Fields:

  • client (struct Client *): The user whose services account changed
  • arg1 (const char *): The user's previous services account
  • arg2 (const void *): Unused (always NULL)

The user's updated account is available via client->user->suser. If the user was previously logged out, arg1 will be an empty string. If the user just logged out, then client->user->suser will be an empty string.

Although unlikely, it is possible for the old and updated account names to be identical or vary only by casing; no checks are made for this before invoking the hook. Hook functions that need to operate only if the services account meaningfully changed should compare the two strings via irccmp().

after_client_exit #

This hook is called after any client (user or server) exits the network. All cleanup has happened for this client already. If the client is local, it has been placed on the dead_list. Remote clients are not added to dead_list.

Hook data: always NULL

Given the lack of hook data, if you need to perform cleanup on the specific client that is exiting, use the client_exit hook instead. The after_client_exit hook is suitable if you need to investigate global state after cleanup has been performed in order to carry out some logic.

burst_client #

This hook is called once for each client we send to another server during a netjoin burst. The client has already been introduced to the remote server before the hook is called.

Hook data: hook_data_client *

Fields:

  • client (struct Client *): The server we are sending the burst to
  • target (struct Client *): The client (user or server) we are bursting

burst_channel #

This hook is called once for each channel we send to another server during a netjoin burst (n.b. local-only '&' channels are not sent during bursts). The channel and its member list have already been sent to the remote server before the hook is called.

Hook data: hook_data_channel *

Fields:

  • client (struct Client *): The server we are sending the burst to
  • chptr (struct Channel *): The channel we are bursting
  • approved (int): Unused (always 0)

burst_finished #

This hook is called after we have finished bursting all clients and channels. We have not yet sent the end of burst marker to the remote server at the time this hook is called.

Hook data: hook_data_client *

Fields:

  • client (struct Client *): The server we have sent the burst to
  • target (struct Client *): Unused (always NULL)

This hook is ideal for other data that needs to be sent to a remote server during a netjoin burst, e.g. new global channel types added by modules since core code only bursts '#' channels.

can_create_channel #

This hook is called when a local user attempts to JOIN a nonexistent channel. Hook functions can reject the channel creation attempt by modifying the passed-in data.

Hook data: hook_data_can_create_channel *

Fields:

  • client (struct Client *): The user attempting to create the channel
  • name (const char *): The channel name the user is attempting to create
  • approved (int): Output field indicating whether this attempt succeeds

The hook function should modify the approved field according to whether the channel creation attempt should succeed or the error that should be sent to the user should it not succeed. The following values are recognized:

  • 0 (default): Allow the channel creation
  • ERR_BADCHANNELKEY: Prevent the channel creation and send the user a message that they have an incorrect channel key (+k)
  • ERR_BANNEDFROMCHAN: Prevent the channel creation and send the user a message that they are banned from the channel (+b)
  • ERR_CHANNELISFULL: Prevent the channel creation and send the user a message that the channel has reached its user limit (+l)
  • ERR_CUSTOM: Prevent the channel creation and do not send any message to the user
  • ERR_INVITEONLYCHAN: Prevent the channel creation and send the user a message that the channel is invite only (+i)
  • ERR_NEEDREGGEDNICK: Prevent the channel creation and send the user a message that a registered nickname is required to join (+r)
  • ERR_THROTTLE: Prevent the channel creation and send the user a message that the join throttle was exceeded and that they should try again (+j)
  • Any other value: Prevent the channel creation and send the user a generic message that they cannot join the channel with the specified value as the numeric sent to the client. The numeric will additionally have the channel name as a parameter.

Sending a custom numeric as the value is not recommended as there is no control over the number of parameters sent nor the error message. Care must be taken to ensure any numeric sent is compatible with three parameters (client, channel, and the generic error message "Cannot join channel"). It is better to set it to ERR_CUSTOM and send an appropriate error message to the client from within your hook function.

can_invite #

This hook is called when a local user invites another user (whether local or remote) into a channel. It is called even when the user does not have permissions to issue invites to the channel. However, if the source user is not on the channel, the invite attempt is rejected before this hook is called. Hook functions can allow or reject the invite attempt by modifying the passed-in data.

Hook data: hook_data_channel_approval *

Fields:

  • client (struct Client *): The user sending the INVITE
  • chptr (struct Channel *): The channel that client is inviting target to
  • msptr (struct membership *): The client's membership on chptr
  • target (struct Client *): The user being invited
  • approved (int): Output field indicating whether this attempt succeeds
  • dir (int): Unused (always 0)
  • modestr (const char *): Unused (always NULL)
  • error (const char *): Output field containing an optional error message to send to the user

The default value for approved is 0 if the client is a channel operator or if the channel has the +g (free invite) channel mode set. Otherwise, the default value is 1.

To approve the invite attempt, approved must be set to 0. If it is set to any other value, how the caller behaves depends on whether error is also set. If approved is nonzero and error is NULL, the invite attempt will be rejected and the client will be sent the numeric ERR_CHANOPPRIVSNEEDED. If approved is nonzero and error is not NULL, the invite attempt will be rejected and the client will be sent the approved value as a numeric with parameters contained in the error string (the client's nick is automatically inserted as the first parameter and should not be present in the string).

Example:

/* reject all invite attempts with a custom message */
void can_invite_example(void *data_)
{
    hook_data_channel_approval *data = data_;
    char error[BUFSIZE];

    snprintf(error, sizeof(error), "%s %s :%s",
        data->target->name,
        chptr->name,
        "The INVITE command is disabled");

    data->approved = ERR_USERONCHANNEL;
    data->error = &error;
}

can_join #

This hook is called when checking whether a local user is able to join a channel. The target channel will always exist, although it may have just been created. Newly-created channels passed to this hook will have no members. Hook functions can allow or reject the join attempt by modifying the passed-in data.

Hook data: hook_data_channel *

Fields:

  • client (struct Client *): The local user attempting to join the channel
  • chptr (struct Channel *): The channel being joined
  • approved (int): Output field indicating whether this attempt succeeds

The hook function should modify the approved field according to whether the join attempt should succeed or the error that should be sent to the user should it not succeed. The following values are recognized:

  • 0: Allow the join
  • ERR_BADCHANNELKEY: Prevent the join and send the user a message that they have an incorrect channel key (+k)
  • ERR_BANNEDFROMCHAN: Prevent the join and send the user a message that they are banned from the channel (+b)
  • ERR_CHANNELISFULL: Prevent the join and send the user a message that the channel has reached its user limit (+l)
  • ERR_CUSTOM: Prevent the join and do not send any message to the user
  • ERR_INVITEONLYCHAN: Prevent the join and send the user a message that the channel is invite only (+i)
  • ERR_NEEDREGGEDNICK: Prevent the join and send the user a message that a registered nickname is required to join (+r)
  • ERR_THROTTLE: Prevent the join and send the user a message that the join throttle was exceeded and that they should try again (+j)
  • Any other value: Prevent the join and send the user a generic message that they cannot join the channel with the specified value as the numeric sent to the client. The numeric will additionally have the channel name as a parameter.

The default value for approved will be either 0 or one of the error numerics specified above (except ERR_CUSTOM), depending on if any of them apply based on the default logic which runs before the hook is called.

Sending a custom numeric as the value is not recommended as there is no control over the number of parameters sent nor the error message. Care must be taken to ensure any numeric sent is compatible with three parameters (client, channel, and the generic error message "Cannot join channel"). It is better to set it to ERR_CUSTOM and send an appropriate error message to the client from within your hook function.

can_kick #

This hook is called when checking if a local user is able to kick a user (who may be either local or remote) from a channel. It is only called if the user has channel operator access to the channel (see also the get_channel_access hook) and the target is on the channel and is not a service (umode +S). Hook functions can reject the kick attempt by modifying the passed-in data.

Hook data: hook_data_channel_approval *

Fields:

  • client (struct Client *): The local user issuing the KICK
  • chptr (struct Channel *): The channel the target is being kicked from
  • msptr (struct membership *): The client's membership on chptr
  • target (struct Client *): The user being kicked
  • approved (int): Output field indicating whether this attempt succeeds
  • dir (int): Always set to MODE_ADD (1)
  • modestr (const char *): Unused (always NULL)
  • error (const char *): Unused (always NULL)

The default value of approved is 1, which indicates that the kick is allowed. Setting approved to 0 rejects the kick attempt. No error message is sent to the client on a rejected attempt; it is up to the hook function to send any appropriate error message. Any nonzero value also indicates that the kick is allowed.

can_kill #

This hook is called when a local oper issues a kill for a user, who may be either local or remote. It is only called after verifying that the oper has the oper:kill privilege. Hook functions can reject the kill attempt by modifying the passed-in data.

Hook data: hook_data_client_approval *

Fields:

  • client (struct Client *): The oper issuing the kill
  • target (struct Client *): The user being killed
  • approved (int): Output field indicating whether this attempt succeeds

The default value of approved is 1, which indicates that the kill is allowed. Setting approved to 0 rejects the kill attempt. No error message is sent to the client on a rejected attempt; it is up to the hook function to send any appropriate error message. Any nonzero value also indicates that the kill is allowed.

can_send #

This hook is called whenever the server needs to check if a user can send a message to a channel, e.g. a PRIVMSG, NOTICE, TAGMSG, TOPIC change, or PART reason. This hook is only called for local users with respect to TOPIC and PART, but is called for both local and remote users for PRIVMSG, NOTICE, and TAGMSG. This hook is not called if the user is not a member of the channel, even if the channel allows external messages (-n). Hook functions can allow or reject the ability to send the message by modifying the passed-in data.

Hook data: hook_data_channel_approval *

Fields:

  • client (struct Client *): The user sending the message
  • chptr (struct Channel *): The channel the message is being sent to
  • msptr (struct membership *): The client's membership on chptr
  • target (struct Client *): Unused (always NULL)
  • approved (int): Output field indicating whether this attempt succeeds
  • dir (int): Set to MODE_QUERY (0) if default logic indicates the user is allowed to send to the channel and MODE_ADD (1) if default logic indicates the user cannot send to the channel
  • modestr (const char *): Unused (always NULL)
  • error (const char *): Unused (always NULL)

The hook function should modify approved to indicate whether the user is allowed to send a message to the channel. The following values are allowed:

  • CAN_SEND_NO: The user is not allowed to send messages to the channel. This is the default value if the user is not opped or voiced on the channel and at least one of the following criteria is met:
    • The channel is moderated (+m)
    • The user is banned or quieted from the channel
    • The user is joined to a RESVed channel and is not an oper with the oper:general privilege and is not RESV exempt from their I-line
  • CAN_SEND_NONOP: The user is allowed to send messages to the channel. Flood and target change controls will apply to the message. This is the default value when none of the criteria in the other two values are met.
  • CAN_SEND_OPV: The user is allowed to send messages to the channel. Flood and target change controls do not apply to the message. This is the default value when the user is opped or voiced on the channel.

cap_change #

This hook is called after successfully processing a CAP REQ command from a local user. The CAP ACK reply has already been sent to the user by the time this hook is called. This hook is not called if the request is unsuccessful (meaning a CAP NAK was sent to the user).

Hook data: hook_data_cap_change *

Fields:

  • client (struct Client *): The user who requested caps
  • oldcaps (int): Bitfield of caps the user had before this request
  • add (int): Bitfield of caps that this request added
  • del (int): Bitfield of caps that this request removed

channel_join #

This hook is called after a local user joins a channel. It is not called for remote users. It is called after the TOPIC and NAMES listing have been sent to the user and the JOIN has been sent to all other members of the channel.

Hook data: hook_data_channel_activity *

Fields:

  • client (struct Client *): The local user who joined the channel
  • chptr (struct Channel *): The channel being joined
  • key (const char *): The key the user specified when joining the channel or NULL if the user did not specify a key

The key does not necessarily match any key specified by the +k channel mode, since the can_join hook may have permitted a join despite having an invalid or missing key. Additionally, a key may be present in the hook data even if the channel is -k.

channel_lowerts #

This hook is called after the server receives a JOIN or SJOIN message for a remote user with a lower channel timestamp than what the server has for the channel.

Hook data: hook_data_channel *

Fields:

client (struct Client *): For JOIN, the remote user joining the channel; for SJOIN, the server issuing the SJOIN message

  • chptr (struct Channel *): The channel being joined
  • approved (int): Unused (always 0)

At the time the hook is called, client has not yet been added to chptr as a member (for JOIN) or the clients specified in the user list have not yet been joined to the channel (for SJOIN), but we have cleared all modes from the local channel.

client_exit #

This hook is called when a client is exiting, but before the client has been removed from the network. The client could be either a user or a server, and is called for both local and remote clients exiting. It is not called when a client exits due to exceeding their send queue or if the server encountered an error while writing to the socket for the client.

Hook data: hook_data_client_exit *

Fields:

  • local_link (struct Client *): The local client originating the exit or NULL if this exit was generated by the server for internal reasons
  • target (struct Client *): The client that is exiting
  • from (struct Client *): The client causing target to exit
  • comment (const char *): Reason for the exit

Because the local_link field can be NULL and otherwise does not have consistent semantics, if you need the local connection for the exiting client or the source of the exit it is better to use target->from or from->from, respectively.

The from field is usually the same as target (for cases where the client causes itself to exit) or is &me (for cases where the server is exiting the client). However, this is not always the case. For example, in KILL, from is the oper issuing the KILL command.

The comment field contains the quit message that will be sent to other clients once the exit is processed (after this hook is run). The field is not editable or replaceable; use the client_quit hook if you need to adjust the quit message.

client_quit #

This hook is called when a local user sends a QUIT to the server. It is called before the user is exited. Hook functions can modify the quit reason displayed to others by modifying the passed-in data.

Hook data: hook_data_client_quit *

Fields:

  • client (struct Client *): The user who is quitting
  • reason (const char *): Output field containing the quit message
  • orig_reason (const char *): The quit reason specified by the user, or the user's nickname if they did not specify a reason

The reason field by default is the same as orig_reason. Hook functions can replace the field with a different pointer value to modify the quit reason. If a hook function changes the reason, the new reason is not prepended with the "Quit: " prefix. Hook functions can also set reason to NULL to default to a quit reason of "Client Quit".

conf_read_end #

This hook is called after reading ircd.conf on start or during a rehash. All configuration options have been read and validated and all consequences of re-reading the configuration (such as updating connection classes) have been applied.

Hook data: always NULL

Modules which define new configuration can make use of this hook to perform their own validation routines to ensure any module-specific values are set correctly, or perform other initialization that depends on specific config values.

conf_read_start #

This hook is called after clearing out any existing configuration (e.g. deallocating string buffers) but before reading the ircd.conf file on start or dring a rehash.

Hook data: always NULL

Modules which define their own configuration can make use of this hook to perform cleanup on their own values as needed. String values read from conf are allocated via rb_malloc() so if a module defines new string values it should call rb_free() on those buffers and set them to NULL here.

doing_info_conf #

This hook is called when an oper executes the INFO command. The server config has been sent to that oper, but subsequent information provided by the command has not yet been sent.

Hook data: hook_data *

Fields:

  • client (struct Client *): The oper executing INFO
  • arg1 (void *): Unused (always NULL)
  • arg2 (void *): Unused (always NULL)

Modules which define their own configuration should make use of this hook to send information about that configuration to opers executing INFO. See the example below for the format string that should be used in order to ensure all module-created options visually align with options printed by default.

Example:

/* This example assumes my_conf_option is a boolean option */
static int my_conf_option;

void send_module_conf(void *data_)
{
    hook_data *data = data_;
    sendto_one(data->client, ":%s %d %s :%-30s %-16s [%s]",
        get_id(&me, data->client), RPL_INFO,
        get_id(data->client, data->client),
        "my_conf_option",
        my_conf_option ? "YES" : "NO",
        "Description of my_conf_option");
}

doing_stats #

This hook is called when a user executes the STATS command, before any handler for the stats letter is executed. If STATS was targeted at a remote server, the hook is only executed on the server where STATS is ultimately running. Hook functions can prevent the default handler for the stats letter from being executed by modifying the passed-in data.

Hook data: hook_data_int *

Fields:

  • client (struct Client *): The user executing STATS
  • arg1 (void *): Unused (always NULL)
  • arg2 (int): The stat character (upcast from char to int)
  • result (int): Output field indicating whether to execute default handler

The result field may be set to any nonzero value to stop processing and not execute any default handlers for the given stats character. Setting result to 0 (which is also the default value) will result in the default handler being run (or an error given to the client if no default handler for that character exists). Modules which add additional stats characters should perform their logic in this hook function, including any output to the client, and then set result to 1. Note that RPL_ENDOFSTATS is given to the client even if default handlers do not execute, so hook functions should not emit that numeric to the client themselves.

doing_stats_show_idle #

This hook is called when a user executes STATS l against a user. Hook functions can prevent details that would expose that user's idle time on the network by modifying the passed-in data.

Hook data: hook_data_client_approval *

Fields:

  • client (struct Client *): The user executing STATS l
  • target (struct Client *): The target of the stats command
  • approved (int): Output field indicating whether idle information is shown

The default value of approved is WHOIS_IDLE_SHOW, which displays information that can be used to determine the target's idle time on the network. Hook functions can set this to WHOIS_IDLE_HIDE instead to make the output return 0's for that data instead. Setting approved to WHOIS_IDLE_AUSPEX or any other nonzero value behaves exactly the same as WHOIS_IDLE_SHOW.

The information reported as 0 when approved is set to WHOIS_IDLE_HIDE is the following:

  • The number of lines in the target's send queue
  • The number of protocol messages sent to the target
  • The size in KiB of all data sent to the target, rounded down
  • The number of protocol messages received from the target
  • The size in KiB of all data received from the target, rounded down
  • The number of seconds since we last received a protocol message from the target

doing_trace_show_idle #

This hook is called when a user executes TRACE against a user. Hook functions can prevent details that would expose that user's idle time on the network by modifying the passed-in data.

Hook data: hook_data_client_approval *

Fields:

  • client (struct Client *): The user executing TRACE
  • target (struct Client *): The target of the trace command
  • approved (int): Output field indicating whether idle information is shown

The default value of approved is WHOIS_IDLE_SHOW, which displays information that can be used to determine the target's idle time on the network. Hook functions can set this to WHOIS_IDLE_HIDE instead to make the output return 0's for that data instead. Setting approved to WHOIS_IDLE_AUSPEX or any other nonzero value behaves exactly the same as WHOIS_IDLE_SHOW.

The information reported as 0 when approved is set to WHOIS_IDLE_HIDE is the following:

  • The number of seconds since we last received a protocol message from the target
  • The number of seconds since the target last sent a PRIVMSG or TAGMSG

doing_version_confopts #

This hook is called when a user executes VERSION. Hook functions can modify which characters are sent in the RPL_VERSION response comment that indicate ircd configuration options.

Hook data: char[256]

See doc/server-version-info.txt for more information on what each character in this response represents. The hook data is pre-filled with the default options supported by comet according to the referenced document. Modules can set members of the array to 1 (or any other nonzero value) to have the associated character show up in the VERSION response. Setting the value to 0 will cause the associated character to be omitted from the VERSION response.

Only the characters A-Z, a-z, and 0-9 will be displayed in VERSION responses; setting other characters to 1 will have no effect on the output. Characters will be output in sorted order, with letters first followed by numbers. If a letter has both uppercase and lowercase variants set, the uppercase letter will be output before the lowercase letter.

Example:

void doing_version_confopts_example(void *data)
{
    char *opts = data;
    /* Causes Q to additionally appear in the VERSION response */
    opts['Q'] = 1;
}

doing_who_show_idle #

This hook is called when a user executes an extended WHO (WHOX) query while specifying the l flag to retrieve idle time. Hook functions can prevent the details that would expose a user's idle time on the network by modifying the passed-in data. The hook is called once for each user returned by the WHO.

Hook data: hook_data_client_approval *

Fields:

  • client (struct Client *): The user executing WHO
  • target (struct Client *): The target matching the WHO query
  • approved (int): Output field indicating whether idle information is shown

The default value of approved is WHOIS_IDLE_SHOW, which displays the target's idle time in seconds if the target is a local user. If the target is a remote user, a value of 0 will always be returned regardless of the value of approved, but the hook functions will nonetheless be called. Hook functions can set this to WHOIS_IDLE_HIDE instead to make the output always return 0. Setting approved to WHOIS_IDLE_AUSPEX or any other nonzero value behaves exactly the same as WHOIS_IDLE_SHOW.

doing_whois #

This hook is called when a local user performs a WHOIS on someone else. The target of the WHOIS will always be a local client. The hook is called after all regular WHOIS lines have been sent to the user, but before the RPL_ENDOFWHOIS numeric is sent to the user.

Hook data: hook_data_client_approval *

Fields:

  • client (struct Client *): The user executing WHOIS
  • target (struct Client *): The target of the WHOIS command
  • approved (int): Set to 1 if this is an operspy WHOIS, 0 otherwise

Hook functions can use this hook to send additional lines to the client as a part of the WHOIS response. The approved field is not an output field; it is an indicator as to whether or not an operspy was performed.

doing_whois_channel_visibility #

This hook is called for each channel the target of a WHOIS command belongs to. The target will always be a local client. Hook functions can prevent the channel from being displayed in the WHOIS output by modifying the passed-in data.

Hook data: hook_data_channel_visibility *

Fields:

  • client (struct Client *): The user executing WHOIS
  • target (struct Client *): The target of the WHOIS command
  • chptr (struct Channel *): The channel being displayed
  • clientms (struct membership *): The client's membership in the channel, or NULL if they are not in the channel.
  • targetms (struct membership *): The target's membership in the channel
  • approved (int): Output field indicating whether the channel is shown

The default value of approved is 1 if client is a member of the channel or if the channel is public (not channel mode +s or +p) and 0 otherwise. If approved is set to 1, the channel will be displayed in the WHOIS output. Otherwise, the channel is either hidden from the output for regular WHOIS or prefixed with the ! character for operspy WHOIS.

doing_whois_global #

This hook is called when a remote user performs a WHOIS on someone else. The target of the WHOIS in such a case will always be a local client. The hook is called after all regular WHOIS lines have been sent to the user, but before the RPL_ENDOFWHOIS numeric is sent to the user.

Hook data: hook_data_client_approval *

Fields:

  • client (struct Client *): The user executing WHOIS
  • target (struct Client *): The target of the WHOIS command
  • approved (int): Set to 1 if this is an operspy WHOIS, 0 otherwise

Hook functions can use this hook to send additional lines to the client as a part of the WHOIS response. The approved field is not an output field; it is an indicator as to whether or not an operspy was performed.

doing_whois_show_idle #

This hook is called when a user runs WHOIS against a local target. The user may be either local or remote. Hook functions can prevent the details that would expose the target's idle time on the network by modifying the passed-in data.

Hook data: hook_data_client_approval *

Fields:

  • client (struct Client *): The user executing WHOIS
  • target (struct Client *): The target of the WHOIS command
  • approved (int): Output field indicating whether idle information is shown

The default value of approved is WHOIS_IDLE_SHOW. The output given to the user varies based on the value of approved and whether the target has the +I user mode (ostensibly from extensions/umode_hide_idle_time):

approved target +I result
WHOIS_IDLE_SHOW - idle time shown
WHOIS_IDLE_HIDE yes idle time hidden, client told the target is hiding their idle time
WHOIS_IDLE_HIDE no idle time hidden, client told the target's idle time is hidden because the client is +I
WHOIS_IDLE_AUSPEX yes idle time shown, client told the target is hiding their idle time but the client has auspex and can see it anyway
WHOIS_IDLE_AUSPEX no idle time shown, client told the target's idle time would be hidden because the client is +I but the client has auspex and can see it anyway

Any values for approved other than the above are treated equivalently to WHOIS_IDLE_AUSPEX. The user mode +I is checked irrespective of which module is providing it, if any.

get_channel_access #

This hook is called whenever a client's access to a channel needs to be verified before the action is allowed to proceed. Hook functions can alter the user's ability to perform the action by modifying the passed-in data.

Hook data: hook_data_channel_approval *

Fields:

  • client (struct Client *): The client performing a channel action
  • chptr (struct Channel *): The channel being checked for client's access
  • msptr (struct membership *): The client's membership on chptr
  • approved (int): Output field indicating client's access to the channel
  • dir (int): One of MODE_ADD, MODE_QUERY, or MODE_OP_QUERY
  • modestr (const char *): The canonicalized set of channel mode changes as would be passed to a MODE command if this hook is called as a result of channel mode changes, otherwise NULL
  • error (const char *): Unused (always NULL)

This hook is called under the following circumstances:

  • Retrieving the values of a queryable mode for a channel
    • The following modes are considered queryable: beIfkq
    • The following modes are considered privileged while querying: eI
    • Module-defined channel modes can be marked queryable by setting either CHM_QUERYABLE or CHM_CAN_QUERY when defining the mode and privileged by setting CHM_OPS_QUERY when defining the mode
    • dir will be MODE_OP_QUERY if at least one of the queried modes in modestr is a privileged mode and MODE_QUERY otherwise
    • modestr will be the canonicalized set of channel modes being queried
  • Updating (adding or removing) modes for a channel
    • If the mode update is combined with a privileged mode query, e.g. MODE #channel +m=I, dir will be MODE_OP_QUERY; otherwise, dir will always be MODE_ADD, regardless of whether modes are being added, removed, or both
    • modestr will be the canonicalized set of channel modes being updated
  • Forwarding (channel mode +f or ban forwards) to another channel
    • The hook is called for the target channel to verify the client has operator status on it and is in addition to the hook invocation for updating modes on the source channel
    • dir will always be MODE_QUERY and modestr will always be NULL
  • Setting the topic for a channel
    • dir will always be MODE_ADD and modestr will always be NULL
  • Kicking a user from a channel
    • dir will always be MODE_ADD and modestr will always be NULL
  • Removing a user from a channel (via extensions/m_remove)
    • dir will always be MODE_ADD and modestr will always be NULL

For mode queries and updates, the direction in modestr is always explicitly specified by prefixing with = for queries, + for additions, and - for removals. For example, a user sending MODE #channel b to query the ban list would cause modestr to be =b. Unrecognized or invalid mode letters are removed from modestr, e.g. attempts to query non-queryable modes. Multiple adjacent modes with the same direction will not duplicate the prefix. For example, a user sending MODE #channel +m+z would cause modestr to be +mz but a user sending MODE #channel +m-R+z would cause modestr to remain as +m-R+z (i.e. modes are not rearranged from what the user specified). modestr will additionally contain all relevant arguments to modes, separated by spaces. These transformations are what the documentation above refers to when it talks about the canonicalized set of channel modes.

The default value of approved is CHFL_CHANOP if client is a member of the channel and has channel operator status on the channel and CHFL_PEON otherwise. In general, for everything except querying non-privileged modes, the action will be denied if approved is set to anything lower than CHFL_CHANOP (e.g. CHFL_PEON or CHFL_VOICE).

introduce_client #

This hook is called whenever a client is introduced to the network. The client has already been fully registered before this hook is called, and the client may be either local or remote.

Hook data: hook_data_client *

Fields:

  • client (struct Client *): The local connection for the introduced client
  • target (struct Client *): The client being introduced

For local clients, client and target will point to the same place. For remote clients, client will be the locally-connected server that is introducing the client to us.

invite #

This hook is called whenever a local user is invited to a channel, after the can_invite hook has already been executed on the sender's server. The user sending the invite may be either local or remote. Hook functions can allow or reject the invite attempt by modifying the passed-in data.

Hook data: hook_data_channel_approval *

Fields:

  • client (struct Client *): The user sending the INVITE
  • chptr (struct Channel *): The channel that client is inviting target to
  • msptr (struct membership *): The client's membership on chptr
  • target (struct Client *): The user being invited
  • approved (int): Output field indicating whether this attempt succeeds
  • dir (int): Unused (always 0)
  • modestr (const char *): Unused (always NULL)
  • error (const char *): Output field containing an optional error message to send to the user

The default value for approved is 0. To approve the invite attempt, approved must be set to 0. If it is set to any other value, how the caller behaves depends on whether error is also set. If approved is nonzero and error is NULL, the invite attempt will silently fail. If approved is nonzero and error is not NULL, the invite attempt will be rejected and the client will be sent the approved value as a numeric with parameters contained in the error string (the client's nick is automatically inserted as the first parameter and should not be present in the string).

local_nick_change #

This hook is called whenever a local user changes their nickname. This hook is only called when the nickname change would succeed, and timing-wise occurs after RPL_MONOFFLINE is sent for the old nickname but before RPL_MONONLINE is sent for the new nickname and before the nick change is propagated to local users and the rest of the network.

Hook data: hook_cdata *

Fields:

  • client (struct Client *): The user whose nick is changing
  • arg1 (const char *): The user's previous nickname
  • arg2 (const char *): The user's new nickname

message_handler #

This hook is called for every incoming message processed by the server. Hook functions can alter the message handler ordinarily called by the ircd for the client by modifying the passed-in data.

Hook data: hook_data *

Fields:

  • client (struct Client *): The client sending the message
  • arg1 (struct MsgBuf *): The message
  • arg2 (struct MessageEntry *): Output field for the function used to handle the message

The default value for arg2 is the defined message handler for the command based on the client's status (unregistered, local user, remote user, server, encap, oper). Hook functions can replace this with a different handler by setting arg2->handler to a different pointer. arg2->min_args may be modified as well if the new handler supports a different minimum number of arguments compared to the default handler.

message_tag #

This hook is called for every message tag present in an incoming message. Hook functions can reject the tag or message or update the tag by modifying the passed-in data.

Hook data: hook_data_message_tag *

Fields:

  • client (struct Client *): The local connection the message came from
  • source (struct Client *): The client originating the message
  • key (const char *): The name of the message tag
  • value (const char *): The value of the message tag, or NULL if a value was not specified with this tag
  • capmask (unsigned int): Output field indicating the client capabilities a user must possess in order to receive this tag in server-sent messages
  • message (const struct MsgBuf *): The full command received, including all tags and parameters
  • approved (int): Output field indicating whether this message tag should be retained before passing the message off to the appropriate handler

The default value of approved is MESSAGE_TAG_REMOVE, which causes the tag to be stripped from the message before being passed to the handler. Hook functions can instead set this to MESSAGE_TAG_ALLOW to include the tag in the message or MESSAGE_TAG_DROP to prevent the entire message from being processed. When using MESSAGE_TAG_DROP it is the hook function's responsibility to provide any relevant error messages to the source. Using any value outside of these will be treated equivalently to MESSAGE_TAG_REMOVE.

The default value of capmask is CLICAP_MESSAGE_TAGS. Hook functions can modify this to be a different client capability or bitwise combination of client capabilities. While there is no guarantee that modifying capmask will accomplish anything, some message handlers pass some or all incoming tags to outgoing messages, and setting this appropriately allows such tags to be propagated appropriately.

Finally, key and value may both be set to different pointer values to adjust the tag's key and value passed to the message handler. The pointed-to buffers are not copied and must persist for the lifetime of the message handler. Setting value to NULL will strip the value from the tag.

new_local_user #

This hook is called after a local user has completed connection registration, but before it is formally introduced to the server or network. This hook is only called for users; see server_introduced for the introduction of new server connections.

Hook data: struct Client *

The hook data is the user being introduced. Hook functions can safely kill this client should the connection attempt not be allowed to proceed.

new_monitor #

This hook is called after a local user has added a new nickname to their MONITOR list.

Hook data: hook_data *

Fields:

  • client (struct Client *): The client adding the monitor entry
  • arg1 (struct monitor *): The monitor entry that was added
  • arg2 (struct Client *): The client corresponding to the new entry, or NULL if no client matching the nickname is online

The client has already been added to the arg1->users list at the time this hook is called. All relevant RPL_MONONLINE and RPL_MONOFFLINE messages have already been sent to the client for the added nickname.

new_remote_user #

This hook is called after a remote user has been introduced to this server. This hook is only called for users; see server_introduced for the introduction of new server connections.

Hook data: struct Client *

The hook data is the user being introduced. Unlike new_local_user, hook functions must not attempt to disconnect clients during this hook. The user will still be introduced to downstream servers after this hook completes running and disconnection attempts will therefore introduce state desynchronization between servers. If a remote user needs to be disconnected as early as possible after having been introduced, the introduce_client hook is the best spot to do that.

outbound_msgbuf #

This hook is called when constructing a message to be sent from the server. Hook functions can manipulate the message tags that would be sent by modifying the passed-in data. This hook is only called once per outbound message, regardless of how many clients the message will be sent to.

Hook data: hook_data_outbound_msgbuf *

Fields:

  • source (struct Client *): The client sending the message
  • msgbuf (struct MsgBuf *): The message buffer being sent
  • target (struct Client *): The single client receiving the message or NULL
  • chptr (struct Channel *): The channel receiving the message or NULL
  • source_sees_message (bool): Whether the client sending the message will also receive it

Hook functions can inspect msgbuf and modify it. Any modifications such as the addition or removal of message tags will be sent to the targets of the message. To add message tags, call msgbuf_append_tag. To remove tags, set the tag's capmask to 0. Changing other aspects of the MsgBuf such as the origin or parameter array is not recommended and may not function as expected.

The target field will be non-NULL when the message is being sent to a single client and will be NULL for messages sent to channels or that are broadcast to potentially many clients. The chptr field will be non-NULL when the message is being sent to a single channel and will be NULL for messages not sent to channels or that are sent to everyone in multiple channels. Both fields will be NULL for broadcast-type messages.

The following send functions currently do not call this hook:

  • kill_client
  • kill_client_serv_butone

parse_end #

This hook is called after we have processed an incoming message, including sending all relevant responses.

Hook data: always NULL

The global state variables incoming_client and incoming_message are still defined at the time this hook is executed. If sending messages from this hook, ensure that you choose a priority lower than HOOK_HIGHEST; any messages sent from hook functions with HOOK_HIGHEST or HOOK_MONITOR will not have proper context for labeled-response.

priv_change #

This hook is called whenever an oper's privilege set changes. This could be due to a user successfully becoming an oper, deopering, or an ircd rehash.

Hook data: hook_data_priv_change *

Fields:

  • client (struct Client *): The oper whose privileges have changed
  • old (struct PrivilegeSet *): The oper's previous privset, or NULL if they are opering up
  • new (struct PrivilegeSet *): The oper's new privset, or NULL if they have deopered
  • added (const struct PrivilegeSet *): The set of privileges added
  • removed (const struct PrivilegeSet *): The set of privileges removed
  • unchanged (const struct PrivilegeSet *): Privileges which remained intact

When ircd.conf is rehashed, this hook will be invoked for every local oper, even if there was no change to the privileges in their privset. Additionally, this hook will not be invoked upon rehash for remote opers even if there were changes to the privileges in their privset.

privmsg_channel #

This hook is called whenever a user sends a message to a channel or is parting a channel. Hook functions can reject the message attempt or alter the message text by modifying the passed-in data. This hook is called for messages sent by both local and remote users.

Hook data: hook_data_privmsg_channel *

Fields:

  • msgtype (enum message_type): The type of message being sent
  • source_p (struct Client *): The user sending the message
  • chptr (struct Channel *): The channel the message is being sent to
  • text (const char *): The message text
  • approved (int): Output field indicating whether the message is allowed to be sent
  • msgbuf (struct MsgBuf *): The full message, including tags, being sent

The default value for approved is 0, which allows the message to be sent. Setting approved to any nonzero value silently drops the message for MESSAGE_TYPE_PRIVMSG, MESSAGE_TYPE_NOTICE, and MESSAGE_TYPE_TAGMSG and causes the user to part without a reason parameter for MESSAGE_TYPE_PART. It is up to the hook function to provide any relevant error output to the user when messages are rejected.

Hook functions may set text to a different pointer value. If this is done, the new text will be used for message content instead of the original text. Hook functions may additionally manipulate the tags present in msgbuf; any modifications to tags will be reflected in the message sent to the channel but other modifications will not be effective.

privmsg_user #

This hook is called whenever a user sends a message to a channel. Hook functions can reject the message attempt or alter the message text by modifying the passed-in data. This hook is called for messages sent by both local and remote users.

Hook data: hook_data_privmsg_user *

Fields:

  • msgtype (enum message_type): The type of message being sent
  • source_p (struct Client *): The user sending the message
  • target_p (struct Client *): The user the message is being sent to
  • text (const char *): The message text
  • approved (int): Output field indicating whether the message is allowed to be sent
  • msgbuf (struct MsgBuf *): The full message, including tags, being sent

The default value for approved is 0, which allows the message to be sent. Setting approved to any nonzero value silently drops the message. It is up to the hook function to provide any relevant error output to the user when messages are rejected.

Hook functions may set text to a different pointer value. If this is done, the new text will be used for message content instead of the original text. Hook functions may additionally manipulate the tags present in msgbuf; any modifications to tags will be reflected in the message sent to the user but other modifications will not be effective.

rehash #

This hook is called after an ircd.conf rehash operation has completed.

Hook data: hook_data_rehash *

Fields:

  • signal (bool): Whether the rehash was the result of a signal or an oper command

The value of signal will be true if the rehash was the result of a SIGHUP sent to the ircd process and false if the rehash was the result of a REHASH or MODRESTART command.

remote_nick_change #

This hook is called whenever a remote user changes their nickname. This hook is only called when the nickname change would succeed, and timing-wise occurs after RPL_MONOFFLINE is sent for the old nickname but before RPL_MONONLINE is sent for the new nickname and before the nick change is propagated to local users and the rest of the network.

Hook data: hook_cdata *

Fields:

  • client (struct Client *): The user whose nick is changing
  • arg1 (const char *): The user's previous nickname
  • arg2 (const char *): The user's new nickname

server_introduced #

This hook is called whenever a server is introduced to the network. The server may be directly connected or remote.

Hook data: hook_data_client *

Fields:

  • client (struct Client *): The server introducing this server
  • target (struct Client *): The server being introduced

For local connections, client will be &me.

server_eob #

This hook is called after a netjoin burst has been processed by a server. The server may be either directly connected or remote.

Hook data: struct Client *

The hook data is the server that has indicated it has finished processing any netjoin burst data sent by us. The EOB flag has already been set on source_p by the time this hook is called.

umode_changed #

This hook is called after a user is introduced and after any changes to their user modes, such as by the MODE, OPER, or DEHELPER commands. Any mode changes have already been applied at the time this hook is called.

Hook data: hook_data_umode_changed *

Fields:

  • client (struct Client *): The user whose modes have changed
  • oldumodes (unsigned int): The user's old user modes
  • oldsnomask (unsigned int): The user's old server notice mask

Because any mode changes have already been applied, hook functions can check the current user modes or snomask for the user to determine what has changed.

user_welcome #

This hook is called while the registration burst for a new local user is being sent. It happens after sending ISUPPORT but before sending LUSERS. Some IRCv3 specifications require sending additional registration data during precisely this timing.

Hook data: struct Client *

The hook data is the client that is being introduced to the network.