IRC Services Manual - 6. Adding features to Services

# IRC Services Manual

## 6. Adding to and modifying Services

6-1. Writing modules
6-1-1. Overview
6-1-2. Module loading and unloading
6-1-3. Inter-module dependencies
6-1-4. Callbacks
6-1-5. Handling messages from the network
6-1-6. Special types of modules
6-1-7. Compilation and installation
6-2. Callbacks provided by Services and standard modules
6-2-1. Services core callbacks
6-2-2. OperServ callbacks
6-2-3. NickServ callbacks
6-2-4. ChanServ callbacks
6-2-5. MemoServ callbacks
6-2-6. StatServ callbacks
6-2-7. HTTP server callbacks
6-3. Submitting additions or changes to Services
6-4. Coding guidelines for Services

Table of Contents

### 6-1. Writing modules

IRC Services 5.0 introduces a new "module" system allowing features to be added to and removed from Services at will with no changes to the core Services code.

Note that this section is not a complete "how-to" for writing modules; please read the source code for existing modules for detailed information. In particular, the "`protocol/unreal`" module (`modules/protocol/unreal.c`) is extensively documented and is intended to serve as an example of how modules should be written.

#### 6-1-1. Overview

Services modules are based on, and in fact use the same system as, standard dynamic-link libraries. Upon startup or reloading of the configuration file, Services will load specified modules into memory and execute each module's initialization function. This function should set up any data structures necessary and register all callback functions needed (see below). At shutdown, or when the module is otherwise unloaded, its cleanup function will be called; this function should free all resources in use and perform other necessary cleanup functions, or return an error if the module cannot unload itself for some reason.

Module interaction with Services takes place primarily through the use of callbacks. These are functions which the module registers with Services to be called when a particular event occurs, such as a message arriving from the remote server or a certain amount of time passing. Typically, a module will register callbacks for events it is interested in in its initialization routine, and remove them when the module is unloaded.

Modules are manipulated using the functions provided by `modules.c` in the Services core. They are as followed:

`Module *load_module(const char *modulename)`
  Loads the given module and returns a pointer to a data structure (`Module *`) for the module. The contents of this data structure are private, and the pointer should only be considered as a "handle" that indicates a particular module. If the module could not be loaded, returns `NULL`. See section 6-1-2 below for details on the module loading process.

`int unload_module(Module *module)`
  Unloads the given module. Returns nonzero if the module was successfully unloaded, zero if not. See section 6-1-2 below for details on the module unloading process.

`Module *find_module(const char *modulename)`
  Returns a handle for the module whose name is given by "`modulename`". If no such module has been loaded, returns `NULL`.

`void use_module(Module *module)`
  Increments the "use count" for the given module. A module whose use count is nonzero is not permitted to be unloaded. This function is typically called by modules which depend on other modules to function properly; see section 6-1-3 for details on inter-module dependencies. A module may call `use_module()` on itself only if the global variable `modules_allow_use_self` is nonzero; otherwise, a "BUG" message will be logged and the call will fail. Modules may never call `use_module()` in such a way as to create a dependency loop of two or more modules, regardless of the setting of `modules_allow_use_self` (doing so will likewise cause a "BUG" message to be logged).

`void unuse_module(Module *module)`
  The opposite of `use_module()`: decrements the "use count" for the given module. As with `use_module()`, a module may not call `unuse_module()` on itself unless the global variable `modules_allow_use_self` is nonzero.

`void *get_module_symbol(Module *module, const char *symname)`
  Looks up the value of the symbol "`symname`" in the given module, and returns its value; if the symbol cannot be found, returns `NULL`. If the symbol is not defined in the given module but is defined by another module, the return value of this function is undefined. If "`module`" is `NULL`, then the main program and all modules are searched for the symbol, and `NULL` is only returned if the symbol does not exist (if the symbol is defined more than once, then a random one will be returned).

  Note that the "value" of a symbol as returned by this function is the address of the symbol. In particular, using this function to look up a variable in another module will return the address of the variable, not not the value it actually contains; thus, if a module declares

  > `int intvar;`

  then code similar to the following should be used to access it:

  > `int *p_intvar = get_module_symbol(mod, "intvar");
  > if (p_intvar)
  > printf("Value of `intvar' is %d\n", *p_intvar);
  > else
  > printf("Unable to resolve symbol `intvar'\n");`

`const char *get_module_name(Module *module)`
  Returns the name of the given module. This is usully the path used to load the module (passed to `load_module()`), but may be different if the module defines a `module_name` symbol. If `NULL` is passed for the "`module`" parameter, returns the string "`core`".

`MODULE_NAME`
  This preprocessor macro is a shortcut for `get_module_name(module)` (where "`module`" is assumed to be a variable pointing to the current module's handle). It can be used just like any other string constant or macro, except that it cannot be inserted into literal string constants; in other words,

  > `printf("Module name: " MODULE_NAME "\n");`

  will not compile.

`int register_callback(Module *module, const char *name)`
  Registers a new callback list. The calling module should pass its own module information structure (as passed to `init_module()` or obtained from `find_module()`). Returns an ID value, which is a non-negative integer.

`int call_callback(Module *module, int id)
int call_callback_1(Module *module, int id, void *arg1)
int call_callback_2(Module *module, int id, void *arg1, void *arg2)
int call_callback_3(Module *module, int id, void *arg1, void *arg2, void *arg3)
int call_callback_4(Module *module, int id, void *arg1, void *arg2, void *arg3, void *arg4)
int call_callback_5(Module *module, int id, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)`
  Calls each function in the given callback list, optionally passing additional arguments to the callback function. If a function in the list returns a nonzero value, that value is returned immediately to the caller without calling any additional callback functions. If all callback functions return zero (or there are no functions to call), returns 0; returns -1 on error (the given callback ID does not exist for the given module).

`int unregister_callback(Module *module, int id)`
  Deletes a callback list. Returns nonzero on success, zero on failure (given callback does not exist).

`int add_callback_pri(Module *module, const char *name, callback_t callback, int priority)`
  Adds the given callback function to the given list with the given priority (a higher priority value means the function is called sooner). The priority must be between CBPRI_MIN and CBPRI_MAX inclusive. Returns nonzero on success, zero on failure (given callback does not exist or priority out of range). An error will not be returned if the same function is registered more than once, but this behavior is not supported and may change in the future.

`int add_callback(Module *module, const char *name, callback_t callback)`
  Adds the given callback function to the given list with priority zero. This is a shortcut for `add_callback_pri(module,name,callback,0)`, and the two forms are exactly equivalent.

`int remove_callback(Module *module, const char *name, callback_t callback)`
  Removes the given callback function from the given list. Returns nonzero on success, zero on failure (given callback does not exist, or given function is not on callback list).

#### 6-1-2. Module loading and unloading

Modules are loaded through the `load_module()` function in `modules.c`, which is called for each module listed in `ircservices.conf` at Services startup (and when an OperServ `REHASH` command is given after adding a new module to `ircservices.conf`). `load_module()` takes one parameter, the name of the module to load, and performs the following steps (assuming dynamically-compiled modules on a system with the `dlXXX()` family of functions):

- Calls `dlopen()` for the module (the filename is the module name passed to `load_module()` with "`.so`" appended). If `dlopen()` fails, `load_module()` returns `NULL`.
- Creates a module information structure (`Module *`) for the module and initializes it.
- Checks the module's version code against the version code of the Services core; if the two do not match or a version number is not present in the module, `load_module()` frees the module information structure, calls `dlclose()` on the module and returns `NULL`. The version code is declared as
  `int32 module_version = MODULE_VERSION_CODE;`
  and must be exported by every module.
- Parses the section of the `modules.conf` file for the module (if any), using the configuration directives in `module_config[]`. `module_config[]` must be defined as an array of `ConfigDirective` elements, terminated by an element with `name == NULL`. If any errors occur during parsing, `load_module()` frees the module information structure, calls `dlclose()` on the module and returns `NULL`.
- Calls the module's `init_module()` function, passing the allocated module information structure. This function should return nonzero on success and zero on failure. If the function returns failure, `load_module()` frees the module information structure, calls `dlclose()` on the module and returns `NULL`.
- Calls the `load module` callback, passing the module pointer to each function in the callback list.
- Links the module information structure into the global module list and returns it.

The `unload_module()` function takes a pointer to the module structure (handle) for the module to unload, and performs the following steps:

- Calls the module's `exit_module()` function. This function takes a single parameter, ``int shutdown`', which is 0 for a regular module unload and 1 if the program is shutting down. The function should return nonzero on success and zero on failure; if it returns failure, `unload_module()` returns zero (failure).
- Calls the `unload module` callback, passing the module pointer to each function in the callback list.
- Calls `dlclose()` for the module.
- Unlinks the module information structure from the global list and returns nonzero (success).

#### 6-1-3. Inter-module dependencies

Some modules may require functions from other modules to work correctly; for example, a module adding a new command to NickServ would obviously require NickServ itself to be loaded first. Such modules can use the `find_module()` function from `modules.c` to check whether the module has been loaded. An example might look like this:

        static Module *module_nickserv;
        ...
        int init_module(Module *me)
        {
            module_nickserv = find_module("nickserv/main");
            if (!module_nickserv)
                return 0;
            ...
            return 1;
        }

`find_module()` returns a pointer to the module found, so this can be saved and used in other function calls as needed.

Note that modules should not attempt to load other modules themselves, as the administrator may have deliberately chosen not to include such modules. Instead, the module should fail to load, optionally writing a log message explaining the problem.

If symbols, such as exported functions, from another module are needed, they can be looked up using the `get_module_symbol` function. This function takes a module pointer and a symbol name and returns the address of the symbol (see the function documentation for details). For example, to access the variable `s_NickServ`, which contains the NickServ pseudoclient's nickname, from the `nickserv/main` module, code such as the following could be used:

        static char **s_NickServ_ptr;
        #define s_NickServ (*s_NickServ_ptr)
        ...
        int init_module(Module *me)
        {
            ...
            s_NickServ_ptr = get_module_symbol(module_nickserv, "s_NickServ");
            if (!s_NickServ_ptr) {
                module_log("Unable to resolve symbol `s_NickServ'");
                return 0;
            }
            ...
        }
        ...
        static void send_message(User *user)
        {
            notice(s_NickServ, user->nick, "message");
        }

This code would retrieve the address of the `s_NickServ` variable during module initialization, then dereference it to obtain the contents of the variable (the nickname itself), using a macro to make the code easier to read. (This macro would have to be defined after any `#include` directives to avoid errors in header declarations.) If the symbol was not found, the module would fail to load.

When using variables from other modules, be aware that while the address of the variable will always remain constant, the value of the variable may change at any time, so you should always access the value by dereferencing a pointer to it, rather than by saving the value locally. The macro used in the example above provides a way to do this which still allows the symbol to be used as if it was an ordinary variable. (However, in the example above the pointer variable is guaranteed not to be `NULL`, because the module returns failure from `init_module()` if the symbol is not found. If the module does not do this or take other steps to ensure that the pointer is valid, the macro would need to be modified to provide a default value in case the pointer is `NULL`.)

A second, though not recommended, method of importing symbols from other modules is to simply reference them as you would ordinary external symbols. This will create an unresolved reference to the symbol in the module object file, which will be resolved at load time. This approach is slightly more efficient than using `get_module_symbol()`, since there is no need to store or retrieve the symbol's address separately. However, if the symbol is not found at load time, then load_module() will log an error message and return failure without calling the module's `init_module()` function, so there is no way for the module to log a more informative error message or take other action. This method should only be used when it can be assumed that the module being used will always be loaded first; for example, the NickServ sub-modules (such as `nickserv/autojoin` and `nickserv/link`) take this approach with respect to the main NickServ module (`nickserv/main`). It is still necessary to retrieve the module pointer using `find_module()` if it is needed for other module-related function calls.

One thing to keep in mind, whichever method is used, is that modules can only reference symbols in modules loaded earlier. If your module depends on symbols from another, make certain you document that so that people who use the module put it in the right place in `ircservices.conf`.

In certain cases, circular references may arise—a case where module A depends on a symbol from module B, while module B depends on a symbol from module A. In such a case, at least one of the modules must use `get_module_symbol()` to access the other module's symbol, and must do it after the module has been loaded (i.e. not failing in `init_module()`), so that the other module has a chance to load as well. This can be accomplished using the `load module` callback in the Services core, which allows the module to take action when subsequent modules are loaded.

Finally, modules which export symbols to be used with other modules must explicitly declare them to be exported, using the `EXPORT_VAR()`, `EXPORT_ARRAY()`, or `EXPORT_FUNC()` macros in modules.h; for example:

        EXPORT_VAR(int,variable)
        int variable = 123;

        EXPORT_ARRAY(char,string)
        char string[] = "Hello world";

        EXPORT_FUNC(function)
        int function(int x) { return x*x+x; }

These macros do not have any actual effect on compilation, but they are used by a script which prepares the modules for static linking. Without these declarations, modules will be unable to access any symbols in the module when compiled statically. Note, however, that the `init_module()` and `exit_module()` functions and the `module_version` and `module_config` variables are automatically exported, and should not be explicitly listed this way (in fact, listing them will cause an error when compiling statically).

#### 6-1-4. Callbacks

`modules.c` provides an interface for registering and calling callback functions; these are functions which are called from a separate module or function when a certain event occurs, such as a module being loaded or a user connecting to IRC. Callbacks are referred to by a combination of module and callback name or ID (callbacks in the Services core use a module pointer of `NULL`).

The type of a callback function is defined by `callback_t`: `int (*)(...)`. The parameters passed to the function depend on the particular callback. If a callback function desires to terminate processing of the callback list, it should return a nonzero value; returning zero will allow the next function in the list to be called. Any pointer parameters should be considered "`const`" (unmodifiable) unless the function processes the event and returns nonzero.

To add a callback function to a callback list registered by another module (or a callback list defined by the Services core), use the `add_callback()` function (or `add_callback_pri()` if you want to use a priority other than zero; priorities are used to determine the order in which the functions are called, as described below). To remove a function previously added to a callback list, use the `remove_callback()` function. It is legal to attempt to remove a callback function which was not added in the first place, or to attempt to remove a callback function from a nonexistent callback; `remove_callback()` will return failure in these cases. Warning: if you forget to remove callbacks before or when your module exits, Services will crash!

To create your own callback list, use the `register_callback()` function; to delete it, call the `unregister_callback()` function, passing in the callback ID returned from `register_callback()`. To call a list of callback functions, use the `call_callback_X()` set of functions, where `X` is the number of parameters passed to each callback function (the `_X` is omitted if no parameters are passed). This syntax is used to help module creators double-check that the right number of parameters are being passed to callback functions, since such functions cannot be checked against prototypes.

Callback functions are called in descending order of priority (so, for example, a function with a priority of 1 is called before one with a priority of 0); functions with the same priority are called in the same order they were added to the callback list.

See section 6-2 below for a list of available callbacks.

#### 6-1-5. Handling messages from the network

Most modules will need to be able to process messages received from the IRC network to perform any useful functions. There are four main ways of doing this:

- Through one of the pseudoclients' "`command`" callbacks. If your module adds functionality to one of the existing pseudoclients (NickServ, ChanServ, etc.), you can hook into the "`command`" callback provided by that pseudoclient to receive each command (other than a CTCP PING) sent to the pseudoclient, and process the commands that apply to you. You will probably also want to hook into the "`HELP`" and "`HELP COMMANDS`" callbacks to provide help information about the commands your module implements. See the section below on the particular pseudoclient's callbacks for details.

- Through the `m_privmsg` callback. This callback is designed for handling user interaction with pseudoclients; if you hook into this callback, you will be able to check each `PRIVMSG` (`/msg`) message received by Services and take appropriate action. (Naturally, you should leave alone any `PRIVMSG` addressed to a nickname your module doesn't use.)

- By registering a message list. This method is intended for use by protocol modules to implement protocol-specific features, but can be used by any module. Set up an array of `Message` structures (see the `messages.h` file) terminated by an entry with `name == NULL`, then call `register_messages(table)` (where `table` is the array of `Message` structures). When a message with a name listed in your message table arrives, the routine given in the `func` member associated with that name will be called with the message source (`char *`) and an argument count (`int`) and array (`char **`); if you specify `NULL` instead of a function pointer, the message will be silently ignored. As with callbacks, make certain you call `unregister_messages()` on your message table when your module is unloaded, or Services will crash on the next message it receives when it tries to access your (nonexistent) table.

  Note that if a particular message is listed in more than one message table, only the function in the most recently registered table will be called; this allows you to override the default behavior for a particular message. If you do this, however, be aware that there is no way to call the previously registered function--if you want to allow the previous handler to run, you will need to use the `receive message` callback, discussed below.

- Through the `receive message` callback. This is the lowest-level method of handling messages; your callback function will be called for each message received from the remote server, and you can decide whether to process the message or pass it on.

#### 6-1-6. Special types of modules

While callbacks are used for most inter-module interactions, to improve performance, some types of modules are integrated more tightly with Services. These include protocol modules, database modules, and mail modules.

#### Protocol modules

Protocol modules allow Services to communicate with different types of IRC servers. Services calls several routines in these modules directly; each protocol module must set the following function pointers to point to appropriate routines:

`void (*send_nick)(const char *nick, const char *user, const char *host, const char *server, const char *name, const char *modes)`
  Sends an appropriate combination of `NICK`/`USER`/`MODE` messages (or their equivalents) to introduce a new nickname onto the network.

`void (*send_nickchange)(const char *nick, const char *newnick)`
  Sends a `NICK` message to change an existing user's nickname.

`void (*send_namechange)(const char *nick, const char *newname)`
  Sends a message to change an existing user's "real name", if possible. (If the protocol does not support it, this can be a do-nothing routine, but it must be defined.)

`void (*send_server)(void)`
  Sends the initial server registration message (typically a `SERVER` message) for Services itself.

`void (*send_server_remote)(const char *server, const char *reason)`
  Sends a server registration message for a "remote" server (used for jupes).

`void (*wallops)(const char *source, const char *fmt, ...)`
  Sends a message to "all IRC operators" (the definition of which may vary between protocols). This is implemented as a `WALLOPS` for RFC1459, but as a `GLOBOPS` for DALnet-compatible servers, for example. This function takes `printf()`-style parameters.

`void (*notice_all)(const char *source, const char *fmt, ...)`
  Sends a notice to all users on the IRC network, typically using "`NOTICE $*`" or equivalent (protocols which do not support `$*` by itself may require a common domain name to be set in the configuration file, as the RFC1459 and most other current protocols do). This function takes `printf()`-style parameters.

`void (*send_channel_cmd)(const char *source, const char *fmt, ...)`
  Sends a command which modifies channel status. This is used where "`source`" is a nickname introduced by Services (such as "ChanServ"); some protocols do not allow arbitrary clients, even those from Services, to change channel commands, so those protocols would need to change "`source`" to "`ServerName`" or take other measures to get the command recognized. If possible, the command should be sent on as is, for example:

  > `va_list args;
  > va_start(args, fmt);
  > vsend_cmd(source, fmt, args);
  > va_end(args);`

  This command takes `printf()`-style parameters.

`void (*send_nickchange_remote)(const char *nick, const char *newnick)`
  Forcibly changes a remote client's nickname. The client's current nickname is given by "`nick`", and the nickname to change to by "`newnick`". Note: This routine must not be called if the `PF_CHANGENICK` flag is not set in `protocol_features` (see below).

The two strings "`pseudoclient_modes`" and "`enforcer_modes`" should also be set to the user modes which pseudoclients and enforcers, respectively, should receive.

Furthermore, the module must set the following four variables (defined in `send.c`) to appropriate values:

`const char *protocol_name`
  The name of the protocol (e.g. RFC1459, Dreamforge, etc.)

`const char *protocol_version`
  A string which identifies what versions of the protocol are supported. If the protocol does not have "versions", the variable should be set to the empty string (`""`).

`uint32 protocol_features`
  A bitmask indicating what features (`PF_*` constants defined in `services.h`) are supported by the protocol; if none of the listed features are supported, the value should be 0.

`int protocol_nickmax`
  The actual maximum length of a nickname, not including the trailing null byte used when storing the string internally; for example, RFC1459-compliant servers would use a value of 9 here. The module should check that `NICKMAX` (defined in `defs.h`) is strictly greater than this value, and refuse to load if it is less or equal. (Remember that `NICKMAX` includes the trailing null byte, so it must be at least 1 byte greater than `protocol_nickmax`, which does not include the trailing null.)

For examples of how protocol modules should be designed, see the Unreal module (`modules/protocol/unreal.c`), which is fairly well documented.

#### Database modules

Database modules provide the means for Services to store non-volatile data (data which will be saved between runs), such as the nickname and channel databases. These modules must provide the functions to open, read, write, and close databases for each of the major Services module groups (OperServ, NickServ, ChanServ, MemoServ, and StatServ).

Details of database modules are still in flux; to be defined later.

#### Mail modules

Mail modules provide the low-level functionality to actually send E-mail, and are called by the high-level mail module (`mail/main`) when mail is to be sent. When loaded (i.e. in the `init_module()` function), mail modules must set the "`low_send`" symbol (exported from the `mail/main` module) to point to a function with the following prototype:

        int low_send(const char *from, const char *fromname, const char *to,
                     const char *subject, const char *body)

"`from`" and "`fromname`" are the address and name, respectively, to be used in the `From:` line; "`to`" is the address for the `To:` line, "`subject`" is the text for the `Subject:` line, and "`body`" is the body text. Except for "`body`", none of the strings will contain newlines, and the "`from`" and "`to`" strings will be valid E-mail addresses (as determined by `valid_email()`). The function should return 0 on success, -1 on failure.

#### 6-1-7. Compilation and installation

In order to compile and install a new module, you should do the following:

1. Place your module in a new subdirectory under the `modules` directory.
2. Create a `Makefile` for your new subdirectory.
3. Run `make` and `make install` (or `gmake` and `gmake install`) as usual.

The `Makefile` in the `modules` directory will automatically detect the presence of your new subdirectory and call its `Makefile` as appropriate. Additionally, the `modules/Makerules` file contains rules for compiling modules both dynamically and statically, so in most cases you only need to set the variables it needs (see the comments at the top of that file) and add "`include ../Makerules`" at the end of your Makefile. See `modules/mail/Makefile` for a simple example of a module `Makefile`, or `modules/protocol/Makefile` for a more complex example.

Once your module has been successfully compiled and installed, you can load it into Services with a `LoadModule` directive in `ircservices.conf`, just as with any of the standard modules, and you can add configuration directives for your new module in `modules.conf` by enclosing them inside `Module` and `EndModule`:

> `Module directory/module-name
> ` (configuration directives go here)`
> EndModule`

Back to top

### 6-2. Callbacks provided by Services and standard modules

The callbacks provided by Services are listed below under the names used to access them (e.g., with `add_callback()`). When using Services core callbacks, use a module parameter of NULL.

Note that where the descriptions below refer to "processing" a message, they refer to a message that is meant specifically for the callback in question and which is not to be passed on to other modules or the Services core.

Except as otherwise specified, callbacks which return zero must leave all arguments unchanged, and callbacks which return nonzero may modify any arguments which are not specified as `const`.

#### 6-2-1. Services core callbacks

`channel create`
Parameters: `Channel *channel`, `User *user`, `int32 mode`
  Called when a channel is created (i.e. a user joins a nonexistent channel). The user that created the channel and that user's modes (CUMODE_*) are also passed to the callback (note that the user is not yet in the channel's user list). The callback routine should always return 0.

`channel delete`
Parameters: `Channel *channel`
  Called when a channel is about to be deleted (i.e. the last user has left the channel). The callback routine should always return 0.

`channel JOIN`
Parameters: `Channel *channel`, `struct c_userlist *u`
  Called when a user has joined a channel. The callback routine should always return 0.

`channel JOIN check`
Parameters: `const char *channel`, `User *user`
  Called when a user is about to join a channel. The callback routine should return 1 to refuse the user access to the channel or 0 to allow the `JOIN` to proceed normally.

`channel KICK`
Parameters: `Channel *channel`, `User *user`, `const char *reason`
  Called when a user has been kicked from a channel. The callback routine should always return 0.

`channel MODE`
Parameters: `const char *source`, `Channel *channel`, `int modechar`, `int add`, `char **av`
  Called for each valid mode character in a `MODE` message, except for channel user modes (`+o`, `+v`, etc.). `add` is nonzero if adding modes, zero if removing them. `av`" points to the first argument for the mode; there are guaranteed to be enough arguments for the mode character (provided the number of parameters in the mode definition is set correctly). The callback routine should return 1 if it processes the mode, 0 otherwise.

`channel mode change`
Parameters: `Channel *channel`
  Called when a channel's modes have changed. The callback routine should always return 0.

`channel umode change`
Parameters: `Channel *channel`, `struct c_userlist *user`
  Called when a user's modes on a channel have changed. The callback routine should always return 0.

`channel PART`
Parameters: `Channel *channel`, `User *user`, `const char *reason`
  Called when a user parts from a channel. The callback routine should always return 0.

`channel TOPIC`
Parameters: `Channel *channel`, `const char *topic`, `const char *setter` `time_t topic_time`
  Called when a `TOPIC` command is received. The callback routine should return 0 to permit the topic change or 1 to refuse it. Note that `setter` may be in the form "`nick!user@host`" instead of just a nickname, depending on the remote server type; however, Services currently stores only the nickname, and discards the username and hostname if present.

`clear channel`
Parameters: `const char *source`, `Channel *channel`, `int what`, `const void *param`
  Called whenever the `clear_channel()` function (in `actions.c`) is called, before any other action is taken. The callback routine should return 1 to terminate `clear_channel()` processing or 0 to continue. Note that even if the callback routine returns 1, `clear_channel()` will still flush out any channel mode changes, so the routine does not need to do this itself. See the comments above `clear_channel()` in `actions.c` for the meaning of the parameters.

`command line`
Parameters: `char *option`, `char *value`
  Called at program startup for each command-line option. `option` is the name of the option (the part of the option string after the initial "`-`", up to and not including any "`=`"), and `value`" is the text after the "`=`" (`NULL` if no "`=`" is present). `option` is guaranteed to be non-`NULL`. The callback routine should return 0 if it does not recognize the option, 1 if it successfully processes the option, 2 if there is an error processing the option, or 3 if no error occurred but Services should terminate successfully.

`connect`
Parameters: none
  Called when a connection to the remote server is established. Modules that want to send data to the remote server on startup should use this callback; attempting to send data to the server before the connection is established will cause the data to be lost. The callback routine should always return 0.

`expire`
Parameters: none
  Called whenever the expire timeout (for expiring nicknames, etc.) runs out, or another action, such as OperServ `UPDATE`, explicitly causes expiration to occur. The callback routine should always return 0.

`introduce_user`
Parameters: `char *nick`
  Called whenever Services may need to introduce a pseudoclient (at startup and when receiving a `KILL` message). The `nick` parameter is the nickname of the pseudoclient which should be introduced (if it exists), or `NULL` when introducing all pseudoclients. The callback routine should return 1 if `nick` is not `NULL` and the routine introduces a pseudoclient, 0 otherwise. (If `nick` is `NULL`, the routine should introduce any pseudoclients necessary and return 0.)

`load module`
Parameters: `Module *module`, `const char *modname`
  Called when a module is loaded, after its `init_module()` routine has been called. `modname` is the name of the module, either specified by the module (`char module_name[]`) or copied from the module's path. The callback routine should always return 0. This callback may be used, for example, to add callback routines to a new module's callbacks when the module is loaded.

`m_privmsg`
Parameters: `char *source`, `char *target`, `char *message`
  Called for every `PRIVMSG` which is not ignored by Services. The callback routine should return 1 if it processes the message, 0 otherwise.

`m_whois`
Parameters: `char *source`, `char *target`, `char *target_server`
  Called for every `WHOIS` command. The callback routine should return 1 if it processes the message, 0 otherwise.

`receive message`
Parameters: `char *source`, `char *cmd`, `int ac`, `char **av`
  Called for every message received from the uplink server. The callback routine should return 1 if it processes the message, 0 otherwise. Note that it is acceptable for a callback routine to modify the parameters passed and return 0 to allow the next routine to continue, subject to the following constraints:
  - The number of message parameters (`ac`) is not changed.
  - None of the strings (`source`, `cmd`, or any element of `av`) are lengthened. (Shortening strings is allowed.)

`reconfigure`
Parameters: `int after_configure`
  Called twice when Services is signalled to re-read its configuration files. The first call is made before any module configuration data is changed (but after the main Services configuration settings have been updated), with `after_configure` set to 0; the second call is made after all configuration data has been successfully read and configuration variables have been updated, with `after_configure` set to 1. Callback routines should always return 0.

`save data`
Parameters: none
  Called whenever the save-data timeout runs out, or another action, such as OperServ `UPDATE`, explicitly causes data saving to occur. The callback routine should always return 0.

`save data complete`
Parameters: `int successful`
  Called when a save-data operation completes. The `successful` parameter indicates whether the operation succeeded or not. The callback routine should always return 0.

`server create`
Parameters: `Server *server`
  Called whenever a new server joins the network, after the server record is created. The callback routine should always return 0.

`server delete`
Parameters: `Server *server`, `const char *reason`
  Called whenever a server splits from the network, before the server record is deleted. The callback routine should always return 0.

`set topic`
Parameters: `const char *source`, `Channel *c`, `const char *topic`, `const char *setter`, `time_t t`
  Called twice whenever `set_topic()` (in `actions.c`) is called to set the topic of a channel from Services. Parameters are as for `set_topic()` (in `actions.c`), except as noted below; `source` will always be non-NULL. The first call is done before the channel structure is modified, and all callback routines must return 0 in this case. The second call is done after the channel structure is modified, but before `c->topic_time` is changed; in this call, `topic` and `setter` are both `NULL`. If the callback routine returns 1, no further processing is done. This is currently used to ensure that the topic time is set so that the IRC servers will acknowledge the change under certain protocols.

`unload module`
Parameters: `Module *module`
  Called when a module is unloaded, after its `exit_module()` routine has been called. The callback routine should always return 0. Note that it is not necessary to use this callback to remove callbacks from a module which is being unloaded, since those callback entries will be removed automatically.

`user check`
Parameters: `int ac`, `char **av`
  Called whenever a user joins the network, before the user record is created. `ac` and `av` are the argument list to the `NICK` (or `USER`) command, as passed to `users.c/do_nick()`. The callback routine should return 0 to allow the user to connect or 1 to disallow (in this case the routine should take action to remove the user from the network, such as sending a `KILL` message).

`user create`
Parameters: `User *user`, `int ac`, `char **av`
  Called whenever a user joins the network, after the user record is created. `ac` and `av` are the argument list to the `NICK` (or `USER`) command, as passed to `users.c/do_nick()`. The callback routine should always return 0.

`user delete`
Parameters: `User *user`, `char *reason`, `int is_kill`
  Called whenever a user leaves the network (whether by `QUIT` or `KILL`), before the user record is deleted. `is_kill` is 1 if the user is quitting because of a `KILL`, 0 if because of a `QUIT`. The callback routine should always return 0.

`user MODE`
Parameters: `User *user`, `int modechar`, `int add`, `char **av`
  Called for every mode character given in a user `MODE` message. `add` is nonzero when adding modes and zero when subtracting. `av` points to the first argument for the mode; there are guaranteed to be enough arguments for the mode character (provided the number of parameters in the mode definition is set correctly). The callback routine should return 1 if it processes the mode, 0 otherwise.

`user nickchange (after)`
Parameters: `User *user`, `const char *oldnick`
  Called whenever a user changes nicks, after the nickname is actually changed. The callback routine should return 1 if it kills the user, 0 otherwise.

`user nickchange (before)`
Parameters: `User *user`, `const char *newnick`
  Called whenever a user changes nicks, before the nickname is actually changed. The callback routine should always return 0. Note that users should never be killed within this callback; use the "`user nickchange (after)`" callback for that.

`user servicestamp change`
Parameters: `User *user`
  Called when a user's Services stamp (the `servicestamp` field) is altered and the network should be informed of it. The callback routine should always return 0.

#### 6-2-2. OperServ callbacks

`command`
Parameters: `User *user`, `char *command`
  Called for every message (other than a CTCP `PING` or empty message) received by OperServ. `command` is the first word in the message (where words are separated by spaces); the remaining text can be obtained through repeated calls to `strtok()` with a `NULL` first parameter. The callback routine should return 1 if it processes the message, 0 otherwise. If the routine does not process the message, it must not call `strtok()`.

`expire maskdata`
Parameters: `uint32 type`, `MaskData *md`
  Called when a `MaskData` entry is about to be expired, just before it is actually deleted. The callback routine should always return 0.

`HELP`
Parameters: `User *user`, `char *param`
  Called when the `HELP` command is used with a parameter. The callback should return 1 if it processes the message, 0 otherwise.

`HELP COMMANDS`
Parameters: `User *user`, `int which`
  Called when the `HELP COMMANDS` command is used. The `which` parameter is 0 when called after the list of unprivileged (IRC operator) commands, 1 after the Services operator commands, 2 after the Services administrator commands, and 3 after the Services root commands. The callback routine should always return 0.

`SET`
Parameters: `User *u`, `char *option`, `char *param`
  Called when the `SET` command is used. All parameters are guaranteed to be valid (non-`NULL`); `param` may contain multiple words. The callback routine should return 1 if it processes the given option, 0 otherwise.

`STATS`
Parameters: `User *user`, `char *extra`
  Called for every `STATS` command that is not plain "`STATS`", "`STATS ALL`", or "`STATS RESET`". "`extra`" contains the remainder of the message (after `STATS` and any following whitespace). The callback routine should return 1 if it processes the command, 0 otherwise.

`STATS ALL`
Parameters: `User *user`, `const char *s_OperServ`
  Called for every `STATS ALL` command. `s_OperServ` is the OperServ pseudoclient nickname; the callback routine should send messages using this nick. The callback routine should always return 0.

`cancel_akill` ("`operserv/akill`" module)
Parameters: `const char *username`, `const char *host`
  Called when an autokill is to be removed from the uplink server. The callback routine should return 1 if it removes the autokill, 0 otherwise.

`send_akill` ("`operserv/akill`" module)
Parameters: `const char *username`, `const char *host`, `time_t expires` `const char *who`, `const char *reason`
  Called when an autokill is to be sent to the uplink server. The callback routine should return 1 if it sends the autokill, 0 otherwise.

#### 6-2-3. NickServ callbacks

`authed` (module `nickserv/mail-auth`)
Parameters: `User *u`, `NickInfo *ni`, `NickGroupInfo *ngi`, `int authreason`
  Called when a user successfully authenticates their nickname with the `AUTH` command, after all normal processing has been performed. `authreason` is the value that the `ngi->authreason` field had before being cleared (one of the `NICKAUTH_*` constants). The callback routine should always return 0.

`cancel_user`
Parameters: `User *u`, `int old_status`, `int old_authstat`
  Called whenever the `cancel_user()` routine is called for a user with a registered nick. `old_status` is the value of the user's nick's `status` field while the nick was in use (when the callback is called, all temporary flags will have been cleared); `old_authstat` is likewise the value before clearing of the nick group's `authstat` field. The callback routine should always return 0.

`check_expire`
Parameters: `NickInfo *ni, NickGroupInfo *ngi`
  Called when a nickname is being checked for expiration, after the nickname's last-used time has been updated (if appropriate). The callback routine should return 1 if the nick should be expired, 0 otherwise. If returning 1, the callback routine should log an appropriate message.

`check recognized`
Parameters: `const User *u`
  Called whenever Services needs to check whether a user is recognized as being the owner of the nick they are using (e.g., when the user connects to the network). The `User` structure is guaranteed to have valid `ni` and `ngi` pointers. The callback routine should return:
  - 0, to indicate that the user was not recognized;
  - 1, to indicate that the user was recognized as the owner of the nick; or
  - 2, to indicate that the user should not be recognized as the owner of the nick, regardless of any subsequent checks.

`collide`
Parameters: `User *u`
  Called whenever NickServ determines that a user should be "collided". The callback should return 1 if it handles colliding the user (for example, by forcibly changing the user's nick), or 0 to allow normal collide processing to continue.

`command`
Parameters: `User *user`, `char *command`
  Called for every message (other than a CTCP `PING` or empty message) received by NickServ. See the OperServ "`command`" callback for details.

`HELP`
Parameters: `User *user`, `char *param`
  Called when the `HELP` command is used with a parameter. The callback should return 1 if it processes the messages, 0 otherwise.

`HELP COMMANDS`
Parameters: `User *user`, `int which`
  Called when the `HELP COMMANDS` command is used. The `which` parameter is 0 when called after the list of unprivileged commands and 1 after the list of privileged commands (those shown to IRC operators). The callback routine should always return 0.

`identified`
Parameters: `User *u`, `int old_authstat`
  Called when a user identifies for their nick. `old_authstat` is the authorization status of the nick before the identify took place. The callback routine should always return 0.

`IDENTIFY check`
Parameters: `User *u`, `char *pass`
  Called when a user attempts to identify for their nick, after the standard checks (whether the nick exists, etc.) are done but before the password itself is checked. The callback routine should return 1 to disallow identification or 0 to allow processing to continue normally.

`nick delete`
Parameters: `NickInfo *ni`
  Called when a nick's registration is deleted. The callback routine should always return 0.

`nickgroup delete`
Parameters: `NickGroupInfo *ngi`, `const char *oldnick`
  Called when a nick group is deleted (i.e. when the last nick for the group is deleted), after the `nick delete` callback is called. `oldnick` is the nickname whose deletion caused this nickgroup to be deleted. The callback routine should always return 0.

`REGISTER/LINK check`
Parameters: `User *u`, `const char *nick`, `char *password`, `char *email`
  Called when a user attempts to register their nickname, after the validity of the parameters has been checked but before checking whether the nickname exists or the password is strong enough. `u` is the user who sent the `REGISTER` command, and `nick` is their nickname (the same as `u->nick`). `email` may be `NULL` (if the user did not provide an E-mail address), but `password` will always be non-`NULL`. The callback routine should return 0 to allow the registration to continue or 1 to disallow it.

  Also called by the `nickserv/link` module when a user attempts to link a new nickname to their own. In this case `nick` is the requested target nickname (different from `u->nick`), and `password` and `email` are both `NULL`. The callback is called after the nickname is checked for validity (length and invalid characters), but before checking whether the user has registered their current nickname. The callback routine should return 0 to allow the link to be made or 1 to disallow it.

`registered`
Parameters: `User *u`, `NickInfo *ni`, `NickGroupInfo *ngi`, `int *replied`
  Called when a user registers their nickname, after the data structures have been set up. The integer pointed to by `replied` can be set to 1 to disable the standard "your nick has been registered" reply. The callback need not call `put_xxx()` even if it modifies `ni` or `ngi`, as the caller will do so afterwards. The callback routine should always return 0.

`SET`
Parameters: `User *u`, `NickInfo *ni`, `NickGroupInfo *ngi`, `char *option`, `char *param`
  Called when the `SET` command is used. All parameters are guaranteed to be valid (non-`NULL`), and standard permission checks will have been performed (so `u` either is identified for `ni`/`ngi` or is a Services admin). The callback routine should return 1 if it processes the given option, 0 otherwise.

`SET EMAIL`
Parameters: `User *u`, `NickGroupInfo *ngi`
  Called when the E-mail address associated with a nick group is set, changed, or unset. `u` is the user making the change; this may not necessarily be the owner of the nick if, for example, a Services admin is making the change. The new address is stored in `ngi->email`. The callback routine should always return 0.

`UNSET`
Parameters: `User *u`, `NickInfo *ni`, `NickGroupInfo *ngi`, `char *option`
  Called when the `UNSET` command is used. All parameters are guaranteed to be valid (non-`NULL`), and standard permission checks will have been performed (so `u` either is identified for `ni`/`ngi` or is a Services admin). The callback routine should return 1 if it processes the given option, 0 otherwise.

#### 6-2-4. ChanServ callbacks

`check_chan_user_modes`
Parameters: `const char *source`, `User *user`, `Channel *c`, `int32 modes`
  Called when checking a user's modes on a channel, before any standard checks are performed. The mode change is being performed by `source` (`NULL` for a channel join) on `user` on channel `c`, and the user's current modes on the channel are `modes`. The callback routine should return 0 to allow normal checking to continue or 1 to abort further checks.

  Note: `source` may be the empty string; this indicates an internal call to verify a user's current modes. In this case callback routines should not make any decisions about modes based on the value of `source`.

`check_kick`
Parameters: `User *user`, `const char *chan`, `ChannelInfo *ci`, `char **mask_ret`, `const char **reason_ret`
  Called when checking whether a user should be kicked from a channel, before any standard checks are performed. All parameters are guaranteed to be non-`NULL`, except for `ci`, which will be `NULL` when the channel is not registered. The callback routine may return:
  - 0, to allow normal processing to continue.
  - 1, to cause the user to be kicked from the channel. In this case, the callback routine must set `*mask_ret` to a newly `malloc()`/`smalloc()`'d memory area containing the mask to be banned (`create_mask()` or `sstrdup()` may also be used) and `*reason_ret` to a string for use as the reason in the kick.
  - 2, to abort further processing and allow the user to enter the channel.

`check_modes`
Parameters: `Channel *channel`, `ChannelInfo *ci`, `int add`, `int32 flag`
  Called when checking a channel's modes against its mode lock for every mode locked either on (`add` nonzero) or off (`add` zero). The callback routine should return 1 if it processes the mode, 0 if not. If a mode is not processed by any callback and is further not specially handled by Services (`+k` and `+l` modes), the default action is to simply modify the channel's current modes to match the mode lock (by calling `set_cmode(...,"+X")` or `set_cmode(...,"-X")` for mode `X`), so it is not necessary to add a callback for modes for which this behavior is sufficient.

`CLEAR`
Parameters: `User *user`, `Channel *channel`, `char *what`
  Called when a valid `CLEAR` command is received (i.e. from a user with permission to use the command on the given channel). The callback routine should return 1 if it processes the command, 0 if not.

`command`
Parameters: `User *user`, `char *command`
  Called for every message (other than a CTCP `PING` or empty message) received by ChanServ. See the OperServ "`command`" callback for details.

`HELP`
Parameters: `User *user`, `char *param`
  Called when the `HELP` command is used with a parameter. The callback should return 1 if it processes the message, 0 otherwise.

`HELP COMMANDS`
Parameters: `User *user`, `int which`
  Called when the `HELP COMMANDS` command is used. The `which` parameter is 0 when called after the list of unprivileged commands and 1 after the list of privileged commands (those shown to IRC operators). The callback routine should always return 0.

`INVITE`
Parameters: `User *u`, `Channel *c`, `ChannelInfo *ci`
  Called whenever a valid `INVITE` command is received (i.e. the user has permission to use `INVITE` for the channel). The callback should return 0 to allow normal processing to continue or 1 to abort further processing.

`SET`
Parameters: `User *u`, `ChannelInfo *ci`, `char *option`, `char *param`
  Called when the `SET` command is used. All parameters are guaranteed to be valid (non-`NULL`), and standard permission checks will have been performed (so ``u`' either is identified for `ci` or is a Services admin). The callback routine should return 1 if it processes the given option, 0 otherwise.

`SET MLOCK`
Parameters: `User *u`, `ChannelInfo *ci`, `int modechar`, `int add`, `char **av`
  Called for each valid mode character in a `SET MLOCK` command. `add` is nonzero if adding modes, zero if removing them. `av` points to the first argument for the mode; there are guaranteed to be enough arguments for the mode character (provided the number of arguments in the mode definition is set correctly. The new mode lock (currently being constructed) is stored in the `ci->mlock_XXX` fields. The callback routine should return 1 if it encounters an invalid parameter for a mode, 0 otherwise.

  Also called with `modechar` set to zero when all modes have been processed. In this case, `add` and `av` are undefined, and the callback routine should return 1 if there is a problem with the mode lock, 0 otherwise.

`UNBAN`
Parameters: `User *u`, `Channel *c`, `ChannelInfo *ci`
  Called whenever a valid `UNBAN` command is received (i.e. the user has permission to use `UNBAN` for the channel). The callback should return 0 to allow normal processing to continue or 1 to abort further processing.

`UNSET`
Parameters: `User *u`, `ChannelInfo *ci`, `char *option`
  Called when the `UNSET` command is used. All parameters are guaranteed to be valid (non-`NULL`), and standard permission checks will have been performed (so ``u`' either is identified for `ci` or is a Services admin). The callback routine should return 1 if it processes the given option, 0 otherwise.

#### 6-2-5. MemoServ callbacks

`command`
Parameters: `User *user`, `char *command`
  Called for every message (other than a CTCP `PING` or empty message) received by MemoServ. See the OperServ "`command`" callback for details.

`HELP`
Parameters: `User *user`, `char *param`
  Called when the `HELP` command is used with a parameter. The callback should return 1 if it processes the message, 0 otherwise.

`HELP COMMANDS`
Parameters: `User *user`, `int which`
  Called when the `HELP COMMANDS` command is used. The `which` parameter is 0 when called after the list of unprivileged commands and 1 after the list of privileged commands (those shown to IRC operators). The callback routine should always return 0.

`receive memo`
Parameters: `int ischan`, `void *who`, `const char *whoname`, `const User *sender`, `char *text`
  Called when a user or channel receives a memo. `ischan` is zero if the recipient is a user, nonzero if a channel, and `who` is the `NickGroupInfo *` or `ChannelInfo *` for the recipient, respectively; `whoname` is the name corresponding to `who` (the one given in the MemoServ `SEND` command). The callback should return nonzero to terminate processing or zero to cause the memo to be received normally.

  Callback routines for this callback should use one of the following two priorities (defined in `memoserv.h`): `MS_RECEIVE_PRI_CHECK` for routines which check whether the memo is allowed to be sent, or `MS_RECEIVE_PRI_DELIVER` for actually sending the memo.

`SET`
Parameters: `User *u`, `MemoInfo *mi`, `char *option`, `char *param`
  Called when the `SET` command is used. All parameters are guaranteed to be valid (non-`NULL`), and standard permission checks will have been performed. `mi` points to the MemoInfo for the user's nickname. The callback routine should return 1 if it handles the given option, 0 otherwise.

#### 6-2-6. StatServ callbacks

`command`
Parameters: `User *user`, `char *command`
  Called for every message (other than a CTCP `PING` or empty message) received by StatServ. See the OperServ "`command`" callback for details.

`HELP`
Parameters: `User *user`, `char *param`
  Called when the `HELP` command is used with a parameter. The callback should return 1 if it processes the message, 0 otherwise.

`HELP COMMANDS`
Parameters: `User *user`, `int which`
  Called when the `HELP COMMANDS` command is used. The `which` parameter is currently always 0. The callback routine should always return 0.

#### 6-2-7. HTTP server callbacks

`auth`
Parameters: `Client *c`, `int *close_ptr`
  Called for each valid request received by the server. `c` is the client from which the request was received; the request data is stored in the client structure (see `http.h`), and at a minimum the `method`, `url`, `version_major`, and `version_minor` fields will be set appropriately. If the request is denied and the connection should be closed after the reply is sent, the callback routine should set `*close_ptr` to a nonzero value (`*close_ptr` should not be modified if the request is not denied). The callback routine should return `HTTP_AUTH_ALLOW` to explicitly allow the request to proceed, `HTTP_AUTH_DENY` to deny the request (in which case the module must send an appropriate error message), or `HTTP_AUTH_UNDECIDED` to pass the request through to the next callback; if all authorization callbacks pass the request through, it is treated as allowed.

`request`
Parameters: `Client *c`, `int *close_ptr`
  Called for each valid request received by the server and either explicitly allowed or passed through by all authorization callbacks. `c` is the client from which the request was received; the request data is stored in the client structure (see `http.h`), and at a minimum the `method`, `url`, `version_major`, and `version_minor` fields will be set appropriately. If the connection should be closed after the reply is sent, the callback routine should set `*close_ptr` to a nonzero value. The callback routine should return 1 if it processes the request, 0 otherwise.

Back to top

### 6-3. Submitting additions or changes to Services

As a rule, suggestions for improvement are generally preferred to submissions of source code, for reasons discussed in FAQ Z.8. However, if you would like to submit a module you have written for inclusion in the standard Services distribution, or you have made a modification to Services itself (or a standard module) that you feel would be useful to others and would like it included in Services, please observe the following guidelines:

- Your code must follow the coding guidelines given below (section 6-4).
- Your code should be reasonably well error-checked and robust. For example, the return value of any function call which can return an error should be checked, and appropriate action taken if an error occurs.
- Your code should compile with no errors or warnings using the default Services compilation options (`gcc -O2 -fno-strict-aliasing -Wall -Wmissing-prototypes`).
- You should use comments well, but not overuse them; you do not need to comment every single line of code, but on the other hand someone looking at your module should not have to decipher several hundred lines of code just to figure out what it does.
- All comments and any accompanying documentation should be in English and use proper grammar, spelling, etc. (Partial sentences are acceptable for inline comments.)

Any submissions which do not meet the above requirements will be categorically rejected, although some leeway may be provided in terms of comment and documentation quality if your native language is not English.

Furthermore, any submissions of modules, code, documentation, or any other information (collectively "information") become the sole property of the author of IRC Services, and by submitting such information, you agree to transfer any and all rights under copyright law you may have in said information to said author. If you do not agree to the above, or you are not permitted (by law, contract, or otherwise) to transfer all of your rights in the information to the author(s) of IRC Services, then you may not submit the information. (This requirement is made to avoid the hassles which would accompany the inclusion of source code or other items owned by multiple people or groups; see also the note on the copyright page. If you cannot comply with this paragraph but would still like to submit something, please contact the author to discuss your situation.)

Back to top

### 6-4. Coding guidelines for Services

In general, observe the same style as is used in the rest of Services and you should have no problems. Specific points follow:

#### Formatting

- Lines must not be longer than 79 columns.

- The basic indentation unit is four columns.

- Tab stops are set at multiples of eight columns (i.e., a tab character is equivalent to two levels of indentation). Tabs should be used in place of sequences of space characters, but this is not required.

- Only one statement is allowed per line. Statements (including the empty statement "`;`") may not be written on the same line as a control keyword (`if`, `for`, or `while`).

- Spaces are placed between binary operators and their operands, except for the member-reference operators "`->`" and "`.`"; spaces are not placed between unary operators and their operands. However, spaces may be omitted around binary operators when it would improve readability. Examples:

  > `i = j * 2;`
  > `user->channelcount++;`
  > `result += i*60 + (j+59)/60;`

- Always use `NULL`, not `0`, in pointer comparisons. For character comparisons, `0` is preferred to `'\0'`, but the latter may also be used if it does not go against the style of the surrounding code. Do not use `'\0'` with variables declared as type `int8` or `uint8`, even though these types are usually equivalent to `char` and `unsigned char`.

- The construct "`!!expression`" should not be used. Use "`expression != 0`" (for characters or integers) or "`expression != NULL`" (for pointers) instead.

- Parentheses are required when `&&` and `||` are used in the same expression to explicitly indicate order of evaluation; do not rely on the default order of evaluation. (Not using parentheses will generate a warning when using `gcc -Wall`.)

- Parentheses following the control statements "`if`", "`for`", and "`while`", as well as the macros `LIST_FOREACH`, `LIST_FOREACH_SAFE`, and `ARRAY_FOREACH`, are preceded by a space. Parentheses following function or macro calls (other than the above) are not preceded by a space. Example:

  > `if (flag)
  > function();`

- Spaces are placed after the comma of each parameter to a function or macro; however, they may be omitted in function calls which are themselves parameters to a function or macro, or when including them would make the line exceed 79 columns in length. Spaces are also placed after (but not before) the semicolons in a `for` statement. Spaces are not placed after the opening parenthesis or before the closing parenthesis of a function/macro call or control statement. Examples:

  > `function(param1, param2);`
  > `function(param1, strchr(string,'/'), param3);`
  > `for (i = 0; i < count; i++)` . . .

- Opening braces of control statement blocks go on the same line as the control statement (`if`, `for`, etc.) associated with the block; function blocks and "naked blocks" (those not associated with any control statement or function) have the opening brace alone on a line, indented to the same level as the surrounding code. Closing braces are indented to the same level as the line containing the opening brace. Examples:

  > `if (flag) {
  > /* indented code goes here */
  > ` . . .`
  > }`
  > `int function(int param1, char *param2)
  > {
  > /* indented code goes here */
  > ` . . .`
  > /* start of a naked block */
  > {
  > int foo;
  > ` . . .`
  > }
  > }`

- If an `if` statement is longer than one line, it should be broken at logical operators (`&&` or `||`); the operator should be placed at the beginning of the next line, and should be indented to show which term the operator belongs with. The closing parenthesis for the `if` statement and the subsequent open brace should be alone on a line, aligned with the first line of the `if` statement. (Braces are required in this case.) Conditions for `for` and `while` statements should never span multiple lines, though a `for` statement may be broken at the semicolons if necessary. Examples:

  > `if (ok && (strcmp(option, "option-name-1") == 0
  > || strcmp(option, "option-name-2") == 0)
  > ) {
  > ` . . .`
  > }`
  > `if (!init_first()
  > || !init_second()
  > || !init_third()
  > || !init_fourth()
  > ) {
  > /* This example outdents the || by three spaces
  > * to make the terms line up. */
  > ` . . .`
  > }`
  > `for (exception = first_maskdata(MD_EXCEPTION); exception != NULL;
  > exception = next_maskdata(MD_EXCEPTION)
  > ) {
  > ` . . .`
  > }`

- An `else` followed by an `if` is considered to be a single keyword, "`else if`". The `if` part should always be placed on the same line as and immediately following the `else`; the `else if` should never be split up onto two lines, nor should braces be used around the `if` block. The exception to this is when the `else` and `if` refer to two conceptually distinct sets of conditions, in which case the `if` block should be enclosed by braces and indented. Example:

  > `res = check_password( /* ... */ );
  > if (res == 0) {
  > ` . . .`
  > } else if (res == 1) {
  > /* "else if" on a single line */
  > ` . . .`
  > } else {
  > if (user->ni != NULL) {
  > /* "if" condition is different from "else"
  > * condition, thus separate */
  > ` . . .`
  > }
  > }`

- Braces are always placed around the body of a control statement (`if`, `for`, etc.). The one exception, aside from the case of `else if` mentioned above, is a statement for which the body, as well as the bodies of any `else if` or `else` statements for an `if` statement, are all one statement in length and do not contain any nested control statements; in this case braces are optional (but recommended if any of the statements span multiple lines). Examples:

  > `for (i = 0; i < count; i++)
  > function(i);`
  > `while (!done) {
  > /* Braces required because of the nested "if" */
  > if (do_stuff())
  > done = 1;
  > }`
  > `if (state == 0) {
  > a = b;
  > } else if (state == 1) {
  > /* Every if/else body gets braces because this body
  > * has two statements */
  > b += a;
  > a = 0;
  > } else {
  > state = 0;
  > }`

- When using multiply-nested control statements, or a single control statement which has a long block, the closing brace of the block should be followed by a comment indicating what control statement the block belongs to. This should usually be the content of the control statement, but may be abbreviated as long as its meaning is clear. If a long `if` block is followed by an `else` clause, the `else` should likewise be documented. Examples:

  > `while (i < count) {
  > ` . . .`
  > } /* while (i < count) */`
  > `if (flag) {
  > ` . . .`
  > } else { /* !flag */
  > ` . . .`
  > } /* if (flag) */`
  > `for (node = first; node != NULL; node = node->next) {
  > ` . . .`
  > } /* for all nodes */`

- Case labels for a `switch` should be indented half of a normal indentation unit (two columns) from the line containing the `switch` with which they are associated; statements associated with a case should be indented a full unit from the line containing the `switch` (half a unit from the `case`). If a case requires its own block, such as when it declares its own local variables, the opening brace is placed after the colon on the `case` line. Example:

  > `switch (variable) {
  > case 123: {
  > int foo;
  > ` . . .`
  > break;
  > } /* case 123 */
  > default:
  > ` . . .`
  > return -1;
  > }`

- When a case in a `switch` block does not contain a `break` (or `return`) statement and deliberately "falls through" to the next case, a comment to this effect should be made at the bottom of the case. Example:

  > `switch (state) {
  > case 0:
  > ` . . .`
  > /* fall through */
  > case 1:
  > ` . . .`
  > break;
  > }`

- Inline comments (comments placed to the right of a line of code) should be aligned to start on the same column, when this does not place too much space between the comment and the code or cause the line to exceed 79 columns in length. Inline comments after unusually long lines (such as long field declarations in structures) should be placed alone on the following line, indented the proper amount.

- The `goto` statement should not be used except in error handling situations where it will help avoid multiple levels of `if` nesting or other awkward code. Labels for `goto` should be outdented half of an indentation unit from the surrounding code (i.e., indented two columns less than the surrounding code).

#### Identifier naming

- Global variables and type names should use mixed upper- and lower-case, with an upper-case letter at the beginning of each distinct word in the name.

- Preprocessor constants and macros should use all upper-case letters, with an underscore between distinct words in the name.

- Structure names and local variables should use all lower-case letters, with an underscore between distinct words in the name.

- Structure names for structures with an associated type name (i.e., from `typedef`) should be given the same name as the type, except with all letters in lowercase and followed by an underscore. Example:

  > `typedef struct mytype_ MyType;
  > struct mytype_ {
  > MyType *next, *prev;
  > ` . . .`
  > };`

- Names should be descriptive. For global variables, preprocessor macros and constants, type names, and structure names, names should generally consist of one or more full words. For local variables, short names and abbreviations are permitted as long as it is clear what the variables are used for. In general, one-letter local variable names should not be used other than the following:
  - `c`: character
  - `f`: file pointer (`FILE *`)
  - `i`, `j`, `k`: integers (usually counters)
  - `n`, `p`, `q`: integers (`p` may also be a pointer variable)
  - `s`: string
  - `t`: string or temporary variable
  - `x`, `y`, `z`: integers (usually position variables)
  Two-letter variable names are often formed from initials of type names, such as `NickInfo *ni`; see the source code for examples.

Back to top

Table of Contents | Previous section: Importing and exporting databases | Top | Next section: Contacting the author