# IRC Services Technical Reference Manual

## 5. IRC server interface

Previous section: The module system | Table of Contents | Next section: Database handling

### 5-1. Protocol modules: the IRC protocol bridge

While the "official" IRC protocol is defined in the document RFC 1459 [www.ietf.org], modern IRC servers have added many extensions to this base protocol over the years, and the current state of IRC software is such that it is rare to find an IRC server implementation that can communicate with a different implementation. This makes the job of Services considerably more difficult, as it must communicate with servers in order to perform its job, and there is no telling what implementation a particular network administrator will choose.

In order to overcome these differences in protocol, Services makes use of protocol modules, a class of modules which interface between the generic IRC server implemented by the Services core and the specific protocols used by different IRC server implementations. While there are some basic assumptions made by the core code about the protocol—for example, that the protocol uses lines of text terminated by a CR/LF pair—most differences seen in current IRC servers can be handled by protocol modules.

The standard protocol modules distributed in Services are located in the `modules/protocol` directory, along with auxiliary source and header files that implement functionality common to multiple protocols. This location is not a requirement, however; third-party modules can be located in different directories, provided that they implement the required functionality described in this section.

Note that, in order to simplify the module initialization process, Services requires that a protocol module be loaded before any other modules; this requirement is enforced by the `load_module()` routine, as discussed in section 4-3-1. A number of pseudoclient modules alter their behavior depending on features of the IRC protocol in use, such as maximum nickname length or support of certain nonstandard IRC messages, and this requirement allows such modules to simply check the protocol information without having to ensure that a protocol module has been loaded (and subsequently watch for protocol module loads and unloads). Implementation note: Although `load_module()` ensures that a protocol module is loaded first, `unload_module()` does not prevent protocol modules from being unloaded! Protocol modules should therefore use an `exit_module()` routine that returns zero on any unload attempt, except during shutdown. The modules included with Services all exhibit this behavior.

Back to top

### 5-2. Specifying protocol features

In addition to providing the functionality listed in subsequent sections, protocol modules must inform Services of certain information about the protocol in use. This is done by setting the following global variables (defined in `send.c`) in the module's `module_init()` routine:

`const char *protocol_name`
  Specifies the name of the protocol supported by the module. This variable is provided for informational purposes only. (The current core code does not make any use of the variable.)
`const char *protocol_version`
  Specifies the version(s) of the protocol supported by the module, if applicable (the variable may be left unset if there is only one version of the protocol). This variable is provided for informational purposes only.
`int32 protocol_features`
  Specifies particular features supported by the protocol. The value should be a combination (bitwise OR) of zero or more of the following flags:
  - `PF_HALFOP`: Supports a "half-op" channel user mode (such as `+h` as used with the Unreal protocol).
  - `PF_CHANPROT`: Supports a "protected" channel user mode (such as `+a` as used with the Unreal protocol).
  - `PF_BANEXCEPT`: Supports channel ban exceptions.
  - `PF_SZLINE`: Supports an `SZLINE` or similar server-to-server command to ban an IP address from connecting to any server on the network.
  - `PF_NOQUIT`: Uses "NOQUIT" mode, in which servers do not send `QUIT` messages for affected clients when a netsplit occurs (see, for example, section 5-6-5).
  - `PF_SVSJOIN`: Supports an `SVSJOIN` or similar server-to-server command to force a client to join a channel.
  - `PF_CHANGENICK`: Supports a method through which a server can forcibly change a client's nickname.
  - `PF_AKILL_EXCL`: Supports autokill exclusions (exceptions to network-wide bans as set by the OperServ `AKILL` command).
  - `PF_MODETS_FIRST`: Sends the timestamp in a channel `MODE` message immediately after the channel name, rather than at the end of the message.
  - `PF_INVITEMASK`: Supports channel invite masks (masks allowing users who match to join an invite-only channel without being invited, such as the `+i` channel mode as used with the Unreal protocol).
  Note that `protocol_features` is initialized to a value of `PF_UNSET` to detect whether its value has been changed, so the protocol module should use a direct assignment (rather than an OR-assignment) to set the value.
`int protocol_nickmax`
  Specifies the maximum number of characters (bytes) allowed in a nickname.

`send.c` hooks into the "`load module`" callback to check that these variables, as well as the functions listed in section 5-3-1, are appropriately set when a protocol module is loaded, generating a fatal error if not. Implementation note: As mentioned in section 2-5-1, there is nothing to mark a protocol module as being such, so the callback function simply assumes that the first module loaded is a protocol module.

There are also three variables which can be optionally set as needed:

`const char *pseudoclient_modes`
  Specifies the user modes, if any, that should be set on any newly introduced pseudoclient; a leading "`+`" should not be added. For example, if a protocol includes a user mode specifically for pseudoclients, that mode should be set here. Defaults to the empty string (no modes). Note that mode `o` (operator privilege) should not be specified even if some pseudoclients need operator privileges to perform their functions; use `pseudoclient_oper` for that instead (see below).
`const char *enforcer_modes`
  Specifies the user modes, if any, that should be set on pseudoclients used as nickname enforcers. Defaults to the empty string (no modes).
`int pseudoclient_oper`
  Indicates whether pseudoclients which perform actions restricted to IRC operators need the IRC operator user mode (`+o`) set. Defaults to 1, causing this mode to be set (0 disables `+o`).

Back to top

### 5-3. Sending messages to the network

The bulk of a protocol module consists of routines to send messages to and process messages from the network, handling any peculiarites of the particular protocol in use.

Back to top

#### 5-3-1. Required functionality

A number of the common message sending operations defined in `send.c` are defined as function pointers, which the protocol module must set to point to appropriate functions (by default, they point to a placeholder function which generates a fatal error). These function pointers (see section 2-5-1 for descriptions of the functions) are:

- `void (*send_nick)(const char *nick, const char *user, const char *host, const char *server, const cahr *name, const char *modes)`
- `void (*send_nickchange)(const char *nick, const char *newnick)`
- `void (*send_namechange)(const char *name, const char *newname)`
- `void (*send_server)()`
- `void (*send_server_remote)(const char *server, const char *desc)`
- `void (*wallops)(const char *source, const char *fmt, ...)`
- `void (*notice_all)(const char *source, const char *fmt, ...)`
- `void (*send_channel_cmd)(const char *source, const char *fmt, ...)`

Protocol modules must also provide a handler for the "`set topic`" callback. The callback function should have the following signature:

int set_topic_handler(const char *source, Channel *c, const char *topic, const char *setter, time_t t)

This callback is called twice for each time the topic is set. The first call is made before the `Channel` structure is changed, and `topic`, `setter`, and `t` are filled in with the new topic text, the nickname to be used as the topic setter, and the timestamp for the topic. The second call is made after the `topic` and `topic_setter` fields of the `Channel` structure have been set to the new values, and the corresponding parameters to the callback (`topic` and `setter`) are `NULL` for this call. Note that the `topic_time` field is not set by the core, and must be set appropriately by the callback function; this is because some protocols require that the timestamp of a channel topic must be newer or older than the current topic's timestamp for the new topic to be accepted.

Back to top

#### 5-3-2. Optional functionality

In addition to the functions listed above, the OperServ modules implementing the autokill and S-line functionality (see sections 7-2-2-2 and 7-2-2-3) register message-sending callbacks which they expect the protocol module to hook into if it supports the relevant messages. These callbacks (see Appendix C for details) are:

- `send_akill`: Sends an autokill to the network.
- `cancel_akill`: Clears an autokill from the network.
- `send_exclude`: Sends an autokill exclusion to the network.
- `cancel_exclude`: Clears an autokill exclusion from the network.
- `send_sgline`: Sends an SGline to the network.
- `cancel_sgline`: Clears an SGline from the network.
- `send_sqline`: Sends an SQline to the network.
- `cancel_sqline`: Clears an SQline from the network.
- `send_szline`: Sends an SZline to the network.
- `cancel_szline`: Clears an SZline from the network.

If any of the callbacks are left unsupported, Services will simply send a `KILL` or other appropriate message each time the autokill or S-line is triggered. (However, hooking into a "send" callback but not the corresponding "cancel" callback can have undesirable consequences!)

Back to top

### 5-4. Receiving messages from the network

While basic message parsing and processing is handled by the Services core, it is up to protocol modules to handle details of the particular protocol as well as additional messages used by the protocol.

In Services, processing of received messages is handled using tables of message names and corresponding processing routines, as described in section 2-5-3. Protocol modules will typically define a message table for messages handled by the module, and call `register_messages()` to register the message table during module initialization.

Back to top

#### 5-4-1. Required functionality

The only functionality required in protocol modules is the ability to recognize new clients connecting to the network and clients changing nicknames. As described in section 2-5-3, the `NICK` and `USER` messages are not supported by the Services core because of the differences between protocols in handling them; thus, the protocol module must supply its own handler routines for these messages, or whatever other messages may be used in their place.

However, in many protocols there are other messages which must be handled properly to maintain the network state. These, of course, must be processed accordingly as well.

Back to top

#### 5-4-2. Optional functionality

As described above, modules can add support for additional messages through a message table; the default processing for core-handled messages can be changed the same way, if (for example) a message takes a different set of parameters than the "standard" server assumed by the core code.

There are some types of functionality shared among several servers, such as the `SJOIN` message used in protocols such as Bahamut and Unreal to update a channel's state with one message, or the "token" systems used by some protocols to reduce the bandwidth and processing required for inter-server messages. These are implemented by separate source files, which can be included in the module's source to implement the particular functionality. These are described in section 5-7.

Back to top

### 5-5. Other functions of protocol modules

Aside from handling the sending and receiving of messages particular to the protocol, protocol modules must handle any other aspect of server-to-server communication that is not done in the core. Chief among these is the handling of protocol-specific modes.

Many IRC server implementations add new modes to the basic set; the module must be able to recognize and process these modes appropriately. This can be done using the core mode-handling facility (see section 2-6-4), or by hooking into the `MODE` message callbacks "`user MODE`" and "`channel MODE`" (in the case of modes that take parameters, it is usually necessary to use the latter method in order to store the parameter values in the channel structure). The module may also need to hook into various pseudoclient callbacks; see the relevant parts of section 7, or see the description of the Unreal protocol module, which uses most of these callbacks, in section 5-6-14 below.

Back to top

### 5-6. Specific protocol module details

This section describes each of the protocol modules supplied with Services. In addition to the source file for the module itself, some protocol modules make use of auxiliary source files in the same directory; these files are described in detail in section 5-7. For the most part, each subsection only covers details unique to that particular module, but the `protocol/unreal` module (section 5-6-14), which makes use of almost all protocol-related functionality, is discussed in more detail.

Back to top

#### 5-6-1. `protocol/rfc1459`

The `protocol/rfc1459` module provides an interface for servers which strictly follow the standard IRC protocol, as defined in RFC 1459. While few if any such servers still remain in operation, this module serves as a reference implementation for Services.

Since the generic IRC server implemented by Services is very similar to the RFC 1459 standard, this module is rather straightforward. In a format shared by the other standard protocol modules as well, the module is divided into four major parts: routines to process received IRC messages, routines to send IRC messages, callback functions, and module-related functions and variables.

The only received messages which need special processing are the `NICK` and `USER` messages used for introducing clients. The module ignores the `NICK` message, assuming that the remote server will take care of checking for collisions with the nicknames of any pseudoclients introduced by Services, and uses the parameters to the `USER` message to initialize the client's data record. Before calling `do_nick()` to perform this action, the handler for the `USER` message, `m_user()`, sets up a new parameter list with the parameters in the order `do_nick()` expects them, filling in default values for parameters unavailable in the RFC 1459 protocol. (Technically, the hop count parameter is available from the `NICK` message, but since it would take considerable extra effort to save this value until the `USER` message arrived, and since the hop count is not used by Services anyway, this field is simply discarded and a default value of 0 used.)

The message-sending routines are likewise simple, for the most part. The only routine that deserves special mention is the `do_notice_all()` routine, which implements the `notice_all()` function of `send.c`. Since the RFC 1459 protocol does not permit a wildcard target of a `NOTICE` (or `PRIVMSG`) to be a simple "`*`", which would target all clients on the network, the module uses the `NetworkDomain` configuration setting, if available, to create a more-specific server wildcard mask which will still target all clients on the network. If this setting is not available, the routine iterates through some common top-level domains (`.com`, `.net`, `.org`, and `.edu`) in an attempt to reach as many clients as possible.

There is only one callback function defined by the module; it hooks into the "`set topic`" callback, used for setting channel topics (see section 2-6-5). Since RFC 1459 places no restrictions on topic-changing messages from servers, the callback simply sends out a `TOPIC` message on the first call, ignoring the second.

The module initialization routine sets the protocol-specific variables (`protocol_name`, `protocol_version`, and so on—since there are no "versions" associated with RFC 1459, the version string is left empty), installs the handlers for the `NICK` and `USER` messages, adds the topic-setting routine to the "`set topic`" callback, and installs the function pointers for the various message sending operations. Of these, the module cleanup routine removes the callback function and message handlers, but it leaves the protocol variables and message sending function pointers alone; this is a shortcut based on the assumption that the module will never be unloaded at runtime (this assumption is enforced by returning zero to refuse unloading if the `shutdown` parameter to the cleanup function is false).

Back to top

#### 5-6-2. `protocol/ts8`

TS8 was one of the earliest additions made to the IRC protocol, and includes timestamp (often called TS) values with many messages to indicate the time at which certain actions took place. The addition of timestamps allowed, among other things, less disruption of the network during netjoins; in the case of a nickname collision, for example, timestamps made it possible to determine which user was the first claimant on a nickname and kill only the second user, rather than killing both of them as the original protocol called for.

Other than the addition of timestamps into some IRC messages, the `protocol/ts8` module, which supports TS8 as used in the `ircd-2.8` series of IRC servers (specifically tested with `ircd-2.8.21+TS8`), is very similar to the `protocol/rfc1459` module. The one difference worth noting is in the operation of the "`set topic`" callback. Under TS8, a server receiving a `TOPIC` message from another server will give preference to the topic with the older timestamp, ignoring the `TOPIC` message if it specifies a timestamp newer than that of the topic currently set on the channel; thus, in order to ensure that the topic gets set correctly, the callback function modifies the caller's topic timestamp to be one second earlier than the timestamp of the channel's current topic if a topic is set. This same approach is used in other protocol modules as well to ensure that changes made by Services are given priority over the current network state regardless of timestamps.

Back to top

#### 5-6-3. `protocol/dalnet`

The `protocol/dalnet` module supports the IRC server released by (and used on) the DALnet IRC network, `ircd.dal`, through version 4.4.13. This server is, incidentally, the server for which Services was originally designed, and as such, the module is nearly as concise as those for the simpler RFC 1459 and TS8 protocols.

The DALnet IRC server introduced several additions to the standard IRC protocol. Most notable, from the point of view of the protocol module, is the addition of the `AKILL` and `RAKILL` messages for adding and removing network-wide client bans, and the OperServ `AKILL` command derives its name from these messages. The module includes callback functions for OperServ's "`send_akill`" and "`cancel_akill`" callbacks; it also includes entries in the message table for these messages, but since there is no need to process `AKILL` messages from the network, the handlers for these are `NULL`, along with several other messages not recognized by the core message processing code. (These messages are included simply to avoid warnings in the log file when such messages are received.)

Other changes made to the server-to-server protocol in the DALnet server are the unification of the `NICK` and `USER` messages into a single `NICK` message, and the addition of the `GLOBOPS` message, a `WALLOPS`-like message that cannot be seen by non-operators (and which is used for the implementation of the `wallops()` routine in preference to `WALLOPS`).

In order to ensure that the `AKILL`-related callbacks mentioned above are added when the appropriate OperServ module (`operserv/akill`) is loaded, this module also hooks into the `"load module"` callback. An "`unload module`" callback is also included for completeness, though it does nothing in this module.

A close look at the module initialization routine, `module_init()`, will show another difference in the DALnet protocol. The RFC 1459 standard specifies that the three characters `[ \ ]` are to be interepreted as equivalent to `{ | }` in nicknames and channel names; this is a holdover from the Scandinavian character set which was used by the creators of the IRC protocol. DALnet does away with this holdover, and treats `[ \ ]` as distinct from `{ | }` when doing such string comparisons. By default, however, Services uses the RFC 1459 rules for comparing nicknames and channel names, so in order to change this behavior, the `module_init()` routine modifies the global `irc_lowertable[]` array. There are also two changes made to the `valid_chan_table[]` array, to accommodate the fact that the DALnet protocol allows channel names to begin with "`+`" and does not allow colons in channel names.

There is also a `mapstring()` call in the initialization routine to change the `OPER_BOUNCY_MODES` message to `OPER_BOUNCY_MODES_U_LINE`; this latter message includes a specific reference to "U: lines", a type of entry in the DALnet server configuration file that indicates servers (like Services) that are allowed to change channel modes arbitrarily. Failure to set this correctly on all servers can result in the "bouncy modes" phenomenon, where a server reverses mode changes made by Services, causing Services to resubmit those changes in an infinite loop (see section 2-6-3 for details).

One other piece of code not present in the `rfc1459` or `ts8` modules is the mode initialization code. The DALnet protocol adds two user modes to the standard mode set: `+g`, indicating whether the client wants to receive `GLOBOPS` messages, and `+h`, indicating whether the user is identified as a "help-op". Neither of these have any effect on the operation of Services, but for completeness, they are added to the global mode tables as described in section 2-6-4. The method used for adding the modes (the three arrays `new_usermodes[]`, `new_chanmodes[]`, and `new_chanusermodes[]`, and the local initialization function `init_modes()`, called from `init_module()`) is shared by other protocol modules are well.

Back to top

#### 5-6-4. `protocol/dreamforge`

The `protocol/dreamforge` module supports the Dreamforge server protocol. Dreamforge is the name given to versions 4.4.15 and later of the DALnet IRC server, and as such, this protocol is a direct successor to the `dalnet` protocol.

The major difference between the classic DALnet protocol and the Dreamforge protocol is the addition of features designed to improve the integration of Services-like programs with the network. Chief among these is the "Services timestamp" or "servicestamp", a timestamp-like value associated with each client which is set by Services and retained by all servers as long as the client is connected. This value, or 0 if it has not been set, is sent as part of the `NICK` message when announcing a new client to the network. Services can set the servicestamp to a unique value, and use that value to distinguish clients with certainty, avoiding problems arising from servers with out-of-sync clocks or clients that connect to the network at the same time. There are also new user and channel modes added to identify registered nicknames and channels, and to prevent clients with unregistered nicknames from joining a channel.

Most of these changes are handled in the `NICK` message handler, `m_nick()`, and the callback functions `do_user_servicestamp_change()`, `do_user_mode()`, and `do_nick_identified()`. The `do_user_mode()` callback function ensures that no other server on the network attempts to change a user's servicestamp (note that this can cause mode floods if two copies of Services are run on the same network!) or registered-nickname status. The function also sets the "Services administrator" (`+a`) user mode if the user is known to be a Services administrator, and clears the mode otherwise.

In order to check whether a user is a Services administrator, the module has to call the `is_services_admin()` function in the `operserv/main` module; however, this module may not be loaded. To avoid having to check for the module and symbol at every location in the code where the function is called, a local helper function, `local_is_services_admin()`, is defined at the top of the source file, and `is_services_admin()` is redefined (via `#define`) to point to this local function. The function itself only checks a cache variable to determine whether `is_services_admin()` is available; this cache variable is set by the "`load module`" callback function when the `operserv/main` module is loaded (and cleared again by the "`unload module`" if `operserv/main` is unloaded).

The final new aspect to this module's source code is the use of the `svsnick.c` auxiliary source file (see section 5-7-6 for details on this particular file), to support the `SVSNICK` message used to forcibly change a client's nickname. In order to avoid complexities resulting from compiling the source file separately and linking it into the module (in particular, identifier collisions during static linking), the `svsnick.c` source file is included directly into the module's main source file, `dreamforge.c`. `svsnick.c` includes its own initialization and cleanup routines, `init_svsnick()` and `exit_svsnick()`, which are called by `init_module()` and `exit_module()` respectively.

With respect to server registration, Dreamforge includes a new `PROTOCTL` message type, used to inform the remote server about the sending server's capabilities; this allows changes and additions to be made to the protocol that can be enabled or disabled depending on whether the remote server supports them. Services does not make use of any such optional features in Dreamforge, however, so the `PROTOCOL` message is given a `NULL` handler.

Back to top

#### 5-6-5. `protocol/bahamut`

Bahamut is the successor to the Dreamforge IRC server, and is the server currently (2006/8) used on the DALnet IRC network. The `protocol/bahamut` module handles versions of Bahamut from 1.8.0 onward.

In addition to the `SVSNICK` feature introduced in Dreamforge, Bahamut includes ban exceptions for channels (clients matching a ban exception mask can join the channel even if they also match a ban mask), supported by the auxiliary source file `banexcept.c`; invite masks for channels (clients matching an invite mask can join an invite-only channel without having to be explicitly invited), supported by `invitemask.c`; and a channel-join message for server-to-server communications, `SJOIN` (which reduces bandwidth use by allowing multiple users and channel modes to be sent in a single message), supported by `sjoin.c`. With respect to the latter file, `bahamut.c` defines the `BAHAMUT_HACK` symbol before including the file; this is to select the Bahamut mode of operation for `SJOIN`, as described in section 5-7-5.

Bahamut includes a `CAPAB` message that functions like Dreamforge's `PROTOCTL` to inform the remote server about supported protocol features. Services checks for one token in the `CAPAB` message: "`NOQUIT`", an extension suppressing (on server-to-server links) the client `QUIT` messages generated when a netsplit occurs. Services always advertises the `NOQUIT` capability, and sets the `PF_NOQUIT` protocol flag if the remote server also supports `NOQUIT`.

One difficulty that can arise when using the Bahamut server is that the server to which Services connects is configured as a "Services hub" (the configuration option "`servtype serviceshub`"). While this seems logical from the name, this option is intended only for the custom Services program that DALnet uses, and is not compatible with this program, Services for IRC Networks. The DALnet Services program is customized to work specifically with the Bahamut server and the DALnet network, and as such takes certain shortcuts; the "Services hub" option causes the Bahamut server to take advantage of these shortcuts, reducing network bandwidth. In particular, activating this option causes messages to pseudoclients such as NickServ to be sent in an abbreviated form (the `NS`, `CS` and similar commands defined under the dummy `#ifdef ALLOW_BAHAMUT_SERVICESHUB`, referring to a macro not defined anywhere). As Services for IRC Networks cannot assume that these clients have particular nicknames, or even that they exist, the `protocol/bahamut` module reports an error and aborts the program if any of these abbreviated messages are seen. Implementation note: It might be feasible to support these if the module exported a `register_pseudoclient()` routine, which took a constant indicating the pseudoclient, like `BAHAMUT_NS`, and the pseudoclient's nickname and stored the nickname locally for processing the given message. The "Services hub" option also prevents channel topics and client `AWAY` messages from reaching Services, causing channel topic retention and MemoServ unaway checking to break.

The Bahamut-specific channel mode `+j` is used to limit the rate at which clients can join a channel. It takes the form `+j num1:num2`, where `num1` and `num2` are positive integers that set the exact limits on joining. Internally, these are stored in the `joinrate1` and `joinrate2` fields of the `Channel` structure as well as the locked-mode set of a registered channel (see section 7-4-1-1), and values of 0 in these fields indicate that the mode is not set; values of -1 in the locked-mode structure indicate that the mode is locked off.

Unlike RFC 1459, Bahamut does not allow control characters in a channel name. It also does not allow ASCII 160 (0xA0), presumably because this corresponds to an "unbreakable space" in the ISO 8859-1 character set and could be confused with an ordinary space by users. The initialization routine adjusts the `valid_chan_table[]` array to account for this.

Back to top

#### 5-6-6. `protocol/hybrid`

The `protocol/hybrid` module supports versions 7.0 and later of the ircd-hybrid IRC server. Hybrid has a fairly simple design compared to other servers, and only supports a few features above the standard set.

The Hybrid server does not ordinarily send channel topics during the initial net burst when a server connects, and only allows the channel topic to be set by a `TOPIC` message from a client currently in the channel. This prevents the ChanServ topic-related functions from working, since ChanServ does not join the channel when setting a topic. There is, however, a module available for Hybrid which restores the synchronization of channel topics on a server link as well as the ability of a server to set topics arbitrarily: the "topic burst" module, `m_tburst.so`. To simplify processing, the `protocol/hybrid` module requires that its uplink server support this topic bursting, and will abort the program if that support is missing (as determined by the `CAPAB` message received on connection).

Back to top

#### 5-6-7. `protocol/inspircd`

The `protocol/inspircd` supports the InspIRCd IRC server. This server shares a number of features with the Unreal server (see section 5-6-14, but was created from scratch, rather than by modifying the source code of an existing IRC server as in the case of most other servers.

The `inspircd` module uses the `banexcept.c`, `invitemask.c`, and `svsnick.c` auxiliary source files mentioned above, as well as the `chanprot.c` and `halfop.c` files, which are used to implement two additional channel user modes supported by InspIRCd: `+a` (protection) and `+h` (half-op). A user with `+a` set cannot be kicked by a channel operator unless that operator also has `+a` set. Half-op privilege is an extra privilege level between `+v` (voiced) and `+o` (channel operator); half-ops can set the channel topic and give `+v` to other users, but cannot change channel modes or perform other actions that ordinary channel operators can do. InspIRCd also has a condensed channel join message (`FJOIN`) similar to Bahamut's `SJOIN`, but its syntax is different enough that a message handler is included in the module itself rather than using the handler in `sjoin.c`.

In addition to autokills and S-lines, InspIRCd supports autokill exclusions, similar to channels' ban exceptions but applying to network-wide autokill masks. As described in detail in section 7-2-2-2, the `operserv/akill` module can take advantage of this to implement autokill exclusions without having to send `KILL` messages for all autokilled users manually.

Another feature of the InspIRCd protocol is the removal of the restriction on sending network-wide messages; as such, the `protocol/inspircd` module does not require a `NetworkDomain` configuration setting for global messages to be correctly sent.

Back to top

#### 5-6-8. `protocol/monkey`

The `protocol/monkey` module supports the Chunky Monkey IRCD server. This server is based on an earlier version of Bahamut; the primary Services-visible change is the addition of the half-op (`+h`) channel user mode. Features like ban exceptions and invite masks, added to Bahamut in later versions, are not present. Other than these differences, this module is essentially the same as the `bahamut` module.

Back to top

#### 5-6-9. `protocol/ptlink`

The `protocol/ptlink` module supports the PTlink IRCd server, version 6.10.0 and later. This server is based on ircd-hybrid-6, and was originally developed for the PTlink IRC network, from which it takes its name.

In PTlink, autokills are set and removed using the `GLINE` message. Since this can also be used by IRC operators to set and remove autokills, Services uses a constant string (defined as `GLINE_WHO` at the top of the source file) as the "nickname" associated with the entry. Services can then check this value when receiving a `GLINE` message from the network, in order to avoid removing entries set by IRC operators. (However, nicknames are not stored with `SGLINE` and `SQLINE` entries, so operator-set entries will be deleted if they do not match a record in Services' databases.) See under the `TKL` message handler in section 5-6-14, which discusses the Unreal server, for an explanation of why these entries need to be cleared when received from the network.

Back to top

#### 5-6-10. `protocol/ratbox`

The `protocol/ratbox` module supports the ircd-ratbox server. This server is derived from ircd-hybrid, and the `ratbox` module is likewise very similar to the `hybrid` module.

Like Hybrid, ircd-ratbox does not normally allow servers like Services to arbitrarily change channel topics, but this behavior can be changed with a similar "topic burst" function. In the case of ircd-ratbox, this functionality is built into the server (rather than being a separate module), and need only be enabled through the appropriate flag ("`topicburst`" in the connect block for Services). If this flag is not enabled, Services will abort at connection time, as for Hybrid.

Back to top

#### 5-6-11. `protocol/solidircd`

The `protocol/solidircd` module supports the solid-ircd server. This server is based on an earlier version of the Bahamut server, with several additions and changes.

One new feature supported by solid-ircd is encrypted connections. If a client connects to the server via SSL, the client is given user mode `+z`; a new channel mode, `+S`, is also available to prevent clients without `+z` from entering the channel. This presents a problem when `+S` is mode-locked on: since the channel does not exist when it is empty, the IRC server will not know to stop a `-z` user from entering the channel, and even if Services then sets `+S` on the channel, the non-secure user will already be in the channel. In order to work around this, Services hooks into the ChanServ "`check_kick`" callback, and if a non-secure user tries to join an empty channel that is registered and mode-locked `+S`, the user is kicked out just as if on the autokick list.

Back to top

#### 5-6-12. `protocol/trircd`

The `protocol/trircd` module supports the tr-ircd server, version 5.5 and later. tr-ircd was originally based on the Bahamut server, but has been rewritten from scratch since version 5.0.

One feature unique to tr-ircd is the availability of a user mode (`+L`)bindicating the language in which the client desires to receive server messages. For users with registered nicknames, Services sets this to the language selected by the user for their nickname (if that language is also supported by tr-ircd) when the user identifies. Implementation node: It would also be theoretically possible to use the mode's value as a default for sending messages unregistered nicknames, as well as the initial language setting of a newly-registered nickname, but this is difficult to accomplish without adding the concept of a language mode for clients to the Services core. The list of languages supported by tr-ircd, taken from the tr-ircd source code, can be found at the top of the `trircd.c` module source file (`langhash_init[]`); the actual values used in the user mode are hashes created from the language name strings, and these hashes are computed and stored in the `langhash[]` array by the `init_langhash()` function, called at module initialization time.

tr-ircd also supports "channel linking"; this is the ability to make one channel into an "alias" for another. When this mode (coincidentally also `+L`) is set for a channel `#A` with a parameter of `#B`, for example, a user who attempts to join channel `#A` will be sent to channel `#B` instead. As a side effect of this, the IRC server keeps track of channels which have `+L` set, and does not delete them even after the last client leaves. Since Services treats a channel with no users as nonexistent, the `trircd` module must hook into the low-level "`receive message`" callback to watch for `+L` or `-L` messages for empty channels and process them accordingly.

At the server-to-server communication level, tr-ircd has the ability to send message names as one- or two-character tokens rather than the full message names; for example, "`PRIVMSG`" becomes simply "`P`", both reducing bandwidth (albeit minimally) and enabling faster lookup of commands. These tokens are defined in the `trircd_tokens[]` array, and processing is handled by the `token.c` auxiliary source file, discussed in section 5-7-7.

Back to top

#### 5-6-13. `protocol/undernet-p9`

The `protocol/undernet-p9` supports version 2.9 of the Undernet IRC server (ircu). This is a fairly old server, derived from the original ircd-2.8 server with TS8 additions; the only additional features it includes are autokills (set using the `GLINE` message, and not propogated in the connection burst) and the merging of the `USER` and `NICK` messages into a single `NICK` message.

Back to top

#### 5-6-14. `protocol/unreal`

The `protocol/unreal` module supports version 3.1.1 and later of the UnrealIRCd server. Unreal is originally derived from Dreamforge, but has made numerous additions, and is one of the most feature-rich IRC servers currently in use. This of course means that the protocol module is similarly complex; Unreal makes use of nearly all protocol-related routines, as well as all seven auxiliary source files. For this reason, the `unreal` module's source code is the most heavily commented of the protocol modules, and can be seen as a model for how the various parts of protocol modules work.

Back to top

##### 5-6-14-1. Module prologue

After including appropriate header files and the auxiliary source files that implement certain protocol-related functions, the module defines a number of local variables:

- `module_chanserv`, `module_operserv`: Module handles used for accessing external symbols (see below).
- `ServerNumeric`, `SetServerTimes`, `SVSTIMEFrequency`: Variables to hold values set in the configuration file.
- `to_svstime`: The timeout (see section 2-7) used to send `SVSTIME` messages at periodic intervals.
- `usermode_admin`, `usermode_secure`, `usermode_hiding`, `chanmode_admins_only`, `chanmode_secure_only`, `chanmode_no_hiding`: Bitmasks corresponding to certain sets of modes, used to determine whether a client is allowed to enter a particular empty channel.
- `unreal_version`: Set to the protocol version number sent by the remote server at connection registration time, and used to determine whether certain protocol features are available.
- `has_nickip`: Set to nonzero if the remote server supports the `NICKIP` feature, used to implement SZlines.

These are followed by `#define`s and local variables used to access the `s_ChanServ` variable in the ChanServ module, containing the nickname of the ChanServ pseudoclient, and the `is_services_admin()` function in the OperServ module, returning whether the given user is a Services administrator. As described in the comments, `#define` macros are used to substitute an access to the symbol pointer every time the symbol is used, in order to simplify the code.

The next section of the file contains a list of user, channel, and channel user modes supported by the Unreal server. In order to register these with the core mode processing facility (see section 2-6-4), `unreal.c` defines three arrays, one for each mode type, containing the mode characters to be added and the corresponding `ModeInfo` structure for each mode. The module also defines six local mode flags, corresponding to the mode bitmask variables listed above; these flags are ignored by the mode processing facility, but the mode setup code uses them to set the bitmask variables.

The setup code itself is located in the `init_modes()` routine, called during module initialization. This routine iterates through each of the arrays, storing the mode data in the arrays exported by the mode processing facility. For the user and channel modes, it also checks for the locally-defined `ModeInfo` flags, and if a given mode has a flag set, the mode's bitmask is added to the appropriate bitmask variable.

Back to top

##### 5-6-14-2. Message receiving

After this comes the first major portion of the actual processing code, the message handlers. Aside from the mandatory client registration handler (the `NICK` message in Unreal), Unreal includes several additional messages not supported by the core processing code. The messages handled are as follows (each message is handled by a routine named `m_message-name`, for example the `NICK` message is handled by `m_nick()`):

`NICK`
  This is the standard client registration message (Unreal merges the dual `NICK` and `USER` of RFC 1459 into a single `NICK` message at the server level, as most other server protocols do). The message is also used for existing clients changing nicknames; in that case (where the message has a prefix indicating that it came from an existing client), control is simply handed off to the `do_nick()` routine from the core's client handling code. In the case of a new client, however, `do_nick()` expects the parameters in a slightly different order than Unreal sends them in, so this rearrangement is performed before calling `do_nick()`. Additionally, on servers supporting `NICKIP` (the sending of IP addresses in the `NICK` message), the message will include an extra parameter containing the binary representation of the client's IP address, encoded using base64 encoding; this is decoded, checked for consistency, and passed to `do_nick()` as well. (If `NICKIP` is not supported, a `NULL` parameter is passed, indicating that the IP address is not available.)
`PROTOCTL`
  Unreal uses this message to send information about features supported by the server. Services checks the list of parameters for recognized keywords, and sets appropriate variables to reflect their presence or absence. The recognized feature tokens are:
  - `NICKv2`: A new format for the `NICK` message, compared to earlier versions of Unreal. The local variable `got_nickv2` is set when this token is found; if the variable is not set at the end of the function, a fatal error is generated. Implementation note: The variable is declared `static` because Unreal can send more than one `PROTOCTL` message when registering (such as when the parameter list is too long). It would be more robust to use a file-global variable that is checked at the first `NICK` message, but current versions of Unreal always send `NICKv2` in the first message, so this is not currently a problem.
  - `NOQUIT`: The `NOQUIT` feature found in other servers such as Bahamut, in which servers generate client `QUIT` messages for netsplits on their own rather than relaying them through the network.
  - `NICKIP`: Indicates that IP addresses are sent with client registration messages.
  - `NICKCHARS`: Specifies additional characters allowed to be used in nicknames. The specification is done by language code; the current version of the module uses the language codes and character sets defined in Unreal 3.2.3.
`UMODE2`
  This is equivalent to the `MODE` message for clients, but omits the nickname parameter (so, for example, "`:nickname MODE nickname -o`" becomes "`:nickname UMODE2 -o`").
`SETHOST`, `CHGHOST`
  These allow setting changing the "fake hostname", the hostname seen in a `WHO` or `WHOIS` reply by non-operators, for a client. `SETHOST` is used by a client to change its own hostname, while `CHGHOST` is used by operators (or servers, such as Services, though Services does not use this at present) to change another client's hostname.
`SETIDENT`, `CHGIDENT`
  These are like `SETHOST` and `CHGHOST` above, but change the client's username (the part of the `user@host` mask before the `@`).
`SETNAME`, `CHGNAME`
  These are like `SETHOST` and `CHGHOST` above, but change the client's "real name" (also called "gecos").
`SJOIN`
  This is a server-to-server message used both to add a user to a channel and to send a channel's state to the network. The message takes a channel name, timestamp, mode string, optional mode parameters, and a space-separated list of nicknames of clients joining the channel. Nicknames in the nickname list are prefixed with the prefix character(s) for the channel user mode of the client, if any (for example, a channel operator would be listed as `@nickname`. Recent versions of Unreal also add channel bans and ban exceptions to the nickname list as well, with prefix characters of `&` and `"` respectively.
`SVSMODE`, `SVS2MODE`
  These messages are used to remotely change a client's modes or to remove all bans on a channel which match a client. In the former case, the messages operate identically to the `MODE` message, except that the source and target client may be different. In the latter case, each server checks the channel's ban list and removes all bans corresponding to that user; Services does this by calling `clear_channel()`, which sends out explicit `MODE` messages to remove the bans, but this is mostly harmless (other than taking up a small amount of bandwidth). The only difference between SVSMODE and SVS2MODE is that, when used to set client modes, the latter sends a `MODE` message to the target client, while the former does not.
`SVSNLINE`
  This message is used for managing what Services refers to as the SGline list, and takes one of two formats:
  - `SVSNLINE + reason :mask`
  - `SVSNLINE - :mask`
  Unlike other access control lists, which are handled by the `TKL` message (discussed below), masks for `SVSNLINE` messages may contain spaces, and thus must be the last parameter of the message. As a corollary, the `reason` parameter cannot contain spaces. In Services, any spaces in the reason are converted to underscores when an `SVSNLINE +` message is sent.
`TKL`
  This message is used for managing network-wide global connection control lists (autokills and S-lines). The message takes as parameters:
  - A `+` or `-` indicating whether the mask is being added or removed.
  - The type of the mask (a single character, such as `Z` for SZlines).
  - The username part of the mask.
  - The hostname part of the mask.
  - The entity (client or server) responsible for the action.
  - (add only) Timestamp (in Unix `time()` style) for when the mask expires, or 0 if it should not expire.
  - (add only) Timestamp at which the mask was added.
  - (add only) Reason string associated with the mask.
  When a new server connects to the network, all active `TKL` masks are forwarded to the server. This can cause problems if a mask is removed while a server is split from the network; when the split server reconnects, it will send the mask back to the network, causing it to reappear. While there is nothing that Services can do about this in the general case, it does watch for new masks added that give Services itself as the adder (Services uses its own server name as the entity adding the mask, and this is preserved when other servers forward the mask). When it sees such a mask sent from the network, it checks whether that mask actually exists in the current databases, and if not, it sends a `TKL -` message to remove the mask, assuming that a split server has rejoined the network and reintroduced an old mask.

The extra messages in Unreal along with their handlers are listed in the `unreal_messages[]` table, which is registered at initialization time using `register_messages()` (see section 2-5-3). Some additional messages are also listed with `NULL` handlers in order to prevent warning messages from being written to the log when those messages are received.

Unreal also supports the concept of tokens, short names for messages used in server-to-server communication. These slightly reduce the bandwidth needed for messages, but more importantly speed up message lookups, since only one or two characters need to be checked rather than a longer text string. The list of tokens used by Unreal is in the `tokens[]` array; this array is passed to `init_tokens()`, the initialization routine defined in the `tokens.c` auxiliary source file (see section 5-7-7).

Back to top

##### 5-6-14-3. Message sending

Most of the remainder of the source file consists of various routines which send messages to the network. These include the required functionality listed in section 5-3-1, as well as a number of callback functions which send messages to the network in response to certain events. The routines are as follows:

`do_send_svstime()`
  Sends a `TSCTL SVSTIME` message to the network to synchronize servers' clocks. Used for periodic time synchronization when requested via the `SetServerTimes` configuration directive.
`do_send_nick()`
`do_send_nickchange()`
`do_send_namechange()`
`do_send_server()`
`do_send_server_remote()`
`do_wallops()`
`do_notice_all()`
`do_send_channel_cmd()`
  Implement the corresponding required message routines.
`do_receive_message()`
  Performs special actions on received messages. This callback function has two purposes; one is to watch for and reverse improper servicestamp changes, and the other is to parse the protocol version embedded in the remote server's `SERVER` message at connection registration time. In both of these cases, we do not want to override the standard behavior for the messages, but the current message framework does not allow for this, so we watch for the messages at this lowest level and perform the necessary processing there. With respect to the former case specifically, it would in theory be possible to handle servicestamp changes in `do_user_mode()`, described below, but poor design in Unreal resulted in Services stamps sharing a mode letter (`+d`) with "deaf" mode, so "+d stamp" needs to be treated separately from other mode changes.
`do_user_create()`
  Called when a new `User` structure is created in response to a `NICK` message, this routine stores the "fake hostname" parameter (not handled by `do_nick()`) in the corresponding field in the `User` structure.
`do_user_servicestamp_change()`
  Called when a client is assigned a servicestamp to send the new stamp out to the network.
`do_user_mode()`
  Called when a client's user modes are being changed. Services handles two user modes exclusively: `+r`, indicating that the client's nickname is registered (and the client is in fact the owner of the nickname), and `+a`, indicating that the client is a Services administrator. If another server tries to change either of these modes, Services reverses the change. This routine also watches for users with Services administrator privilege who newly gain IRC operator status, and sets `+a` on them.
`do_channel_mode()`
  Called when a channel's modes are being changed. This routine takes care of storing the `+L` (channel link), `+f` (flood protection), and `+j` (join rate limiting) parameters in the `Channel` structure.
`do_clear_channel()`
  Handles requests to clear bans and exceptions from a channel. Ordinarily, `clear_channel()` will handle bans itself and ban exceptions are handled by the `banexcept.c` auxiliary source file, but Unreal has a feature called "extended ban types", in which a prefix like `~r:` changes the meaning of the ban or exception mask. In order to process these bans correctly, the `protocol/unreal` module has to intervene via the "`clear_channel`" callback and process ban and exception clear requests itself. The actual work is done by `unreal_clear_bans_excepts()`, which calls `unreal_match_ban()` to determine whether a particular mask matches a given client.
`do_nick_identified()`
  Called when a client identifies for its nickname, and sets the `+a` user mode on the client if it has Services administrator (and IRC operator) privileges.
`do_set_topic()`
  Called when setting a topic on a channel (see section 5-3-1). Unreal ignores topic changes if the topic timestamp is earlier than that of the channel's current topic, so if necessary, the supplied timestamp is advanced to force the topic to be changed.
`do_check_modes()`
  Called when checking the validity of a mode change on a registered channel; ensures that the `+L`, `+f`, and `+j` modes are set properly according to the mode lock data. (The "off" versions, `-L` and so on, do not require parameters and thus can be handled by the standard ChanServ code.)
`do_check_chan_user_modes()`
  Called when checking whether to modify a client's channel user modes. This routine watches for and excludes from the check two classes of clients: other service-type pseudoclients (with user mode `+S` set), since they generally should not be subject to the same restrictions as ordinary clients, and "hiding" users (`+I`), whose presence would be revealed by a mode change.
`do_check_kick()`
  Called when checking whether to allow a user to join a channel. This routine watches for users entering administrator-only, secure-only, or no-hiding channels, and kicks them out if their user modes are incompatible with the channel. (Normally the IRC server will take care of this and Services will never see the join attempt, but when joining a registered channel which is empty, the server will not know anything about the channel's locked modes, so Services must perform the check.)
`do_set_mlock()`
  Called when setting a new mode lock on a registered channel. This routine takes care of setting the aforementioned `+L`, `+f`, and `+j` mode parameters in the mode lock structure, and also ensures that the new mode lock is consistent (`+K` requires `+i` to be set, and `+L` requires `+l`).
`do_send_svsjoin()`
  Called to send a message which forces a user to join a channel. In Unreal, the message is called `SVSJOIN`.
`do_send_akill()`
`do_cancel_akill()`
`do_send_exclude()`
`do_cancel_exclude()`
`do_send_sgline()`
`do_send_sqline()`
`do_send_szline()`
`do_cancel_sgline()`
`do_cancel_sqline()`
`do_cancel_szline()`
  Called to add or remove autokill, autokill exclusion, or S-line masks.

Back to top

##### 5-6-14-4. Module initialization and cleanup

The final part of the module source file is the module framework, used by the core module subsystem when loading and unloading the module, along with callback functions to monitor loading and unloading of relevant modules and set appropriate callbacks and local variables.

The module's configuration data is stored in the exported array `module_config[]`. The `protocol/unreal` module includes two Unreal-specific configuration options: `ServerNumeric`, to assign Services a "numeric" value to be used with Unreal servers, and `SetServerTimes`, which sets whether and how often Services should synchronize other servers' clocks. The array also includes configuration data from the `SJOIN` handler (defined in `sjoin.h`).

The `do_load_module()` and `do_unload_module()` routines watch for various pseudoclient-related modules to be loaded or unloaded. For modules that provide callbacks which the `protocol/unreal` module hooks into, the appropriate callback functions are added here. Additionally, the handles for the `operserv/main` and `chanserv/main` modules, as well as the values (addresses) of the symbols `is_services_admin` and `s_ChanServ`, are saved when the relevant module is loaded and cleared when it is unloaded (see section 5-6-14-1).

Finally, the initialization and cleanup functions call the appropriate subroutines in a fairly arbitrary order (except that cleanup is performed in the opposite order of initialization). The initialization and cleanup for the auxiliary source files is also handled here.

Back to top

### 5-7. Auxiliary source file details

In addition to the main protocol files described in section 5-6, there are several auxiliary source files which implement functions common to two or more protocols. These are listed below. The source files are all designed to be included directly into the modules which use them; this is to avoid symbol clashes caused by linking the same object file into two or more modules when compiling modules statically. (Each source file also has a corresponding header file, but as this is included by the source file, modules do not need to include the header files separately.)

Back to top

#### 5-7-1. `banexcept.c`, `banexcept.h`

The `banexcept.c` source file implements support for ban exceptions, masks specifying clients which are not subject to channel bans. The channel mode for exceptions is assumed to be `+e`.

In addition to handling the actual setting and clearing of ban exceptions (adding or removing masks on the channel's ban exception array), the file also implements a handler for clearing ban exceptions using the `CLEAR_EXCEPTS` flag to `clear_channel()`, as well as a callback function for the ChanServ `CLEAR` command allowing `CLEAR EXCEPTIONS` to be used. Since the latter function is part of the `chanserv/main` module rather than the core, this necessitates the use of "`load module`" and "`unload module`" callbacks to watch for that module being loaded and unloaded.

Back to top

#### 5-7-2. `chanprot.c`, `chanprot.h`

The `chanprot.c` source file implements support for a "protected" channel user mode, preventing ordinary channel operators from kicking a user with that mode. However, the only "support" required is the modification of one language string, which is modified by the initialization routine and restored to its original value by the cleanup routine.

Back to top

#### 5-7-3. `halfop.c`, `halfop.h`

The `halfop.c` source file implements support for a "half-op" channel user mode, assumed to be `+h`. Half-ops occupy a privilege level between voiced users (`+v`) and channel operators (`+o`); typically, they can set `+v` on users and change the channel topic, but cannot change channel modes or kick channel operators. In addition to updating several language strings, the source file adds a callback function for the ChanServ `CLEAR` command allowing `CLEAR HALFOPS` to be used. As with `banexcept.c`, this necessitates the use of load/unload-module callback functions to watch for the ChanServ module being loaded or unloaded.

Back to top

#### 5-7-4. `invitemask.c`, `invitemask.h`

The `invitemask.c` source file implements support for invite masks, masks specifying clients which are allowed to join a `+i` channel without being explicitly invited into the channel. The channel mode for exceptions is assumed to be `+I`. Except for the actual mode character used and structure fields modified, this source file is identical to `banexcept.c`.

Back to top

#### 5-7-5. `sjoin.c`, `sjoin.h`

The `sjoin.c` source file implements support for the `SJOIN` message used in IRC servers such as Bahamut and Unreal. This is easily the most complex of the auxiliary source files.

The basic idea of the `SJOIN` message is to condense several types of channel information—channel creation time, channel modes, and users on the channel—into a single message, reducing the bandwidth required to send channel state across the network and reducing the potential for race conditions. There are a few slightly different message formats used, as noted in the source code comments, but in general the message takes the channel name, channel creation time, mode string, (optional) mode parameters, and finally (in a colon-prefixed parameter) the list of clients (nicknames) on the channel. In this last parameter, each client's nickname is prefixed with characters indicating the client's channel user modes; for example, if `Nickname` is a channel operator on the channel, the client will be listed as `@NickName` in the message.

Processing of the `SJOIN` message is handled by the `do_sjoin()` routine; the module should call this routine when in receives an `SJOIN` message. `do_sjoin()` looks at the parameters to determine the message's format, then walks through the list of nicknames (and possibly bans and exceptions), adding each one to the channel. If any users actually joined the channel, then the channel modes and timestamp are set afterwards.

The `SJOIN` message can also be used as a shortcut to remove all users from a channel with one command. By sending an `SJOIN` with a channel timestamp older than the channel's current timestamp, the remote server will treat Services' `SJOIN` as authoritative. If that message indicates that no users are in the channel (an empty last parameter), then the IRC server will automatically kick all the users currently on the channel, saving Services the trouble of having to send `KICK` messages for all users.

Additionally, since `SJOIN` includes the channel's creation time as a parameter, it is possible to use a dummy `SJOIN` message to deliberately set the creation timestamp of a registered channel to the time the channel was registered, ensuring that the channel will always be considered the "canonical" one even if the same channel is created on a split server.. The configuration option `CSSetChannelTime` activates this behavior. (This configuration option is defined in a macro, `SJOIN_CONFIG`, in `sjoin.h`; the module should include this macro as part of its `module_config[]` array.) The actual setting of the channel creation time is done using the "`channel create`" callback; when a user first enters an empty channel, Services sends an `SJOIN` message to the network with the altered channel creation time, including the newly-joining user in the user list; this results in the channel creation time being updated without any other changes being made to the channel.

Unfortunately, both of the major IRC servers using `SJOIN`, Bahamut and Unreal, have idiosyncrasies that prevent this behavior from working exactly as designed; Bahamut ignores any channel user modes set on clients who are not behind the sending server, causing the client to lose channel operator privileges, while Unreal interprets an empty mode string (a single "`+`") to mean "clear all channel modes". To work around this, if the module defines the preprocessor symbol `BAHAMUT_HACK` before including this source file, this processing will be modified to set `+o` again on the newly-joining client if it was `+o` before the Services `SJOIN` was sent; this restores the original channel state, at the cost of having an unsightly `MODE -o` and `MODE +o` in quick succession when a user first enters a registered channel. Likewise, defining `UNREAL_HACK` will work around the Unreal problem by using the channel mode parameter to set `+o` on the user rather than listing the user in the final parameter.

Back to top

#### 5-7-6. `svsnick.c`, `svsnick.h`

The `svsnick.c` source file implements support for forced changing of clients' nicknames; this is used, for example, by NickServ to change a client's nickname to a "guest" nickname rather than disconnecting the client outright. The implementation itself simply consists of setting the `PF_CHANGENICK` protocol feature flag and assigning a function to the `send_nickchange_remote()` function pointer. However, unlike most other auxiliary source files, the initialization function `init_svsnick()` takes a parameter; this is so that the protocol module can specify the name of the message used, in case it is not `SVSNICK`.

Back to top

#### 5-7-7. `token.c`, `token.h`

The `token.c` source file implements the use of one- or two-character tokens to substitute for full message names in server-to-server communications. The mapping of tokens to message names is given by a `TokenInfo` array passed to `init_tokens()`, terminated by an entry with the `token` field of the structure set to `NULL`. The initialization routine uses this mapping array to generate a 65,536-entry lookup table, indexed by the two characters of the token taken as a 16-bit value with the first character in the high eight bits (a value of zero is used for the second character in the case of a one-character token). The values in the lookup table point directly to the handler functions; this eliminates the necessity to search through the message table, but also means that any later changes to the message table will not be seen.

The actual processing of the tokens is done by a callback function added to the "`receive message`" callback. The function checks whether the message name is two characters long or less and whether there is a function in the table corresponding to the one- or two-character message name; if so, that function is called, and the ordinary message processing is skipped.

Back to top

Previous section: The module system | Table of Contents | Next section: Database handling