mirror of
https://github.com/NishiOwO/ircservices5.git
synced 2025-04-21 16:54:38 +00:00
3816 lines
198 KiB
HTML
3816 lines
198 KiB
HTML
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11-strict.dtd">
|
|
<!-- Annoyingly, <u> has been removed, so replacing it with <span style="text-decoration: underline">, as in 2.html -->
|
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
<head>
|
|
<meta http-equiv="Content-Style-Type" content="text/css"/>
|
|
<style type="text/css">@import "style.css";</style>
|
|
<title>IRC Services Technical Reference Manual - 7. Services pseudoclients</title>
|
|
</head>
|
|
|
|
<body>
|
|
<h1 class="title" id="top">IRC Services Technical Reference Manual</h1>
|
|
|
|
<h2 class="section-title">7. Services pseudoclients</h2>
|
|
|
|
<p class="section-toc">
|
|
7-1. <a href="#s1">Basic features of a pseudoclient</a>
|
|
<br/>7-2. <a href="#s2">OperServ</a>
|
|
<br/> 7-2-1. <a href="#s2-1">OperServ core functionality</a>
|
|
<br/> 7-2-2. <a href="#s2-2">Usermask-related functions</a>
|
|
<br/> 7-2-2-1. <a href="#s2-2-1">Common mask data support</a>
|
|
<br/> 7-2-2-2. <a href="#s2-2-2">Autokills</a>
|
|
<br/> 7-2-2-3. <a href="#s2-2-3">S-lines</a>
|
|
<br/> 7-2-3. <a href="#s2-3">Session limiting</a>
|
|
<br/> 7-2-4. <a href="#s2-4">News</a>
|
|
<br/>7-3. <a href="#s3">NickServ</a>
|
|
<br/> 7-3-1. <a href="#s3-1">NickServ core functionality</a>
|
|
<br/> 7-3-1-1. <a href="#s3-1-1">Nickname data structures and utility macros</a>
|
|
<br/> 7-3-1-2. <a href="#s3-1-2">Overall module structure</a>
|
|
<br/> 7-3-1-3. <a href="#s3-1-3">The <tt>SET</tt> and <tt>UNSET</tt> commands</a>
|
|
<br/> 7-3-1-4. <a href="#s3-1-4">NickServ utility routines</a>
|
|
<br/> 7-3-1-5. <a href="#s3-1-5">Nickname colliding</a>
|
|
<br/> 7-3-2. <a href="#s3-2">Nickname access lists</a>
|
|
<br/> 7-3-3. <a href="#s3-3">Nickname auto-join lists</a>
|
|
<br/> 7-3-4. <a href="#s3-4">Linking and nickname groups</a>
|
|
<br/> 7-3-5. <a href="#s3-5">E-mail address authentication</a>
|
|
<br/>7-4. <a href="#s4">ChanServ</a>
|
|
<br/> 7-4-1. <a href="#s4-1">ChanServ core functionality</a>
|
|
<br/> 7-4-1-1. <a href="#s4-1-1">Channel data structures</a>
|
|
<br/> 7-4-1-2. <a href="#s4-1-2">Overall module structure</a>
|
|
<br/> 7-4-1-3. <a href="#s4-1-3">Channel status checking and modification</a>
|
|
<br/> 7-4-1-4. <a href="#s4-1-4">The <tt>SET</tt> and <tt>UNSET</tt> commands</a>
|
|
<br/> 7-4-1-5. <a href="#s4-1-5">ChanServ utility routines</a>
|
|
<br/> 7-4-2. <a href="#s4-2">Channel access list handling</a>
|
|
<br/> 7-4-2-1. <a href="#s4-2-1">Access list basics</a>
|
|
<br/> 7-4-2-2. <a href="#s4-2-2">Manipulation via <tt>ACCESS</tt> and <tt>LEVELS</tt></a>
|
|
<br/> 7-4-2-3. <a href="#s4-2-3">Manipulation via <tt>XOP</tt></a>
|
|
<br/>7-5. <a href="#s5">MemoServ</a>
|
|
<br/> 7-5-1. <a href="#s5-1">MemoServ core functionality</a>
|
|
<br/> 7-5-1-1. <a href="#s5-1-1">Memo data structures</a>
|
|
<br/> 7-5-1-2. <a href="#s5-1-2">The <tt>memoserv/main</tt> module</a>
|
|
<br/> 7-5-2. <a href="#s5-2">Memo ignore lists</a>
|
|
<br/> 7-5-3. <a href="#s5-3">Memo forwarding</a>
|
|
<br/>7-6. <a href="#s6">StatServ</a>
|
|
<br/> 7-6-1. <a href="#s6-1">StatServ data structures</a>
|
|
<br/> 7-6-2. <a href="#s6-2">The StatServ module</a>
|
|
<br/>7-7. <a href="#s7">Miscellaneous pseudoclients</a>
|
|
<br/> 7-7-1. <a href="#s7-1">HelpServ</a>
|
|
<br/> 7-7-2. <a href="#s7-2">DevNull</a>
|
|
</p>
|
|
|
|
<p class="backlink"><a href="6.html">Previous section: Database handling</a> |
|
|
<a href="index.html">Table of Contents</a> |
|
|
<a href="8.html">Next section: Other modules</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<h3 class="subsection-title" id="s1">7-1. Basic features of a pseudoclient</h3>
|
|
|
|
<p>Pseudoclients are the user-visible part of Services, providing the
|
|
actual service functions which IRC users take advantage of. While the
|
|
details of each pseudoclient differ greatly, all pseudoclients share some
|
|
common features:</p>
|
|
|
|
<ul>
|
|
<li class="spaced">Register one or more nicknames via the
|
|
"<tt>introduce_user</tt>" callback, through which the pseudoclient
|
|
communicates with IRC clients; see below for details.</li>
|
|
<li class="spaced">Receive commands from IRC clients via <tt>PRIVMSG</tt>
|
|
messages. (According to RFC 1459, pseudoclients <i>must not</i>
|
|
respond to <tt>NOTICE</tt> messages, in order to prevent infinite
|
|
loops in which two pseudoclients repeatedly respond to each others'
|
|
notices.)</li>
|
|
<li class="spaced">Communicate to IRC clients via <tt>NOTICE</tt> messages.
|
|
(While not explicitly mandated by the RFC, this is in order to
|
|
avoid potential message loops with other pseudoclients. Occasional
|
|
requests have been made for Services to allow using
|
|
<tt>PRIVMSG</tt> for communication with users, apparently because
|
|
some IRC programs do not show <tt>NOTICE</tt> messages to users,
|
|
but such requests have been intentionally disregarded for the above
|
|
reason.)</li>
|
|
<li class="spaced">Provide a set of commands which users can use to invoke
|
|
the pseudoclient's functions. (The miscellaneous modules described
|
|
in <a href="#s7">section 7-7</a> are an exception; the HelpServ
|
|
pseudoclient has only one function with no associated command name,
|
|
and the DevNull pseudoclient has none.) Each message to a
|
|
pseudoclient is interpreted as a command name and parameters, each
|
|
separated by one or more space characters (ASCII 0x20—note
|
|
that the tab character is <i>not</i> treated as a separator).</li>
|
|
</ul>
|
|
|
|
<p>The "<tt>introduce_user</tt>" callback mentioned above is called by the
|
|
same-named function, <tt>introduce_user()</tt>, to request that each module
|
|
introduce its nickname(s) to the IRC network by calling
|
|
<tt>send_pseudo_nick()</tt> (in <tt>send.c</tt>). The callback takes a
|
|
single <tt>const char *</tt> parameter, which is <tt>NULL</tt> when
|
|
the callback is called at startup, or the nickname seen in a <tt>KILL</tt>
|
|
message when one is received; accordingly, pseudoclients should introduce
|
|
their nicknames only when the parameter either is <tt>NULL</tt> or matches
|
|
(case-insensitively, as determined by <tt>irc_stricmp()</tt>) the nickname
|
|
to be introduced. <tt>send_pseudo_nick()</tt> takes three parameters: the
|
|
nickname of the pseudoclient to be introduced, the pseudoclient's "real
|
|
name" string (usually a description of the pseudoclient), and a flag value,
|
|
composed of zero or more of the following flags:</p>
|
|
<ul>
|
|
<li><tt><b>PSEUDO_OPER</b></tt>: The client requires IRC operator
|
|
privileges. (This does not necessarily guarantee that the client
|
|
will be given the <tt>+o</tt> user mode; some IRC servers allow any
|
|
Services pseudoclient to use IRC operator functions, and
|
|
<tt>+o</tt> is omitted with such servers.)</li>
|
|
<li><tt><b>PSEUDO_INVIS</b></tt>: The client should be marked invisible
|
|
(user mode <tt>+i</tt>).</li>
|
|
</ul>
|
|
|
|
<p>Command processing is typically performed by hooking into the core's
|
|
"<tt>m_privmsg</tt>" callback, which is intended specifically for this
|
|
purpose. Depending on the pseudoclient, it may also be necessary to
|
|
respond to other events on the IRC network, such as channel joins or mode
|
|
changes; these can typically be handled by hooking into the relevant
|
|
callback. In extreme cases, it may be necessary to make use of the
|
|
low-level "<tt>receive message</tt>" callback as well; this should be
|
|
avoided when possible, however, as it circumvents the standard message
|
|
processing and can result in network desynchronization.</p>
|
|
|
|
<p>For databases maintained by pseudoclients, the following six functions
|
|
are typically provided by the pseudoclient for other code that needs direct
|
|
access to the database (<tt><span style="text-decoration: underline"><i>type</i></span></tt> is the record data
|
|
type, <tt><span style="text-decoration: underline"><i>name</i></span></tt> is a distinguishing name generally
|
|
derived from <tt><span style="text-decoration: underline"><i>type</i></span></tt>, and <tt><span style="text-decoration: underline"><i>keytype</i></span></tt>
|
|
is the data type of the key field):</p>
|
|
|
|
<dl>
|
|
<dt><tt><span style="text-decoration: underline"><i>type</i></span> *<b>add_<span style="text-decoration: underline"><i>name</i></span></b>(<span style="text-decoration: underline"><i>type</i></span> *<i>record</i>)</tt></dt>
|
|
<dd>Adds the given record to the database, returning a pointer to the
|
|
record structure as stored (which may be different from the pointer
|
|
passed into the function).</dd>
|
|
|
|
<dt><tt>void <b>del_<span style="text-decoration: underline"><i>name</i></span></b>(<span style="text-decoration: underline"><i>type</i></span> *<i>record</i>)</tt></dt>
|
|
<dd>Deletes the given record from the database. <tt><i>record</i></tt>
|
|
is assumed to be valid, <i>i.e.</i> a pointer returned by a
|
|
previous call to another database function.</dd>
|
|
|
|
<dt><tt><span style="text-decoration: underline"><i>type</i></span> *<b>get_<span style="text-decoration: underline"><i>name</i></span></b>(<span style="text-decoration: underline"><i>keytype</i></span> *<i>key</i>)</tt></dt>
|
|
<dd>Returns the record for the given key, or <tt>NULL</tt> if the key
|
|
is not found in the database.</dd>
|
|
|
|
<dt><tt>void <b>put_<span style="text-decoration: underline"><i>name</i></span></b>(<span style="text-decoration: underline"><i>type</i></span> *<i>record</i>)</tt></dt>
|
|
<dd>Indicates that the given record is no longer in use. Each call to
|
|
a database's <tt>get()</tt> function must be followed by a
|
|
<tt>put()</tt> call for the same record, unless the record is
|
|
deleted first. <i>Implementation note: A <tt>put()</tt> function
|
|
was first introduced in 5.0 with the intention that it be used for
|
|
indicating when a record had been updated, for the purpose of
|
|
storing the updated data into persistent storage such as an
|
|
SQL-based database. DBMS support never materialized, and the
|
|
function's purpose was redefined for version 5.1 to keep track of
|
|
which records are actively in use, but I have no confidence that
|
|
the code does <tt>put()</tt> calls in all necessary cases and no
|
|
others; in fact, it appears that <tt>put_nickgroupinfo()</tt> (at
|
|
least) is called more often than it should be. It may have been
|
|
better to drop <tt>put()</tt> entirely and use other methods of
|
|
checking whether records are in use before expiring them.</i></dd>
|
|
|
|
<dt><tt><span style="text-decoration: underline"><i>type</i></span> *<b>first_<span style="text-decoration: underline"><i>name</i></span></b>()</tt>
|
|
<br/><tt><span style="text-decoration: underline"><i>type</i></span> *<b>next_<span style="text-decoration: underline"><i>name</i></span></b>()</tt></dt>
|
|
<dd>Iterates through the database. <tt>first()</tt> returns the first
|
|
record in the database, and subsequent <tt>next()</tt> calls return
|
|
each successive record, finally returning <tt>NULL</tt> when all
|
|
records have been iterated through (if there are no records at all,
|
|
both <tt>first()</tt> and <tt>next()</tt> return <tt>NULL</tt>).
|
|
The order is database-dependent, but no record will be returned
|
|
more than once from the time <tt>first()</tt> is called to the time
|
|
<tt>next()</tt> returns <tt>NULL</tt>. It is unspecified whether
|
|
records added while iterating through the database will be
|
|
included in the iteration. Note that these functions are
|
|
<i>not</i> considered a "get" for the purposes of the
|
|
<tt>put()</tt> function; thus a call to <tt>put()</tt> is not
|
|
required for simple iteration, but if any other database function
|
|
is to be called before the following <tt>next()</tt>, then an
|
|
explicit call to <tt>get()</tt> (and a matching call to
|
|
<tt>put()</tt>) must be made to prevent the record in use from
|
|
being deleted.</dd>
|
|
</dl>
|
|
|
|
<p>The following sections describe each of the Services pseudoclients in
|
|
detail. The reader is assumed to be familiar with the functions of each
|
|
pseudoclient from a user's point of view, as described in
|
|
<a href="../3.html">section 3 of the user's manual</a>.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<h3 class="subsection-title" id="s2">7-2. OperServ</h3>
|
|
|
|
<p>The OperServ pseudoclient provides services to IRC operators allowing
|
|
control of the network and of Services itself. While not seen by most IRC
|
|
users, this pseudoclient is discussed first since it provides functionality
|
|
used by most other pseudoclients.</p>
|
|
|
|
<p>As with most of the Services pseudoclients, OperServ is composed of one
|
|
core or "main" module, <tt>operserv/main</tt>, and several optional modules
|
|
providing additional functionality. These are each discussed in separate
|
|
sections below.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s2-1">7-2-1. OperServ core functionality</h4>
|
|
|
|
<p>The core functionality of OperServ is contained in the
|
|
<tt>modules/operserv/main.c</tt> source file, compiled into the
|
|
<tt>operserv/main</tt> module. In addition to the implementation of the
|
|
core OperServ commands, this file also defines several utility functions
|
|
used by several other pseudoclient modules; external declarations of these
|
|
functions and associated constants are located in
|
|
<tt>modules/operserv/operserv.h</tt>.</p>
|
|
|
|
<p><tt>main.c</tt> is written using the same general structure as most
|
|
module source files. At the top of the file are variable definitions,
|
|
including configuration variables, which are given the same names as their
|
|
corresponding configuration directives; these are followed by forward
|
|
declarations of individual command routines and the command list. Next
|
|
come database-related structures and routines, followed by the top-level
|
|
pseudoclient routines (the "<tt>introduce_user</tt>", "<tt>m_privmsg</tt>",
|
|
and "<tt>m_whois</tt>" callback functions), and finally the actual command
|
|
routines, along with any utility routines needed.</p>
|
|
|
|
<p>For OperServ, the first item of note is the list of several commands and
|
|
command routines inside <tt>#ifdef DEBUG_COMMANDS</tt>. These are, as the
|
|
conditional name suggests, commands used for debugging Services, and are
|
|
only available to the Services super-user; the commands are described in
|
|
detail below.</p>
|
|
|
|
<p>OperServ stores several values to persistent storage, including the
|
|
maximum client count, the time at which that maximum was reached, and the
|
|
super-user (<tt>SU</tt> command) password. This data is stored by
|
|
aggregating the data into a single structure, <tt>operserv_data</tt>, and
|
|
storing that structure as a single database "record" in a table named
|
|
"<tt>oper</tt>". Two exported functions, <tt>get_operserv_data()</tt> and
|
|
<tt>put_operserv_data()</tt>, are also provided to allow external modules,
|
|
in particular the XML import and export modules (see
|
|
<a href="8.html#s4">section 8-4</a>), to access the data as well.</p>
|
|
|
|
<p>In order to check a client's Services privilege level (Services
|
|
operator, Services administrator, or Services super-user), OperServ requires
|
|
access to the nickname data, in which each registered nickname group's
|
|
privilege level is stored (the <tt>os_priv</tt> member). However, since
|
|
NickServ requires that OperServ be loaded first, OperServ must look up the
|
|
symbols for these routines during its normal operation. Six local functions
|
|
are defined, one for each of the imported routines (<tt>get_nickinfo()</tt>,
|
|
<tt>put_nickinfo()</tt>, <tt>_get_ngi()</tt>, <tt>put_nickgroupinfo()</tt>,
|
|
<tt>first_nickgroupinfo()</tt>, and <tt>next_nickgroupinfo()</tt>), taking
|
|
the same parameters and returning the same values as the real routines; the
|
|
local versions look up the symbol for each routine and then call the
|
|
corresponding address, returning an appropriate error value if the symbol
|
|
cannot be resolved (or NickServ is not loaded).</p>
|
|
|
|
<p>The main processing routine itself, <tt>operserv()</tt>, is registered
|
|
as a callback function for the "<tt>m_privmsg</tt>" callback, called for
|
|
each <tt>PRIVMSG</tt> received by Services. The routine first checks that
|
|
the message is intended for OperServ; then it ensures that the client that
|
|
sent the message is an IRC operator, to avoid any possibility of
|
|
non-operator clients exploiting a bug in the OperServ code. The message
|
|
received is then logged in the log file, except that parameters to the
|
|
<tt>SU</tt> and <tt>SET SUPASS</tt> commands are replaced with a dummy
|
|
string to avoid leaving the super-user password in the log file. (OperServ
|
|
has no way to detect if one of these commands is misspelled, so for
|
|
example, a mistaken <tt>SET SUPSAS</tt> will be logged in full, including
|
|
the password.) Next, the command name is extracted from the message
|
|
using the <tt>strtok()</tt> function; this also prepares <tt>strtok()</tt>
|
|
for the command handler to use in extracting the command parameters (see
|
|
below). After handling CTCP <tt>PING</tt> messages separately, OperServ
|
|
calls a "<tt>command</tt>" callback, allowing other modules a chance to
|
|
process the command first. Finally, if no callback function responds to
|
|
the command, it is looked up in the command table and the command's handler
|
|
function is called (if the command is not found, an error message is sent
|
|
instead).</p>
|
|
|
|
<p>Following this and other callback functions are the privilege check
|
|
functions; <tt>is_services_root()</tt>, <tt>is_services_admin()</tt>, and
|
|
<tt>is_services_oper()</tt>, which check whether a client has Services
|
|
super-user, Services administrator, and Services operator privileges
|
|
respectively (any client with a higher privilege level is treated as
|
|
having all lower privilege levels as well). The <tt>is_services_root()</tt>
|
|
function relies on the <tt>ServicesRoot</tt> configuration setting, along
|
|
with the <tt>UF_SERVROOT</tt> flag in the <tt>User</tt> structure
|
|
indicating clients which have successfully used the <tt>SU</tt> command;
|
|
lower privilege levels check the <tt>os_priv</tt> field of the nickname
|
|
group data structure (see <a href="#s3-1">section 7-3-1</a>) for privilege
|
|
determination. These routines are exported, and used widely throughout the
|
|
other pseudoclient modules to perform privilege checks on clients; in
|
|
particular, they can be used as privilege check functions in the
|
|
<tt>has_priv</tt> member of a <tt>Command</tt> structure. There is another
|
|
routine, <tt>nick_is_services_admin()</tt>, which checks if a particular
|
|
nickname can potentially can Services administrator access, ignoring
|
|
whether the nickname is actually in use at the time; this is used by
|
|
NickServ to prevent certain operations from being performed on such
|
|
nicknames by clients without Services administrator privilege.</p>
|
|
|
|
<p>The command routines themselves are fairly straightforward. One thing
|
|
to note is that the routines all obtain parameters via
|
|
<tt>strtok(NULL,...)</tt> and <tt>strtok_remaining()</tt>; this relies on
|
|
the fact that <tt>operserv()</tt> leaves the message string in the
|
|
<tt>strtok()</tt> buffer after stripping the command name, so that each
|
|
routine can parse the command's parameters as appropriate for that command.
|
|
<tt>strtok_remaining()</tt> is used when the full remaining string is
|
|
desired, such as when sending a global message; this function is preferred
|
|
to <tt>strtok(NULL,"")</tt> because the latter can leave leading or
|
|
trailing whitespace in the result.</p>
|
|
|
|
<p>The processing for the <tt>HELP</tt> command, in <tt>do_help()</tt>, is
|
|
somewhat tortuous (although still simpler than in other pseudoclients) in
|
|
order to give proper help responses depending on how Services is
|
|
configured. In the case of OperServ, some commands may or may not be
|
|
available depending on what submodules are loaded; the <tt>COMMANDS</tt>
|
|
help text, which lists the available commands, is combined from several
|
|
language strings depending on whether the appropriate modules are available
|
|
to provide a list of the commands which are actually usable at that
|
|
particular time. Other modules include more complex processing, such as
|
|
checking the configuration variable values or protocol features. For
|
|
commands that do not need such special-casing, the <tt>help_cmd()</tt>
|
|
routine in the Services core (see <a href="2.html#s10">section 2-10</a>)
|
|
sends a help message as defined by the <tt>Command</tt> structure.</p>
|
|
|
|
<p>The debug commands, defined toward the bottom of <tt>main.c</tt>, are as
|
|
follows:</p>
|
|
|
|
<dl>
|
|
<dt><tt><b>LISTSERVERS</b></tt> (<tt>send_server_list()</tt>)</dt>
|
|
<dd>Sends a <tt>NOTICE</tt> for each server in the server list giving
|
|
the contents of the <tt>Server</tt> structure.</dd>
|
|
|
|
<dt><tt><b>LISTCHANS</b></tt> (<tt>send_channel_list()</tt>)</dt>
|
|
<dd>Sends two <tt>NOTICE</tt>s for each channel in the channel list.
|
|
The first <tt>NOTICE</tt> gives the channel name, creation time,
|
|
modes (as a string), limit, key ("<tt>-</tt>") if none, and
|
|
topic; the second contains each client on the channel along with
|
|
that client's channel user modes on the channel. Any messages
|
|
which exceed the maximum length of an IRC line are silently
|
|
truncated.</dd>
|
|
|
|
<dt><tt><b>LISTCHAN</b> <i>channel</i></tt> (<tt>send_channel_info()</tt>)</dt>
|
|
<dd>Sends a <tt>NOTICE</tt> for each client on the given channel with
|
|
the client's channel user modes (as a hexadecimal value) and
|
|
nickname.</dd>
|
|
|
|
<dt><tt><b>LISTUSERS</b></tt> (<tt>send_user_list()</tt>)</dt>
|
|
<dd>Sends a <tt>NOTICE</tt> for each client on the network, giving
|
|
the client's nickname and usermask, the "fake host" or "<tt>-</tt>"
|
|
if none, the IP address or "<tt>-</tt>" if none, user modes as a
|
|
string, signon timestamp (from the remote server), servicestamp,
|
|
server name, nickname status flags or "<tt>-</tt>" if the nickname
|
|
is not registered, ignore value, and real name field.</dd>
|
|
|
|
<dt><tt><b>LISTUSER</b> <i>nickname</i></tt> (<tt>send_user_info()</tt>)</dt>
|
|
<dd>Sends three <tt>NOTICE</tt>s describing the state of the given
|
|
client. The first is identical to the line that <tt>LISTUSERS</tt>
|
|
would output for the client; the second gives the channels which
|
|
the client has currently joined; and the third gives the channels
|
|
which the client has identified to ChanServ for.</dd>
|
|
|
|
<dt><tt><b>LISTTIMERS</b></tt> (<tt>send_timeout_list()</tt>, in
|
|
<tt>timeout.c</tt>)</dt>
|
|
<dd>Sends a <tt>NOTICE</tt> giving the current time, followed by a
|
|
<tt>NOTICE</tt> for each timeout currently in the list giving the
|
|
timeout pointer, timestamp, function pointer, and function
|
|
argument. This routine is defined in <tt>timeout.c</tt> because
|
|
the internal timeout fields are hidden from other source
|
|
files.</dd>
|
|
|
|
<dt><tt><b>MATCHWILD</b> <i>pattern</i> <i>string</i> </tt> (<tt>do_matchwild()</tt>)</dt>
|
|
<dd>Sends a <tt>NOTICE</tt> giving the result of calling
|
|
<tt>match_wild</tt> with the given parameters.</dd>
|
|
|
|
<dt><tt><b>SETCMODE [<i>channel</i> <i>modes</i> <i>mode-params</i>...]</b></tt> (<tt>do_setcmode()</tt>)</dt>
|
|
<dd>Calls <tt>set_cmode()</tt> with the given parameters, using
|
|
<tt>ServerName</tt> as the message sender. If no parameters are
|
|
given, calls <tt>set_cmode(NULL,NULL)</tt> to flush out all
|
|
pending mode changes. Note that the number of mode parameters
|
|
(including the mode string itself) is limited by the
|
|
<tt>SETCMODE_NPARAMS</tt> macro, defined to 10 in the source
|
|
code.</dd>
|
|
|
|
<dt><tt><b>MONITOR-IGNORE</b> [<i>nickname</i>]</tt> (<tt>do_monitor_ignore()</tt>)</dt>
|
|
<dd>If a nickname is given, starts recording that nickname's ignore
|
|
value to the log file at 100ms intervals; if no nickname is given,
|
|
cancels any previous monitoring. Note that in order to ensure
|
|
sub-second resolution, the <tt>TimeoutCheck</tt> configuration
|
|
variable is set to 10 (milliseconds) when a nickname is given,
|
|
potentially causing a reduction in performance; the old value is
|
|
<i>not</i> restored when the command is given without a
|
|
nickname.</dd>
|
|
|
|
<dt><tt><b>GETSTRING</b> <i>language</i> <i>string</i></tt> (<tt>do_getstring()</tt>)</dt>
|
|
<dd>Sends a <tt>NOTICE</tt> containing the text corresponding to the
|
|
given string in the given language. Both <tt><i>language</i></tt>
|
|
and <tt><i>string</i></tt> can be given as either names or raw
|
|
numbers.</dd>
|
|
|
|
<dt><tt><b>SETSTRING</b> <i>language</i> <i>string</i> [<i>text</i>]</tt> (<tt>do_setstring()</tt>)</dt>
|
|
<dd>Calls <tt>setstring()</tt> to set the given string in the given
|
|
language to the given text. If no text is given, the string
|
|
becomes empty.</dd>
|
|
|
|
<dt><tt><b>MAPSTRING</b> <i>old</i> <i>new</i></tt> (<tt>do_mapstring()</tt>)</dt>
|
|
<dd>Calls <tt>mapstring()</tt> to map one string to another.
|
|
<tt><i>old</i></tt> and <tt><i>new</i></tt> are string names or
|
|
numbers.</dd>
|
|
|
|
<dt><tt><b>ADDSTRING</b> <i>name</i></tt> (<tt>do_addstring()</tt>)</dt>
|
|
<dd>Calls <tt>addstring()</tt> to add a string with the given name to
|
|
the language table. On success, sends a <tt>NOTICE</tt> with the
|
|
new string number.</dd>
|
|
</dl>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s2-2">7-2-2. Usermask-related functions</h4>
|
|
|
|
<h5 class="subsubsubsection-title" id="s2-2-1">7-2-2-1. Common mask data support</h5>
|
|
|
|
<p>One of the major features of OperServ not included in the core module
|
|
is the autokill and S-line functionality. While these are in fact defined
|
|
in separate modules, most of the processing for both sets of functions is
|
|
subsumed under the <tt>MaskData</tt> structure and its generic processing
|
|
routines included in the core OperServ module, located in
|
|
<tt>maskdata.c</tt> and <tt>maskdata.h</tt>. This structure contains the
|
|
elements common to all of these features: a mask string (whose
|
|
interpretation is left to the particular module), an associated reason
|
|
string, the nickname of the client that added the mask, the time the mask
|
|
was added, the time it expires, and the last time the mask was applied to a
|
|
client; these are stored in the <tt>mask</tt>, <tt>reason</tt>,
|
|
<tt>who</tt>, <tt>time</tt>, <tt>expires</tt>, and <tt>lastused</tt>
|
|
fields of the structure, respectively. The remaining fields (other than
|
|
the record management fields <tt>next</tt>, <tt>prev</tt>, and
|
|
<tt>usecount</tt>) are: <tt>type</tt>, containing an 8-bit value
|
|
identifying the type of mask; <tt>num</tt>, giving the user-visible index
|
|
number for session exception records (see <a href="#s2-3">section
|
|
7-2-3</a>); and <tt>limit</tt>, used for the session limit in session
|
|
exception records.</p>
|
|
|
|
<p>As mentioned above, each <tt>MaskData</tt> record has an associated
|
|
type; more accurately, there are multiple sets of <tt>MaskData</tt>
|
|
records, one set for each type (the <tt>type</tt> field is only used
|
|
internally for loading and saving data from or to persistent storage, and
|
|
does not need to be set by the caller). The available types, defined by
|
|
the <tt>MD_*</tt> constants in <tt>maskdata.h</tt>, are:</p>
|
|
<ul>
|
|
<li><b><tt>MD_AKILL</tt>:</b> An autokill record.</li>
|
|
<li><b><tt>MD_EXCLUDE</tt>:</b> An autokill exclusion record.</li>
|
|
<li><b><tt>MD_EXCEPTION</tt>:</b> A session exception record.</li>
|
|
<li><b><tt>MD_SGLINE</tt>:</b> An SGline record.</li>
|
|
<li><b><tt>MD_SQLINE</tt>:</b> An SQline record.</li>
|
|
<li><b><tt>MD_SZLINE</tt>:</b> An SZline record.</li>
|
|
</ul>
|
|
<p>It is worth noting that (as also mentioned in <a href="#s2-2-3">section
|
|
7-2-2-3</a> below) the values chosen for <tt>MD_SGLINE</tt>,
|
|
<tt>MD_SQLINE</tt>, and <tt>MD_SZLINE</tt> are the ASCII values of the
|
|
characters <tt>G</tt>, <tt>Q</tt>, and <tt>Z</tt> respectively; this was
|
|
done in order to simplify common code for all three types of S-lines, so
|
|
that the type value could be used as is in messages sent to clients rather
|
|
than having to make separate tests to determine the appropriate text to
|
|
send. (For example, most of the language file messages for S-line actions
|
|
use "<tt>S%cLINE</tt>" in the format string, where the <tt>%c</tt> is
|
|
replaced by the type value.)</p>
|
|
|
|
<p>One other structure, <tt>MaskDataCmdInfo</tt>, can be found in
|
|
<tt>maskdata.h</tt>; this structure collects information particular to a
|
|
single <tt>MaskData</tt> type for use by the common command processing.
|
|
The bulk of the structure consists of language string indices, which are
|
|
used in sending responses to commands procesed by the common code. These
|
|
are preceded by: <tt>name</tt>, the command name (such as "<tt>AKILL</tt>"
|
|
or "<tt>SGLINE</tt>"); <tt>type</tt>, the record type to be used; and
|
|
<tt>def_expiry_ptr</tt>, a pointer to a variable containing the default
|
|
expiration period in seconds (a pointer is used so that any changes to the
|
|
value can be recognized immediately without having to modify this structure
|
|
as well). There are also five function pointers, all of which are optional
|
|
(and should be set to <tt>NULL</tt> if not needed):</p>
|
|
|
|
<dl>
|
|
<dt><tt>void (*<b>mangle_mask</b>)(char *<i>mask</i>)</tt></dt>
|
|
<dd>Makes any necessary changes to a mask before it is operated on.
|
|
Any modifications may be made to the mask as long as they do not
|
|
lengthen it beyond the original string length. Currently, this
|
|
is used to force case-insensitive masks to lowercase, to avoid the
|
|
possibility of multiple matching masks with differing case from
|
|
being added to the database.</dd>
|
|
|
|
<dt><tt>int (*<b>check_add_mask</b>)(const User *<i>u</i>, uint8 <i>type</i>, char *<i>mask</i>, time_t *<i>expiry_ptr</i>)</tt></dt>
|
|
<dd>Checks whether the given mask of the given type is allowed to be
|
|
added with the given expiration time (in seconds from the present
|
|
time, passed as a pointer); returns nonzero to allow the mask to be
|
|
added, zero to deny it. The mask and expiration time may be
|
|
modified by the function, provided that the mask is not lengthened
|
|
beyond the original string length.</dd>
|
|
|
|
<dt><tt>void (*<b>do_add_mask</b>)(const User *<i>u</i>, uint8 <i>type</i>, MaskData *<i>md</i>)</tt></dt>
|
|
<dd>Performs any extra actions necessary when a mask is added to the
|
|
database, such as sending that mask to the network.</dd>
|
|
|
|
<dt><tt>void (*<b>do_del_mask</b>)(const User *<i>u</i>, uint8 <i>type</i>, MaskData *<i>md</i>)</tt></dt>
|
|
<dd>Performs any extra actions necessary when a mask is removed from
|
|
the database.</dd>
|
|
|
|
<dt><tt>int (*<b>do_unknown_cmd</b>)(const User *<i>u</i>, const char *<i>cmd</i>, char *<i>mask</i>)</tt></dt>
|
|
<dd>Processes an unknown subcommand for the associated command,
|
|
returning nonzero if the subcommand was handled, zero otherwise.</dd>
|
|
</dl>
|
|
|
|
<p>The function which implements the mask-related command support is
|
|
<tt>do_maskdata_cmd()</tt>, defined below the database support functions in
|
|
<tt>maskdata.c</tt>. This function behaves in the same way as a standard
|
|
pseudoclient command handler, taking a single <tt>User *</tt>
|
|
parameter giving the client that sent the command, and retrieving command
|
|
parameters via <tt>strtok(NULL,...)</tt>. After obtaining the subcommand
|
|
name and its parameters (with double-quote processing for masks), the
|
|
routine checks for the known subcommands <tt>ADD</tt>, <tt>DEL</tt>,
|
|
<tt>CLEAR</tt>, <tt>LIST</tt>, <tt>VIEW</tt>, <tt>CHECK</tt>, and
|
|
<tt>COUNT</tt>, processing each appropriately. If the subcommand is not
|
|
one of these, it is passed to the command's <tt>do_unknown_cmd()</tt>
|
|
function; if that function returns zero or does not exist, an error is
|
|
sent to the client.</p>
|
|
|
|
<p>The subcommands themselves are implemented by local routines:
|
|
<tt>do_maskdata_add()</tt>, <tt>do_maskdata_del()</tt>, and so on. These
|
|
check the validity of the parameters for the particular subcommand and
|
|
perform the appropriate action, using the language string indices in the
|
|
<tt>MaskDataCmdInfo</tt> structure to send replies to the client that
|
|
issued the command.</p>
|
|
|
|
<p>There are also three utility functions defined after the command
|
|
handler routines:</p>
|
|
|
|
<dl>
|
|
<dt><tt>void *<b>new_maskdata</b>()</tt></dt>
|
|
<dd>Allocates, initializes, and returns a new <tt>MaskData</tt>
|
|
structure. (The return value is <tt>void *</tt> to avoid a
|
|
type warning in the database table declaration.)</dd>
|
|
|
|
<dt><tt>void <b>free_maskdata</b>(void *<i>record</i>)</tt></dt>
|
|
<dd>Frees the <tt>MaskData</tt> structure pointed to by
|
|
<tt><i>record</i></tt>, Does nothing if <tt><i>record</i></tt> is
|
|
<tt>NULL</tt>.</dd>
|
|
|
|
<dt><tt>char *<b>make_reason</b>(const char *<i>format</i>, const MaskData *<i>data</i>)</tt></dt>
|
|
<dd>Returns the reason string for the given mask and format string.
|
|
If <tt><i>format</i></tt> contains a "<tt>%s</tt>", it will be
|
|
replaced by the mask's reason string in the return value;
|
|
otherwise, the returned string is identical to
|
|
<tt><i>format</i></tt>. The result string is stored in a static
|
|
buffer, which is overwritten by subsequent calls to
|
|
<tt>make_reason()</tt>.</dd>
|
|
</dl>
|
|
|
|
<p>With respect to the database management routines, <tt>MaskData</tt>
|
|
records are stored in variable-length arrays, one array for each of the 256
|
|
possible mask types. (Arrays were chosen over hash tables for simplicity;
|
|
since the most common use of these records is searching all records of a
|
|
particular type for one that matches a given string, there would be little
|
|
benefit to the use of a hash table.) The <tt>next</tt> and <tt>prev</tt>
|
|
fields are not ordinarily required for array handling, but the code stores
|
|
the (integer) array index value in the <tt>next</tt> field via a cast to
|
|
<tt>void *</tt>, allowing the code to know where in the array a given
|
|
record is located without having to search through the array every time
|
|
(note that the <tt>num</tt> field is used for a different
|
|
purpose—index numbers for session exceptions—and is not
|
|
available). Aside from the standard database operations, which take a
|
|
<tt>uint8 <i>type</i></tt> parameter in addition to the mask record itself,
|
|
the following functions are included:</p>
|
|
|
|
<dl>
|
|
<dt><tt>MaskData *<b>get_matching_maskdata</b>(uint8 <i>type</i>, const char *<i>str</i>)</tt></dt>
|
|
<dd>Searches for and returns (in the same manner as
|
|
<tt>get_maskdata()</tt>, including expiration checks) a mask which
|
|
matches the wildcard pattern given by <tt><i>str</i></tt>. If more
|
|
than one mask matches, an arbitrary one is returned.</dd>
|
|
|
|
<dt><tt>MaskData *<b>get_exception_by_num</b>(int <i>num</i>)</tt></dt>
|
|
<dd>Retrieves a mask of the <tt>MD_EXCEPTION</tt> (session exception)
|
|
type by its index number.</dd>
|
|
|
|
<dt><tt>MaskData *<b>move_exception</b>(MaskData *<i>except</i>, int <i>newnum</i>)</tt></dt>
|
|
<dd>Changes the index number of the given <tt>MD_EXCEPTION</tt> mask.
|
|
(Internally, this reorders the mask array so that entries remain in
|
|
order by index number.)</dd>
|
|
</dl>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s2-2-2">7-2-2-2. Autokills</h5>
|
|
|
|
<p>As mentioned above, one of the main uses of this mask data code is the
|
|
autokill module, <tt>operserv/akill</tt>, defined in <tt>akill.c</tt> and
|
|
<tt>akill.h</tt>. While support for the OperServ <tt>AKILL</tt> and
|
|
<tt>EXCLUDE</tt> commands simply makes use of the aforementioned
|
|
<tt>do_maskdata_cmd()</tt> function, handling for the actual autokills and
|
|
autokill exclusions themselves is defined within the autokill module. This
|
|
includes:</p>
|
|
|
|
<ul>
|
|
<li class="spaced"><tt>send_akill()</tt> and <tt>cancel_akill()</tt>, and
|
|
their autokill exclusion companions <tt>send_exclude()</tt> and
|
|
<tt>cancel_exclude()</tt>, which (via callbacks hooked into by
|
|
protocol modules) take care of adding and removing autokills and
|
|
exclusions on the network. Note that <tt>cancel_akill()</tt> and
|
|
<tt>cancel_exclude()</tt> destroy their <tt>char *</tt>
|
|
parameters, but as they are only called when deleting a record,
|
|
this is not a problem.</li>
|
|
|
|
<li class="spaced"><tt>do_user_check()</tt>, which hooks into the
|
|
"<tt>user check</tt>" callback to check whether a newly connecting
|
|
client matches any active autokills or exclusions and take
|
|
appropriate action.</li>
|
|
|
|
<li class="spaced"><tt>create_akill()</tt>, an exported function which
|
|
creates a new autokill record given the usermask, reason, setter
|
|
nickname, and time to expiration. (There is currently no
|
|
equivalent function for autokill exclusions.)</li>
|
|
|
|
<li class="spaced">The data structure and helper functions for
|
|
<tt>do_maskdata_cmd()</tt>. In particular, <tt>check_add_akill</tt>
|
|
uses simple heuristics to check for masks that are so general that
|
|
they would match any conceivable combination of username and
|
|
hostname (for example, "<tt>*@*</tt>" or "<tt>?*@*?.*?*</tt>") and
|
|
disallow such masks, in order to avoid situations where no one
|
|
could connect to the network because every client matched the
|
|
autokill.</li>
|
|
|
|
<li class="spaced">The <tt>AKILLCHAN</tt> command implementation,
|
|
<tt>do_akillchan()</tt>. It is worth noting that (as commented in
|
|
the code) there is a race condition that can allow the client to
|
|
reconnect in the miniscule interval between disconnecting the
|
|
client with a <tt>KILL</tt> command and adding an autokill for that
|
|
client; this negligible risk was taken in light of the fact that
|
|
doing it the other way (sending the autokill first) causes some IRC
|
|
servers to automatically kill the client, and OperServ's
|
|
<tt>KILL</tt> would cause a "user not found" message to be logged
|
|
(which did in fact generate some complaints from users). However,
|
|
once the client is killed, the username and hostname from the
|
|
<tt>User</tt> structure are no longer available, so they must be
|
|
saved ahead of time (the code at one point failed to do this,
|
|
predictably resulting in crashes—hence the vitriolic comment
|
|
about that mistake).</li>
|
|
|
|
<li class="spaced">The "<tt>connect</tt>" callback function, which sends
|
|
all autokills to the network on initial connection.</li>
|
|
|
|
<li class="spaced">The "<tt>expire_maskdata</tt>" timeout function, which
|
|
checks for autokill expiration. In order to prevent flooding of
|
|
IRC operators, for example when autokills set by an
|
|
<tt>AKILLCHAN</tt> command expire, expiration announcements via
|
|
<tt>WALLOPS</tt> are (if enabled) only sent at the rate of one per
|
|
second, and any further expirations are merged into a single
|
|
"<i>nnn</i> more autokills have expired" message sent after all
|
|
expirations are complete.</li>
|
|
|
|
<li class="spaced">The OperServ "<tt>HELP</tt>" callback function, which
|
|
is required to display the <tt>AKILL</tt> help message correctly.</li>
|
|
|
|
<li class="spaced">The OperServ "<tt>STATS ALL</tt>" callback function,
|
|
used to calculate and display autokill memory usage in response to
|
|
a <tt>STATS ALL</tt> command.</li>
|
|
</ul>
|
|
|
|
<p>One item of interest in the module setup code at the bottom of the
|
|
source file is the handling of the <tt>EXCLUDE</tt> command.
|
|
<tt>EXCLUDE</tt> can be enabled or disabled via a configuration file
|
|
option (<tt>EnableExclude</tt>), for reasons discussed below. Rather than
|
|
create two command tables, one with <tt>EXCLUDE</tt> and one without, the
|
|
code simply modifies the <tt>EXCLUDE</tt> entry to have an empty command
|
|
name if the command is disabled; since command names parsed by OperServ
|
|
cannot be empty, the command will never be found. (In order to locate the
|
|
entry again once the name has been cleared, a static variable is used, set
|
|
during module initialization to point to the proper element of the command
|
|
array. The name is restored during module cleanup so that a subsequent
|
|
initialization will likewise be able to find it.)</p>
|
|
|
|
<p>The handling of autokill exclusions is trickier than it may seem at
|
|
first glance, due to protocols which support network-wide autokill masks
|
|
but not exclusion masks. If one takes the naive approach of simply adding
|
|
autokill masks as usual, one will find that the effectiveness of the
|
|
autokill exclusions is severely limited. For example, consider a network
|
|
with an autokill for <tt>*@*.example.com</tt> and an exclusion for
|
|
<tt>*@oper.example.com</tt>. As long as the only users in the
|
|
<tt>example.com</tt> domain to connect are from the
|
|
<tt>oper.example.com</tt> host, the exclusion will function as expected.
|
|
However, as soon as a user from another <tt>example.com</tt> host connects,
|
|
the autokill will be triggered and sent out to the network—with the
|
|
unintended result that even users from <tt>oper.example.com</tt> are
|
|
prevented from connecting, since Services has no way to inform other
|
|
servers on the network about the autokill exclusion.</p>
|
|
|
|
<p>In order to avoid this problem, the <tt>operserv/akill</tt> module will
|
|
not make use of a protocol's autokill features if that protocol does not
|
|
also support autokill exclusions, and will simply send out a <tt>KILL</tt>
|
|
message for each user to be disconnected from the network. However, this
|
|
may not be desirable for networks that have no intention of using the
|
|
autokill exclusion functionality. For this reason, the
|
|
<tt>EnableExclude</tt> configuration option was added, allowing such
|
|
networks to choose between taking advantage of the protocol's autokill
|
|
feature and using exclusions with autokills.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s2-2-3">7-2-2-3. S-lines</h5>
|
|
|
|
<p>S-lines are the other primary user of the mask data code. These sets of
|
|
client restriction masks (SGlines, SQlines, and SZlines) are implemented by
|
|
the <tt>operserv/sline</tt> module, defined in <tt>sline.c</tt> and
|
|
<tt>sline.h</tt>.</p>
|
|
|
|
<p>The S-line module is very similar to the autokill module, both
|
|
functionally and internally (in fact, <tt>sline.c</tt> started out as a
|
|
copy of <tt>akill.c</tt>). The primary difference is in the sharing of
|
|
data and code between the three mask types. Since the command names differ
|
|
only in a single character, the code takes the shortcut of using that
|
|
character as the mask type for use with the <tt>MaskData</tt> support
|
|
functions—this is the reason for the warning comment in
|
|
<tt>maskdata.h</tt> about changing the type values—and including a
|
|
<tt>%c</tt> token in relevant messages which is replaced by the mask type.
|
|
This enables a single message, such as "<tt>%d masks on the S%cLINE
|
|
list.</tt>", to be shared by code for all three sets of masks. Several
|
|
functions are likewise shared by the three mask types, taking a type
|
|
parameter to select which data set to operate on.</p>
|
|
|
|
<p>Naturally, each type of mask is interpreted differently, so the actual
|
|
processing for each type is handled separately. SQlines, in particular,
|
|
are checked by the utility routine <tt>check_sqline()</tt>, called from the
|
|
callback functions <tt>do_user_check()</tt> and
|
|
<tt>do_user_nickchange_after()</tt>, because there are several possible
|
|
ways of handling a match:</p>
|
|
|
|
<ul>
|
|
<li class="spaced">If the client is an IRC operator and the
|
|
<tt>SQlineIgnoreOpers</tt> configuration option is enabled, then
|
|
SQlines are not checked at all. (The <tt><i>new_oper</i></tt>
|
|
parameter is required because, when called from
|
|
<tt>do_user_check()</tt>, the <tt>User</tt> record will not yet
|
|
have been created.)</li>
|
|
|
|
<li class="spaced">If the SQline is for a guest nick, the client is left
|
|
alone, though the SQline itself is sent to the network.</li>
|
|
|
|
<li class="spaced">If the <tt>SQlineKill</tt> configuration option is
|
|
<i>not</i> set and the protocol in use supports forced nickname
|
|
changing, the client's nickname is changed to a guest nickname, and
|
|
the SQline is sent to the network.</li>
|
|
|
|
<li class="spaced">Otherwise, the client is killed, and the SQline is sent
|
|
to the network.</li>
|
|
</ul>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s2-3">7-2-3. Session limiting</h4>
|
|
|
|
<p>The last method of client control, session limiting, is implemented by
|
|
the <tt>operserv/sessions</tt> module, defined in <tt>sessions.c</tt>. The
|
|
module consists of two major parts: the actual session maintenance and
|
|
limiting code, and session exception mask management via the
|
|
<tt>EXCEPTION</tt> command.</p>
|
|
|
|
<p>The session maintenance functionality is contained within the
|
|
<tt>add_session()</tt> and <tt>del_session()</tt> routines, called from
|
|
callback functions when clients join or leave the network, respectively.
|
|
These routines keep track of each host with one or more clients on the
|
|
network by means of a hash table containing <tt>Session</tt> structures,
|
|
one per host. When <tt>add_session</tt> is called, it looks up the
|
|
<tt>Session</tt> record for the new client's hostname, creating a new
|
|
record with a count of zero if no record for that host already exists, then
|
|
increments the host's client count by one; conversely,
|
|
<tt>del_session()</tt> decrements the host's client count, deleting the
|
|
<tt>Session</tt> structure when the count reaches zero.</p>
|
|
|
|
<p>The "limiting" part of session limiting is a check in
|
|
<tt>add_session()</tt> to determine whether the host has "too many"
|
|
clients on the network. Here, "too many" is defined by either the limit
|
|
given in an exception record (see below) or the default limit given in the
|
|
<tt>DefSessionLimit</tt> configuration directive; if adding the new client
|
|
would cause the host's client count to exceed the relevant limit, then the
|
|
client is killed (any clients already connected are left alone). An
|
|
autokill can also be added for thte host depending on the configuration
|
|
settings.</p>
|
|
|
|
<p>The module also provides a <tt>SESSION</tt> command to allow Services
|
|
operators to view the contents of the session list; this can be used to
|
|
find hosts from which a large number of clients are connecting. The
|
|
<tt>SESSION</tt> command is available even if actual limiting of sessions
|
|
is disabled (by setting <tt>DefSessionLimit</tt> to zero).</p>
|
|
|
|
<p>The second part of the module, session exceptions, allows fine-tuning of
|
|
the default limit on client connections. For example, it may be desirable
|
|
to allow extra connections from a particular host known to be used by IRC
|
|
operators. Such exceptions to the default session limit are stored using
|
|
<tt>MaskData</tt> structures with the <tt>MD_EXCEPTION</tt> type; each
|
|
record is treated as a wildcard pattern against which a connecting client's
|
|
hostname is matched, and if a match is found, that limit is used instead of
|
|
the default, as described above. If more than one matching session
|
|
exception exists, the first one in the list (from a user's point of view,
|
|
the one with the lowest index number) is used.</p>
|
|
|
|
<p>Session exceptions are maintained using the <tt>EXCEPTION</tt> command.
|
|
The processing for this command, in <tt>do_exception()</tt> and its
|
|
subroutines, is very similar to other mask-type commands like
|
|
<tt>AKILL</tt>; however, session exceptions require a limit value in
|
|
addition to the mask, and it was considered simpler to keep the handling
|
|
for <tt>EXCEPTION</tt> separate rather than modify
|
|
<tt>do_maskdata_cmd()</tt> to handle both command formats. The
|
|
<tt>EXCEPTION</tt> command also includes a <tt>MOVE</tt> subcommand,
|
|
allowing one record to be moved relative to another within the list (so,
|
|
for example, a newly-added exception can be moved earlier in the list to
|
|
take precedence over other exceptions).</p>
|
|
|
|
<p>A careful look at the initialization code will show that the
|
|
"<tt>user check</tt>" callback function, <tt>check_sessions()</tt> (which
|
|
in turn calls <tt>add_session()</tt>), is added at a priority of -10. This
|
|
is in order to ensure that the function is called after all other checks
|
|
have been performed, as described in <a href="4.html#s5-2">section
|
|
4-5-2</a> (and is a poor design choice for the reasons described there).</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s2-4">7-2-4. News</h4>
|
|
|
|
<p>The last OperServ submodule is the <tt>operserv/news</tt> module,
|
|
defined in <tt>news.c</tt> and <tt>news.h</tt>. This is a fairly simple
|
|
module, containing routines to handle news item storage and implement the
|
|
<tt>LOGONNEWS</tt> and <tt>OPERNEWS</tt> commands.</p>
|
|
|
|
<p>News items are stored in a single variable-length array of
|
|
<tt>News</tt> structures, <tt>newslist</tt>. The <tt>News</tt> structure
|
|
contains, aside from the text of the news item itself, the news type
|
|
(<tt>NEWS_LOGON</tt> or <tt>NEWS_OPER</tt>), an index number used when
|
|
deleting the item, the nickname of the client that added the item, and the
|
|
time the item was added. As with <tt>MaskData</tt> structures,
|
|
<tt>next</tt> and <tt>prev</tt> fields are included only to mirror other
|
|
structures, and the array index is stored in the <tt>next</tt> field.</p>
|
|
|
|
<p>The two news commands, <tt>LOGONNEWS</tt> and <tt>OPERNEWS</tt>, share
|
|
the same code, <tt>do_news()</tt>; this routine is called by the actual
|
|
command handlers with one of the news type codes, either
|
|
<tt>NEWS_LOGON</tt> or <tt>NEWS_OPER</tt>, and accesses an array of
|
|
language string indices (<tt>msgarray[]</tt>) to return proper messages
|
|
for each command, similar to <tt>do_maskdata_cmd()</tt>. Unlike most other
|
|
commands (but like the <tt>OPER</tt> and <tt>ADMIN</tt> commands in the
|
|
OperServ core), the news commands include a <tt>LIST</tt> command available
|
|
to all IRC operators, and other subcommands (<tt>ADD</tt> and <tt>DEL</tt>)
|
|
restricted to Services operators; thus, the command handler must perform
|
|
privilege checks on its own rather than specifying a privilege check
|
|
routine in the command table.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<h3 class="subsection-title" id="s3">7-3. NickServ</h3>
|
|
|
|
<p>NickServ is typically the first pseudoclient IRC users interact with.
|
|
It was also the first pseudoclient created during Services' initial design,
|
|
on which all other pseudoclients were based. The current NickServ is
|
|
divided into a core module and four submodules implementing additional
|
|
features; each module is described in its own section below.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s3-1">7-3-1. NickServ core functionality</h4>
|
|
|
|
<p>The core NickServ functionality is implemented by the
|
|
<tt>nickserv/main</tt> module. Alongside the primary module source file,
|
|
<tt>main.c</tt>, the module makes use of three additional source files:
|
|
<tt>collide.c</tt>, handling the disconnection or forced removal of clients
|
|
using unauthorized nicknames; <tt>set.c</tt>, implementing the <tt>SET</tt>
|
|
command and its subcommands; and <tt>util.c</tt>, containing various
|
|
utility routines used by NickServ.</p>
|
|
|
|
<p>NickServ makes use of two distinct header files. One,
|
|
<tt>nickserv.h</tt>, defines the data structures used for storing nickname
|
|
information (<tt>NickInfo</tt> and <tt>NickGroupInfo</tt>, described below)
|
|
along with declarations of exported routines and macros used with nickname
|
|
records. The other, <tt>ns-local.h</tt>, contains declarations of routines
|
|
used within NickServ, necessarily declared <tt>extern</tt> because they
|
|
reside in separate source files but not intended to be used outside the
|
|
NickServ modules.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s3-1-1">7-3-1-1. Nickname data structures and utility macros</h5>
|
|
|
|
<p>Two separate structures are used to store nickname data; this is to
|
|
facilitate the implementation of nickname links, as described in
|
|
<a href="#s3-4">section 7-3-4</a>. One structure, <tt>NickInfo</tt>,
|
|
contains data that is distinct for each individual nickname; the other,
|
|
<tt>NickGroupInfo</tt>, contains data that is shared among each group of
|
|
linked nicknames.</p>
|
|
|
|
<p>The <tt>NickInfo</tt> structure includes the following data:</p>
|
|
|
|
<dl>
|
|
<dt><tt>NickInfo *<b>next</b>, *<b>prev</b></tt></dt>
|
|
<dd>Used to link records together in the internal hash table.</dd>
|
|
|
|
<dt><tt>int <b>usecount</b></tt></dt>
|
|
<dd>The record's usage count (number of gets minus number of puts).
|
|
<i>(Implementation note: As noted in <a href="#s1">section 7-1</a>,
|
|
this field currently serves no actual purpose.)</i></dd>
|
|
|
|
<dt><tt>char <b>nick</b>[NICKMAX]</tt></dt>
|
|
<dd>The actual nickname. Capitalization is as used when the nickname
|
|
was registered, and does not change due to later actions. The
|
|
buffer size, <tt>NICKMAX</tt>, is defined in the global header
|
|
file <tt>defs.h</tt>.</dd>
|
|
|
|
<dt><tt>int16 <b>status</b></tt></dt>
|
|
<dd>The nickname's status. This is a combination of zero or more of
|
|
the following flags:
|
|
<ul>
|
|
<li><b><tt>NS_VERBOTEN</tt>:</b> The nickname is a forbidden
|
|
nickname set with the <tt>FORBID</tt> command. ("Verboten"
|
|
is German for "forbidden", but there is no particular
|
|
meaning behind this choice other than a whim of the
|
|
developer as he was writing the code.)</li>
|
|
<li><b><tt>NS_NOEXPIRE</tt>:</b> The nickname is not to be expired
|
|
regardless of how long it remains unused (<tt>SET
|
|
NOEXPIRE</tt>).</li>
|
|
<li><b><tt>NS_KILL_HELD</tt>:</b> The nickname is currently being
|
|
held by an "enforcer" pseudoclient after killing (or
|
|
changing the nickname of) a client that was using the
|
|
nickname without permission.</li>
|
|
<li><b><tt>NS_GUESTED</tt>:</b> An IRC message has been sent to
|
|
change the nickname of the client using the nickname, but
|
|
the nickname change has not yet occurred.</li>
|
|
</ul>
|
|
Of these flags, <tt>NS_VERBOTEN</tt> and <tt>NS_NOEXPIRE</tt> are
|
|
"permanent" flags (collected in the <tt>NS_PERMANENT</tt> mask),
|
|
which are retained across restarts of Services, while
|
|
<tt>NS_KILL_HELD</tt> and <tt>NS_GUESTED</tt> are "temporary" flags
|
|
(collected in the <tt>NS_TEMPORARY</tt> mask), which are cleared
|
|
each time the database is loaded from persistent storage. Note
|
|
that the value 0x0001 (bit 0) is not used because it served a
|
|
separate purpose in previous versions of Services.</dd>
|
|
|
|
<dt><tt>char *<b>last_usermask</b></tt></dt>
|
|
<dd>The last <tt><i>user</i>@<i>host</i></tt> mask used by the owner
|
|
of the nickname (<i>i.e.,</i> a client authorized to use the
|
|
nickname). If the owner is currently online, that client's
|
|
<tt><i>user</i>@<i>host</i></tt> mask is used. On IRC networks
|
|
where a "fake hostname" is available, that hostname is used
|
|
instead of the client's actual hostname.</dd>
|
|
|
|
<dt><tt>char *<b>last_realmask</b></tt></dt>
|
|
<dd>Like <tt>last_usermask</tt>, but uses the real hostname instead of
|
|
any "fake hostname". On networks without such a fake hostname,
|
|
this field is identical to <tt>last_usermask</tt>.</dd>
|
|
|
|
<dt><tt>char *<b>last_realname</b></tt></dt>
|
|
<dd>The last "real name" string used by the owner of the nickname.</dd>
|
|
|
|
<dt><tt>char *<b>last_quit</b></tt></dt>
|
|
<dd>The message used the last time the owner quit IRC. <tt>NULL</tt>
|
|
if not available, such as for newly-registered nicknames.</dd>
|
|
|
|
<dt><tt>time_t <b>time_registered</b></tt></dt>
|
|
<dd>The timestamp when the nickname was registered.</dd>
|
|
|
|
<dt><tt>time_t <b>last_seen</b></tt></dt>
|
|
<dd>The timestamp at which the owner most recently used the nickname.
|
|
Only updated when the owner stops using the nickname; if the
|
|
nickname is currently in use, this field should not be relied on.</dd>
|
|
|
|
<dt><tt>uint32 <b>nickgroup</b></tt></dt>
|
|
<dd>The ID of the nickname group with which this nickname is associated
|
|
(see below).</dd>
|
|
|
|
<dt><tt>uint32 <b>id_stamp</b></tt></dt>
|
|
<dd>The servicestamp of the client that last identified for the
|
|
nickname. Used to retain identification status across Services
|
|
restarts.</dd>
|
|
|
|
<dt><tt>User *<b>user</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> A pointer to the
|
|
<tt>User</tt> structure for the client currently using the
|
|
nickname, or <tt>NULL</tt> if the nickname is not currently in
|
|
used.</dd>
|
|
|
|
<dt><tt>int16 <b>authstat</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> Zero or more of the
|
|
following flags, indicating the nickname's authentication status:
|
|
<ul>
|
|
<li><b><tt>NA_IDENTIFIED</tt>:</b> The current user of the nickname
|
|
has identified by password as the nickname's owner.
|
|
Mutually exclusive with <tt>NA_IDENT_NOEMAIL</tt>.</li>
|
|
<li><b><tt>NA_IDENT_NOMAIL</tt>:</b> The current user of the
|
|
nickname has identified by password as the nickname's
|
|
owner, but has not registered an E-mail address with the
|
|
nickname when one is required. Mutually exclusive with
|
|
<tt>NA_IDENTIFIED</tt>.</li>
|
|
<li><b><tt>NA_RECOGNIZED</tt>:</b> The current user of the nickname
|
|
is recognized via the nickname access list (see
|
|
<a href="#s3-2">section 7-3-2</a>).</li>
|
|
</ul></dd>
|
|
|
|
<dt><tt>int <b>bad_passwords</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> The number of consecutive
|
|
failed password identification attempts for the nickname. Used to
|
|
determine whether or not to kill a client for attempted password
|
|
cracking.</dd>
|
|
</dl>
|
|
|
|
<p>The <tt>NickGroupInfo</tt> structure includes the following data:</p>
|
|
|
|
<dl>
|
|
<dt><tt>NickGroupInfo *<b>next</b>, *<b>prev</b></tt></dt>
|
|
<dd>Used to link records together in the internal hash table.</dd>
|
|
|
|
<dt><tt>int <b>usecount</b></tt></dt>
|
|
<dd>The record's usage count (number of gets minus number of puts).
|
|
<i>(Implementation note: As noted in <a href="#s1">section 7-1</a>,
|
|
this field currently serves no actual purpose.)</i></dd>
|
|
|
|
<dt><tt>uint32 <b>id</b></tt></dt>
|
|
<dd>The nickname group's ID, a unique 32-bit value. Typically, this
|
|
value is randomly assigned. The value zero is not allowed (it is
|
|
used in <tt>NickInfo</tt> records to indicate the lack of an
|
|
associated nickname group, as for forbidden nicknames).</dd>
|
|
|
|
<dt><tt>nickname_t *<b>nicks</b></tt>
|
|
<br/><tt>uint16 <b>nicks_count</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> A variable-length array
|
|
containing the nicknames associated with this nickname group. Used
|
|
for convenience, to avoid having to search through the nickname
|
|
database every time a list of nicknames is needed.</dd>
|
|
|
|
<dt><tt>uint16 <b>mainnick</b></tt></dt>
|
|
<dd>The "main nickname" for the group, used to represent the nickname
|
|
group in things such as channel access lists. Specified as an
|
|
index into the <tt>nicks[]</tt> array.</dd>
|
|
|
|
<dt><tt>Password <b>pass</b></tt></dt>
|
|
<dd>The password used for identification for the nickname group.</dd>
|
|
|
|
<dt><tt>char *<b>url</b></tt></dt>
|
|
<dd>A URL associated with the nickname group. Can be arbitrarily set
|
|
by the owner.</dd>
|
|
|
|
<dt><tt>char *<b>email</b></tt></dt>
|
|
<dd>An E-mail address associated with the nickname group. Can be
|
|
arbitrarily set by the owner, and may be required at registration
|
|
time by setting the <tt>NSRequireEmail</tt> configuration option.</dd>
|
|
|
|
<dt><tt>char *<b>last_email</b></tt></dt>
|
|
<dd>When mail-based authentication (see <a href="#s3-5">section
|
|
7-3-5</a>) is in use, this field is set to the previous contents
|
|
of the <tt>email</tt> field when the owner changes the E-mail
|
|
address, and is cleared when the new address is authenticated; this
|
|
allows the <tt>RESTOREMAIL</tt> command to function.</dd>
|
|
|
|
<dt><tt>char *<b>info</b></tt></dt>
|
|
<dd>A free-form text string associated with the nickname group. Can be
|
|
arbitrarily set by the owner.</dd>
|
|
|
|
<dt><tt>int32 <b>flags</b></tt></dt>
|
|
<dd>A bitmask containing zero or more of the following nickname group
|
|
flags:
|
|
<ul>
|
|
<li><b><tt>NF_KILLPROTECT</tt>:</b> NickServ should prevent other
|
|
clients from using the nickname by either killing them or
|
|
changing their nicknames, depending on the IRC protocol in
|
|
use (<tt>SET KILL ON/QUICK/IMMED</tt>).</li>
|
|
<li><b><tt>NF_SECURE</tt>:</b> NickServ should require password
|
|
identification for the nickname even if the client is on
|
|
the nickname's access list (<tt>SET SECURE</tt>).</li>
|
|
<li><b><tt>NF_MEMO_HARDMAX</tt>:</b> The client is not permitted to
|
|
change the nickname's memo limit (MemoServ <tt>SET LIMIT
|
|
HARD</tt>).</li>
|
|
<li><b><tt>NF_MEMO_SIGNON</tt>:</b> MemoServ should inform the
|
|
client of new memos at signon (MemoServ <tt>SET NOTIFY
|
|
ON/LOGON</tt>).</li>
|
|
<li><b><tt>NF_MEMO_RECEIVE</tt>:</b> MemoServ should inform the
|
|
client of new memos when they are sent (MemoServ <tt>SET
|
|
NOTIFY ON/NEW</tt>).</li>
|
|
<li><b><tt>NF_PRIVATE</tt>:</b> The nickname is hidden from the
|
|
<tt>LIST</tt> and <tt>LISTEMAIL</tt> command output, except
|
|
when used by Services administrators (<tt>SET
|
|
PRIVATE</tt>).</li>
|
|
<li><b><tt>NF_HIDE_EMAIL</tt>:</b> The nickname's E-mail address is
|
|
hidden from the <tt>INFO</tt> and <tt>LISTEMAIL</tt>
|
|
command output, except when used by Services administrators
|
|
(<tt>SET HIDE EMAIL</tt>).</li>
|
|
<li><b><tt>NF_HIDE_MASK</tt>:</b> The nickname's
|
|
<tt><i>user</i>@<i>host</i></tt> mask is hidden from the
|
|
<tt>INFO</tt> and <tt>LIST</tt> command output, except when
|
|
used by Services administrators (<tt>SET HIDE
|
|
USERMASK</tt>).</li>
|
|
<li><b><tt>NF_HIDE_QUIT</tt>:</b> The nickname's last quit message
|
|
is hidden from the <tt>INFO</tt> command output, except
|
|
when used by Services administrators (<tt>SET HIDE
|
|
QUIT</tt>).</li>
|
|
<li><b><tt>NF_KILL_QUICK</tt>:</b> NickServ should allow only 20
|
|
seconds instead of 60 for an unauthorized client to change
|
|
nickname (<tt>SET KILL QUICK/IMMED</tt>).</li>
|
|
<li><b><tt>NF_KILL_IMMED</tt>:</b> NickServ should kill or
|
|
nickchange unauthorized clients immediately with no grace
|
|
period (<tt>SET KILL IMMED</tt>).</li>
|
|
<li><b><tt>NF_MEMO_FWD</tt>:</b> MemoServ should forward received
|
|
memos to the nickname's E-mail address (MemoServ <tt>SET
|
|
FORWARD ON/COPY</tt>).</li>
|
|
<li><b><tt>NF_MEMO_FWDCOPY</tt>:</b> MemoServ should save copies of
|
|
forwarded memos (MemoServ <tt>SET FORWARD COPY</tt>).</li>
|
|
<li><b><tt>NF_SUSPENDED</tt>:</b> The nickname group is suspended
|
|
(<tt>SUSPEND</tt>).</li>
|
|
<li><b><tt>NF_NOOP</tt>:</b> ChanServ should prevent the nickname
|
|
from being added to channel access lists (<tt>SET
|
|
NOOP</tt>).</li>
|
|
</ul>
|
|
Note that the value 0x00000004 (bit 2) is not included in the above
|
|
flags because it served a separate purpose in previous versions of
|
|
Services. Instead, this value is used as a temporary flag
|
|
(<tt>NF_NOGROUP</tt>) when loading databases from earlier versions
|
|
of Services, to indicate that a nickname group does not yet have an
|
|
ID value assigned.</dd>
|
|
|
|
<dt><tt>int16 <b>os_priv</b></tt></dt>
|
|
<dd>The nickname group's privilege level with respect to OperServ.
|
|
Can be any value, but typically either zero (no special
|
|
privileges) or one of the following values:
|
|
<ul>
|
|
<li><b><tt>NP_SERVOPER</tt>:</b> Services operator privilege.</li>
|
|
<li><b><tt>NP_SERVADMIN</tt>:</b> Services administrator
|
|
privilege.</li>
|
|
</ul>
|
|
Other values are treated by the OperServ privilege checking code
|
|
(see <a href="#s2-1">section 7-2-1</a>) as having the next lowest
|
|
recognized value.</dd>
|
|
|
|
<dt><tt>int32 <b>authcode</b></tt></dt>
|
|
<dd>The authentication code set for the nickname group, or zero if no
|
|
code is set. See <a href="#s3-5">section 7-3-5</a>.</dd>
|
|
|
|
<dt><tt>time_t <b>authset</b></tt></dt>
|
|
<dd>The timestamp when the nickname group's authentication code was
|
|
set (meaningless if no code is set).</dd>
|
|
|
|
<dt><tt>int16 <b>authreason</b></tt></dt>
|
|
<dd>The reason the nickname group's current authentication code was
|
|
set (meaningless if no code is set). One of the following
|
|
constants:
|
|
<ul>
|
|
<li><b><tt>NICKAUTH_REGISTER</tt>:</b> The nickname was newly
|
|
registered, and the E-mail address provided in the
|
|
<tt>REGISTER</tt> command requires authentication.</li>
|
|
<li><b><tt>NICKAUTH_SET_EMAIL</tt>:</b> The nickname group's
|
|
E-mail address was changed with the <tt>SET EMAIL</tt>
|
|
command, and the new address requires authentication.</li>
|
|
<li><b><tt>NICKAUTH_SETAUTH</tt>:</b> Authentication is required as
|
|
the result of a Services administrator using the
|
|
<tt>SETAUTH</tt> command.</li>
|
|
<li><b><tt>NICKAUTH_REAUTH</tt>:</b> Authentication is required as
|
|
the result of the nickname owner using the <tt>REAUTH</tt>
|
|
command.</li>
|
|
</ul></dd>
|
|
|
|
<dt><tt>char <b>suspend_who</b>[NICKMAX]</tt></dt>
|
|
<dd>The nickname of the client that suspended the nickname group
|
|
(meaningless if the <tt>NF_SUSPENDED</tt> flag is not set).</dd>
|
|
|
|
<dt><tt>char *<b>suspend_reason</b></tt></dt>
|
|
<dd>The reason the nickname group was suspended (meaningless if the
|
|
<tt>NF_SUSPENDED</tt> flag is not set).</dd>
|
|
|
|
<dt><tt>time_t <b>suspend_time</b></tt></dt>
|
|
<dd>The timestamp when the nickname group was suspended (meaningless if
|
|
the <tt>NF_SUSPENDED</tt> flag is not set).</dd>
|
|
|
|
<dt><tt>time_t <b>suspend_expires</b></tt></dt>
|
|
<dd>The timestamp at which the nickname group's suspension expires
|
|
(meaningless if the <tt>NF_SUSPENDED</tt> flag is not set).</dd>
|
|
|
|
<dt><tt>int16 <b>language</b></tt></dt>
|
|
<dd>The language preferred for messages sent to the nickname group.
|
|
One of the <tt>LANG_*</tt> constants in the Services core's
|
|
<tt>language.h</tt>.</dd>
|
|
|
|
<dt><tt>int16 <b>timezone</b></tt></dt>
|
|
<dd>The time zone offset specified by the nickname group's owner for
|
|
use in displaying times. A number of minutes (possibly negative)
|
|
to be added to the UTC timestamps, or <tt>TIMEZONE_DEFAULT</tt> to
|
|
use the Services process' default.</dd>
|
|
|
|
<dt><tt>int16 <b>channelmax</b></tt></dt>
|
|
<dd>The maximum number of channels the nickname group is allowed to
|
|
register, <tt>CHANMAX_UNLIMITED</tt> for no limit, or
|
|
<tt>CHANMAX_DEFAULT</tt> for the default limit (set by the
|
|
ChanServ <tt>CSMaxReg</tt> configuration setting).</dd>
|
|
|
|
<dt><tt>char **<b>access</b></tt>
|
|
<br/><tt>int <b>access_count</b></tt></dt>
|
|
<dd>The nickname group's access list (see <a href="#s3-2">section
|
|
7-3-2</a>).</dd>
|
|
|
|
<dt><tt>char **<b>ajoin</b></tt>
|
|
<br/><tt>int <b>ajoin_count</b></tt></dt>
|
|
<dd>The nickname group's auto-join list (see <a href="#s3-3">section
|
|
7-3-3</a>).</dd>
|
|
|
|
<dt><tt>char **<b>ignore</b></tt>
|
|
<br/><tt>int <b>ignore_count</b></tt></dt>
|
|
<dd>The nickname group's memo ignore list (see <a href="#s5-2">section
|
|
7-5-2</a>).</dd>
|
|
|
|
<dt><tt>MemoInfo <b>memos</b></tt></dt>
|
|
<dd>The nickname group's stored memos (see <a href="#s5-1">section
|
|
7-5-1</a>).</dd>
|
|
|
|
<dt><tt>channame_t *<b>channels</b></tt>
|
|
<br/><tt>int <b>channels_count</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> The names of the channels
|
|
currently registered by this nickname group.</dd>
|
|
|
|
<dt><tt>User **<b>id_users</b></tt>
|
|
<br/><tt>int <b>id_users_count</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> Pointers to <tt>User</tt>
|
|
structures for clients which have identified for this nickname
|
|
group.</dd>
|
|
|
|
<dt><tt>time_t <b>last_sendauth</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> The timestamp when the
|
|
<tt>SENDAUTH</tt> command was last used for this nickname group
|
|
(see <a href="#s3-5">section 7-3-5</a>).</dd>
|
|
|
|
<dt><tt>int <b>bad_auths</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> The number of times the
|
|
<tt>AUTH</tt> command has been used with a bad authentication code
|
|
for this nickname group (see <a href="#s3-5">section 7-3-5</a>).</dd>
|
|
</dl>
|
|
|
|
<p>In addition, <tt>nickserv.h</tt> declares the following convenience
|
|
functions and macros:</p>
|
|
|
|
<dl>
|
|
<dt><tt>int <b>nick_recognized</b>(const NickInfo *<i>ni</i>)</tt>
|
|
<br/><tt>int <b>user_recognized</b>(const User *<i>u</i>)</tt></dt>
|
|
<dd>Returns whether the given client is recognized via an access list
|
|
entry, regardless of whether the client has identified for the
|
|
nickname group or not. The client can be specified by either
|
|
<tt>NickInfo</tt> or <tt>User</tt> structure. (Nickname
|
|
authorization flags are always cleared when a client disconnects
|
|
from the network, so the <tt>NickInfo</tt> form will always return
|
|
false if used on a nickname not currently in use.)</dd>
|
|
|
|
<dt><tt>int <b>nick_identified</b>(const NickInfo *<i>ni</i>)</tt>
|
|
<br/><tt>int <b>user_identified</b>(const User *<i>u</i>)</tt></dt>
|
|
<dd>Returns whether the given client has identified for its nickname.
|
|
The client can be specified by either <tt>NickInfo</tt> or
|
|
<tt>User</tt> structure.</dd>
|
|
|
|
<dt><tt>int <b>nick_id_or_rec</b>(const NickInfo *<i>ni</i>)</tt>
|
|
<br/><tt>int <b>user_id_or_rec</b>(const User *<i>u</i>)</tt></dt>
|
|
<dd>Returns whether the given client is recognized via access list or
|
|
has identified for its nickname (or both). The client can be
|
|
specified by either <tt>NickInfo</tt> or <tt>User</tt> structure.</dd>
|
|
|
|
<dt><tt>int <b>nick_ident_nomail</b>(const NickInfo *<i>ni</i>)</tt>
|
|
<br/><tt>int <b>user_ident_nomail</b>(const User *<i>u</i>)</tt></dt>
|
|
<dd>Returns whether the <tt>NA_IDENT_NOMAIL</tt> flag is set for the
|
|
given client; <i>i.e.,</i> evaluates to true when has identified
|
|
for its nickname but the nickname lacks a required E-mail address.
|
|
The client can be specified by either <tt>NickInfo</tt> or
|
|
<tt>User</tt> structure.</dd>
|
|
|
|
<dt><tt>int <b>ngi_unauthed</b>(const NickGroupInfo *<i>ngi</i>)</tt></dt>
|
|
<dd>Returns whether the given nickname group (more accurately, the
|
|
nickname group's E-mail address) is unauthenticated. Note that use
|
|
of the <tt>REAUTH</tt> command does not cause the nickname group to
|
|
lose its authenticated status.</dd>
|
|
|
|
<dt><tt>int <b>valid_ngi</b>(const NickGroupInfo *<i>ngi</i>)</tt></dt>
|
|
<dd>Returns whether the given <tt>NickGroupInfo</tt> structure pointer
|
|
is valid; <i>i.e.,</i> evaluates to true when <tt><i>ngi</i></tt>
|
|
is neither <tt>NULL</tt> nor <tt>NICKGROUPINFO_INVALID</tt>. (The
|
|
latter value is used in <tt>User</tt> structures to indicate that
|
|
the client has an associated <tt>NickInfo</tt> structure but no
|
|
<tt>NickGroupInfo</tt> structure, as is the case for forbidden
|
|
nicknames.)</dd>
|
|
|
|
<dt><tt>const char *<b>ngi_mainnick</b>(const NickGroupInfo *<i>ngi</i>)</tt></dt>
|
|
<dd>Returns the given nickname group's main nickname.</dd>
|
|
|
|
<dt><tt>NickGroupInfo *<b>get_ngi</b>(const NickInfo *<i>ni</i>)</tt>
|
|
<br/><tt>NickGroupInfo *<b>get_ngi_id</b>(uint32 <i>id</i>)</tt></dt>
|
|
<dd>Retrieves the <tt>NickGroupInfo</tt> record corresponding to the
|
|
given <tt>NickInfo</tt> or ID value; if there is no corresponding
|
|
record, a warning message is logged and <tt>NULL</tt> is returned.
|
|
(Like any other database "get" routine, the structure must be "put"
|
|
with <tt>put_nickgroupinfo()</tt> when no longer needed.)</dd>
|
|
|
|
<dt><tt>int <b>check_ngi</b>(const NickInfo *<i>ni</i>)</tt>
|
|
<br/><tt>int <b>check_ngi_id</b>(uint32 <i>id</i>)</tt></dt>
|
|
<dd>Returns whether there is a corresponding <tt>NickGroupInfo</tt>
|
|
record for the given <tt>NickInfo</tt> or ID value, logging a
|
|
warning message if not. (The
|
|
<tt>put_<i>xxx</i>(get_<i>xxx</i>(...))</tt> is a common way of
|
|
checking for the existence of a record, since the
|
|
<tt>put_<i>xxx</i>()</tt> explicitly allow a <tt>NULL</tt>
|
|
parameter.)</dd>
|
|
</dl>
|
|
|
|
<p>The <tt>STANDALONE_NICKSERV</tt> define and (non-macro) utility
|
|
functions are discussed in <a href="#s3-1-4">section 7-3-1-4</a>.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s3-1-2">7-3-1-2. Overall module structure</h5>
|
|
|
|
<p>The overall structure of the NickServ module generally follows the same
|
|
pattern as the OperServ module: variable and command declarations, database
|
|
handling, <tt>PRIVMSG</tt> and other callbacks, and command routines.</p>
|
|
|
|
<p>The <tt>nickserv/main</tt> module uses two separate databases, one for
|
|
<tt>NickInfo</tt> records and one for <tt>NickGroupInfo</tt> records. The
|
|
database handling code is more or less straightforward, using <tt>hash.h</tt>
|
|
to maintain the in-memory tables; however, since most of the record
|
|
management routines take additional actions (for example, the "add" and
|
|
"get" functions update the record's use count), the base hash functions are
|
|
defined with a trailing underscore, like <tt>add_nickinfo_()</tt>, and the
|
|
actual functions (like <tt>add_nickinfo()</tt>) are wrapped around these.
|
|
NickServ exports all of the <tt>NickInfo</tt> and <tt>NickGroupInfo</tt>
|
|
database functions, as well as a "put" function for each record type.</p>
|
|
|
|
<p>When saving the databases to persistent storage, the <tt>nicks[]</tt>
|
|
array and <tt>mainnick</tt> field of <tt>NickGroupInfo</tt> records are not
|
|
saved directly; rather, the main nickname itself is saved as a
|
|
<tt>NICKMAX</tt>-sized buffer, and the nickname group is initialized with
|
|
this nickname when first loaded (the array is subsequently filled in when
|
|
the relevant <tt>NickInfo</tt> records are loaded).</p>
|
|
|
|
<p>NickServ keeps track of clients' status using callback functions for
|
|
new clients, clients changing nickname, and disconnecting clients. The
|
|
routines that do the actual processing, <tt>validate_user()</tt> (for a
|
|
client starting to use a nickname) and <tt>cancel_user()</tt> (for a client
|
|
no longer using a nickname), are located in <tt>util.c</tt>, discussed in
|
|
<a href="#s3-1-4">section 7-3-1-4</a>.</p>
|
|
|
|
<p>The NickServ commands themselves tend to be fairly complex, especially
|
|
when compared to the OperServ command handlers. This is in part due to the
|
|
wide range of features available in NickServ, and in part due to the fact
|
|
that NickServ and its system of registered nicknames are the primary way by
|
|
which clients authenticate themselves, and as such must handle a variety of
|
|
circumstances to maintain security, while other pseudoclients simply rely
|
|
on the authorization flags set by NickServ. The following command handlers
|
|
are particularly worthy of note:</p>
|
|
|
|
<dl>
|
|
<dt><b><tt>do_help()</tt></b></dt>
|
|
<dd>NickServ's commands include a number whose help text changes based
|
|
on factors such as the requesting client's IRC operator status,
|
|
features available in the IRC protocol in use, and NickServ
|
|
configuration settings. The code to handle help requests is
|
|
accordingly complex, with many commands unable to rely on the
|
|
<tt>help_cmd()</tt> routine.</dd>
|
|
|
|
<dt><b><tt>do_register()</tt></b></dt>
|
|
<dd>Just as NickServ is the gateway to the rest of Services' functions,
|
|
the <tt>REGISTER</tt> command is the gateway to NickServ, providing
|
|
one of the two methods by which a client can gain access to
|
|
Services (the other being authentication to a previously registered
|
|
nickname). The <tt>REGISTER</tt> handler must therefore be
|
|
particularly careful to guard against abuse, both to prevent
|
|
improper access to other Services commands and to prevent the
|
|
<tt>REGISTER</tt> command itself from being abused by arbitrary
|
|
clients. The command handler takes the following precautions
|
|
before allowing a nickname to be registered:
|
|
<ul>
|
|
<li class="spaced">Prevents the command from being used by any
|
|
particular client more than once every <tt>NSRegDelay</tt>
|
|
seconds. This stops mass-registration of nicknames by
|
|
automated clients, avoiding both the accompanying load on
|
|
Services itself (more memory usage, more time spent looking
|
|
up nicknames) and any undesirable side effects, such as the
|
|
sending of automated E-mail to arbitrary address when mail
|
|
authentication is in use (see <a href="#s3-5">section
|
|
7-3-5</a>).</li>
|
|
<li class="spaced">Prevents the command from being used by a client
|
|
within <tt>NSInitialRegDelay</tt> seconds of connecting to
|
|
the network. This prevents automated clients from getting
|
|
around the <tt>NSRegDelay</tt> limitation by repeatedly
|
|
connecting, issuing a <tt>REGISTER</tt> command, and
|
|
disconnecting in rapid succession. (It is still possible
|
|
to avoid the limitation by connecting a large number of
|
|
clients at once, but as a practical matter it is impossible
|
|
for NickServ to distinguish such attempts from ordinary
|
|
registration requests, and the sudden presence of a large
|
|
number of clients on the network should itself be an
|
|
indication of trouble.)</li>
|
|
<li class="spaced">Prevents "guest" nicknames from being
|
|
registered, which could result in unauthenticated clients
|
|
suddenly gaining Services access after a forced nickname
|
|
change. (The check itself is performed by the
|
|
<tt>do_reglink_check()</tt> earlier in <tt>main.c</tt>,
|
|
a callback function attached to the "<tt>REGISTER/LINK
|
|
check</tt>" callback; <tt>do_register()</tt> calls this
|
|
callback via the <tt>reglink_check()</tt> function in
|
|
<tt>util.c</tt>.)</li>
|
|
<li class="spaced">Ensures that the nickname is not already
|
|
registered. Ordinarily, if a nickname is registered then
|
|
the client's <tt>User</tt> structure will have a pointer to
|
|
the record in its <tt>ni</tt> field, but if the nickname is
|
|
missing a corresponding nickname group (a database error
|
|
unless the nickname is forbidden), the <tt>ni</tt> field
|
|
will be <tt>NULL</tt> and the <tt>ngi</tt> field will be
|
|
set to the constant <tt>NICKGROUPINFO_INVALID</tt>, so that
|
|
combination is checked for as well. Also, just in case,
|
|
<tt>do_register()</tt> performs a final check by accessing
|
|
the database directly, to ensure that the nickname is not
|
|
registered in duplicate.</li>
|
|
<li class="spaced">Checks that the E-mail address, if given, is (1)
|
|
syntactically valid and (2) not disallowed due to a
|
|
<tt>RejectEmail</tt> configuration directive.</li>
|
|
<li class="spaced">Prevents more than <tt>NSRegEmailMax</tt>
|
|
nicknames from being registered to the same address, again
|
|
to avoid undue load on Services from a registration flood.
|
|
This check calls <tt>count_nicks_with_email()</tt> to
|
|
actually count nicknames; this routine has to search the
|
|
entire nickname database, which can take a significant
|
|
amount of time if many nicknames are registered, so this
|
|
check is performed last.</li>
|
|
<li class="spaced">As an adjunct to the previous check (and
|
|
regardless of the setting of <tt>NSRegEmailMax</tt>), also
|
|
prevents a nickname from being registered if the E-mail
|
|
address given is already in use by another nickname which
|
|
is awaiting mail authentication; this acts as a further
|
|
guard to prevent a particular mail address from getting
|
|
"mailbombed" by multiple registration requests that make it
|
|
through the previous checks.</li>
|
|
</ul></dd>
|
|
|
|
<dt><b><tt>do_dropemail()</tt></b>
|
|
<br/><b><tt>do_dropemail_confirm()</tt></b></dt>
|
|
<dd>The <tt>DROPEMAIL</tt> and <tt>DROPEMAIL-CONFIRM</tt> commands are
|
|
the only two commands in Services that require state to be kept
|
|
specifically for those commands. Due to the potential for data
|
|
loss through an erroneous <tt>DROPEMAIL</tt> command, some form of
|
|
confirmation was desired, such as an "Are you sure?" requester in
|
|
response to a user deleting a file in a GUI. Since Services'
|
|
interface is limited to single-line commands, however, this can
|
|
only be accomplished through two commands, the second of which
|
|
(<tt>DROPEMAIL-CONFIRM</tt>) serves to confirm the action requested
|
|
by the first (<tt>DROPEMAIL</tt>). In order for this to be
|
|
effective, the <tt>DROPEMAIL-CONFIRM</tt> handler must know which
|
|
commands have been sent by whom, so that clients cannot send
|
|
arbitrary <tt>DROPEMAIL-CONFIRM</tt> commands to get around the
|
|
confirmation check. This is accomplished through the file-local
|
|
<tt>dropemail_buffer[]</tt> array, which holds the most recently
|
|
issued, unconfirmed <tt>DROPEMAIL</tt> commands. (A single state
|
|
record stored in the <tt>User</tt> structure was another
|
|
possibility, but one that was discarded to avoid bloat in that
|
|
structure, particularly since the vast majority of clients would
|
|
never use the command anyway.) When a valid <tt>DROPEMAIL</tt>
|
|
command is given, the client is told the number of nicknames that
|
|
would be deleted, and the given mask is stored in the buffer array,
|
|
with the oldest unconfirmed mask removed if no slots are empty. A
|
|
<tt>DROPEMAIL-CONFIRM</tt> for the same mask will then locate the
|
|
appropriate buffer slot, ensure that the same client sent both
|
|
commands and that the elapsed time between the two commands is not
|
|
too long (as defined by the <tt>NSDropEmailExpire</tt> option), and
|
|
performs the actual nickname deletion.</dd>
|
|
|
|
<dt><tt><b>do_info()</b></tt></dt>
|
|
<dd>The <tt>INFO</tt> command has the ability to show extended
|
|
information about a nickname with the option <tt>ALL</tt> (only
|
|
available to the nickname owner or Services administrators).
|
|
However, not all nicknames have any additional information to be
|
|
displayed. To prevent the "use <tt>ALL</tt> for more information"
|
|
message from being appended if there is not actually anything else
|
|
to show, the <tt>INFO</tt> command handler uses a method inspired
|
|
by super-user privilege checks in the Linux kernel, which keeps
|
|
track of whether a process has taken advantage of those privileges.
|
|
When the macro <tt>CHECK_SHOW_ALL</tt> is included in a conditional
|
|
test, it will evaluate to true when the <tt>ALL</tt> option is
|
|
present (and the client has permission to use it), but a separate
|
|
flag variable, <tt>used_all</tt>, will also be set regardless of
|
|
the presence of <tt>ALL</tt>; the routine can then determine
|
|
whether there were any items that would have been displayed if
|
|
<tt>ALL</tt> was given. As noted in the source code comments, the
|
|
macro should be the last test in any conditional expression which
|
|
uses it, to prevent <tt>used_all</tt> from being set for an item
|
|
that will not actually be displayed due to a subsequent test.</dd>
|
|
|
|
<dt><tt><b>do_set()</b></tt>
|
|
<br/><tt><b>do_unset()</b></tt></dt>
|
|
<dd>As the <tt>SET</tt> and <tt>UNSET</tt> commands (<tt>SET</tt> in
|
|
particular) have a large number of options, they are defined in a
|
|
separate source file, <tt>set.c</tt>. See
|
|
<a href="#s3-1-3">section 7-3-1-3</a> for details.</dd>
|
|
</dl>
|
|
|
|
<p>NickServ also includes a debug command enabled by the
|
|
<tt>DEBUG_COMMANDS</tt> preprocessor symbol: <tt>LISTNICK</tt>, which
|
|
displays the <tt>NickInfo</tt> and (if present) <tt>NickGroupInfo</tt> data
|
|
for a given nickname.</p>
|
|
|
|
<p>In addition to the ordinary module setup code, the
|
|
<tt>nickserv/main</tt> module supports two command-line options. One,
|
|
<tt>-encrypt-all</tt>, is recognized by the Services core; NickServ's
|
|
<tt>init_module()</tt> routine checks the corresponding global flag,
|
|
<tt>encrypt_all</tt> and, if it is set, encrypts all nicknames using the
|
|
encryption type specified by the core's <tt>EncryptionType</tt> setting.
|
|
The other option, <tt>-clear-nick-email</tt>, is NickServ-specific, and is
|
|
handled by <tt>do_command_line()</tt>, a callback function for the core's
|
|
"<tt>command line</tt>" callback; when the option is encountered, the
|
|
callback function clears the E-mail address from all nickname groups.</p>
|
|
|
|
<p>As several messages used by NickServ can change based on configuration
|
|
options or the features available in the IRC server, the initialization and
|
|
cleanup code (as well as the reconfiguration handler,
|
|
<tt>do_reconfigure()</tt>) call <tt>mapstring()</tt> to adjust the messages
|
|
appropriately. The commands <tt>REGISTER</tt>,
|
|
<tt>DROPEMAIL</tt>/<tt>DROPEMAIL-CONFIRM</tt>, and <tt>GETPASS</tt> can
|
|
also be disabled by configuration options; the commands are disabled by
|
|
setting the <tt>name</tt> field of the corresponding <tt>Command</tt>
|
|
structure to the empty string, so that it will not be found when the
|
|
command table is searched. Pointers to the structures are saved in
|
|
file-local variables so that the names can be restored at reconfiguration
|
|
or module cleanup time.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s3-1-3">7-3-1-3. The <tt>SET</tt> and <tt>UNSET</tt> commands</h5>
|
|
|
|
<p>The handlers for the <tt>SET</tt> and <tt>UNSET</tt> commands,
|
|
<tt>do_set()</tt> and <tt>do_unset()</tt> in <tt>set.c</tt>, work much like
|
|
miniature versions of the top-level NickServ message handler
|
|
<tt>nickserv()</tt>, in that they check which option name was used with the
|
|
command and call an appropriate subroutine to do the actual work. However,
|
|
the <tt>do_set()</tt> routine parses the option parameters itself, rather
|
|
than leave such parsing to the individual routines (<tt>UNSET</tt> does not
|
|
take any additional parameters, so no such parsing is needed); for this
|
|
reason, the setup code in <tt>do_set()</tt> is more complex than that in
|
|
<tt>nickserv()</tt>, since the <tt>INFO</tt> option treats the entire line
|
|
as a single parameter, <tt>HIDE</tt> takes two single-word parameters
|
|
separated by a space, and the other options take a single one-word
|
|
parameter.</p>
|
|
|
|
<p>Additionally, both <tt>SET</tt> and <tt>UNSET</tt> can be used by
|
|
Services administrators to set options for other users' nicknames. For
|
|
this reason, the individual option-setting routines take both a
|
|
<tt>User *</tt> and a <tt>NickInfo *</tt> parameter, where the
|
|
<tt>NickInfo *</tt> parameter is the nickname whose options are to be
|
|
changed (if the client giving the command is not a Services administrator
|
|
or does not give a target nickname, this will simply be equal to the
|
|
<tt>ni</tt> field of the <tt>User</tt> structure). <i>Implementation note:
|
|
This raises an interesting problem—how does an option's handler
|
|
routine tell the difference between <tt>SET <i>option</i></tt>, <tt>SET
|
|
!MyNick <i>option</i></tt>, and <tt>SET !OtherNick <i>option</i></tt> when
|
|
sending result messages? The simple answer is that it doesn't: all option
|
|
handlers use the "your nick" message style, as mentioned in
|
|
<a href="../d.html">Appendix D of the user's manual</a>. If implemented,
|
|
it would probably be reasonable to ignore the distinction between the
|
|
<tt>SET <i>option</i></tt> and <tt>SET !MyNick <i>option</i></tt> cases,
|
|
and simply judge which message to use by comparing the <tt>NickInfo</tt>
|
|
parameter with the <tt>ni</tt> field of the client's <tt>User</tt>
|
|
structure.</i></p>
|
|
|
|
<p>The option handlers themselves are simple for the most part, checking
|
|
the option value given and setting or clearing the relevant flag or field
|
|
in the <tt>NickInfo</tt> structure or its associated <tt>NickGroupInfo</tt>
|
|
structure. Routines which deserve special mention are:</p>
|
|
|
|
<dl>
|
|
<dt><b><tt>do_set_password()</tt></b></dt>
|
|
<dd>The password setting itself is straightforward (note that the
|
|
memory containing the cleartext password and the temporary copy of
|
|
the encrypted password is cleared as soon as it is no longer
|
|
needed); however, the routine first checks the
|
|
<tt>NSSecureAdmins</tt> option, and disallows the change if the
|
|
target is a (different) Services administrator and the command
|
|
sender is not the Services super-user.</dd>
|
|
|
|
<dt><b><tt>do_set_email()</tt></b></dt>
|
|
<dd>This routine makes several checks mostly related to mail
|
|
authentication before allowing the E-mail address to be changed:
|
|
<ul>
|
|
<li>The address must be a valid E-mail address.</li>
|
|
<li>The address must not be rejected by a <tt>RejectEmail</tt>
|
|
configuration directive.</li>
|
|
<li>The address must not be in use by a nickname awaiting mail
|
|
authentication (as with <tt>REGISTER</tt>).</li>
|
|
<li>The number of nicknames currently using the address must be
|
|
less than <tt>NSRegEmailMax</tt>, if set. (Note that if
|
|
the current nickname's group contains other linked
|
|
nicknames, the E-mail address change can cause the nickname
|
|
total to exceed <tt>NSRegEmailMax</tt>. This is not seen as
|
|
a significant problem, and it avoids the opposite problem in
|
|
which a user who somehow exceeded the limit would no longer
|
|
be able to change their E-mail address at all.)</li>
|
|
<li>The time since the last successful <tt>SET EMAIL</tt> must be
|
|
at least <tt>NSSetEmailDelay</tt> seconds, if set.</li>
|
|
</ul>
|
|
If the above checks all pass, the change is performed, and if the
|
|
client used to have the <tt>NA_IDENT_NOMAIL</tt> status and an
|
|
E-mail address was set, the status is changed to
|
|
<tt>NA_IDENTIFIED</tt>. The routine also features its own
|
|
callback, "<tt>SET EMAIL</tt>", used by the mail authentication
|
|
code. Note that this routine does <i>not</i> check the
|
|
<tt>NSRequireEmail</tt> configuration option, and assumes that if
|
|
it is passed a <tt>NULL</tt> value, indicating that the address
|
|
should be unset, then that is valid. (In fact, <tt>do_unset()</tt>
|
|
checks <tt>NSRequireEmail</tt> before calling
|
|
<tt>do_set_email()</tt>.)</dd>
|
|
|
|
<dt><b><tt>do_set_timezone()</tt></b></dt>
|
|
<dd><tt>SET TIMEZONE</tt> allows the time zone to be specified as
|
|
either a literal time offset (-5, +6:30, etc.) or a time zone
|
|
name. Time zone names (other than "GMT+/-<i>n</i>" and
|
|
"UTC+/-<i>n</i>", which are treated as literal offsets) are parsed
|
|
using the <tt>timezones[]</tt> table defined just above the
|
|
<tt>do_set_timezone()</tt> routine itself, which (hopefully)
|
|
includes most common time zones; the table can of course be
|
|
modified to include other time zones as particular networks
|
|
desire. Once the the time zone has been set, a message is sent to
|
|
the calling client giving the current time in the resulting time
|
|
zone; however, this is tricky if the calling client is a Services
|
|
administrator changing the setting for another nickname, because
|
|
<tt>strftime_lang()</tt> always uses the time zone setting of the
|
|
nickname used to select the language. To get around this, the
|
|
routine determines the difference between the calling nickname's
|
|
time zone and the target nickname's time zone, adjusting the
|
|
timestamp passed to <tt>strftime_lang()</tt> by that amount
|
|
(multiplied by 60, since the <tt>timezone</tt> field is specified
|
|
in minutes). Incidentally, support for "daylight saving time"
|
|
as used in some countries was deliberately omitted, partly due to
|
|
the difficulty of supporting the various systems used in different
|
|
countries, and partly because the details of such systems are
|
|
highly dependent upon each country's political landscape and can
|
|
change at any time (witness the abrupt extension to DST proposed,
|
|
and eventually implemented, in the United States of America in
|
|
2006).</dd>
|
|
</dl>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s3-1-4">7-3-1-4. NickServ utility routines</h5>
|
|
|
|
<p>Most of the utility routines used by NickServ are collected in the file
|
|
<tt>util.c</tt>. This file has two functions: aside from providing utility
|
|
functions to NickServ itself (several of which are exported for use by
|
|
other modules), it can also be <tt>#include</tt>'d in an external source
|
|
file to provide definitions of the four routines <tt>new_nickinfo()</tt>,
|
|
<tt>free_nickinfo()</tt>, <tt>new_nickgroupinfo()</tt>, and
|
|
<tt>free_nickgroupinfo()</tt>, so that such files do not have to define
|
|
similar routines themselves. This latter mode is activated by defining
|
|
the <tt>STANDALONE_NICKSERV</tt> preprocessor symbol, as documented in the
|
|
comments at the top of <tt>util.c</tt>. In this case, only the four
|
|
routines mentioned above are defined, with the rest of the file commented
|
|
out using <tt>#ifndef</tt>; additionally, the <tt>new_nickgroupinfo()</tt>
|
|
routine does not check for the presence of the nickname group IDs it
|
|
generates, as it cannot assume that <tt>get_nickgroupinfo()</tt> is
|
|
available.</p>
|
|
|
|
<p>With respect to its primary use as part of NickServ, <tt>util.c</tt>
|
|
defines the following routines:</p>
|
|
|
|
<dl>
|
|
<dt><tt>NickInfo *<b>new_nickinfo</b>()</tt></dt>
|
|
<dd>Returns a pointer to a newly-allocated and initialized
|
|
<tt>NickInfo</tt> structure. (For creating a new record in the
|
|
database, <tt>makenick()</tt> is preferred; see below.)</dd>
|
|
|
|
<dt><tt>void <b>free_nickinfo</b>(NickInfo *<i>ni</i>)</tt></dt>
|
|
<dd>Frees the given <tt>NickInfo</tt> structure and all associated
|
|
data. (See <tt>delnick()</tt> below for removing a nickname
|
|
record from the database.)</dd>
|
|
|
|
<dt><tt>NickGroupInfo *<b>new_nickgroupinfo</b>(const char *<i>seed</i>)</tt></dt>
|
|
<dd>Returns a pointer to a newly-allocated and initialized
|
|
<tt>NickGroupInfo</tt> structure. If <tt><i>seed</i></tt> is not
|
|
<tt>NULL</tt>, then it is used to generate an initial ID value for
|
|
the nickname group; if that ID value is used, new values are
|
|
randomly generated until an unused one is found. (If the code
|
|
loops <tt>NEWNICKGROUP_TRIES</tt> times without finding an unused
|
|
value, an error is returned; assuming a good random number
|
|
generator, the default value of 1000 should ensure success on
|
|
typical databases. <tt>NEWNICKGROUP_TRIES</tt> is defined in
|
|
<tt>ns-local.h</tt>.) If <tt><i>seed</i></tt> is <tt>NULL</tt>,
|
|
then the new nickname group's ID is left at zero.</dd>
|
|
|
|
<dt><tt>void <b>free_nickgroupinfo</b>(NickGroupInfo *<i>ngi</i>)</tt></dt>
|
|
<dd>Frees the given <tt>NickGroupInfo</tt> structure and all associated
|
|
data. (See <tt>delgroup()</tt> below for removing a nickname group
|
|
and all its nicknames from the database.)</dd>
|
|
|
|
<dt><tt>NickGroupInfo *<b>_get_ngi</b>(NickInfo *<i>ni</i>,
|
|
const char *<i>file</i>, int <i>line</i>)</tt>
|
|
<br/><tt>NickGroupInfo *<b>_get_ngi_id</b>(uint32 <i>id</i>,
|
|
const char *<i>file</i>, int <i>line</i>)</tt></dt>
|
|
<dd>Implement the <tt>get_ngi()</tt> and <tt>get_ngi_id()</tt> macros,
|
|
respectively. <tt><i>file</i></tt> and <tt><i>line</i></tt> are
|
|
the source file and line from which the function was called, and
|
|
are filled in by the corresponding macro with <tt>__FILE__</tt> and
|
|
<tt>__LINE__</tt>.</dd>
|
|
|
|
<dt><tt>int <b>has_identified_nick</b>(const User *<i>u</i>, uint32 <i>group</i>)</tt></dt>
|
|
<dd>Returns whether the given client has identified for the nickname
|
|
group indicated by <tt><i>group</i></tt>.</dd>
|
|
|
|
<dt><tt>int <b>reglink_check</b>(User *<i>u</i>, const char *<i>nick</i>,
|
|
char *<i>password</i>, char *<i>email</i>)</tt></dt>
|
|
<dd>Calls the "<tt>REGISTER/LINK check</tt>" callback and returns its
|
|
result. (A utility function is used rather than directly calling
|
|
<tt>call_callback_4()</tt> because the <tt>nickserv/link</tt>
|
|
module needs to make use of the callback as well, and the module
|
|
system does not allow one module to call another's callbacks (which
|
|
would be bad design in any case).</dd>
|
|
|
|
<dt><tt>void <b>update_userinfo</b>(const User *<i>u</i>)</tt></dt>
|
|
<dd>Updates the user information for the client's nickname. The
|
|
<tt>NickInfo</tt> fields <tt>last_usermask</tt>,
|
|
<tt>last_realmask</tt>, and <tt>last_realname</tt> are set from
|
|
the corresponding fields of the <tt>User</tt> structure, and the
|
|
<tt>last_seen</tt> field is set to the current time.
|
|
<tt><i>u</i>->ni</tt> is assumed to be non-<tt>NULL</tt>.</dd>
|
|
|
|
<dt><tt>int <b>validate_user</b>(User *<i>u</i>)</tt></dt>
|
|
<dd>Sets the <tt>ni</tt> and <tt>ngi</tt> fields of the <tt>User</tt>
|
|
structure to point to the <tt>NickInfo</tt> and associated
|
|
<tt>NickGroupInfo</tt>, if any, for the client's nickname (if an
|
|
error occurs looking up the nickname group, <tt><i>u</i>->ni</tt>
|
|
is set to <tt>NULL</tt> and <tt><i>u</i>->ngi</tt> is set to
|
|
<tt>NICKGROUP_INVALID</tt>); then compares the client's information
|
|
with the nickname data and determines what level of access for the
|
|
nickname should be granted to the client. Returns 1 if the client
|
|
is granted either <tt>NA_IDENTIFIED</tt> or <tt>NA_RECOGNIZED</tt>
|
|
access, otherwise zero.
|
|
|
|
<p>This routine, along with the <tt>REGISTER</tt> command handler,
|
|
is one of the two "points of entry" into Services, and as such is a
|
|
critical point for Services security. This is particularly
|
|
relevant for the section of code conditionally granting full
|
|
(<tt>NA_IDENTIFIED</tt>) nickname access; while
|
|
<tt>has_identified_nick()</tt>, mentioned above, operates purely on
|
|
data which has been seen since Services started (specifically, the
|
|
list of nicknames the client is known to have become identified
|
|
for, maintained by <tt>set_identified()</tt>) and is comparatively
|
|
safe, the second check, which matches the servicestamp, username,
|
|
and hostname of the last client to identify with those of the
|
|
current client, needs special attention to ensure that it does not
|
|
allow clients to gain improper access. As noted in the comments in
|
|
that section of code, the servicestamp provides a fairly high level
|
|
of protection on servers which support it natively, while that
|
|
level is reduced for servers which do not (such servers are rare
|
|
nowadays). The entire section of code can be disabled with the
|
|
<tt>NoSplitRecovery</tt> configuration option for added security.</p>
|
|
|
|
<p>If the client is determined not to have identified for the
|
|
nickname previously, the routine continues, determining whether to
|
|
give <tt>NA_RECOGNIZED</tt> access. "Recognized" status is only
|
|
implemented by access lists (see <a href="#s3-2">section 7-3-2</a>),
|
|
and if the corresponding module is not loaded, the
|
|
<tt>NA_RECOGNIZED</tt> flag will never be set on any nickname,
|
|
except when set along with <tt>NA_IDENTIFIED</tt>. Likewise, if a
|
|
nickname's <tt>NF_SECURE</tt> flag is set, then
|
|
<tt>NA_RECOGNIZED</tt> will not be set (and zero will be returned
|
|
from the routine) even if the client is in fact recognized.</p>
|
|
|
|
<p>If the client is not identified or recognized for the nickname,
|
|
<tt>validate_user()</tt> checks whether the client should be
|
|
killed or nick-changed, setting an appropriate timeout or calling
|
|
the collide routines (see <a href="#s3-1-5">section 7-3-1-5</a>
|
|
below) depending on the nickname group settings. However, if the
|
|
client was recognized (which will only be true if the nickname has
|
|
the <tt>SECURE</tt> option set and thus <tt>NA_RECOGNIZED</tt> was
|
|
not set), the kill checks are not performed, allowing the client to
|
|
identify at its leisure.</p>
|
|
|
|
<p>Finally, the routine checks the nickname's expiration time, and
|
|
if it is due to expire "soon" (as defined by the
|
|
<tt>NSExpireWarning</tt> configuration option), a warning notice is
|
|
sent to the client.</p></dd>
|
|
|
|
<dt><tt>void <b>cancel_user</b>(User *<i>u</i>)</tt></dt>
|
|
<dd>Updates a client's nickname data when the client stops using the
|
|
nickname. The <tt>last_seen</tt> field is updated if the client
|
|
was either identified or recognized; the <tt>authstat</tt> field
|
|
is cleared along with temporary status flags in the <tt>status</tt>
|
|
field; an enforcer is introduced if the client was killed or
|
|
nick-changed (see <a href="#s3-1-5">section 7-3-1-5</a>); the
|
|
<tt>cancel user</tt>" callback is called; and any active nick
|
|
collide timeouts are removed. The <tt>ni</tt> and <tt>ngi</tt>
|
|
fields of the client's <tt>User</tt> structure are also reset to
|
|
<tt>NULL</tt>.</dd>
|
|
|
|
<dt><tt>void <b>set_identified</b>(User *<i>u</i>)</tt></dt>
|
|
<dd>Marks the given client as having identified for the nickname it is
|
|
currently using. In addition to setting the authentication status
|
|
to <tt>NA_IDENTIFIED | NA_RECOGNIZED</tt> and updating the
|
|
nickname's <tt>id_stamp</tt> field, the routine adds the nickname
|
|
group ID to the list of nickname groups for which the client has
|
|
identified, stored in the <tt>User</tt> structure and checked by
|
|
<tt>has_identified_nick()</tt>.</dd>
|
|
|
|
<dt><tt>NickInfo *<b>makenick</b>(const char *<i>nick</i>,
|
|
NickGroupInfo **<i>nickgroup_ret</i>)</tt></dt>
|
|
<dd>Creates a new <tt>NickInfo</tt> record with the given nickname,
|
|
adds it to the database, and returns a pointer to the new record.
|
|
If <tt><i>nickgroup_ret</i></tt> is not <tt>NULL</tt>, then a new
|
|
<tt>NickGroupInfo</tt> record is also created for the nickname,
|
|
the nickname's <tt>nickgroup</tt> field is set accordingly, and a
|
|
pointer to the <tt>NickGroupInfo</tt> record (which is also added
|
|
to the database) is stored in the variable pointed to by
|
|
<tt><i>nickgroup_ret</i></tt>. Returns <tt>NULL</tt> on error.</dd>
|
|
|
|
<dt><tt>int <b>delnick</b>(NickInfo *<i>ni</i>)</tt></dt>
|
|
<dd>Removes the given nickname from the database and frees all
|
|
resources used by the <tt>NickInfo</tt> structure. If the nickname
|
|
was the last of its group, then the nickname group is deleted as
|
|
well. Returns nonzero on success, zero on error.</dd>
|
|
|
|
<dt><tt>int <b>delgroup</b>(NickGroupInfo *<i>ngi</i>)</tt></dt>
|
|
<dd>Removes the given nickname group from the database, along with all
|
|
associated nicknames. Returns nonzero on success, zero on error.</dd>
|
|
|
|
<dt><tt>int <b>drop_nickgroup</b>(NickGroupInfo *<i>ngi</i>,
|
|
const User *<i>u</i>, const char *<i>dropemail</i>)</tt></dt>
|
|
<dd>Removes the given nickname group from the database, like
|
|
<tt>delgroup()</tt>, but first log information about the nicknames
|
|
to be deleted. <tt><i>u</i></tt> is the <tt>User</tt> structure
|
|
for the client that sent the command resulting in the deletion.
|
|
<tt><i>dropemail</i></tt> should be:
|
|
<ul>
|
|
<li><tt>NULL</tt> if the call is because of a <tt>DROP</tt> command
|
|
from the nickname owner;</li>
|
|
<li><tt>PTR_INVALID</tt> if the call is because of a <tt>DROPNICK</tt>
|
|
command from a Services administrator;</li>
|
|
<li>for a <tt>DROPEMAIL</tt> command, the parameter (address
|
|
wildcard) used with the command.</li>
|
|
</ul></dd>
|
|
|
|
<dt><tt>void <b>suspend_nick</b>(NickGroupInfo *<i>ngi</i>,
|
|
const char *<i>reason</i>, const char *<i>who</i>,
|
|
const time_t <i>expires</i>)</tt></dt>
|
|
<dd>Suspends the given nickname group, copying the parameters
|
|
<tt><i>reason</i></tt>, <tt><i>who</i></tt>, and
|
|
<tt><i>expires</i></tt> into the suspension data fields. (If
|
|
<tt><i>expires</i></tt> is zero, then the suspension will not
|
|
expire.)</dd>
|
|
|
|
<dt><tt>void <b>unsuspend_nick</b>(NickGroupInfo *<i>ngi</i>, int <i>set_time</i>)</tt></dt>
|
|
<dd>Cancels the suspension on the given nickname group. If
|
|
<tt><i>set_time</i></tt> is nonzero, the last-seen time of each
|
|
nickname in the group will be updated according to
|
|
<tt>NSSuspendGrace</tt> to prevent the nickname from expiring for
|
|
that length of time (if <tt>NSSuspendGrace</tt> or <tt>NSExpire</tt>
|
|
are not set, or if the nickname already has enough time before
|
|
expiration, the last-seen time will not be changed).</dd>
|
|
|
|
<dt><tt>int <b>nick_check_password</b>(User *<i>u</i>, NickInfo *<i>ni</i>,
|
|
const char *<i>password</i>, const char *<i>command</i>,
|
|
int <i>failure_msg</i>)</tt></dt>
|
|
<dd>Performs a password check for a nickname as part of a NickServ
|
|
command. If the password is incorrect or an error occurs when
|
|
checking, a notice will be sent to the client; a <tt>WALLOPS</tt>
|
|
will also be sent for repeated bad password attempts on the same
|
|
nickname. <tt><i>u</i></tt> is the <tt>User</tt> structure for
|
|
the client that issued the command; <tt><i>ni</i></tt> is the
|
|
<tt>NickInfo</tt> structure for the nickname whose password is
|
|
being checked; <tt><i>password</i></tt> is the password given by
|
|
the client, <tt><i>command</i></tt> is the name of the command
|
|
being executed; and <tt><i>failure_msg</i></tt> is the index of the
|
|
message (language string) to be sent if an error occurs when
|
|
checking the password.</dd>
|
|
|
|
<dt><tt>int <b>count_nicks_with_email</b>(const char *<i>email</i>)</tt></dt>
|
|
<dd>Counts and returns the number of registered nicknames with the
|
|
given E-mail address. If a nickname has the given address but it
|
|
is awaiting mail authentication, the value returned is negative;
|
|
for example, if there are five nicknames using a given address but
|
|
the address is not authenticated, -5 would be returned. Note that
|
|
this function must scan through the entire nickname database, so
|
|
care should be taken not to call it too frequently.</dd>
|
|
</dl>
|
|
|
|
<p><tt>util.c</tt> also defines initialization and cleanup routines,
|
|
<tt>init_util()</tt> and <tt>exit_util()</tt>, which take care of
|
|
registering and unregistering the callbacks used by various utility
|
|
functions. The routines are called as part of the <tt>nickserv/main</tt>
|
|
module iniitialization and cleanup.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s3-1-5">7-3-1-5. Nickname colliding</h5>
|
|
|
|
<p>In the general sense, a "nickname collision" is what happens when a
|
|
client on an IRC network attempts to use a nickname that is already in use
|
|
by another client. The original (RFC 1459) solution to this was to kill
|
|
both clients, but with the advent of timestamps, most modern servers only
|
|
kill one or the other depending on the timestamps of the two colliding
|
|
clients. Early versions of Services took advantage of this behavior to
|
|
implement kill protection: by introducing an "enforcer" pseudoclient with
|
|
an appropriate timestamp, the old client would be killed and would not be
|
|
able to reconnect with the same nickname.</p>
|
|
|
|
<p>With respect to Services, then, "nickname colliding" is the act of
|
|
forcing a client to stop using a particular nickname. While the nickname
|
|
collision method itself has been abandoned, both to avoid depending on
|
|
particular collision semantics and to provide a more meaningful disconnect
|
|
message to the client ("Nick kill enforced" rather than an arbitrary server
|
|
collision message), and although many modern IRC servers allow Services to
|
|
forcibly change a client's nickname without going as far as disconnecting
|
|
the client altogether, the term "colliding" is still used to refer to this
|
|
set of actions.</p>
|
|
|
|
<p>Nickname colliding functionality is provided by the file
|
|
<tt>collide.c</tt>. This file provides two methods of colliding nicknames:
|
|
directly, or via timeouts. The nickname colliding code also has its own
|
|
initialization and cleanup functions (<tt>init_collide()</tt> and
|
|
<tt>exit_collide()</tt>), which are called from the <tt>nickserv/main</tt>
|
|
module initialization and cleanup routines.</p>
|
|
|
|
<p>The following routines are used when colliding nicknames directly:</p>
|
|
|
|
<dl>
|
|
<dt><tt>void <b>collide_nick</b>(NickInfo *<i>ni</i>, int <i>from_timeout</i>)</tt></dt>
|
|
<dd>Collides the given nickname, either killing the client using the
|
|
nickname or forcibly changing the client's nickname to a "guest"
|
|
nickname depending on configuration settings.
|
|
<tt><i>from_timeout</i></tt> is used internally with collide
|
|
timeouts, and should always be zero when called externally. This
|
|
routine automatically calls <tt>introduce_enforcer()</tt> after
|
|
the client has been killed or nick-changed (in the case of a
|
|
forced nickname change, the enforcer is introduced by
|
|
<tt>cancel_user()</tt> upon receipt of the <tt>NICK</tt> message
|
|
indicating that the client's nickname has been changed). Any
|
|
pending nickname collide or "433" timeouts (see below) on the
|
|
nickname are cancelled by thie routine.</dd>
|
|
|
|
<dt><tt>void <b>introduce_enforcer</b>(NickInfo *<i>ni</i>)</tt></dt>
|
|
<dd>Introduces an enforcer pseudoclient for the given nickname, to
|
|
prevent other clients from using the nickname. This routine
|
|
automatically adds a timeout to call <tt>release_nick()</tt> after
|
|
<tt>NSReleaseTimeout</tt> seconds have passed.</dd>
|
|
|
|
<dt><tt>void <b>release_nick</b>(NickInfo *<i>ni</i>, int <i>from_timeout</i>)</tt></dt>
|
|
<dd>Removes the enforcer pseudoclient for the given nickname, allowing
|
|
other clients to use it again. As with <tt>collide_nick()</tt>,
|
|
<tt><i>from_timeout</i></tt> is used internally with collide
|
|
timeouts, and should always be zero when called externally. Any
|
|
pending release timeouts on the nickname are cancelled by this
|
|
routine.</dd>
|
|
</dl>
|
|
|
|
<p>Callers can also establish timeouts to collide or release a nick after
|
|
a certain time. To avoid each caller having to include its own timeout
|
|
handlers, <tt>collide.c</tt> provides two wrapper routines around the
|
|
generic timeout functions:</p>
|
|
|
|
<dl>
|
|
<dt><tt>void <b>add_ns_timeout</b>(NickInfo *<i>ni</i>, int <i>type</i>,
|
|
time_t <i>delay</i>)</tt></dt>
|
|
<dd>Adds a timeout of the given type on the given nickname to occur in
|
|
<tt><i>delay</i></tt> seconds. <tt><i>type</i></tt> can be any of
|
|
the following:
|
|
<ul>
|
|
<li><b><tt>TO_COLLIDE</tt>:</b> A timeout for colliding a nickname
|
|
(<tt>collide_nick()</tt> will be called).</li>
|
|
<li><b><tt>TO_RELEASE</tt>:</b> A timeout for releasing the hold on
|
|
a nickname (<tt>release_nick()</tt> will be called).</li>
|
|
<li><b><tt>TO_SEND_433</tt>:</b> A timeout for sending the client
|
|
using the nickname a "433" error message. (433 is the code
|
|
for the <tt>ERR_NICKCOLLISION</tt> reply to a <tt>NICK</tt>
|
|
message, and will cause most interactive client software to
|
|
request a new nickname from the user. However, some server
|
|
software has been known to disallow servers from sending
|
|
433 replies to remote clients.)</li>
|
|
</ul></dd>
|
|
|
|
<dt><tt>void <b>rem_ns_timeout</b>(NickInfo *<i>ni</i>, int <i>type</i>,
|
|
int <i>del_to</i>)</tt></dt>
|
|
<dd>Removes any timeout of the given type on the given nickname;
|
|
<tt><i>ni</i></tt> can be <tt>NULL</tt> to cause timeouts on all
|
|
nicknames (of the given type) to be removed. <tt><i>type</i></tt>
|
|
can be any of the type values used with <tt>add_ns_timeout()</tt>,
|
|
or -1 to remove timeouts of all types. <tt><i>del_to</i></tt>
|
|
should always be nonzero when called externally (the parameter is
|
|
used for calling from within a timeout function, where it is not
|
|
necessary to delete the <tt>Timeout</tt> structure as well).</dd>
|
|
</dl>
|
|
|
|
<p>Each of the three timeout types has its own timeout function:
|
|
<tt>timeout_collide()</tt>, <tt>timeout_release()</tt>, and
|
|
<tt>timeout_send_433()</tt>. The timeout functions check for any change
|
|
in status (such as identification for the nickname) before performing
|
|
their respective functions.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s3-2">7-3-2. Nickname access lists</h4>
|
|
|
|
<p>Nickname access lists are managed by the <tt>nickserv/access</tt>
|
|
module, defined in <tt>access.c</tt>. The module is quite simple,
|
|
consisting of a database table, two callback functions, and a NickServ
|
|
command (<tt>ACCESS</tt>). For simplicity, the module (along with other
|
|
NickServ submodules) assumes the presence of the <tt>nickserv/main</tt>
|
|
module rather than explicitly importing every required NickServ symbol,
|
|
although the module's initialization does look up the
|
|
<tt>nickserv/main</tt> module handle for use in adding the requisite
|
|
callbacks and the <tt>ACCESS</tt> command.</p>
|
|
|
|
<p>One unusual feature is the use of a static buffer for reading and
|
|
writing database records. Since the access list itself is stored in the
|
|
corresponding nickname group's <tt>NickGroupInfo</tt> structure as an
|
|
array, the entries must be extracted and made available to the database
|
|
subsystem in an independent (normalized) format. This is done by using a
|
|
<tt>{<i>nickgroup-ID</i>,<i>access-mask</i>}</tt> record format, and
|
|
providing a single record buffer. When loading data from persistent
|
|
storage, the table's <tt>newrec()</tt> function returns a pointer to this
|
|
buffer, taking advantage of the fact that only one record is loaded at a
|
|
time (see <a href="6.html#s2-1">section 6-2-1</a>); the <tt>insert()</tt>
|
|
routine then looks up the nickname group stored in the buffer and appends
|
|
the given access mask to that nickname group's access list, while the
|
|
<tt>freerec()</tt> routine frees the access mask string. When saving data,
|
|
the <tt>first()</tt> and <tt>next()</tt> routines call
|
|
<tt>first_nickgroupinfo()</tt> and <tt>next_nickgroupinfo()</tt> in turn,
|
|
looping through each access mask of each nickname group.</p>
|
|
|
|
<p>Ideally, the access lists would be stored in memory in the same fashion,
|
|
as a distinct table using the nickname group ID as a key. However, since
|
|
the current database implementation does not provide an efficient way to
|
|
look up records matching arbitrary criteria (like a <tt>SELECT</tt>
|
|
statement in SQL), and since problems would ensue when trying to save the
|
|
data using the (deprecated, but still available) <tt>database/version4</tt>
|
|
module, the in-memory data structures were left as is. See the comments in
|
|
the <tt>autojoin.c</tt> source file for a more complete description.</p>
|
|
|
|
<p>Other than this, there is little of note in the module; the
|
|
<tt>do_access()</tt> command handler simply adds or removes the requested
|
|
mask to or from the access list, and the callback functions take care of
|
|
setting an initial access mask on registration and determining whether the
|
|
user should be treated as recognized by <tt>validate_user()</tt>.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s3-3">7-3-3. Nickname auto-join lists</h4>
|
|
|
|
<p>Nickname auto-join lists are managed by the <tt>nickserv/autojoin</tt>
|
|
module, defined in <tt>autojoin.c</tt>. Aside from the details of its
|
|
operation, this module is nearly identical to the <tt>nickserv/access</tt>
|
|
module, including the hack used for database loading and saving; see the
|
|
discussion of that module above (<a href="#s3-2">section 7-3-2</a>) for
|
|
details.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s3-4">7-3-4. Linking and nickname groups</h4>
|
|
|
|
<p>The distinction between "nicknames" and "nickname groups" has been made
|
|
several times above. Ordinarily, this is only of importance as far as
|
|
which structure is accessed (<tt>NickInfo</tt> or <tt>NickGroupInfo</tt>);
|
|
however, the <tt>nickserv/link</tt> module, defined in <tt>link.c</tt>,
|
|
allows nicknames to be linked together by assigning the same nickname group
|
|
to both nicknames. This results in all information in the
|
|
<tt>NickGroupInfo</tt> structure being shared among all linked nicknames,
|
|
with only the data in the <tt>NickInfo</tt> structure kept separately for
|
|
each nickname.</p>
|
|
|
|
<p>The <tt>nickserv/link</tt> includes three commands: <tt>LINK</tt>,
|
|
<tt>UNLINK</tt>, and <tt>LISTLINKS</tt>, as well as one additional
|
|
<tt>SET</tt> option, <tt>SET MAINNICK</tt> (linked in through NickServ's
|
|
"<tt>SET</tt>" callback). Of these, <tt>LISTLINKS</tt> and <tt>SET
|
|
MAINNICK</tt> are straightforward: <tt>do_listlinks()</tt> simply echoes
|
|
the contents of the <tt>nicks[]</tt> array for the calling client's
|
|
nickname group (or the specified nickname's group for Services
|
|
operators), and <tt>do_set_mainnick()</tt> modifies the nickname
|
|
group's <tt>mainnick</tt> field based on the given nickname.</p>
|
|
|
|
<p>When the <tt>LINK</tt> command is given to link a new nickname to the
|
|
caller's nickname group, <tt>do_link()</tt> first ensures that the new
|
|
nickname is not already in use and that creating the link would not cause
|
|
the caller's total number of nicknames to exceed the <tt>NSRegEmailMax</tt>
|
|
limit, if set. (Note that <tt>LINK</tt> does <i>not</i> abort if the mail
|
|
address is not authenticated, simply checking the absolute value of the
|
|
return from <tt>count_nicks_with_email()</tt> against the limit; creating
|
|
the link does not in itself grant any additional privileges to the user,
|
|
and can at most be used to "hide" from other users while maintaining
|
|
current privileges.) If these checks pass, a new <tt>NickInfo</tt>
|
|
structure is created, passing <tt>NULL</tt> as the
|
|
<tt><i>nickgroup_ret</i></tt> parameter to <tt>makenick()</tt> to indicate
|
|
that a new nickname group is not required; updates the <tt>NickInfo</tt>
|
|
structure with the calling user's data; stores the nickname group ID in the
|
|
new nickname's <tt>nickgroup</tt> field; and appends the new nickname to
|
|
the nickname group's <tt>nicks[]</tt> array.</p>
|
|
|
|
<p><tt>UNLINK</tt> acts similarly to the Services administrator command
|
|
<tt>DROPNICK</tt> for a single nickname in a group. However, since
|
|
<tt>UNLINK</tt> is not limited to Services administrators, care must be
|
|
taken that an unprivileged client is not allowed to delete nicknames from
|
|
other nickname groups; this check is made by ensuring that (1) the target
|
|
nickname has a valid associated <tt>NickGroupInfo</tt> structure and (2)
|
|
the nickname group IDs of the two nickname groups are equal, and
|
|
disallowing the command otherwise if the <tt>FORCE</tt> option is not
|
|
given. (The check is made on the <tt>FORCE</tt> option, disallowed for
|
|
unprivileged clients, rather than on the client's privilege level in order
|
|
to prevent accidental deletion of others' nicknames by Services
|
|
administrators.) If the command is allowed, the routine then deletes the
|
|
nickname by calling <tt>delnick()</tt>; if the nickname group's main
|
|
nickname is the one being unlinked, <tt>delnick()</tt> automatically
|
|
adjusts the <tt>mainnick</tt> field to the next nickname in the
|
|
<tt>nicks[]</tt> array (or the previous nickname, if the deleted nickname
|
|
was the last one in the array).</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s3-5">7-3-5. E-mail address authentication</h4>
|
|
|
|
<p>While it has long been possible to associate an E-mail address with a
|
|
registered nickname, there was traditionally no way to ensure that the
|
|
address given was in fact a valid one belonging to the nickname owner.
|
|
Since Services 5.0, such functionality has been provided by the
|
|
<tt>nickserv/mail-auth</tt> module, defined in <tt>mail-auth.c</tt>. As
|
|
described in the user's manual, E-mail address authentication works by
|
|
assigning a random "authentication code" to the nickname, then sending an
|
|
E-mail message to the registered address containing that code; the owner is
|
|
not allowed to identify to the nickname until the code has been entered,
|
|
ensuring that the address is one which the owner has (or at least had, at
|
|
the time the message was sent) access to.</p>
|
|
|
|
<p>Internally, this processing is managed with the <tt>authcode</tt>,
|
|
<tt>authset</tt>, and <tt>authreason</tt> fields of the
|
|
<tt>NickGroupInfo</tt> structure. When an event occurs requiring E-mail
|
|
address authentication, the module generates a random 9-digit numeric code
|
|
(100000000 through 999999999 inclusive—codes with leading zeroes are
|
|
not used in order to avoid confusion), stores the code in the nickname
|
|
group's <tt>authcode</tt> field, and calls the mail subsystem's
|
|
<tt>sendmail()</tt> routine to send a message to the nickname group's
|
|
registered E-mail address (see <a href="8.html#s3">section 8-3</a> for
|
|
information about how mail is sent). Additionally, the current timestamp
|
|
is stored in the <tt>authset</tt> field, and the reason for setting the
|
|
code (one of the <tt>NICKAUTH_*</tt> constants) is stored in the
|
|
<tt>authreason</tt> field. (In early versions of the module, the reason
|
|
was stored in two bits of the authentication code; this method was later
|
|
rejected, however, as being too kludgey and inflexible as well as leaking
|
|
information.) The presence of a nonzero value in the <tt>authcode</tt>
|
|
field indicates that the nickname group is awaiting authentication, and a
|
|
nickname identification callback function ensures that clients are not
|
|
allowed to identify for such nicknames (nor does <tt>validate_user()</tt>
|
|
allow automatic identification if an authentication code is present), thus
|
|
effectively preventing their use. By issuing an AUTH command with the
|
|
correct code, a nickname owner can clear the nickname group's
|
|
<tt>authcode</tt> field, allowing identification to the nickname(s) once
|
|
more.</p>
|
|
|
|
<p>The module begins with several utility functions:</p>
|
|
|
|
<dl>
|
|
<dt><tt>void <b>make_auth</b>(NickGroupInfo *<i>ngi</i>, int16 <i>reason</i>)</tt></dt>
|
|
<dd>Generates a random authentication code, and sets the nickname
|
|
group's <tt>authcode</tt>, <tt>authset</tt>, and <tt>authreason</tt>
|
|
fields appropriately. The <tt><i>reason</i></tt> parameter is
|
|
copied directly into the <tt>authreason</tt> field, without checks
|
|
on its value.</dd>
|
|
|
|
<dt><tt>void <b>clear_auth</b>(NickGroupInfo *<i>ngi</i>)</tt></dt>
|
|
<dd>Clears the given nickname group's authentication code (if any), as
|
|
well as all related nickname group data fields (including the
|
|
previous E-mail address).</dd>
|
|
|
|
<dt><tt>int <b>send_auth</b>(User *<i>u</i>, NickGroupInfo *<i>ngi</i>,
|
|
const char *<i>nick</i>, int <i>what</i>)</tt></dt>
|
|
<dd>Sends an E-mail to the given nickname group's owner containing the
|
|
nickname's current authentication code. <tt><i>u</i></tt> is the
|
|
<tt>User</tt> structure for the client whose command caused the
|
|
message to be sent; <tt><i>nick</i></tt> is the specific nickname
|
|
for which the command was issued; and <tt><i>what</i></tt> is
|
|
one of the <tt>IS_*</tt> constants defined for the routine
|
|
(internally a language string index for the mail body or -1 for the
|
|
special case of <tt>SETAUTH</tt>). The routine itself is defined
|
|
as <tt>send_auth_</tt>, taking a <tt><i>line</i></tt> parameter
|
|
indicating where in the source the routine was called from; this is
|
|
filled in automatically by the <tt>send_auth()</tt> macro. In
|
|
order to inform the calling client of the success or failure of
|
|
sending the message, <tt>send_auth()</tt> creates a
|
|
<tt>sendauth_data</tt> structure for each message sent, used in the
|
|
two routines listed below.</dd>
|
|
|
|
<dt><tt>void <b>send_auth_callback</b>(int <i>status</i>, void *<i>data</i>)</tt></dt>
|
|
<dd>The callback function used for <tt>sendmail()</tt> when called from
|
|
<tt>send_auth()</tt>. This routine uses the <tt>sendauth_data</tt>
|
|
structure for the sent message (passed as the <tt><i>data</i></tt>
|
|
parameter) to send a reply to the client that issued the original
|
|
command, and also to clear the nickname group's
|
|
<tt>last_sendauth</tt> field if the command used was
|
|
<tt>SENDAUTH</tt> and the message could not be sent. The structure
|
|
is then removed from the global list and freed.</dd>
|
|
|
|
<dt><tt>int <b>sendauth_userdel</b>(User *<i>user</i>,
|
|
const char *<i>reason</i>, int <i>is_kill</i>)</tt></dt>
|
|
<dd>Used as a callback function for the core's "<tt>user delete</tt>"
|
|
callback. Iterates through the <tt>sendauth_data</tt> list,
|
|
clearing the <tt>User</tt> pointer of any entries for the user
|
|
being removed; this causes <tt>send_auth_callback()</tt> to skip
|
|
sending a reply when the mail sending completes.</dd>
|
|
</dl>
|
|
|
|
<p>These routines are followed by the command handlers for the commands
|
|
supported by the module: <tt>AUTH</tt>, <tt>SENDAUTH</tt>, <tt>REAUTH</tt>,
|
|
<tt>RESTOREMAIL</tt>, and the Services administrator commands
|
|
<tt>SETAUTH</tt>, <tt>GETAUTH</tt>, and <tt>CLEARAUTH</tt>. Of these, the
|
|
only fairly complex one is the <tt>AUTH</tt> handler, <tt>do_auth()</tt>,
|
|
as it must watch for attempts to guess the authentication code. (Invalid
|
|
<tt>AUTH</tt> commands are treated the same as bad passwords to
|
|
<tt>IDENTIFY</tt> or other commands, by calling <tt>bad_password()</tt> and
|
|
incrementing the nickname group's <tt>bad_auths</tt> field, which itself
|
|
can generate a warning via <tt>WALLOPS</tt>.)</p>
|
|
|
|
<p>Finally, the module includes four callback functions. Two of these,
|
|
<tt>do_registered()</tt> and <tt>do_set_email()</tt>, hook into the
|
|
NickServ callbacks for the <tt>REGISTER</tt> and <tt>SET EMAIL</tt>
|
|
commands, respectively, dropping the client's identified status and
|
|
generating and sending an authentication code. (Changes to the E-mail
|
|
address made by Services administrators are not subject to authentication,
|
|
however.) These are followed by the <tt>IDENTIFY</tt> command callback
|
|
function <tt>do_identify()</tt>, which disallows <tt>IDENTIFY</tt> for
|
|
nicknames with pending authentication codes, and the expiration check
|
|
callback function <tt>do_check_expire()</tt>, which drops a
|
|
newly-registered nickname after <tt>NSNoAuthExpire</tt> seconds (if set) if
|
|
not authenticated, and also clears any pending <tt>REAUTH</tt> after the
|
|
same amount of time (the user can subsequently use a second <tt>REAUTH</tt>
|
|
if necessary).</p>
|
|
|
|
<p>Since the ability to send E-mail is essential for this module to work,
|
|
the <tt>init_module()</tt> function checks for the presence of the
|
|
<tt>mail/main</tt> module, refusing to load if it is not available.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<h3 class="subsection-title" id="s4">7-4. ChanServ</h3>
|
|
|
|
<p>ChanServ is the most complex of the standard Services pseudoclients,
|
|
owing to the variety of operations that can be performed on channels.
|
|
There is no easy way to split those operations up into separate modules
|
|
(except by individual command, an avenue which has not been pursued), and
|
|
as a result, the core ChanServ module is itself the largest module in
|
|
Services.</p>
|
|
|
|
<p>As with NickServ, the core ChanServ module, <tt>chanserv/main</tt>, is
|
|
split up over several source files. These are discussed in
|
|
<a href="#s4-1">section 7-4-1</a> and its subsections, with the exception
|
|
of the <tt>access.c</tt> source file, which is discussed in
|
|
<a href="#s4-2">section 7-4-2</a> along with the access list manipulation
|
|
submodules, <tt>chanserv/access-levels</tt> and
|
|
<tt>chanserv/access-xop</tt>.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s4-1">7-4-1. ChanServ core functionality</h4>
|
|
|
|
<p>The core ChanServ module, <tt>chanserv/main</tt>, is built from several
|
|
source files, along much the same lines as the core NickServ module:
|
|
<tt>main.c</tt>, containing the central module code and most command
|
|
handlers; <tt>access.c</tt>, for handling channel access lists;
|
|
<tt>check.c</tt>, for checking the status of a channel against registered
|
|
data and making appropriate changes; <tt>set.c</tt>, implementing the
|
|
<tt>SET</tt> command; and <tt>util.c</tt>, defining various ChanServ
|
|
utility functions.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-1-1">7-4-1-1. Channel data structures</h5>
|
|
|
|
<p>As with NickServ, ChanServ splits its declarations into two header
|
|
files: <tt>chanserv.h</tt>, containing structures and routine declarations
|
|
intended to be exported to other modules, and <tt>cs-local.h</tt>, for
|
|
internal use by ChanServ only. (There is also a separate header file,
|
|
<tt>access.h</tt>, specifically for channel access list definitions; this
|
|
is discussed in <a href="#s4-2-1">section 7-4-2-1</a>.)</p>
|
|
|
|
<p>The channel data structure, <tt>ChannelInfo</tt>, is naturally exported.
|
|
As ChanServ does not have the concept of "links" or "channel groups" that
|
|
NickServ uses with nicknames, all data for a channel is stored in this
|
|
single structure. <tt>ChannelInfo</tt> does, however, include three
|
|
substructures, defined before it in <tt>chanserv.h</tt>:</p>
|
|
|
|
<dl>
|
|
<dt><tt>ChanAccess</tt></dt>
|
|
<dd>Contains the data for an entry on a channel's access list. Access
|
|
list entries are stored by nickname group ID, rather than by
|
|
nickname, both to optimize checking an access list for a particular
|
|
client and to eliminate the possibility of two or more entries
|
|
matching a single client. (This is why only registered nicknames
|
|
are allowed on channel access lists, and why channel access list
|
|
entries are always displayed using the nickname group's main
|
|
nickname, regardless of the nickname actually added to the list.)
|
|
Rather than resizing the array every time a change is made to the
|
|
list, deleted entries are left in memory with a nickname group ID
|
|
of zero, and subsequent adds reuse these entries before attempting
|
|
to expand the array. The <tt>channel</tt> field is used to link
|
|
the record with its associated channel when loading and saving
|
|
data. There are also a number of access-level related constants
|
|
declared below this structure, such as the maximum and minimum
|
|
access levels and the equivalent access levels for the <tt>XOP</tt>
|
|
commands.</dd>
|
|
|
|
<dt><tt>AutoKick</tt></dt>
|
|
<dd>Contains the data for an entry on a channel's autokick list. As
|
|
with access list entries, deleted entries are left in the list
|
|
rather than resizing the array (a <tt>NULL</tt> value for the mask
|
|
string indicates an unused entry), and the <tt>channel</tt> field
|
|
is used when loading and saving data to link the record with its
|
|
associated channel.</dd>
|
|
|
|
<dt><tt>ModeLock</tt></dt>
|
|
<dd>Contains a channel's mode lock data, including the set of modes
|
|
locked on and off along with parameters for modes that require
|
|
them; with the exception of the presence of two mode sets rather
|
|
than one (modes can be locked on, locked off, or neither), the
|
|
structure's contents are the same as the fields used in channel
|
|
data structures (<tt>Channel</tt> records) to record the channel's
|
|
current mode. The mode sets are normally maintained as bitmasks,
|
|
but when used in the <tt>convert-db</tt> tool, they are defined as
|
|
strings instead to allow lossless conversion to XML without needing
|
|
to know the specific set of modes supported by the program that
|
|
created the database. Unlike the <tt>ChanAccess</tt> and
|
|
<tt>AutoKick</tt> structures, there is only one <tt>ModeLock</tt>
|
|
structure per channel, and the data in the structure is saved along
|
|
with the channel record itself, so there is no need for a separate
|
|
channel field.</dd>
|
|
</dl>
|
|
|
|
<p><tt>chanserv.h</tt> also defines constants for each of the channel
|
|
privilege levels (indices into the <tt>levels[]</tt> array of the
|
|
<tt>ChannelInfo</tt> structure). As noted in the comment above the list
|
|
of definitions, changing the values of any of the constants will cause
|
|
malfunctions when using the <tt>database/version4</tt> module, since that
|
|
module simply reads in the list of levels as a block. (This is why the
|
|
index 18, formerly used for <tt>CA_AUTOOWNER</tt>, is unused—to avoid
|
|
problems with databases in which that index is used.)</p>
|
|
|
|
<p>The <tt>ChannelInfo</tt> structure itself contains the following
|
|
fields:</p>
|
|
|
|
<dl>
|
|
<dt><tt>ChannelInfo *<b>next</b>, *<b>prev</b></tt></dt>
|
|
<dd>Used to link records together in the internal hash table.</dd>
|
|
|
|
<dt><tt>int <b>usecount</b></tt></dt>
|
|
<dd>The record's usage count (number of gets minus number of puts).
|
|
<i>(Implementation note: As noted in <a href="#s1">section 7-1</a>,
|
|
this field currently serves no actual purpose.)</i></dd>
|
|
|
|
<dt><tt>char <b>name</b>[CHANMAX]</tt></dt>
|
|
<dd>The channel's name. Capitalization is as used when the channel was
|
|
registered, and does not change due to later actions. The buffer
|
|
size, <tt>CHANMAX</tt>, is defined in the global header file
|
|
<tt>defs.h</tt>.</dd>
|
|
|
|
<dt><tt>uint32 <b>founder</b></tt>
|
|
<br/><tt>uint32 <b>successor</b></tt></dt>
|
|
<dd>The nickname group ID of the channel's founder and successor,
|
|
respectively. If the channel does not have a successor set, the
|
|
<tt>successor</tt> field will be zero.</dd>
|
|
|
|
<dt><tt>Password <b>founderpass</b></tt></dt>
|
|
<dd>The founder password for the channel.</dd>
|
|
|
|
<dt><tt>char *<b>desc</b></tt></dt>
|
|
<dd>The channel's description, as specified at registration time or
|
|
with a later <tt>SET DESC</tt> command.</dd>
|
|
|
|
<dt><tt>char *<b>url</b></tt></dt>
|
|
<dd>The URL associated with the channel, as set with the <tt>SET
|
|
URL</tt> command. <tt>NULL</tt> if no URL has been set.</dd>
|
|
|
|
<dt><tt>char *<b>email</b></tt></dt>
|
|
<dd>The E-mail address associated with the channel, as set with the
|
|
<tt>SET EMAIL</tt> command. <tt>NULL</tt> if no E-mail address
|
|
has been set.</dd>
|
|
|
|
<dt><tt>char *<b>entry_message</b></tt></dt>
|
|
<dd>The channel's entry message (the message sent as a <tt>NOTICE</tt>
|
|
to clients entering the channel), as set with the <tt>SET
|
|
ENTRYMSG</tt> command. <tt>NULL</tt> if no entry message has been
|
|
set.</dd>
|
|
|
|
<dt><tt>time_t <b>time_registered</b></tt></dt>
|
|
<dd>The timestamp at which the channel was registered.</dd>
|
|
|
|
<dt><tt>time_t <b>last_used</b></tt></dt>
|
|
<dd>The timestamp at which the channel was last used (see
|
|
<a href="../3.html#2-3">section 3-2-3 of the user's manual</a> for
|
|
details of how the last-used time is set).</dd>
|
|
|
|
<dt><tt>char *<b>last_topic</b></tt>
|
|
<br/><tt>char <b>last_topic_setter</b>[NICKMAX]</tt>
|
|
<br/><tt>time_t <b>last_topic_time</b></tt></dt>
|
|
<dd>The topic most recently set on the channel, along with the nickname
|
|
of the client that set the topic and the timestamp at which it was
|
|
set. If no topic has been set on the channel, <tt>last_topic</tt>
|
|
will be <tt>NULL</tt>, and the other two fields will be
|
|
undefined.</dd>
|
|
|
|
<dt><tt>int32 <b>flags</b></tt></dt>
|
|
<dd>A bitmask containing zero or more of the following channel flags:
|
|
<ul>
|
|
<li><b><tt>CF_KEEPTOPIC</tt>:</b> ChanServ should restore the
|
|
channel's previous topic each time the channel is created
|
|
on the IRC network (<tt>SET KEEPTOPIC</tt>).</li>
|
|
<li><b><tt>CF_SECUREOPS</tt>:</b> Clients without a positive access
|
|
level on the channel should be prevented from getting ops
|
|
(<tt>SET SECUREOPS</tt>).</li>
|
|
<li><b><tt>CF_PRIVATE</tt>:</b> The channel is hidden from the
|
|
<tt>LIST</tt> command output, except when used by Services
|
|
administrators (<tt>SET PRIVATE</tt>).</li>
|
|
<li><b><tt>CF_TOPICLOCK</tt>:</b> ChanServ should prevent the topic
|
|
from being changed except by the <tt>SET TOPIC</tt> command
|
|
(<tt>SET TOPICLOCK</tt>).</li>
|
|
<li><b><tt>CF_RESTRICTED</tt>:</b> ChanServ should automatically
|
|
kick and ban any clients without a positive access level
|
|
that attempt to join the channel (<tt>SET
|
|
RESTRICTED</tt>).</li>
|
|
<li><b><tt>CF_LEAVEOPS</tt>:</b> ChanServ should not remove
|
|
server-generated ops for the first client to join a
|
|
channel, even if that client would not normally be
|
|
auto-opped (<tt>SET LEAVEOPS</tt>).</li>
|
|
<li><b><tt>CF_SECURE</tt>:</b> When checking a client's channel
|
|
access level, require the client to have identified to
|
|
NickServ regardless of the setting of the nickname's
|
|
<tt>SECURE</tt> option (<tt>SET SECURE</tt>).</li>
|
|
<li><b><tt>CF_VERBOTEN</tt>:</b> The channel is forbidden
|
|
(<tt>FORBID</tt>).</li>
|
|
<li><b><tt>CF_NOEXPIRE</tt>:</b> The channel does not expire
|
|
(<tt>SET NOEXPIRE</tt>).</li>
|
|
<li><b><tt>CF_OPNOTICE</tt>:</b> ChanServ should send a notice to
|
|
the channel whenever any of the <tt>OP</tt>, <tt>VOICE</tt>,
|
|
or related commands are used (<tt>SET OPNOTICE</tt>).</li>
|
|
<li><b><tt>CF_ENFORCE</tt>:</b> ChanServ should prevent clients
|
|
from removing automatically-set channel user modes such as
|
|
auto-ops (<tt>SET ENFORCE</tt>).</li>
|
|
<li><b><tt>CF_HIDE_EMAIL</tt>:</b> The channel's E-mail address is
|
|
hidden from the <tt>INFO</tt> command output, except when
|
|
used by Services administrators (<tt>SET HIDE
|
|
EMAIL</tt>).</li>
|
|
<li><b><tt>CF_HIDE_TOPIC</tt>:</b> The channel's current or last
|
|
topic is hidden from the <tt>INFO</tt> command output,
|
|
except when used by Services administrators (<tt>SET HIDE
|
|
TOPIC</tt>).</li>
|
|
<li><b><tt>CF_HIDE_MLOCK</tt>:</b> The channel's mode lock is
|
|
hidden from the <tt>INFO</tt> command output, except when
|
|
used by Services administrators (<tt>SET HIDE
|
|
MLOCK</tt>).</li>
|
|
<li><b><tt>CF_SUSPENDED</tt>:</b> The channel is suspended
|
|
(<tt>SUSPEND</tt>).</li>
|
|
<li><b><tt>CF_MEMO_RESTRICTED</tt>:</b> Only users with the
|
|
<tt>MEMO</tt> privilege are permitted to send memos to the
|
|
channel (<tt>SET MEMO-RESTRICTED</tt>).</li>
|
|
</ul>
|
|
The flag values 0x00000100 and 0x00000400 are unused to avoid
|
|
difficulties with databases from earlier versions of Services
|
|
which used these values.</dd>
|
|
|
|
<dt><tt>char <b>suspend_who</b>[NICKMAX]</tt>
|
|
<br/><tt>char *<b>suspend_reason</b></tt>
|
|
<br/><tt>time_t <b>suspend_time</b></tt>
|
|
<br/><tt>time_t <b>suspend_expires</b></tt></dt>
|
|
<dd>Suspension data for the channel, if the <tt>CF_SUSPENDED</tt> flag
|
|
is set. Used in the same fashion as the same-named fields in the
|
|
<tt>NickGroupInfo</tt> structure (see <a href="#s3-1-1">section
|
|
7-3-1-1</a>).</dd>
|
|
|
|
<dt><tt>int16 <b>levels</b>[CA_SIZE]</tt></dt>
|
|
<dd>The channel access levels corresponding to each of the channel
|
|
privileges (<tt>CA_INVITE</tt>, <tt>CA_AKICK</tt>, and so on).
|
|
A value of <tt>ACCLEV_DEFAULT</tt> indicates that the corresponding
|
|
privilege should use the default access level defined in
|
|
<tt>access.c</tt>; a value of <tt>ACCLEV_INVALID</tt> indicates
|
|
that the corresponding privilege is disabled entirely, except with
|
|
respect to the channel founder.</dd>
|
|
|
|
<dt><tt>ChanAccess *<b>access</b></tt>
|
|
<br/><tt>int16 <b>access_count</b></tt></dt>
|
|
<dd>A variable-length array containing the channel's access list.</dd>
|
|
|
|
<dt><tt>AutoKick *<b>akick</b></tt>
|
|
<br/><tt>int16 <b>akick_count</b></tt></dt>
|
|
<dd>A variable-length array containing the channel's autokick list.</dd>
|
|
|
|
<dt><tt>ModeLock <b>mlock</b></tt></dt>
|
|
<dd>The channel's mode lock data.</dd>
|
|
|
|
<dt><tt>Channel *<b>c</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> Points to the
|
|
<tt>Channel</tt> structure for the channel if it is currently in
|
|
use.</dd>
|
|
|
|
<dt><tt>int <b>bad_passwords</b></tt></dt>
|
|
<dd><i>Not saved to persistent storage.</i> The number of times an
|
|
incorrect password has been given for a channel command (such as
|
|
<tt>IDENTIFY</tt>) since the last correct password. Used to warn
|
|
about attempts to crack the password.</dd>
|
|
</dl>
|
|
|
|
<p>The <tt>cs-local.h</tt> header file includes three definitions used
|
|
internally by ChanServ:</p>
|
|
|
|
<dl>
|
|
<dt><tt>MAX_MLOCK_PARAMS</tt> (constant)</dt>
|
|
<dd>Sets the maximum number of command parameters that the <tt>SET
|
|
MLOCK</tt> command will process. This constant is used to avoid
|
|
the overhead of dynamically allocating an argument array for every
|
|
invocation of the command; any parameters passed beyond this limit
|
|
will be silently ignored. The default value, 256, is more
|
|
parameters than are can be passed in an RFC-standard 512-byte line
|
|
(even if every parameter is one character, subtracting out the
|
|
"<tt>SET MLOCK</tt>" leaves space for only 251 parameters, not
|
|
considering the trailing CR/LF and other IRC protocol overhead).</dd>
|
|
|
|
<dt><tt>ChanOpt</tt> (structure)</dt>
|
|
<dd>Used to define channel option data, for use by the <tt>SET</tt> and
|
|
<tt>INFO</tt> commands. The structure has the following fields:
|
|
<ul>
|
|
<li><b><tt>name</tt>:</b> The name of the option, as a string.</li>
|
|
<li><b><tt>flag</tt>:</b> The corresponding <tt>ChannelInfo.flags</tt>
|
|
flag value.</li>
|
|
<li><b><tt>namestr</tt>:</b> The option's descriptive name (for use
|
|
in <tt>INFO</tt> output), as a language string index. If
|
|
-1, the option will not be included in <tt>INFO</tt>
|
|
output.</li>
|
|
<li><b><tt>onstr</tt>, <tt>offstr</tt></b>: Response messages for
|
|
turning the option on and off via <tt>SET</tt>, as language
|
|
string indices.</li>
|
|
<li><b><tt>syntaxstr</tt></b>: The syntax message for setting the
|
|
option, as a language string index.</li>
|
|
</ul></dd>
|
|
|
|
<dt><tt>RET_*</tt> (constants)</dt>
|
|
<dd>Return values from access list modification routines. See
|
|
<a href="#s4-2-1">section 7-4-2-1</a> for details.</dd>
|
|
</dl>
|
|
|
|
<p id="s4-1-1-rename">One other point of note in <tt>cs-local.h</tt> is the
|
|
renaming of several functions in <tt>set.c</tt> and <tt>util.c</tt> using
|
|
<tt>#define</tt> directives, such as renaming <tt>init_set()</tt> to
|
|
<tt>init_set_cs()</tt>. This is to avoid conflicts with NickServ, which
|
|
includes functions of the same name in its own <tt>set.c</tt> and
|
|
<tt>util.c</tt>. While the functions involved are not used outside of
|
|
ChanServ, they must be declared external for the multi-file link to
|
|
succeed; as a result, if the functions are not renamed, linking the final
|
|
program when using static modules will result in a symbol name clash when
|
|
the symbols from both modules are processed. This problem does not occur
|
|
when using dynamic modules, since the presence of conflicting symbols in
|
|
dynamic modules is not itself an error, and no attempt is made to reference
|
|
either set of symbols from any other module (the references within the
|
|
respective modules are resolved at module link time).</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-1-2">7-4-1-2. Overall module structure</h5>
|
|
|
|
<p>The main source file for ChanServ, <tt>main.c</tt>, follows the same
|
|
pattern as its NickServ counterpart; see <a href="#s3-1-2">section
|
|
7-3-1-2</a> for a more extensive description.</p>
|
|
|
|
<p>One of the first things defined in <tt>main.c</tt> is the
|
|
<tt>chanopts[]</tt> table, an array of <tt>ChanOpt</tt> structures (see
|
|
above) describing on/off options available on channels from a user's
|
|
perspective. This table is used primarily for the <tt>SET</tt> command
|
|
(thus only flags which have a corresponding <tt>SET</tt> command are
|
|
listed), and secondarily for outputting the channel's option set in
|
|
response to the <tt>INFO</tt> command. Note that the three <tt>HIDE</tt>
|
|
options are not listed here, as they are handled specially for the
|
|
<tt>SET</tt> command and not listed in the <tt>INFO</tt> output.</p>
|
|
|
|
<p>In addition to the standard command list in the <tt>cmds[]</tt> array,
|
|
two separate arrays are defined, for the <tt>HALFOP</tt>/<tt>DEHALFOP</tt>
|
|
and <tt>PROTECT</tt>/<tt>DEPROTECT</tt> command pairs. These commands can
|
|
only be used if the IRC server protocol supports the corresponding feature,
|
|
so the module initialization routine checks the protocol's feature flags
|
|
and conditionally registers these two pairs of commands.</p>
|
|
|
|
<p>ChanServ includes a significant number of callback functions for various
|
|
IRC events: in addition to watching for newly-created channels and users
|
|
joining channels, it must also monitor changes of status such as channel
|
|
modes and channel topics to ensure that they remain consistent with the
|
|
registered settings and to record changes, as well as take notice of
|
|
nickname-related events that can affect channels. Of these, the
|
|
<tt>do_nickgroup_delete()</tt> callback function, called when a nickname
|
|
group is deleted, is easily the most complex. Since channel founders must
|
|
be registered nicknames, the disappearance of a nickname group means that
|
|
any channels with that group as founder will no longer have a valid founder
|
|
group ID. If the channel has a successor set, the successor may be able to
|
|
assume foundership of the channel—but the code must be careful that
|
|
this does not cause the successor to exceed his registered channel limit,
|
|
or users could circumvent the limit by registering multiple nicknames,
|
|
setting one as founder and another as successor, and deliberately dropping
|
|
the founder nickname. In addition, if the channel was suspended, it is
|
|
changed to a forbidden channel to prevent users from getting around a
|
|
suspension by dropping and re-registering their nickname. Because of these
|
|
various potentialities, the callback function always logs any actions it
|
|
takes in response to the deletion of a nickname group.</p>
|
|
|
|
<p>Of the command routines, the basic commands (<tt>HELP</tt>,
|
|
<tt>REGISTER</tt>, <tt>IDENTIFY</tt>, <tt>DROP</tt>, <tt>DROPCHAN</tt>,
|
|
<tt>INFO</tt>, <tt>LIST</tt>) are very similar to their NickServ
|
|
counterparts, and are not discussed in detail here. The two
|
|
ChanServ-specific commands whose handlers are fairly complex are
|
|
<tt>AKICK</tt> and the <tt>OP</tt>/<tt>VOICE</tt> command set.</p>
|
|
|
|
<p>The <tt>AKICK</tt> command handler <tt>do_akick()</tt>, despite its
|
|
length (including a separate helper routine used with <tt>LIST</tt> and
|
|
<tt>VIEW</tt>), is not dissimilar to the OperServ autokill and S-line
|
|
commands, or the NickServ <tt>ACCESS</tt> and <tt>AJOIN</tt> handlers. The
|
|
only significant difference is the presence of the <tt>ENFORCE</tt>
|
|
subcommand; this is implemented by calling <tt>check_kick()</tt>, the
|
|
routine used to check whether newly-joined clients are allowed to join a
|
|
channel (see <a href="#s4-1-3">section 7-4-1-3</a>) for each client on the
|
|
channel, causing clients which match an entry on the autokill list to be
|
|
kickbanned.</p>
|
|
|
|
<p>The <tt>OP</tt> and <tt>VOICE</tt> family of commands all perform a
|
|
common function—adding or removing a channel user mode—and for
|
|
this reason, all eight commands are handled by a single routine,
|
|
<tt>do_opvoice()</tt>, with handlers for each command that call the common
|
|
routine with the appropriate command name to indicate the mode of
|
|
operation. The data used by the common routine is stored in
|
|
<tt>opvoice_data[]</tt>, an array of structures with the following
|
|
fields:</p>
|
|
|
|
<ul>
|
|
<li><b><tt>cmd</tt>:</b> The command name.</li>
|
|
<li><b><tt>add</tt>:</b> Nonzero if the command adds a mode, zero if it
|
|
removes a mode.</li>
|
|
<li><b><tt>mode</tt>:</b> The mode character added or removed.</li>
|
|
<li><b><tt>target_acc</tt>:</b> A channel privilege index (<tt>CA_*</tt>
|
|
constant); if the target client is in this privilege class, the
|
|
command will be refused.</li>
|
|
<li><b><tt>success_msg</tt>:</b> The language string index for the message
|
|
to be sent when the command succeeds.</li>
|
|
<li><b><tt>already_msg</tt>:</b> The language string index for the message
|
|
to be sent when the client already has the mode added (or lacks the
|
|
mode removed) by the command.</li>
|
|
<li><b><tt>failure_msg</tt>:</b> The language string index for the message
|
|
to be sent when the command is rejected.</li>
|
|
</ul>
|
|
|
|
<p>When called, <tt>do_opvoice()</tt> first extracts the data for the
|
|
command from the <tt>opvoice_data[]</tt> table, and sets an additional
|
|
variable, <tt>target_nextacc</tt>, used for mode-removal commands to set an
|
|
upper bound on the target client access level check; this is used to, for
|
|
example, allow <tt>DEVOICE</tt> on an auto-op client (since the client can
|
|
just give themselves voice status again if necessary). The routine then
|
|
loops through all target nicknames given with the command; a
|
|
<tt>do/while</tt> loop is used so that if no nicknames are given at all,
|
|
the code will still be run once (in this case the client that gave the
|
|
command is used as the target). For each target client, the standard
|
|
permission and channel status checks are performed, and then the routine
|
|
determines whether to allow the command:</p>
|
|
<ol>
|
|
<li>If the target client is the client giving the command, the command is
|
|
allowed.</li>
|
|
<li>If the command removes a mode (<tt>DEOP</tt>, <tt>DEVOICE</tt>, etc.)
|
|
and the channel does not have the <tt>ENFORCE</tt> option set, the
|
|
command is allowed.</li>
|
|
<li>If a channel privilege check (<tt>target_acc</tt>) is not specified for
|
|
the command, the command is allowed.</li>
|
|
<li>If the the target client is not in the privilege class specified for
|
|
the command's privilege check, the command is allowed.</li>
|
|
<li>If an upper limit for the privilege check (<tt>target_nextacc</tt>) is
|
|
set for the command and the target client is in that privilege
|
|
class, the command is allowed.</li>
|
|
<li>Otherwise, the command is refused.</li>
|
|
</ol>
|
|
|
|
<p>Once the command has been allowed, the routine determines which mode
|
|
flags need to be set or cleared for the target client. (For extensibility,
|
|
the code allows for more than one flag to be set or cleared for a single
|
|
command, though the data table only allows one mode character.) If there
|
|
are no modes to be set or cleared, a notice to that effect is sent to the
|
|
caller; otherwise, the necessary mode changes are performed, a notice of
|
|
the mode change is sent to the channel if the <tt>OPNOTICE</tt> option is
|
|
enabled, and a success notice is sent to the caller. In addition, if the
|
|
command was an <tt>OP</tt> command, the channel's last-used time is updated
|
|
as for auto-ops.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-1-3">7-4-1-3. Channel status checking and modification</h5>
|
|
|
|
<p>ChanServ's routines for checking and adjusting channel status are
|
|
located in the source file <tt>check.c</tt>. There are six routines
|
|
exported from this file; two, <tt>init_check()</tt> and <tt>exit_check()</tt>,
|
|
are initialization and cleanup routines called by the module initialization
|
|
and cleanup code, respectively. The remaining routines are:</p>
|
|
|
|
<dl>
|
|
<dt><tt>void <b>check_modes</b>(Channel *<i>c</i>)</tt></dt>
|
|
<dd>Checks the given channel's modes, making any changes necessary.
|
|
For registered channels, the "registered" mode (if any) is always
|
|
added, and other modes are set or cleared according to the mode
|
|
lock; for unregistered channels, the "registered" mode is always
|
|
cleared. This routine also checks for the "bouncy modes"
|
|
phenomenon in tandem with the channel <tt>MODE</tt> message
|
|
handle (see <a href="2.html#s6-3">section 2-6-3</a>).</dd>
|
|
|
|
<dt><tt>void <b>check_chan_user_modes</b>(const char *<i>source</i>,
|
|
struct c_userlist *<i>u</i>, Channel *<i>c</i>, int32 <i>oldmodes</i>)</tt></dt>
|
|
<dd>Checks the channel user modes of the given client (<tt><i>u</i></tt>)
|
|
on the given channel, making any changes necessary. The set of
|
|
"necessary" changes depends not only on the client's current modes,
|
|
but also on the source of the <tt>MODE</tt> message that caused the
|
|
change (passing an empty string for the <tt><i>source</i></tt>
|
|
parameter will cause such checks to be skipped).
|
|
<tt><i>oldmodes</i></tt> is the client's previous set of modes, or
|
|
-1 for a client joining a channel. The sequence of operations is
|
|
fairly complicated:
|
|
<ul>
|
|
<li class="spaced">If the channel is not registered (or forbidden),
|
|
no changes are made.</li>
|
|
<li class="spaced">If <tt><i>source</i></tt> is Services' server
|
|
name or the ChanServ or OperServ pseudoclient nickname,
|
|
no changes are made (under the assumption that anything
|
|
done by Services has been otherwise checked).</li>
|
|
<li class="spaced">If <tt><i>source</i></tt> is the client whose
|
|
modes are being checked, then no changes are made
|
|
<i>unless</i> the user is either not opped or is about to
|
|
be deopped, in which case the mode changes made by the
|
|
client are reversed. However, this check is not performed
|
|
for IRC operators (since some IRC servers allow operators
|
|
to set arbitrary modes regardless of chanop status), and on
|
|
servers supporting halfops, the mode change is not reversed
|
|
if the user has halfops and is only changing the halfop or
|
|
voice modes.</li>
|
|
<li class="spaced">If the mode change is the opping by a server of
|
|
the first client to join a channel and the channel does not
|
|
have the <tt>LEAVEOPS</tt> option set, the client's channel
|
|
access level is checked against the auto-op privilege
|
|
level. If the client has auto-op privileges, then the
|
|
channel's last-used time is updated as for ordinary auto-op
|
|
processing (see below); otherwise, a "channel is
|
|
registered" notice is sent to the client and the client is
|
|
deopped (in that order, so that a human user will see the
|
|
reason for the deop before the mode change itself).</li>
|
|
<li class="spaced">The "<tt>check_chan_user_modes</tt>" callback is
|
|
called, allowing protocol modules to handle modes not
|
|
recognized by the standard processing.</li>
|
|
<li class="spaced">The client's new modes, based on channel
|
|
privilege level, are calculated by calling
|
|
<tt>check_access_cumode()</tt> (see
|
|
<a href="#s4-2-1">section 7-4-2-1</a>).</li>
|
|
<li class="spaced">If the client just joined the channel, the mode
|
|
change was done by a server, or the <tt>ENFORCE</tt> option
|
|
is set on the channel, all missing automatic modes are
|
|
added. (This has the effect of allowing automatic modes to
|
|
be removed from clients if <tt>ENFORCE</tt> is not set.)
|
|
In addition, if the mode change included a <tt>+o</tt>, the
|
|
channel's last-used time is updated.</li>
|
|
<li class="spaced">If the client is not an IRC operator, any
|
|
necessary mode removals are performed.</li>
|
|
</ul>
|
|
<p>The mode changes in these last two steps are performed by a
|
|
helper routine, <tt>local_set_cumodes()</tt>, which in turn calls
|
|
<tt>set_cmode()</tt> for each mode in the given set.</p></dd>
|
|
|
|
<dt><tt>int <b>check_kick</b>(User *<i>user</i>, const char *<i>chan</i>,
|
|
int <i>on_join</i>)</tt></dt>
|
|
<dd>Checks whether a client is permitted to be on a channel; if so,
|
|
returns zero, otherwise kickbans the client and returns nonzero.
|
|
This routine is normally called when a client joins a channel,
|
|
before the actual join processing, but setting the
|
|
<tt><i>on_join</i></tt> parameter to zero allows this routine to be
|
|
called for clients already in the channel as well, such as for the
|
|
<tt>AKICK ENFORCE</tt> command. A client can be denied access to a
|
|
channel for any number of reasons, checked in the following order:
|
|
<ul>
|
|
<li class="spaced">If the channel name is the single character
|
|
"<tt>#"</tt>" and the <tt>CSForbidShortChannel</tt>
|
|
configuration option is set, the client is kickbanned.</li>
|
|
<li class="spaced">If the client is a Services administrator, the
|
|
client is allowed.</li>
|
|
<li class="spaced">If the "<tt>check_kick</tt>" callback returns 1,
|
|
the client is kickbanned; if it returns 2, the client is
|
|
allowed.</li>
|
|
<li class="spaced">If the client is an IRC operator, the client is
|
|
allowed.</li>
|
|
<li class="spaced">If the channel already exists with an
|
|
IRC-operators-only mode, the client is kickbanned.
|
|
(Ordinarily, the IRC server takes care of such processing,
|
|
but this code is included to handle desynchs and other
|
|
network problems.)</li>
|
|
<li class="spaced">If the channel is not registered, then the
|
|
client is kickbanned if the <tt>CSRegisteredOnly</tt>
|
|
configuration option is set, and allowed otherwise.</li>
|
|
<li class="spaced">If the channel is forbidden or suspended, the
|
|
client is kickbanned.</li>
|
|
<li class="spaced">If the channel's mode lock has an
|
|
IRC-operators-only mode set, the client is kickbanned.</li>
|
|
<li class="spaced">If the channel's mode lock includes a
|
|
registered-nicknames-only mode and the client's nickname is
|
|
not registered, the client is kickbanned. (However, this
|
|
check is skipped if the <tt>CSSkipModeRCeck</tt>
|
|
configuration option is set.)</li>
|
|
<li class="spaced">If the client's
|
|
<tt><i>nick</i>!<i>user</i>@<i>host</i></tt> string
|
|
matches an autokick mask, the client is kickbanned.</li>
|
|
<li class="spaced">If the client matches the <tt>NOJOIN</tt>
|
|
privilege on the channel, the client is kickbanned.
|
|
The client is also kickbanned if it would match the
|
|
<tt>NOJOIN</tt> privilege when identified to its nickname;
|
|
however, this check is skipped if less time than specified
|
|
in the <tt>CSRestrictDelay</tt> configuration option has
|
|
passed since Services startup.</li>
|
|
<li class="spaced">Otherwise, the client is allowed.</li>
|
|
</ul>
|
|
<p>If the client is to be kickbanned, the routine first checks
|
|
whether kicking the client would cause the channel to become empty
|
|
(and thus be deleted, nullifying the effecet of any ban); if so, a
|
|
<tt>JOIN</tt> message is sent to the network to cause ChanServ to
|
|
join the channel, and a timeout for <tt>CSInhabit</tt> seconds is
|
|
added to cause ChanServ to leave the channel after that time.
|
|
Following this, the routine ensures that the ban mask is properly
|
|
formatted (containing a nickname as well as user and host), then
|
|
clears any ban exceptions matching the user and adds the ban mask
|
|
if it is not already present. Once the ban is present, the client
|
|
is then kicked from the channel, and removed from the internal data
|
|
structures if necessary.</p>
|
|
<p><i>Implementation note: As mentioned in
|
|
<a href="../d.html">Appendix D of the user manual</a>, when
|
|
ChanServ temporarily enters a channel for a kickban, it is not
|
|
added to the internal channel data; as a result, a subsequent
|
|
<tt>UNBAN</tt> or <tt>INVITE</tt> on the channel will return a
|
|
"channel does not exist" error. It would probably be better to
|
|
add ChanServ to the channel's client list like any ordinary
|
|
client.</i></p>
|
|
</dd>
|
|
|
|
<dt><tt>int <b>check_topiclock</b>(Channel *<i>c</i>, time_t <i>topic_time</i>)</tt></dt>
|
|
<dd>Called on a channel topic change (<tt><i>topic_time</i></tt> is the
|
|
timestamp associated with the topic change) to restore the topic to
|
|
its previous value if the channel's <tt>TOPICLOCK</tt> option is
|
|
set. Returns nonzero if the topic is changed by the routine,
|
|
otherwise zero.</dd>
|
|
</dl>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-1-4">7-4-1-4. The <tt>SET</tt> and <tt>UNSET</tt> commands</h5>
|
|
|
|
<p>The handlers for the <tt>SET</tt> and <tt>UNSET</tt> commands are
|
|
located in the <tt>set.c</tt> source file; as for NickServ, they function
|
|
like miniature versions of the main <tt>chanserv()</tt> routine. One
|
|
noteworthy difference is that the on/off options (other than the three
|
|
<tt>HIDE</tt> options) are all handled by a single routine,
|
|
<tt>do_set_boolean()</tt>; the main <tt>SET</tt> handler, <tt>do_set()</tt>,
|
|
looks up the option name in the <tt>chanopts[]</tt> table defined in
|
|
<tt>main.c</tt> (checking privileges for the <tt>NOEXPIRE</tt> option),
|
|
then calls <tt>do_set_boolean()</tt>, which uses the data from the
|
|
<tt>ChanOpt</tt> structure to set channel flags and send responses to the
|
|
calling client.</p>
|
|
|
|
<p>Other noteworthy option handlers are:</p>
|
|
|
|
<dl>
|
|
<dt><tt><b>do_set_founder()</b></tt>
|
|
<br/><tt><b>do_set_successor()</b></tt></dt>
|
|
<dd>Both <tt>SET FOUNDER</tt> and <tt>SET SUCCESSOR</tt> follow the
|
|
same general pattern: the given nickname is looked up to retrieve
|
|
its nickname group ID, the ID is checked to ensure that both
|
|
founder and successor are not set to the same nickname group, and
|
|
an informational message is logged to record the change. The major
|
|
difference is that <tt>SET FOUNDER</tt> checks to ensure that the
|
|
new founder has not reached the channel registration limit, while
|
|
<tt>SET SUCCESSOR</tt> makes no such check. (Even if it did, the
|
|
check would only make sense at the time the <tt>SET SUCCESSOR</tt>
|
|
command was given, and would not reflect any future channel
|
|
registrations or drops by the successor. An alternative
|
|
possibility would be to have successor channels count against the
|
|
channel limit as well, as mentioned in <a href="11.html#s1">section
|
|
11-1</a>.)</dd>
|
|
|
|
<dt><tt><b>do_set_mlock()</b></tt></dt>
|
|
<dd>The <tt>SET MLOCK</tt> handler is fairly complex, as it must parse
|
|
the mode string and parameters to ensure that users cannot
|
|
inadvertently (or maliciously) cause an invalid <tt>MODE</tt>
|
|
message to be sent to the IRC network. The routine parses the mode
|
|
string character by character; if a mode is found that conflicts
|
|
with an earlier setting in the string, the later occurrence takes
|
|
precedence. Additionally, in order to simplify cleanup in case a
|
|
problem is found, the new mode lock is accumulated in a temporary
|
|
<tt>ModeLock</tt> structure, which is copied into the channel's
|
|
data when the routine successfully completes.
|
|
<p>In order to support additional modes provided by particular IRC
|
|
protocols, <tt>do_set_mlock()</tt> defines a callback, "<tt>SET
|
|
MLOCK</tt>", which is called once for every mode in the mode
|
|
string; it is also called once after all modes have been processed,
|
|
to allow for a final validity check (for example, to check for
|
|
modes that require other modes to be set, as the Unreal protocol
|
|
module does).</p></dd>
|
|
</dl>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-1-5">7-4-1-5. ChanServ utility routines</h5>
|
|
|
|
<p>ChanServ's utility functions are defined in <tt>util.c</tt>. As with
|
|
NickServ, the preprocessor symbol <tt>STANDALONE_CHANSERV</tt> can be
|
|
defined before including <tt>util.c</tt> in another file; this causes the
|
|
routines <tt>new_channelinfo()</tt>, <tt>free_channelinfo()</tt>, and
|
|
<tt>reset_levels</tt> to be defined as <tt>static</tt>, and eliminates all
|
|
other code. <tt>new_channelinfo()</tt> and <tt>free_channelinfo()</tt> are
|
|
used to allocate and free resources for a <tt>ChannelInfo</tt> structure,
|
|
like their nickname counterparts; <tt>reset_levels()</tt> resets the
|
|
privilege levels for a channel (the <tt>levels[]</tt> array) to default
|
|
values.</p>
|
|
|
|
<p>Aside from these three functions (all exported from ChanServ),
|
|
<tt>util.c</tt> defines the following routines, along with the
|
|
initialization and cleanup routines <tt>init_util()</tt> and
|
|
<tt>exit_util()</tt>:</p>
|
|
|
|
<dl>
|
|
<dt><tt>int <b>check_channel_limit</b>(const NickGroupInfo *<i>ngi</i>, int *<i>max_ret</i>)</tt></dt>
|
|
<dd>Compares the given nickname group's registered channel count with
|
|
the limit applied to that nickname, returning -1 if the limit has
|
|
yet to be reached, 0 if the limit has been reached, and 1 if the
|
|
limit has been exceeded (much like string comparison functions).
|
|
Also stores the registered channel limit in the variable pointed to
|
|
by <tt><i>max_ret</i></tt> if <tt><i>max_ret</i></tt> is not
|
|
<tt>NULL</tt>. This routine is exported.</dd>
|
|
|
|
<dt><tt>ChannelInfo *<b>makechan</b>(const char *<i>chan</i>)</tt></dt>
|
|
<dd>Creates a new <tt>ChannelInfo</tt> structure for the given channel
|
|
name, adds it to the database, and returns it.</dd>
|
|
|
|
<dt><tt>int <b>delchan</b>(ChannelInfo *<i>ci</i>)</tt></dt>
|
|
<dd>Removes the given channel from the database, returning nonzero on
|
|
success, zero on failure.</dd>
|
|
|
|
<dt><tt>void <b>count_chan</b>(ChannelInfo *<i>ci</i>)</tt></dt>
|
|
<dd>Updates the <tt>NickGroupInfo</tt> record for the given channel's
|
|
founder to indicate that that nickname group owns the channel,
|
|
incrementing the owned-channel count.</dd>
|
|
|
|
<dt><tt>void <b>uncount_chan</b>(ChannelInfo *<i>ci</i>)</tt></dt>
|
|
<dd>Removes the given channel from its founder's owned-channel list
|
|
and decrements the owned-channel count.</dd>
|
|
|
|
<dt><tt>int <b>is_founder</b>(const User *<i>user</i>, const ChannelInfo *<i>ci</i>)</tt></dt>
|
|
<dd>Returns whether the given user has founder access to the given
|
|
channel, whether due to being the actual channel founder or to
|
|
identifying for the channel with its founder password.</dd>
|
|
|
|
<dt><tt>int <b>is_identified</b>(const User *<i>user</i>, const ChannelInfo *<i>ci</i>)</tt></dt>
|
|
<dd>Returns whether the given user has identified for the channel with
|
|
its founder password. A subset of <tt>is_founder()</tt>.</dd>
|
|
|
|
<dt><tt>void <b>restore_topic</b>(Channel *<i>c</i>)</tt></dt>
|
|
<dd>Restores the saved topic on a newly-created channel if the
|
|
channel is registered and its <tt>KEEPTOPIC</tt> option is set.</dd>
|
|
|
|
<dt><tt>void <b>record_topic</b>(ChannelInfo *<i>ci</i>, const char *<i>topic</i>, const char *<i>setter</i>, time_t <i>topic_time</i>)</tt></dt>
|
|
<dd>Records the given topic in the given channel's data structure.</dd>
|
|
|
|
<dt><tt>void <b>suspend_channel</b>(ChannelInfo *<i>ci</i>, const char *<i>reason</i>, const char *<i>who</i>, const time_t <i>expires</i>)</tt></dt>
|
|
<dd>Suspends the given channel, copying the parameters
|
|
<tt><i>reason</i></tt>, <tt><i>who</i></tt>, and
|
|
<tt><i>expires</i></tt> into the suspension data fields. (If
|
|
<tt><i>expires</i></tt> is zero, then the suspension will not
|
|
expire.)</dd>
|
|
|
|
<dt><tt>void <b>unsuspend_channel</b>(ChannelInfo *<i>ci</i>, int <i>set_time</i>)</tt></dt>
|
|
<dd>Cancels the suspension on the given channel. If
|
|
<tt><i>set_time</i></tt> is nonzero, the last-used time of the
|
|
channel will be updated according to <tt>CSSuspendGrace</tt> to
|
|
prevent the channel from expiring for that length of time (if
|
|
<tt>CSSuspendGrace</tt> or <tt>CSExpire</tt> are not set, or if the
|
|
channel already has enough time before expiration, the last-used
|
|
time will not be changed).</dd>
|
|
|
|
<dt><tt>void <b>chan_bad_password</b>(User *<i>u</i>, ChannelInfo *<i>ci</i>)</tt></dt>
|
|
<dd>Records a bad password attempt for the given channel, sending out a
|
|
<tt>WALLOPS</tt> if the number of consecutive bad password attempts
|
|
for the channel reaches the limit specified by the
|
|
<tt>BadPassWarning</tt> configuration option (if set).</dd>
|
|
|
|
<dt><tt>ChanOpt *<b>chanopt_from_name</b>(const char *<i>optname</i>)</tt></dt>
|
|
<dd>Returns the <tt>ChanOpt</tt> corresponding to the given
|
|
(case-insensitive) option name, or <tt>NULL</tt> if no matching
|
|
option is found.</dd>
|
|
|
|
<dt><tt>char *<b>chanopts_to_string</b>(const ChannelInfo *<i>ci</i>, const NickGroupInfo *<i>ngi</i>)</tt></dt>
|
|
<dd>Returns a string describing the set of options active on the given
|
|
channel in human-readable form. <tt>ngi</tt> indicates the client
|
|
to which the string will be sent and is used in <tt>getstring()</tt>
|
|
calls. The returned string is stored in a static buffer, which
|
|
will be overwritten by the next call to this routine.</dd>
|
|
</dl>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s4-2">7-4-2. Channel access list handling</h4>
|
|
|
|
<p>As discussed in the user's manual, user privileges on channels are
|
|
maintained via channel access lists. Channel access list handling in
|
|
ChanServ is split into three files. One, <tt>access.c</tt>, contains
|
|
common routines and privilege level definitions, and is included in the
|
|
<tt>chanserv/main</tt> module; the other two, <tt>access-levels.c</tt> and
|
|
<tt>access-xop.c</tt>, are independent modules which provide two different
|
|
ways of manipulating access lists. A separate header file,
|
|
<tt>access.h</tt>, contains definitions related to channel access lists.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-2-1">7-4-2-1. Access list basics</h5>
|
|
|
|
<p>The <tt>access.c</tt> source file, included as part of the main ChanServ
|
|
module, serves two main purposes; to define the set of privileges
|
|
associated with channels, and to provide utility routines for performing
|
|
common operations related to channel access lists. It also makes use of
|
|
the <tt>access.h</tt> header file for certain structure and constant
|
|
definitions.</p>
|
|
|
|
<p>The list of channel privileges is defined in the <tt>levelinfo[]</tt>
|
|
array at the top of the file; each element in the array is a
|
|
<tt>LevelInfo</tt> structure that describes one privilege. (The word
|
|
"level" in the identifiers comes from the command, <tt>LEVELS</tt>, used
|
|
to modify the settings on a per-channel basis; that command name was
|
|
originally used for the meaning of "setting the <i>levels</i> at which
|
|
privileges are given".)</p>
|
|
|
|
<p>The word "privilege" itself is something of a misnomer, as it includes
|
|
two "negative privileges", <tt>CA_AUTODEOP</tt> and <tt>CA_NOJOIN</tt>.
|
|
While these can be used in the same manner as ordinary privileges, their
|
|
primary function is in conjunction with the <tt>SECUREOPS</tt> and
|
|
<tt>RESTRICTED</tt> channel options; these cause the respective privilege
|
|
levels to be treated as zero, preventing users not on the channel access
|
|
list to be auto-deopped or blocked from entering the channel. However,
|
|
these two privileges are not visible to users, so "privilege" is considered
|
|
clear enough to use in the documentation</p>
|
|
|
|
<p>The <tt>LevelInfo</tt> structure, defined in <tt>access.h</tt>, contains
|
|
the following fields:</p>
|
|
|
|
<dl>
|
|
<dt><tt>int <b>what</b></tt></dt>
|
|
<dd>The <tt>CA_*</tt> constant used for this privilege.</dd>
|
|
|
|
<dt><tt>int <b>defval</b></tt></dt>
|
|
<dd>The default channel access level corresponding to this
|
|
privilege.</dd>
|
|
|
|
<dt><tt>const char *<b>name</b></tt></dt>
|
|
<dd>The user-visible name for this privilege, used in the
|
|
<tt>LEVELS</tt> command. An empty string makes the privilege
|
|
invisible to users.</dd>
|
|
|
|
<dt><tt>int <b>desc</b></tt></dt>
|
|
<dd>The message string index giving the privilege's description.</dd>
|
|
|
|
<dt><tt>int <b>action</b></tt></dt>
|
|
<dd>The "meaning" of the privilege: the action to be performed for
|
|
clients with the appropriate access level. One of the following
|
|
flags, any of which may be combined (OR'd) with
|
|
<tt>CL_LESSEQUAL</tt> to make the privilege's associated level a
|
|
maximum rather than a minimum:
|
|
<ul>
|
|
<li><b><tt>CL_SET_MODE</tt>:</b> Sets channel user modes on the
|
|
corresponding client.</li>
|
|
<li><b><tt>CL_CLEAR_MODE</tt>:</b> Clears channel user modes from
|
|
the corresponding client.</li>
|
|
<li><b><tt>CL_ALLOW_CMD</tt>:</b> Allows a command or set of
|
|
commands to be used.</li>
|
|
<li><b><tt>CL_OTHER</tt>:</b> Handled separately (or a no-op).</li>
|
|
</ul></dd>
|
|
|
|
<dt><tt>union {...} <b>target</b></tt></dt>
|
|
<dd>Data used in implementing the privilege. The union has two
|
|
members:
|
|
<ul>
|
|
<li class="spaced"><tt>struct {...} <b>cumode</b></tt>: Used for
|
|
<tt>CL_SET_MODE</tt> and <tt>CL_CLEAR_MODE</tt>. Includes
|
|
three fields:
|
|
<ul>
|
|
<li><tt>const char *<b>modes</b></tt>: The string of
|
|
mode(s) to set on the client.</li>
|
|
<li><tt>int <b>cont</b></tt>: Used to "chain" privileges
|
|
together, so that only the first applicable mode
|
|
set is used (used for <tt>AUTOOP</tt>,
|
|
<tt>AUTOHALFOP</tt>, and <tt>AUTOVOICE</tt>).</li>
|
|
<li><tt>int32 <b>flags</b></tt>: The mode flags equivalent
|
|
to <tt>modes</tt>. Set at module initialization
|
|
time (this field can be left uninitialized).</li>
|
|
</ul></li>
|
|
<li class="spaced"><tt>struct {...} <b>cmd</b></tt>: Used for
|
|
<tt>CL_ALLOW_CMD</tt>. Includes two string fields:
|
|
<ul>
|
|
<li><tt>const char *<b>main</b></tt>: The relevant command
|
|
name.</li>
|
|
<li><tt>const char *<b>sub</b></tt>: The relevant subcommand
|
|
for the given command, or <tt>NULL</tt> if none.</li>
|
|
</ul></li>
|
|
</ul></dd>
|
|
</dl>
|
|
|
|
<p>In addition to the <tt>levelinfo[]</tt> table itself, exported for use
|
|
by other modules (particularly the <tt>http/dbaccess</tt> module, described
|
|
in <a href="8.html#s2-7">section 8-2-7</a>), the file's initialization
|
|
routine <tt>init_access()</tt> copies relevant parts of the table into two
|
|
local arrays indexed by privilege (<tt>CA_*</tt> value):
|
|
<tt>def_levels[]</tt>, containing each entry's default access level (the
|
|
<tt>defval</tt> field), and <tt>lev_is_max[]</tt>, containing a boolean
|
|
indication of whether the privilege's access level is a maximum (whether
|
|
the <tt>action</tt> field has <tt>CL_LESSEQUAL</tt> set).</p>
|
|
|
|
<p>The main portion of <tt>access.c</tt> defines the following utility
|
|
functions for use by the main ChanServ module and the two access list
|
|
manipulation modules. Several of the routines are exported for use by
|
|
external modules as well.</p>
|
|
|
|
<dl>
|
|
<dt><tt>int <b>get_ci_level</b>(const ChannelInfo *<i>ci</i>, int <i>what</i>)</tt></dt>
|
|
<dd><i>Exported routine.</i> Returns the given channel's level for the
|
|
given privilege, translating <tt>ACCLEV_DEFAULT</tt> in the
|
|
channel's <tt>levels[]</tt> into the appropriate default value.
|
|
Returns <tt>ACCLEV_INVALID</tt> (and logs an error message) on
|
|
invalid parameters.</dd>
|
|
|
|
<dt><tt>int <b>check_access</b>(const User *<i>user</i>, const ChannelInfo *<i>ci</i>, int <i>what</i>)</tt></dt>
|
|
<dd><i>Exported routine.</i> Returns whether the given client has the
|
|
given privilege (<tt><i>what</i></tt>) on the given channel. Due
|
|
to an unfortunate coincidence of terms, this routine can seem
|
|
confusing to call for the <tt>CA_NOJOIN</tt> "privilege": the
|
|
return value will be 1 if the client does <i>not</i> "have access
|
|
to" the channel, <i>i.e.</i> matches the <tt>CA_NOJOIN</tt>
|
|
privilege.</dd>
|
|
|
|
<dt><tt>int check_access_if_idented(const User *user, const ChannelInfo *ci, int what)</tt></dt>
|
|
<dd><i>Exported routine.</i> Like <tt>check_access()</tt>, but returns
|
|
what the result would be if the client was identified for its
|
|
nickname. (If the nickname is not registered, the return value
|
|
will be the same as for <tt>check_access()</tt>.</dd>
|
|
|
|
<dt><tt>int check_access_cmd(const User *user, const ChannelInfo *ci, const char *command, const char *subcommand)</tt></dt>
|
|
<dd><i>Exported routine.</i> Returns whether the client is allowed to
|
|
use the given command on the given channel. If the check is being
|
|
made for a specific subcommand, that subcommand is specified in
|
|
<tt><i>subcommand</i></tt>; otherwise, <tt><i>subcommand</i></tt>
|
|
is <tt>NULL</tt>.</dd>
|
|
|
|
<dt><tt>int <b>get_access</b>(const User *<i>user</i>, const ChannelInfo *<i>ci</i>)</tt></dt>
|
|
<dd>Returns the given client's access level on the given channel,
|
|
taking into account whether the client has identified for its
|
|
nickname with respect to the nickname and channel <tt>SECURE</tt>
|
|
options.</dd>
|
|
|
|
<dt><tt>static int get_access_if_idented(const User *user, const ChannelInfo *ci)</tt></dt>
|
|
<dd>Like <tt>get_access()</tt>, but returns the access level associated
|
|
with the client's nickname regardless of whether the client has
|
|
identified or not.</dd>
|
|
|
|
<dt><tt>int check_access_cumode(const User *user, const ChannelInfo *ci, int32 newmodes, int32 changemask)</tt></dt>
|
|
<dd>Checks the channel user modes of a client on a channel, returning a
|
|
bitmask of modes that are to be changed (in other words, the
|
|
bitwise exclusive-or of the current and resulting mode sets).
|
|
<tt><i>newmodes</i></tt> is the client's current set of modes on
|
|
the channel; <tt><i>changemask</i></tt> indicates which modes
|
|
changed due to the action that caused this function to be called.</dd>
|
|
|
|
<dt><tt>int access_add(ChannelInfo *ci, const char *nick, int level, int uacc)</tt>
|
|
<br/><tt>int access_del(ChannelInfo *ci, const char *nick, int uacc)</tt></dt>
|
|
<dd>Adds (<tt>access_add()</tt>) or deletes (<tt>access_del()</tt>) an
|
|
entry to or from the given channel's access list for the nickname
|
|
group to which <tt><i>nick</i></tt> belongs. <tt><i>level</i></tt>
|
|
is the access level for the new entry, and <tt><i>uacc</i></tt> is
|
|
the access level of the client making the change. Both routines
|
|
return one of the following result codes, defined in
|
|
<tt>cs-local.h</tt>:
|
|
<ul>
|
|
<li><b><tt>RET_ADDED</tt></b> (<tt>access_add()</tt> only): The
|
|
target nickname did not previously exist on the channel's
|
|
access list, and was successfully added.</li>
|
|
<li><b><tt>RET_CHANGED</tt></b> (<tt>access_add()</tt> only): The
|
|
target nickname previously had a different access level on
|
|
the channel, and the access level was successfully
|
|
changed.</li>
|
|
<li><b><tt>RET_UNCHANGED</tt></b> (<tt>access_add()</tt> only): The
|
|
target nickname already had the desired access level on the
|
|
channel.</li>
|
|
<li><b><tt>RET_DELETED</tt></b> (<tt>access_del()</tt> only): The
|
|
target nickname was successfully deleted from the channel's
|
|
access list.</li>
|
|
<li><b><tt>RET_LISTED</tt></b> (used by other modules): The target
|
|
nickname was listed.</li>
|
|
<li><b><tt>RET_PERMISSION</tt>:</b> The calling user does not have
|
|
permission to make the requested change.</li>
|
|
<li><b><tt>RET_NOSUCHNICK</tt>:</b> The given nickname is not
|
|
registered.</li>
|
|
<li><b><tt>RET_NICKFORBID</tt>:</b> The given nickname is
|
|
forbidden.</li>
|
|
<li><b><tt>RET_LISTFULL</tt></b> (<tt>access_add()</tt> only): The
|
|
channel's access list is full and the given nickname is not
|
|
already on the list.</li>
|
|
<li><b><tt>RET_NOENTRY</tt></b> (<tt>access_del()</tt> only): The
|
|
given nickname does not exist on the channel's access
|
|
list.</li>
|
|
<li><b><tt>RET_INTERR</tt></b>: An internal error occurred.</li>
|
|
</ul>
|
|
Note that successful return codes are positive, while failure
|
|
return codes are negative (zero is not used as a return code).</dd>
|
|
</dl>
|
|
|
|
<p><tt>access.c</tt> also includes initialization code in the
|
|
<tt>init_access()</tt> routine, called from the <tt>chanserv/main</tt>
|
|
module's <tt>init_module()</tt>. This routine initializes the
|
|
<tt>def_levels[]</tt> and <tt>lev_is_max[]</tt> arrays, as well as the
|
|
<tt>flags</tt> field of the <tt>target.cumode</tt> structure for privileges
|
|
that set or clear modes; it also disables (by removing from the table) any
|
|
privileges for features not supported by the IRC protocol in use.
|
|
<i>Implementation note: This relies on the current design of Services, in
|
|
which the protocol module will never be changed while the program is
|
|
running. If this design is changed, the code will need to be updated to
|
|
leave the appropriate entries in the table, perhaps by adding a "disabled"
|
|
flag or field to each entry.</i></p>
|
|
|
|
<p>There is also a corresponding <tt>exit_access()</tt> routine; it does
|
|
nothing, but is included for completeness.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-2-2">7-4-2-2. Manipulation via <tt>ACCESS</tt> and <tt>LEVELS</tt></h5>
|
|
|
|
<p>The <tt>chanserv/access-levels</tt> module is one of two modules for
|
|
manipulating channel access lists. It provides direct access to both the
|
|
channel access list itself, via the <tt>ACCESS</tt> command, and channels'
|
|
privilege level settings, via the <tt>LEVELS</tt> command.</p>
|
|
|
|
<p>Since the functions available in both commands change depending on
|
|
runtime parameters, their help messages are handled by a "<tt>HELP</tt>"
|
|
callback function, <tt>do_help()</tt>. In addition to description of the
|
|
two commands, a third help option, <tt>LEVELS DESC</tt> provides
|
|
descriptions of the available channel privileges; unlike most other help
|
|
messages, the text is generated on the fly from the <tt>levelinfo[]</tt>
|
|
array and corresponding description strings.</p>
|
|
|
|
<p>The <tt>ACCESS</tt> command handler, <tt>do_access()</tt>, calls one of
|
|
several subroutines to perform each of the available actions, depending on
|
|
the subcommand given. Two of these, <tt>do_access_add()</tt> and
|
|
<tt>do_access_del()</tt>, simply call the <tt>access_add()</tt> and
|
|
<tt>access_del()</tt> routines mentioned in <a href="#s4-2-1">section
|
|
7-4-2-1</a>, sending appropriate result messages to the calling client
|
|
depending on the return codes from those functions.
|
|
<tt>do_access_list()</tt> and <tt>do_access_listlevel()</tt> select access
|
|
entries for listing based on the given parameters, then call
|
|
<tt>access_list()</tt>, defined below the <tt>ACCESS</tt> subcommand
|
|
handlers, to actually send the list text to the calling client. (The
|
|
<tt><i>sent_header</i></tt> parameter to <tt>access_list()</tt> is used to
|
|
to record whether a list header message has been sent.) The final
|
|
subcommand handler, <tt>do_access_count()</tt>, simply counts up the number
|
|
of (active) access entries in the list and sends that in a response message
|
|
to the calling client.</p>
|
|
|
|
<p>The <tt>LEVELS</tt> command also has several subcommands, though these
|
|
are all handled within the command handler <tt>do_levels()</tt>. The
|
|
command implementation itself is fairly straightforward, with <tt>SET</tt>
|
|
and <tt>DISABLE</tt> searching the <tt>levelinfo[]</tt> array for the named
|
|
privilege, and <tt>LIST</tt> iterating through that array to display the
|
|
current level for each privilege.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s4-2-3">7-4-2-3. Manipulation via <tt>XOP</tt></h5>
|
|
|
|
<p>The <tt>chanserv/access-xop</tt> module, defined in <tt>access-xop.c</tt>,
|
|
provides the <tt>XOP</tt> command set for managing channel access lists.
|
|
While these commands present the access list as several distinct lists
|
|
(SOP, AOP, and so on), they are in fact only different views of the same
|
|
channel access list, each containing nicknames with a particular predefined
|
|
access level. This has the following side effects:</p>
|
|
|
|
<ul>
|
|
<li class="spaced">A particular nickname cannot be present on two or more
|
|
lists simultaneously (this would require having two entries on the
|
|
access list for the same nickname). Attempting to add a nickname
|
|
that is already on a different list is treated as an access level
|
|
change.</li>
|
|
|
|
<li class="spaced">Changes to the access list made by the <tt>ACCESS</tt>
|
|
command will also be reflected in the relevant XOP lists. If the
|
|
<tt>ACCESS</tt> command is used to add a nickname at a level not
|
|
associated with any <tt>XOP</tt> command, that nickname will be
|
|
invisible from every <tt>XOP</tt> list, even if it would have
|
|
privileges associated with one or more of the lists.
|
|
<i>Implementation note: One could conceivably change the <tt>XOP
|
|
LIST</tt> commands to list all entriess in intermediate or extreme
|
|
ranges; for example, nicknames with an access level between
|
|
<tt>ACCLEV_AOP</tt> and <tt>ACCLEV_SOP</tt> could be shown on the
|
|
AOP list, and nicknames with an access level below
|
|
<tt>ACCLEV_NOP</tt> could be shown on the NOP list.</i></li>
|
|
|
|
<li class="spaced">If the channel's privilege levels are changed (such as
|
|
with the <tt>LEVELS</tt> command), the <tt>XOP</tt> commands may no
|
|
longer perform as their help messages describe, because the XOP
|
|
lists are associated with hardcoded access levels rather than
|
|
specific privileges. <i>Implementation note: Attempting to base
|
|
the lists on privilege levels could have unusual results if, for
|
|
example, the auto-op privilege was lowered below auto-voice.</i></li>
|
|
</ul>
|
|
|
|
<p>The commands themselves are handled by a single central routine,
|
|
<tt>handle_xop()</tt>, which is called from each command's particular
|
|
handler with the appropriate access level. <tt>handle_xop()</tt> works
|
|
much like <tt>do_access()</tt> from <tt>access-levels.c</tt> (in fact, it
|
|
is a modified copy of <tt>do_access()</tt>, and its subroutines are
|
|
likewise derived from the handlers for the <tt>ACCESS</tt> subcommands).
|
|
Response messages include the list name (SOP, AOP, etc.) as a parameter in
|
|
the message, and this name is determined based on the command's access
|
|
level using the <tt>XOP_LISTNAME</tt> macro at the top of the file.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<h3 class="subsection-title" id="s5">7-5. MemoServ</h3>
|
|
|
|
<p>The MemoServ pseudoclient serves as an adjunct to NickServ, allowing
|
|
short messages (memos) to be sent between users and storing those messages
|
|
for the recipient. Like other pseudoclients, the majority of MemoServ's
|
|
functionality is implemented in the <tt>memoserv/main</tt> module,
|
|
described below in <a href="#s5-1">section 7-5-1</a>; additional modules
|
|
allow users to maintain "ignore" lists (<tt>memoserv/ignore</tt>,
|
|
<a href="#s5-2">section 7-5-2</a>) and have memos forwarded via E-mail
|
|
(<tt>memoserv/forward</tt>, <a href="#s5-3">section 7-5-3</a>).</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s5-1">7-5-1. MemoServ core functionality</h4>
|
|
|
|
<p>Unlike OperServ, NickServ, and ChanServ, MemoServ only provides
|
|
additional functionality to users (nicknames and, to an extent, channels)
|
|
already registered with Services. For this reason, the main MemoServ
|
|
code is comparatively simple, and the <tt>memoserv/main</tt> module is
|
|
implemented by a single source file, <tt>main.c</tt>. There is also a
|
|
header file, <tt>memoserv.h</tt>, containing structures and definitions
|
|
used by MemoServ.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s5-1-1">7-5-1-1. Memo data structures</h5>
|
|
|
|
<p>The structure used to store memos are defined in the header file
|
|
<tt>memoserv.h</tt>. Each memo is stored in a structure called
|
|
(appropriately enough) <tt>Memo</tt>, and the set of memos belonging to a
|
|
particular nickname group is stored in a <tt>MemoInfo</tt> structure. The
|
|
<tt>Memo</tt> structure contains the following fields:</p>
|
|
|
|
<dl>
|
|
<dt><tt>uint32 <b>number</b></tt></dt>
|
|
<dd>The index number associated with this memo, for use with MemoServ
|
|
commands such as <tt>READ</tt> and <tt>DEL</tt>. Note that while
|
|
the array of memos in a <tt>MemoInfo</tt> structure is kept free of
|
|
holes caused by memo deletions, memo index numbers do not change
|
|
except as a result of the <tt>RENUMBER</tt> command, so there is
|
|
not a one-to-one mapping between the two.</dd>
|
|
|
|
<dt><tt>int16 <b>flags</b></tt></dt>
|
|
<dd>Flags associated with the memo. Zero or more of the following
|
|
constants, OR'd together:
|
|
<ul>
|
|
<li><b><tt>MF_UNREAD</tt>:</b> The memo has not yet been read.</li>
|
|
<li><b><tt>MF_EXPIREOK</tt>:</b> The memo is allowed to expire.
|
|
<i>Implementation note: The sense of this flag is opposite
|
|
that of MemoServ's design, which expires any memos except
|
|
those explicitly locked with the <tt>SAVE</tt> command.
|
|
This was done to keep compatibility with memos sent using
|
|
old versions of Services, in which memos did not expire;
|
|
rather than explicitly adding a "locked" flag to every memo
|
|
when loading databases from such a version, the flag takes
|
|
advantage of the fact that the corresponding bit in such
|
|
memos is zero. This way, for a memo to expire, the flag
|
|
must be explicitly enabled by the new version when the memo
|
|
is sent.</i></li>
|
|
</ul></dd>
|
|
|
|
<dt><tt>time_t <b>time</b></tt></dt>
|
|
<dd>The timestamp when the memo was sent.</dd>
|
|
|
|
<dt><tt>time_t <b>firstread</b></tt></dt>
|
|
<dd>The time at which the memo was first read by its recipient. This
|
|
is stored to ensure that an unread memo does not expire immediately
|
|
after it is first read (the <tt>MSExpireDelay</tt> configuration
|
|
option controls how long MemoServ will wait after this timestamp
|
|
before expiring the memo).</dd>
|
|
|
|
<dt><tt>char <b>sender</b>[NICKMAX]</tt></dt>
|
|
<dd>The nickname of the client that sent the memo.</dd>
|
|
|
|
<dt><tt>char *<b>channel</b></tt></dt>
|
|
<dd>For memos sent to channels, the name of the channel to which the
|
|
memo was sent. <tt>NULL</tt> for other memos.</dd>
|
|
|
|
<dt><tt>char *<b>text</b></tt></dt>
|
|
<dd>The text of the memo.</dd>
|
|
</dl>
|
|
|
|
<p>The <tt>MemoInfo</tt> structure contains:</p>
|
|
|
|
<dl>
|
|
<dt><tt>Memo *<b>memos</b></tt>
|
|
<br/><tt>int16 <b>memos_count</b></tt></dt>
|
|
<dd>A variable-length array containing the memos associated with this
|
|
structure.</dd>
|
|
|
|
<dt><tt>int16 <b>memomax</b></tt></dt>
|
|
<dd>The maximum number of memos this nickname group is allowed to have
|
|
stored. Either a nonnegative literal value (zero is allowed, and
|
|
means that the nickname group cannot receive any memos at all), or
|
|
one of the following constants:
|
|
<ul>
|
|
<li><b><tt>MEMOMAX_UNLIMITED</tt>:</b> There is no limit on the
|
|
number of nicknames that can be stored.</li>
|
|
<li><b><tt>MEMOMAX_DEFAULT</tt>:</b> The default limit, set by the
|
|
<tt>MSMaxMemos</tt> configuration option, is used. (If
|
|
<tt>MSMaxMemos</tt> is changed, the new limit will be
|
|
automatically applied.)</li>
|
|
</ul>
|
|
The constant <tt>MEMOMAX_MAX</tt> is also provided to give the
|
|
largest possible maximum value (a side-effect of storing the value
|
|
in 16 bits).</dd>
|
|
</dl>
|
|
|
|
<p>Each <tt>MemoInfo</tt> structure is stored as part of the corresponding
|
|
nickname group's <tt>NickGroupInfo</tt> structure (see
|
|
<a href="#s3-1-1">section 7-3-1-1</a>). In previous versions, channels had
|
|
associated memo lists as well, but this feature was removed for version 5.1
|
|
in favor of the current system of distributing channel memos to privileged
|
|
users.</p>
|
|
|
|
<p>There are two more constants in <tt>memoserv.h</tt>:
|
|
<tt>MS_RECEIVE_PRI_CHECK</tt> and <tt>MS_RECEIVE_PRI_DELIVER</tt>. These
|
|
are callback priorities that can be used with <tt>add_callback_pri()</tt>
|
|
to allow a "<tt>receive memo</tt>" callback function to ensure it is called
|
|
before functions which try to deliver the memo. Both the
|
|
<tt>memoserv/ignore</tt> and <tt>memoserv/forward</tt> modules take
|
|
advantage of these constants.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h5 class="subsubsubsection-title" id="s5-1-2">7-5-1-2. The <tt>memoserv/main</tt> module</h5>
|
|
|
|
<p>The <tt>memoserv/main</tt> module is defined in the file <tt>main.c</tt>,
|
|
and aside from the lack of any auxiliary source files, its structure is
|
|
more or less the same as that of the other pseudoclients' core modules.</p>
|
|
|
|
<p>Since, as mentioned above, the <tt>MemoInfo</tt> structures containing
|
|
memo data are stored as part of the corresponding <tt>NickGroupInfo</tt>
|
|
structures, special handling is required when moving the data to or from
|
|
persistent storage. MemoServ uses iterator functions for the
|
|
<tt>MemoInfo</tt> table that in turn iterate through <tt>NickGroupInfo</tt>
|
|
structures, and sets up a dummy record containing each record's associated
|
|
nickname group ID, much like the <tt>nickserv/access</tt> module (see
|
|
<a href="#s3-2">section 7-3-2</a>). For individial <tt>Memo</tt> records,
|
|
an additional iterator is used to loop through each memo in every
|
|
<tt>MemoInfo</tt> structure.</p>
|
|
|
|
<p>Most MemoServ actions are implemented by separate routines in the
|
|
"MemoServ private routines" section of the file, rather than directly in
|
|
the command handlers. These functions are:</p>
|
|
<ul>
|
|
<li><b><tt>send_memo()</tt></b> and <b><tt>send_chan_memo()</tt></b>,
|
|
which send memos to users and channels respectively;</li>
|
|
<li><b><tt>list_memo()</tt></b>, which sends the calling client a one-line
|
|
description of a memo, and <b><tt>list_memo_callback()</tt></b>, a
|
|
callback function to do the same thing;</li>
|
|
<li><b><tt>read_memo()</tt></b> and <b><tt>read_memo_callback</tt></b>,
|
|
which display the text of a memo;</li>
|
|
<li><b><tt>save_memo()</tt></b> and <b><tt>save_memo_callback</tt></b>,
|
|
which mark memos as non-expiring; and</li>
|
|
<li><b><tt>del_memo()</tt></b> and <b><tt>del_memo_callback()</tt></b>,
|
|
which delete memos.</li>
|
|
</ul>
|
|
|
|
<p>Other utility routines include:</p>
|
|
|
|
<dl>
|
|
<dt><tt>void <b>check_memos</b>(User *<i>u</i>)</tt></dt>
|
|
<dd>Checks whether the given client's nickname group has any unread
|
|
memos, sending an appropriate message to the client if so (and if
|
|
the <tt>NF_MEMO_SIGNON</tt> flag is set for the nickname group).
|
|
Called by the "<tt>user create</tt>" callback function
|
|
<tt>do_user_create()</tt> if the client is recognized.</dd>
|
|
|
|
<dt><tt>void <b>expire_memos</b>(MemoInfo *<i>mi</i>)</tt></dt>
|
|
<dd>Deletes all memos in the given <tt>MemoInfo</tt> structure that
|
|
are eligible for expiration.</dd>
|
|
|
|
<dt><tt>MemoInfo *<b>get_memoinfo</b>(const char *<i>name</i>, NickGroupInfo **<i>owner_ret</i>, int *<i>error_ret</i></tt></dt>
|
|
<dd>Returns the <tt>MemoInfo</tt> structure corresponding to the given
|
|
nickname, or <tt>NULL</tt> on error. On success,
|
|
<tt>*<i>owner_ret</i></tt> is set to point to the
|
|
<tt>NickGroupInfo</tt> structure for the corresponding nickname
|
|
group; on error, <tt>*<i>error_ret</i></tt> is set to one of the
|
|
following error codes:
|
|
<ul>
|
|
<li><b>GMI_INTERR</b>: An internal error occurred.</li>
|
|
<li><b>GMI_FORBIDDEN</b>: The given nickname is forbidden.</li>
|
|
<li><b>GMI_SUSPENDED</b>: The given nickname is suspended.</li>
|
|
<li><b>GMI_NOTFOUND</b>: The given nickname is not registered.</li>
|
|
</ul>
|
|
<i>Implementation note: This function's primary purpose of handling
|
|
both nicknames and channel names transparently was obsoleted with
|
|
the redesign of channel memo handling; however, the function still
|
|
serves the purpose of checking for unregistered, forbidden, or
|
|
suspended nicknames, eliminating the necessity to include such
|
|
checks directly in every command handler.</i></dd>
|
|
</dl>
|
|
|
|
<p>The command handlers are for the most part simple, only needing to parse
|
|
parameters and call the appropriate utility routine. The two commands
|
|
which are implemented directly, <tt>SET</tt> and <tt>INFO</tt>, are mostly
|
|
branching trees for the various nickname options and statuses.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s5-2">7-5-2. Memo ignore lists</h4>
|
|
|
|
<p>The <tt>memoserv/ignore</tt> module was added to allow users a way to
|
|
block memos from unwanted senders such as spammers. The module is
|
|
implemented by the source file <tt>ignore.c</tt>.</p>
|
|
|
|
<p>For the most part, <tt>ignore.c</tt> works in the same way as the
|
|
<tt>nickserv/access</tt> and <tt>nickserv/autojoin</tt> modules. The
|
|
one routine unique to <tt>memoserv/ignore</tt> is the
|
|
<tt>check_if_ignored()</tt> routine, added as a callback function to the
|
|
core MemoServ module's "<tt>receive memo</tt>" callback at priority
|
|
<tt>MS_RECEIVE_PRI_CHECK</tt>. The routine runs through the nickname
|
|
group's ignore list twoce: the first time, it treats each entry as a
|
|
<tt><i>Nick</i>!<i>user</i>@<i>host</i></tt> mask which is compared against
|
|
that of the memo sender, while the second time, it treats each entry as a
|
|
nickname, and the memo is blocked if that nickname's nickname group ID
|
|
matches that of the sender (thus proventing malicious clients from evading
|
|
the block by simply changing to another linked nick). If thd memo is
|
|
blocked, the routine returns the language string index
|
|
<tt>MEMO_C_GETS_NO_MEMOS</tt> (the same as is used if the target has a
|
|
memo limit of zero), to prevent leaking information about the ignore list
|
|
contents.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s5-3">7-5-3. Memo forwarding</h4>
|
|
|
|
<p>The <tt>memoserv/forward</tt> module, defined in the source file
|
|
<tt>forward.c</tt>, provides an interface between memos in Services and an
|
|
external mail system. As such, one of its prerequisite modules (other than
|
|
<tt>memoserv/main</tt>, of course) is <tt>nickserv/mail-auth</tt>, to
|
|
reduce the possibility of Services being abused to send mail to arbitrary
|
|
addresses.</p>
|
|
|
|
<p>The module includes two methods of forwarding memos: manual and
|
|
automatic. The former, manual forwarding, is done through the
|
|
<tt>FORWARD</tt> command, implemented by the <tt>do_forward()</tt> routine
|
|
and its subroutines <tt>fwd_memo()</tt> and <tt>fwd_memo_callback()</tt>.
|
|
As with other commands that allow users to send mail via Services, the
|
|
command handler includes a check that the command is not used more
|
|
frequently than the configuration option <tt>MSForwardDelay</tt> specifies.
|
|
If this and the other checks pass, the command handler takes one of two
|
|
actions, depending on its parameter. If "<tt>ALL</tt>" was given, then
|
|
<tt>fwd_memo()</tt> is called for each memo in the calling client's
|
|
<tt>MemoInfo</tt> structure; otherwise, the parameter is passed to
|
|
<tt>process_numlist()</tt> to call the <tt>fwd_memo_callback()</tt>
|
|
callback function (which in turn calls <tt>fwd_memo()</tt>) for each memo
|
|
specified in the index list.</p>
|
|
|
|
<p><tt>fwd_memo()</tt> itself does not perform the actual sending of the
|
|
mail message; rather, it accumulates message body text in its parameters
|
|
<tt>char **<i>p_body</i></tt> and <tt>int *<i>p_bodylen</i></tt> (both of
|
|
which are modified as a result of the routine). This allows the caller to
|
|
combine multiple memos into a single message without knowing ahead of time
|
|
how many or which memos are to be sent.</p>
|
|
|
|
<p>The second method of forwarding, automatic forwarding on receipt, is
|
|
implemented by the <tt>do_receive_memo()</tt> function, attached to the
|
|
MemoServ "<tt>receive memo</tt>" callback, along with the
|
|
<tt>do_set_forward()</tt> option handler (attached to the "<tt>SET</tt>"
|
|
callback) to set forwarding options for a nickname group. Of the
|
|
forwarding options, <tt>ON</tt> (flag <tt>NF_MEMO_FWD</tt>) and
|
|
<tt>OFF</tt> (no flags set) are fairly obvious. For <tt>COPY</tt> (flags
|
|
<tt>NF_MEMO_FWD</tt> and <tt>NF_MEMO_FWDCOPY</tt>), however, it is worth
|
|
noting that (as also mentioned in the user's manual and help messages)
|
|
memos will be rejected when the recipient has a full list of memos. This
|
|
is partly a result of the callback implementation, in the sense that the
|
|
check for a full memo list is made before the callback is ever called; but
|
|
it also ensures that every memo received by the user is either processed
|
|
completely (both forwarded and saved) or not at all. This can be important
|
|
if, for example, a user uses <tt>SET FORWARD COPY</tt> to save copies of
|
|
memos to an archival E-mail address, but only uses Services to actually
|
|
read the memos. If a memo that would overflow the list was nonetheless
|
|
forwarded and treated as "sent", the sender would be left wondering why the
|
|
recipient was not responding to the purportedly delivered memo.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<h3 class="subsection-title" id="s6">7-6. StatServ</h3>
|
|
|
|
<p>The StatServ pseudoclient is intended to record and provide network
|
|
statistics; however, mostly due to lack of developer interest, it has
|
|
floundered. The module is defined in the <tt>modules/statserv</tt>
|
|
directory by a single source file, <tt>main.c</tt>, and an accompanying
|
|
header file, <tt>statserv.h</tt>.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s6-1">7-6-1. StatServ data structures</h4>
|
|
|
|
<p>The current implementation of StatServ only records data for servers.
|
|
This data is stored in a <tt>ServerStats</tt> structure, defined in
|
|
<tt>statserv.h</tt> and containing the following fields:</p>
|
|
|
|
<dl>
|
|
<dt><tt>ServerStats *<b>next</b>, *<b>prev</b></tt></dt>
|
|
<dd>Used to maintain the linked list of <tt>ServerStats</tt>
|
|
structures.</dd>
|
|
|
|
<dt><tt>char *<b>name</b></tt></dt>
|
|
<dd>The name of the server to which this structure applies.</dd>
|
|
|
|
<dt><tt>time_t <b>t_join</b></tt></dt>
|
|
<dd>The timestamp at which the server most recently joined the network.
|
|
Zero if the server is not currently connected.</dd>
|
|
|
|
<dt><tt>time_t <b>t_quit</b></tt></dt>
|
|
<dd>The timestamp at which the server last disconnected. Zero if
|
|
Services has never seen the server disconnect.</dd>
|
|
|
|
<dt><tt>char *<b>quit_message</b></tt></dt>
|
|
<dd>The quit message used the last time the server disconnected.
|
|
<tt>NULL</tt> if Services has never seen the server disconnect.</dd>
|
|
|
|
<dt><tt>int <b>usercnt</b>, <b>opercnt</b></tt></dt>
|
|
<dd>The current number of clients and IRC operators on the server.</dd>
|
|
</dl>
|
|
|
|
<p>As the data has no direct references to other data stored persistently,
|
|
saving the <tt>ServerStats</tt> records is straightforward.</p>
|
|
|
|
<p>There are also commented-out definitions for <tt>MinMax</tt> and
|
|
<tt>MinMaxHistory</tt> structures, and <tt>MinMaxHistory</tt> fields in
|
|
<tt>ServerStats</tt>; these were originally intended for keeping a history
|
|
of client and operator counts on each server, but this functionality was
|
|
never implemented.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s6-2">7-6-2. The StatServ module</h4>
|
|
|
|
<p>The module itself, defined in <tt>main.c</tt>, is likewise quite simple.
|
|
It follows the general layout of other pseudoclient core modules, though it
|
|
only has two commands (other than <tt>HELP</tt>) and a few callback
|
|
routines.</p>
|
|
|
|
<p>The two commands, <tt>SERVERS</tt> and <tt>USERS</tt>, are handled by
|
|
<tt>do_servers()</tt> and <tt>do_users()</tt> respectively. The latter
|
|
needs no additional explanation, as it only sends the calling client the
|
|
user and operator counts it maintains internally (via the client-related
|
|
callbacks mentioned below). <tt>do_servers()</tt> is only slightly more
|
|
complex, handling four distinct subcommands. Three of these
|
|
(<tt>STATS</tt>, <tt>LIST</tt>, and <tt>VIEW</tt>) extract information from
|
|
the <tt>ServerStats</tt> structures and send them to the calling client;
|
|
the last, <tt>DELETE</tt>, allows a structure to be deleted, and is
|
|
protected by an <tt>is_services_admin()</tt> check in the <tt>if</tt>
|
|
chain.</p>
|
|
|
|
<p>In order to keep track of statistics, StatServ naturally relies on
|
|
callbacks. To watch for connecting and disconnecting servers, StatServ
|
|
adds callback functions to the core's "<tt>server create</tt>" and
|
|
"<tt>server delete</tt>" callbacks; these functions update the relevant
|
|
<tt>ServerStats</tt> structures as servers join and leave the network, with
|
|
<tt>stats_do_server()</tt> (the "<tt>server create</tt>" handler) creating
|
|
a new <tt>ServerStats</tt> structure if it sees a server connect that is
|
|
not recorded in the <tt>ServerStats</tt> structure table.</p>
|
|
|
|
<p>StatServ also uses the "<tt>user create</tt>", "<tt>user delete</tt>",
|
|
and "<tt>user MODE</tt>" callbacks to keep track of the number of clients
|
|
and IRC operators on the network, stored in the file-scope variables
|
|
<tt>usercnt</tt> and <tt>opcnt</tt> respectively. Note that this overlaps
|
|
slightly with the maximum client count maintained by OperServ; a good
|
|
argument could be made for moving this functionality to StatServ as well,
|
|
but it has been left in OperServ for historical reasons (the maximum client
|
|
count monitoring functionality has been part of OperServ since the earliest
|
|
versions of Services, long before StatServ existed).</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<h3 class="subsection-title" id="s7">7-7. Miscellaneous pseudoclients</h3>
|
|
|
|
<p>Aside from the standard pseudoclients listed above, there are two
|
|
additional pseudoclients, HelpServ and DevNull, provided in case they can
|
|
be of use. These pseudoclients are disabled in the default Services
|
|
configuration. These modules are so simple as to not warrant their own
|
|
subdirectories, and are stored in the <tt>modules/misc</tt> directory.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s7-1">7-7-1. HelpServ</h4>
|
|
|
|
<p>The HelpServ pseudoclient, defined in <tt>modules/misc/helpserv.c</tt>,
|
|
uses the parameters given in each <tt>PRIVMSG</tt> it receives to form a
|
|
pathname for a text file, which (if it exists) is then sent to the calling
|
|
client as <tt>NOTICE</tt>s. The pathname is formed by concatenating the
|
|
path given by the <tt>HelpDir</tt> module configuration setting (the
|
|
example configuration file uses <tt>helpfiles</tt>, relative to the
|
|
Services data directory) with each of its space-separated parameters,
|
|
inserting a path separator ("<tt>/</tt>") between each element. To allow
|
|
the parameter to be case-insensitive even when using case-sensitive
|
|
filesystems, all uppercase characters in the parameters are lowercased.</p>
|
|
|
|
<p>Since this involves access to a file specified by an arbitrary
|
|
user-specified string, the routine must take care not to allow access to
|
|
inappropriate files. This is done by replacing all slashes and periods in
|
|
the string with underscores, to prevent a malicious user from specifying
|
|
a parameter like "<tt>../../../../../etc/passwd</tt>". (If there are
|
|
explicit symbolic links to parent directories, of course, HelpServ will
|
|
happily follow them.) <i>Implementation note: As a general rule, when
|
|
sanitizing user input like this it is better to make a set of explicitly
|
|
allowed characters and delete or convert anything outside that set.
|
|
However, pathnames on Unix systems are simple and well defined:
|
|
disregarding the effects of symbolic links, only a slash can allow access
|
|
to file entries outside of the current directory, and "<tt>.</tt>" and
|
|
"<tt>..</tt>" are the only entries that are automatically created within
|
|
each directory, so the set of allowed characters is essentially "everything
|
|
except <tt>/</tt> and <tt>.</tt>".</i></p>
|
|
|
|
<p>In the earliest versions of Services, HelpServ was a standard
|
|
pseudoclient, and other pseudoclients made use of its functionality to
|
|
display their own help messages, which were stored as text files under the
|
|
<tt>data/helpfiles</tt> directory. However, the help messages were later
|
|
moved to string data stored directly in the executable file, and then to
|
|
the current model of language files, diluting the usefulness of HelpServ
|
|
itself. The module has nonetheless been left in Services in case it is
|
|
useful for things such as providing network information.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
|
|
<h4 class="subsubsection-title" id="s7-2">7-7-2. DevNull</h4>
|
|
|
|
<p>The DevNull pseudoclient, defined in <tt>modules/misc/devnull.c</tt>, is
|
|
an extremely simple pseudoclient which, like its Unix namesake
|
|
<tt>/dev/null</tt>, simply discards any <tt>PRIVMSG</tt>s sent to it. It
|
|
is not particularly useful in the ordinary course of events, but the author
|
|
has made use of it as a default <tt>/query</tt> target to prevent messages
|
|
from going to unintended users.</p>
|
|
|
|
<p class="backlink"><a href="#top">Back to top</a></p>
|
|
|
|
<!------------------------------------------------------------------------>
|
|
<hr/>
|
|
|
|
<p class="backlink"><a href="6.html">Previous section: Database handling</a> |
|
|
<a href="index.html">Table of Contents</a> |
|
|
<a href="8.html">Next section: Other modules</a></p>
|
|
|
|
</body>
|
|
</html>
|