# 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: ```c /* 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: ```c /* 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: ```c /* 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: ```c 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.