<CHAPTER ID="ZADM-ROUTER"><?dbhtml filename="zadm-router.html">
<TITLE><COMMAND>Router</> Configuration</TITLE>

<PARA>
The names (determined at compile-time) and interface specifications for the
routing and crossbar functions, are the only crucial ``magical'' things one
needs to contend with in a proper <COMMAND>router</> configuration.
The syntax and semantics of the configuration file's contents are dealt
with in the following subsection. The details of the two functions
introduced here are specified after that, once the necessary background
information has been given.
</PARA>

<PARA>
<COMMAND>Router</> behavior is controlled by a configuration file read at
startup. It is a <COMMAND>zmsh</>(1) script that uses facilities provided
built into the <COMMAND>router</>.
</PARA>

<PARA>
The configuration file looks like a <EMPHASIS>Bourne Shell</EMPHASIS>
script at first glance.  There are minor syntax changes from standard
<COMMAND>sh</>(1), but the aim is to be as close to the
<EMPHASIS>Bourne Shell language</EMPHASIS> as is practical.
The contents of the file are compiled into bytecode, which can then
be interpreted by the <COMMAND>router</>.
The configuration file is usually self-contained, although an easy
mechanism exists to make use of external UNIX programs when so desired.
Together with a very flexible database lookup mechanism, functions, and
address manipulation based on token-matching regular expressions, the
configuration file language is an extremely flexible substrate to
accomplish its purpose. When the language is inadequate, or if speed
becomes an issue, it is possible to call built in (C coded) functions. The
interface to these functions is mostly identical to what a standalone
program would expect (modulo symbol name clashes and return values), to
ease migration of external programs to inclusion in the <COMMAND>router</>
process.
</PARA>

<SECT1 ID=zadm-rtr-cfg-script-language>
<TITLE>Configuration File Programming Language</TITLE>

<PARA>
Whenever the <COMMAND>router</> process starts, its first action
is to read its configuration file.  The configuration file is a text
file which contains statements interpreted immediately when the file
is read.  Some statements are functions, in which case the function
is defined at that point in reading the configuration file.
The purpose of the configuration file is to provide a simple way to
customize the behavior of routing process of the mailer, and this is
primarily achieved by defining the <LINK LINKEND=zmshref.router>
<CITETITLE><FUNCTION>router</></></>, and <LINK LINKEND=zmshref.crossbar>
<CITETITLE><FUNCTION>crossbar</></></> functions.
For these to work properly, some initialization code and auxiliary
functions will usually be needed.
</PARA>

<PARA>
At first sight, a configuration file looks like a Bourne shell script.
The ideal is to duplicate the functionality, syntax, and to a large
degree the semantics, of a shell script.  Therefore, the configuration file
programming language is defined in terms of its deviation from standard
Bourne shell syntax and semantics.  The present differences are:

<ITEMIZEDLIST>
<LISTITEM><PARA>
No <FUNCTION>repeat</> statement.
</PARA></LISTITEM>

<LISTITEM><PARA>
Functions are allowed, parameter lists are allowed. If not enough
arguments are present in a function call to exhaust the parameter
list, the so-far unbound parameter variables are bound to <LITERAL>''</>
(the empty string) as local variables. For example, this is the identity
address rewriting function:
<PROGRAMLISTING>
<![CDATA[
  null (address) {
    return $address  # surprise!
  }
]]>
</PROGRAMLISTING>
</PARA></LISTITEM>

<LISTITEM><PARA>
Multiple-value returns are allowed.
The <FUNCTION>return</> statement can be used to return a non-``''
(non-empty string) value from a function.
The following are all legal <FUNCTION>return</> statements:
<PROGRAMLISTING>
<![CDATA[
  return
  return $address
  return $channel ${next_host} ${next_address}
]]>
</PROGRAMLISTING>
</PARA></LISTITEM>

<LISTITEM><PARA>
Variables are dynamically scoped, local variables are the ones in a function's
parameter list and those declared with the ``<FUNCTION>local</>'' statement.
Only the first value of a multiple-value return may be assigned to a variable.
All values are strings, so no type information, checking, or declaration, is
necessary.
</PARA></LISTITEM>

<LISTITEM><PARA>
Quoting is a bit stilted.
All quotes (double-, single-, back-), must appear in matching pairs at
the beginning and at the end of a word.
</PARA>

<PARA>
{\bf\large CHECK!}
Single quotes are not stripped, double quotes cause the enclosed character
sequence to be collected into a quoted-string RFC822 token.
</PARA>

<PARA>
For example, the statement:
<PROGRAMLISTING>
<![CDATA[
  foo `bar "`baz`"`
]]>
</PROGRAMLISTING>
is evaluated as
<PROGRAMLISTING>
<![CDATA[
  (apply 'foo (apply 'bar (baz)))
]]>
</PROGRAMLISTING>
</PARA></LISTITEM>

<LISTITEM><PARA>
<EMPHASIS ROLE=BOLD>FIXME! FIXME!</>
IFS, quoted strings, and evaluation results -- and how things differ
from usual shell languages, and how they are alike with PERL!
</PARA></LISTITEM>

<LISTITEM><PARA>
Conditional substitution forms are supported:
<PROGRAMLISTING>
<![CDATA[
  ${variable:=value}
  ${variable:-value}
  ${variable:+value}
]]>
</PROGRAMLISTING>
<EMPHASIS ROLE=BOLD>But appear to be borken ??</>
</PARA></LISTITEM>

<LISTITEM><PARA>
Patterns (in case labels) are evaluated once,
the first time they are encountered.
</PARA></LISTITEM>

<LISTITEM><PARA>
At the end of a case label, the sequentially next case labels of
the same case statement will be tried for successful pattern matching
(and the corresponding case label body executed).
The only exceptions (apart from encountering a return statement) are:
<VARIABLELIST>
<VARLISTENTRY><TERM><FUNCTION>again</></><LISTITEM><PARA>
a function which retries the current case label for a match
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>break</></><LISTITEM><PARA>
continues execution after the current case statement
</PARA></LISTITEM></VARLISTENTRY>
</VARIABLELIST>
</PARA></LISTITEM>

<LISTITEM><PARA>
A regular expressions using variant of ``<FUNCTION>case</>'', with two
flavours:
<VARIABLELIST>

<VARLISTENTRY><TERM><FUNCTION>ssift</></><LISTITEM><PARA>
A ``String Shift'' where the input string is handled as is.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>tsift</></><LISTITEM><PARA>
A ``Token Shift'' where the input string is spliced according to
RFC-822 tokenization rules.
Especially RFC-822 special characters cause tokens to split.
</PARA>

<PARA>
With ``<FUNCTION>tsift</>'' the ``<LITERAL>.</>'' (dot) becomes match
any single ``token'', that is, input string ``foo.bar'' has three
tokens: ``foo'' (atom), ``.'' (dot, special), and ``bar'' (atom).
</PARA></LISTITEM></VARLISTENTRY>
</VARIABLELIST>
</PARA>

<PARA>
Overall usage of these ``<FUNCTION>sifts</>'' is very much like that of
``<FUNCTION>case</>'', including the need for matching termination tokens:
<PROGRAMLISTING>
<![CDATA[
  ssift "$invar" in
  pattern
        statements
        ;;
  tfiss
  tsift "$invar" in
  pattern
        statements
        ;;
  tfist
]]>
</PROGRAMLISTING>
</PARA></LISTITEM>

<LISTITEM><PARA>
Various standard Bourne shell functions do not exist built in.
</PARA></LISTITEM>

<LISTITEM><PARA>
The general form of function calls in the system is:
<PROGRAMLISTING>
<![CDATA[
  $(funcname arguments)
]]>
</PROGRAMLISTING>
It returns a scalar or list object, and the result can be stored
into variables at will.
</PARA></LISTITEM>

<LISTITEM><PARA>
Relations, and other database lookups are constructed as function calls
where the relation name is the function name.
More about this later.
</PARA></LISTITEM>
</ITEMIZEDLIST>
</PARA>

<PARA>
There are currently only three entry points (i.e. magic names known to the
<COMMAND>router</> code) in the configuration scripts, namely the
<FUNCTION>process</>, the
<FUNCTION>router</>, and the
<FUNCTION>crossbar</> -functions.
</PARA>

<ITEMIZEDLIST>
<LISTITEM>
<PARA>
The <FUNCTION>process()</> script function is called with a file name
as argument. 
The file is typically located in the 
<FILENAME CLASS="DIRECTORY">$<VARNAME>POSTOFFICE</>/router/</> directory.
The <FUNCTION>process()</> is a protocol switch function which uses
the  form of the file name to determine how to process different types
of  messages.
</PARA></LISTITEM>

<LISTITEM>
<PARA>
The <FUNCTION>router()</> script function is called with an address
as argument, and returns a quad of (channel, host, user, attribs) as
three separate values,  corresponding to <EMPHASIS>the channel</>
the message should be sent out on (or, the <FUNCTION>router</> function
can also be called to check on who sent a message), <EMPHASIS>the host</> or
node name for that channel (semantics depend per what channel is in effect),
and <EMPHASIS>the address</> the receiving agent should transmit to.
<EMPHASIS>The fourth parameter</> is ``attribute'' storage
<EMPHASIS>variable name</> from which a ``privilege'' value-pair is
picked for recipient address security control functions.
</PARA></LISTITEM>

<LISTITEM>
<PARA>
The <FUNCTION>crossbar()</> function is in charge of rewriting envelope
addresses, selecting message header address munging type (a function to
be called with each message header address), and possibly doing per-message
logging or enforcing restrictions deemed necessary.
It takes a sender-quad and a receiver-quad as arguments (eight parameters
altogether).
It returns the new values for each element of the two quads, and in addition
a function name corresponding to the function to be used to rewrite header
addresses for the specific destination.
If the destination is to be ignored, returning a null function name will
accomplish this.
</PARA></LISTITEM>
</ITEMIZEDLIST>


<PARA>
There is a fourth script entrypoint used by the <COMMAND>smtpserver</>
program, namely the <FUNCTION>server()</>, which is used to implement
smtpserver's realtime support facilities for
``<USERINPUT>EXPN</>'' and ``<USERINPUT>VRFY</>'' commands, and optionally
also to process addresses in ``<USERINPUT>MAIL FROM:&lt;&hellip;&gt;</>'',
and ``<USERINPUT>RCPT TO:&lt;&hellip;&gt;</>'' commands.
</PARA>

<PARA>
The <COMMAND>router</> has several built in (C coded) functions.
Their calling sequence and interface specification is exactly
the same as for the functions defined in the configuration file.
Some of these functions have special semantics, and they fall
into three classes, as follows:

<ITEMIZEDLIST>
<LISTITEM><PARA>
Functions that are critical to the proper functioning of the configuration
file interpreter:
<VARIABLELIST>
<VARLISTENTRY><TERM><FUNCTION>return</></><LISTITEM><PARA>
returns its argument(s) as the value of a function call
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>again</></><LISTITEM><PARA>
repeats the current <FUNCTION>case</>, and <FUNCTION>*sift</> label
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>break</></><LISTITEM><PARA>
exits <FUNCTION>case</>, and <FUNCTION>*sift</> statements
</PARA></LISTITEM></VARLISTENTRY>
</VARIABLELIST>

</PARA></LISTITEM>

<LISTITEM><PARA>
Functions that are necessary to complete the capabilities of the
interpreter:

<VARIABLELIST>
<VARLISTENTRY><TERM><FUNCTION>relation</></><LISTITEM><PARA>
defines a database to the database lookup mechanism
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>sh</></><LISTITEM><PARA>
an internal function which runs its arguments as <COMMAND>/bin/sh</> would
</PARA></LISTITEM></VARLISTENTRY>
</VARIABLELIST>
</PARA></LISTITEM>

<LISTITEM><PARA>
Non-critical but recommended functions:
<VARIABLELIST>
<VARLISTENTRY><TERM><FUNCTION>getzenv</></><LISTITEM><PARA>
retrieves global ZMailer configuration values
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>echo</></><LISTITEM><PARA>
emulates <COMMAND>/bin/echo</>
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>exit</></><LISTITEM><PARA>
aborts the <COMMAND>router</> with the specified status code
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>hostname</></><LISTITEM><PARA>
internal function to get and set the system name
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>trace</></><LISTITEM><PARA>
turns on selected debugging output
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><FUNCTION>untrace</></><LISTITEM><PARA>
turns off selected debugging output
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY>
<TERM><FUNCTION>[</></>
<TERM><FUNCTION>test</></TERM>
<LISTITEM><PARA>
emulates a subset of ``<COMMAND>/bin/test</>''
(a.k.a. ``<COMMAND>/bin/[</>'') functionality.
</PARA></LISTITEM></VARLISTENTRY>
</VARIABLELIST>
</PARA></LISTITEM>
</ITEMIZEDLIST>
</PARA>

<PARA>
The <LINK LINKEND=zmshref.relation><CITETITLE><FUNCTION>relation</></></>
function is described in ``<EMPHASIS>Databases</>'', at section
<XREF LINKEND="zadm-rtr-databases">.
Functions <LINK LINKEND=zmshref.trace><CITETITLE><FUNCTION>trace</></></>,
and <LINK LINKEND=zmshref.untrace><CITETITLE><FUNCTION>untrace</></></>
are described in connection with debugging. 
</PARA>

<PARA>
See <EMPHASIS>Logging and Statistics</>, section
<XREF LINKEND="zadm-logging">.
(<EMPHASIS ROLE="BOLD">This will probably change to Reference, Router, Debugging</>)
</PARA>

<PARA>
The <LINK LINKEND=zmshref.hostname><CITETITLE><FUNCTION>hostname</></></>
function requires some further explanation.
It is intended to emulate the BSD UNIX <COMMAND>/bin/hostname</>
functionality, except that setting the hostname will only set
the <COMMAND>router</>'s idea of the hostname, not the system's.
Doing so will enable generation of ``Message-Id:'' and ``Received:''
``trace'' headers on all messages processed by the <COMMAND>router</>.
</PARA>

<PARA>
It is done this way since the <COMMAND>router</> needs to know the official
domain name of the local host in order to properly generate these headers,
and this method is cleaner than reserving a magic variable for the purpose.
</PARA>

<PARA>
The <COMMAND>router</> cannot assume the hostname reported by the system is
a properly qualified domain name, so the configuration file may generate it
using whichever method it chooses.
</PARA>

<PARA>
If the hostname indeed is a fully qualified domain name, then:
<PROGRAMLISTING>
<![CDATA[
  hostname "hostname"
]]>
</PROGRAMLISTING>
will enable generation of trace headers.
</PARA>

<PARA>
Finally, note that a symbol can have both a function-value and a string-value.
The string value is of course accessed using the ``<USERINPUT>$</>'' prefix
convention of the Bourne shell language.
</PARA>

<PARA>
<EMPHASIS>However careless usage of ``<USERINPUT>$var</>'' is
<EMPHASIS ROLE="BOLD">dangerous</>, as you may trip on following:</EMPHASIS>
<PROGRAMLISTING>
<![CDATA[
  SS="1 -o bar"
  if [ $SS = 0 ]; then
    ...
  fi
]]>
</PROGRAMLISTING>
<EMPHASIS>
In this kind of places you must tread carefully, and use double-quotes
to save your neck:
</EMPHASIS>
<PROGRAMLISTING>
<![CDATA[
  SS="1 -o bar"
  if [ "$SS" = 0 ]; then
    ...
  fi
]]>
</PROGRAMLISTING>
</PARA>

<PARA>
To test the configuration or routing data, proceed as
shown in figure <XREF LINKEND="fig.adm.testing">.
</PARA>

<FIGURE ID="fig.adm.testing" PGWIDE="1" FLOAT="1">
<TITLE>Example of running tests on <COMMAND>router</></TITLE>
<SCREEN>
<PROMPT>sh$ </><USERINPUT>$MAILBIN/router -i</>         (select interactive mode)
<PROMPT>z$ </><USERINPUT>rtrace</>                      (turn tracing on)
<PROMPT>z$ </><USERINPUT>router user@broken.address</>  (the address that gave you trouble)
<PROMPT>z$ </><USERINPUT>router another@address</>      (and so on)
</SCREEN>
</FIGURE>

<PARA>
Old salts can use ``<USERINPUT>/usr/lib/sendmail -bt</>'' instead
of ``<USERINPUT>router -i</>''.
Once satisfied that routing works, command:
<PROGRAMLISTING>
<![CDATA[
  zmailer router
]]>
</PROGRAMLISTING>
will restart the <COMMAND>router</>.
</PARA>

<PARA>
You can also run the <COMMAND>router</> directly on a message.
Copy your message to someplace other than the postoffice
(<FILENAME CLASS="DIRECTORY">/tmp/</> is usually good), to a numeric
file name.
If the file name is ``<FILENAME>123</>'', you run
<PROGRAMLISTING>
<![CDATA[
  $MAILBIN/router 123
]]>
</PROGRAMLISTING>
this will create the file ``<FILENAME>.123</>'' containing the control
information produced by the <COMMAND>router</>.
</PARA>
</SECT1>


<SECT1 ID="zadm-rtr-databases">
<TITLE>Databases</TITLE>

<PARA>
Many of the decisions and actions taken by configuration file code depend
on the specifics of the environment the MTA finds itself in.
So, not just the facts that the local host is attached to (say)
the UUCP network and a Local Area Network are important, but it
is also essential to know the specific hosts that are reachable
by this method.
Hardcoding large amounts of such information into the configuration
file is not practical.
It is also undesirable to change what is really a program
(the configuration file), when the information (the data) changes.
</PARA>

<PARA>
The desirable solution to this data abstraction problem is to provide a way
for the configuration file programmer to manage such information externally
to ZMailer, and access it from within the <COMMAND>router</>.
The logical way to do this is to have an interface to externally
maintained databases.
These databases need not be terribly complicated;
  after all the simplest kind of information needed is that a string
  is a member of some collection.
This could simply correspond to finding that string as a word in a list of
words.
</PARA>

<PARA>
However, there are many ways to organize databases, and the necessary
interfaces cannot be known in advance.
The <COMMAND>router</> therefore implements a framework that allows
flexible interfacing to databases, and easy extension to cover new types
of databases.
</PARA>

<PARA>
To use a database, two things are needed: the name of the database,
and a way of retrieving the data associated with a particular key from
that database.
In addition to this knowledge, the needs of an MTA do include some special
processing pertinent to its activities and the kind of keys to be looked up.
</PARA>

<PARA>
Specifically, the result of the data lookup can take different forms: one
may be interested only in the existence of a datum, not its value, or one
may be looking up paths in a <EMPHASIS>pathalias</> database and need
to substitute the proper thing in place of ``<COMPUTEROUTPUT>%s</>'' in
the string returned from the database lookup.
It should be possible to specify that this kind of postprocessing should
be carried out in association with a specific data access.
Similarly, there may be a need for search routines that depend on
the semantics of keys or the retrieved data.
These possibilities have all been taken into consideration in
the definition of a <FUNCTION>relation</>.
A <FUNCTION>relation</> maps a key to a value obtained by applying
the appropriate lookup and search routines, and perhaps a postprocessing
step, applied to a specified database that has a specified access method.
</PARA>

<PARA>
The various attributes that define a <FUNCTION>relation</> are largely
independent.
There will of course be dependencies due to the contents or other semantics
of a database.
In addition to the features mentioned, each relation may optionally have
associated with it a subtype, which is a string value used to tell
the lookup routine which table of several in a database one is interested in.
</PARA>

<PARA>
There are no predefined relations in the <COMMAND>router</>.
They must all be specified in the configuration file before first use.
This is done by calling the special function <FUNCTION>relation</> with
various options, as indicated by the usage strings printed by
the <FUNCTION>relation</> function when called the wrong way.
See figure <XREF LINKEND="fig.relation.params">.
</PARA>

<FIGURE ID="fig.relation.params" PGWIDE="0" FLOAT="1">
<TITLE>``Usage:'' of <FUNCTION>relation</> function</TITLE>
<INFORMALEXAMPLE>
<CMDSYNOPSIS>
<COMMAND>relation</>
 <ARG>-t <REPLACEABLE>dbtype</><ARG>,<REPLACEABLE>subtype</></></>
 <ARG>-f <REPLACEABLE>file</></>
 <ARG>-e<REPLACEABLE>#</></>
 <ARG>-s<REPLACEABLE>#</></>
 <ARG>-bilmnpu</>
 <ARG>-d <REPLACEABLE>driver</></>
 <ARG CHOICE=PLAIN><REPLACEABLE>name</></>
</CMDSYNOPSIS>
<PARA>
Where dbtypes include:
<SIMPLELIST TYPE=INLINE>
 <MEMBER>incore</>
 <MEMBER>header</>
 <MEMBER>unordered</>
 <MEMBER>ordered</>
 <MEMBER>hostsfile</>
 <MEMBER>bind</>
 <MEMBER>selfmatch</>
 <MEMBER>ndbm</>
 <MEMBER>gdbm</>
 <MEMBER>btree</>
 <MEMBER>bhash</>
</SIMPLELIST>
</PARA>
</INFORMALEXAMPLE>
</FIGURE>

<PARA>
The ``<OPTION>-t</>'' option specifies one of several predefined
database types, each with their specific lookup routine.
It determines a template for the set of attributes associated with
a particular relation.
The predefined database types are:

<VARIABLELIST>
<VARLISTENTRY><TERM><OPTION>bash</></><LISTITEM><PARA>
the database is in BSD DB HASH format.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>bind</></><LISTITEM><PARA>
the database is the BIND nameserver, accessed through the standard resolver routines.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>btree</></><LISTITEM><PARA>
the database is in BSD/SleepyCat DB BTREE format.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>bhash</></><LISTITEM><PARA>
the database is in BSD/SleepyCat DB HASH format.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>dbm</></><LISTITEM><PARA>
the database is in DBM format.
Note that the original dbm had no <FUNCTION>dbm_close</>() function,
thus there was no way to dissociate active database from a process.
A bit newer variant of dbm has the close function, and multiple
dbm's can be used.  (You propably won't encounter this beast at all..)
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>gdbm</></><LISTITEM><PARA>
the database is in GNU GDBM format.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>headers</></><LISTITEM><PARA>
router internal database of various headers, and how they are
to be treated.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>hostsfile</></><LISTITEM><PARA>
<FILENAME>/etc/hosts</> lookup using <FUNCTION>gethostbyname()</>.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>incore</></><LISTITEM><PARA>
the database is a high-speed bundle of data kept entirely in the router
process core memory.
This is for a short-term data storage, like handling duplicate detection.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>ldap</></><LISTITEM><PARA>
Mechanism for X.500 Directory access lookup with the "Light-weight
Directory Access Protocol."
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>ndbm</></><LISTITEM><PARA>
the database is in NDBM (new DBM) format.
(At which the length of key + length of data must not exceed 1024 bytes!)
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>ordered</></><LISTITEM><PARA>
the database is a text file with key-datum pairs on each line,
keys are looked up using a linewise binary search in the sorted file.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>selfmatch</></><LISTITEM><PARA>
a special type that does translate the numerical address of format
<LITERAL>12.34.56.78</> (from within address-literal bracets)
into binary form, and checks that it is (or is not) actually our own
local IP addresses.
This is used in address literal testing of addresses of type:
<LITERAL>localpart@[12.34.56.78]</>.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>unordered</></><LISTITEM><PARA>
the database is a text file with key-datum pairs on each line,
keys are looked up using a sequential search.
First to match is used.
</PARA></LISTITEM></VARLISTENTRY>

<VARLISTENTRY><TERM><OPTION>yp</></><LISTITEM><PARA>
Sun SunOS 4.x YP (these days "NIS") interface library.
</PARA></LISTITEM></VARLISTENTRY>
</VARIABLELIST>
</PARA>

<PARA>
A subtype is specified by appending it to the database type name separated
by a slash, or a comma.
For example, specifying <OPTION>bind/mx</> as the argument to
the ``<OPTION>-t</>'' option will store ``<OPTION>mx</>'' for reference
by the access routines whenever a query to that relation is processed.
The subtypes must therefore be recognized by either the database-specific
access routines (for translation into some other form), or by the database
interface itself.
</PARA>

<PARA>
For <OPTION>unordered</> and <OPTION>ordered</> database types,
the datum corresponding to a particular key may be null.
This situation arises if the database is a simple list, with
one key per line and nothing else. 
In this situation, the use of an appropriate post-processor option 
(e.g. ``<OPTION>-b</>'') is recommended to be able to detect whether or not 
the lookup succeeded.
</PARA>

<PARA>
The ``<OPTION>-f</>'' option specifies the name of the database.
This is typically a path that either names the actual (and single)
database file, or gives the root path for a number of files comprising
the database (e.g. ``<FILENAME>foo</>'' may refer to the NDBM files
``<FILENAME>foo.pag</>'' and ``<FILENAME>foo.dir</>'').
For the <OPTION>hostsfile</> type of database, the <FILENAME>/etc/hosts</>
file is the one used (and since the normal ``hosts'' file access routines
do not allow specifying  different file, this cannot be overridden).
</PARA>

<PARA>
The ``<OPTION>-s</>'' option specifies the size of the cache.
If this value is non-zero (by default it is 10), then an LRU cache
of this size is maintained for previous queries to this relation,
including both positive and negative results.
</PARA>

<PARA>
The ``<OPTION>-e</>'' option specifies the cache data expiration time
in seconds.
</PARA>

<PARA>
The ``<OPTION>-b</>'' option asks that a postprocessor is applied to
the database lookup result, so the empty string is returned from
the relation query if the database search failed, and the key itself
it returned if the search succeeded.
In the latter case, any retrieved data is discarded.
The option letter is short for Boolean.
</PARA>

<PARA>
The ``<OPTION>-n</>'' option asks that a postprocessor is applied to
the database lookup result, so the key string is returned from
the relation query if the database search failed, and the retrieved
datum string is returned if the search succeeded.
The option letter is short for Non-Null.
</PARA>

<PARA>
The ``<OPTION>-l</>'' option asks that all keys are converted to
<EMPHASIS>lowercase</> before lookup in the database.
This is mutually exclusive with the ``<OPTION>-u</>'' option.
</PARA>

<PARA>
The ``<OPTION>-u</>'' option asks that all keys are converted to
<EMPHASIS>uppercase</> before lookup in the database.
This is mutually exclusive with the ``<OPTION>-l</>'' option.
</PARA>

<PARA>
The ``<OPTION>-d</>'' option specifies a search routine.
Currently the only legal argument to this option is ``<OPTION>pathalias</>'',
specifying a driver that searches for the key using domain name lookup rules.
</PARA>

<FIGURE ID="fig.adm-dbfrag1" PGWIDE="0" FLOAT="1">
<TITLE>Some examples of <FUNCTION>relation</> definitions</TITLE>
<PROGRAMLISTING>
<![CDATA[
 if [ -f /etc/resolv.conf ]; then
   relation -nt bind/cname -s 100 canon # T_CNAME canonicalize hostname
   relation -nt bind/uname uname        # T_UNAME UUCP name
   relation -bt bind/mx neighbour       # T_MX/T_WKS/T_A reachability
   relation -t  bind/mp pathalias       # T_MP pathalias lookup
 else
   relation -nt hostsfile -s 100 canon  # canonicalize hostname
   relation -t unordered -f $MAILBIN/db/hosts.uucp uname
   relation -bt hostsfile neighbour
   relation -t unordered -f /dev/null pathalias
 fi
]]>
</PROGRAMLISTING>
</FIGURE>

<FIGURE ID="fig.adm-dbfrag2" PGWIDE="0" FLOAT="1">
<TITLE>More examples of alternate forms of database reference</TITLE>
<PROGRAMLISTING>
<![CDATA[
 #
 # We maintain an aliases database, and may access it via NDBM,
 # or via indirect indexing:
 #
 if [ -f $MAILBIN/db/aliases.dat ]; then
     relation -t ndbm -f $MAILBIN/db/aliases aliases
 else
     relation -it ordered -f $MAILBIN/db/aliases.idx aliases
 fi
]]>
</PROGRAMLISTING>
</FIGURE>

<FIGURE ID="fig.adm-dbfrag3" PGWIDE="0" FLOAT="1">
<TITLE>More miscellaneous <FUNCTION>relation</> definitions
to illustriate various possibilities</TITLE>
<PROGRAMLISTING>
<![CDATA[
 relation -t unordered -f /usr/lib/news/active -b newsgroup
 relation -t unordered -f /usr/lib/uucp/L.sys -b ldotsys
 relation -t ordered -f $MAILBIN/db/hosts.transport -d pathalias transport
]]>
</PROGRAMLISTING>
</FIGURE>

<PARA>
The final argument for the <FUNCTION>relation</> is not preceeded by
an option letter.
It specifies the name the relation is known under.
Note that it is quite possible for different relations to use
the same database (like in case of ``<OPTION>bind</>'').
</PARA>

<PARA>
Some sample relation definitions are in figure
 <XREF LINKEND="fig.adm-dbfrag1">.
That fragment defines a set of relations that can be accessed in the
same way, using the same names, independent of their actual definition.
</PARA>

<PARA>
<EMPHASIS ROLE=BOLD>CHECK! (<OPTION>-i</> option!)</>
As the comment says, the relation name <EMPHASIS>aliases</> has
special significance to the <COMMAND>router</>.
Although the relation is not special in any other way (i.e. it can
be used in the normal fashion), the semantics of the data retrieved
are bound by assumptions in the aliasing mechanism.
(Or more specifically, actually database compilation in case
this isn't ``ordered'' or ``unordered'' file will handle this.)
</PARA>

<PARA>
These assumptions are that key strings are local-name's, and the corresponding
datum gives a byte offset into another file (the root name of the aliases file,
with a ``<FILENAME>.dat</>'' extention), which contains the actual addresses
associated with that alias.
</PARA>

<PARA>
The reason for this indirection is that the number of addresses associated
with a particular alias can be very large, and this makes the traditional
simple database formats inadequate.
For example, quick lookup in a text file is only practical if it is sorted
and has a regular structure.
A large number of addresses associated with an alias makes structuring a
problem.
The situation for DBM files and variations have problems too, due to
the intrinsic limits of the storage method.
The chosen indirection scheme avoids such problems without loss of efficiency.
</PARA>

<PARA>
More examples on figure <XREF LINKEND="fig.adm-dbfrag3">, where
the first two illustrate convenient coincidences of format, and
the last definition shows what might be used if outgoing channel
information is maintained in a pathalias-format database
(e.g. ``bar smtp!bar'' means to send mail to ``bar'' via the SMTP channel).
</PARA>

<SECT2 ID=zadm-rdr-databases-using>
<TITLE>Using a Pathalias Database</TITLE>

<PARA>
Accessing route databases is a rather essential capability for a mailer.
At the University of Toronto, all hosts access a centrally stored database
through a slightly modified nameserver program.
If such a setup is not practical at your site, other methods are available.
The most widespread kind of route database is produced by
the <APPLICATION>pathalias</> program.
</PARA>

<PARA>
<APPLICATION>pathalias</> generates key-value pairs of the form:
<PROGRAMLISTING>
<![CDATA[
  uunet    ai.toronto.edu!uunet!%s
  .css.gov ai.toronto.edu!uunet!seismo!%s
]]>
</PROGRAMLISTING>
which when queried about ``uunet'' and ``beno.css.gov''
correspond to the routes:
<PROGRAMLISTING>
<![CDATA[
  ai.toronto.edu!uunet
  ai.toronto.edu!uunet!seismo!beno.css.gov
]]>
</PROGRAMLISTING>
</PARA>

<PARA>
Notice that there are two basic forms of routes listed: routes to UUCP node
names and routes to subdomain gateways.
Depending on the type of route query, the value returned from a pathalias
database lookup needs to be treated differently.
For now, this may be accomplished by a configuration file relation definition
and interface function as shown in figure
  <XREF LINKEND="fig.adm-pathalias-driver">.
</PARA>

<FIGURE ID="fig.adm-pathalias-driver" PGWIDE="0" FLOAT="1">
<TITLE>An example of lookup driver for <COMMAND>pathalias</>
generated database</TITLE>
<PROGRAMLISTING>
<![CDATA[
relation -t ndbm -f $MAILBIN/uuDB -d pathalias padb

# pathalias database lookup function
padblookup (name) {
    local path
    path = $(padb "$name")
    tsift "$path" in
    ((.+)!)?([^!]+)!%s
        if [ "$3" == "$name" ]; then
            path = "$2!$3"
        else
            path = "$2!$3!$name"
        fi
        ;;
    .*%s.*
        echo "illegal route in pathalias db: $path"
        ;;
    tfist
    return "$path"
}
]]>
</PROGRAMLISTING>
</FIGURE>

<PARA>
This is actually a simplistic algorithm, but it does illustrate the method.
The lookup algorithm used when the ``<OPTION>-d</>'' flag is specified in
the <FUNCTION>relation</> definition command is rather simple; it doesn't
test various case combinations for the keys it tries.
Therefore, the keys in the pathalias output data should probably
be converted to a single case, and the ``<OPTION>-l</>'' or ``<OPTION>-u</>''
option given in the <FUNCTION>relation</> definition as well..
</PARA>

</SECT2>

<SECT2 ID="mailing-list-maintenance">
<TITLE>Mailing Lists and <FILENAME>~/.forward</></TITLE>

<PARA> FIXME! FIXME! -- VERIFY! UPDATE!
</PARA>

<PARA>
One form of mailing lists are implemented as files in
the <FILENAME CLASS="DIRECTORY">$<VARNAME>MAILSHARE</>/lists/</>
directory (or symlinks in there to files residing elsewere, though
from a system reliability standpoint it is better to have them in
that directory, and let users have symlinks to those files &mdash;
consider the NFS with the user home directories in other machines&hellip;)
</PARA>

<PARA>
An alternate mechanism is to implement lists in the traditional
<APPLICATION>sendmail</> manner, however it means feeding the message
to the <COMMAND>scheduler</>, and external program
(<COMMAND>>/usr/lib/sendmail</>) before it comes back to
the <COMMAND>router</>.
</PARA>

<PARA>
The list <EMPHASIS>file</> must have protection 0664 or stricter,
as an example: 0700 has invalid bits.
(ok, so the ``x''-bit is not used, but illegal it is, all the same.)
Preferrable protection is: 0600
</PARA>

<PARA>
The <FILENAME CLASS="DIRECTORY">$<VARNAME>MAILSHARE</>/lists/</> directory
must be owned by root.
The directory containing the ``<EMPHASIS>aliases</>'' file
(<FILENAME CLASS="DIRECTORY">$<VARNAME>MAILSHARE</>/db/</>) must be owned
by root, and the aliases file must comply with above mentioned protections.
</PARA>

<PARA>
The owner of ``<EMPHASIS>FILE</>'' gets ``<EMPHASIS>FILE-owner</>'',
and ``<EMPHASIS>FILE-request</>'' mails,
<EMPHASIS ROLE="STRONG">unless any of the above listed limitations
 are breached</>.
</PARA>

<PARA>
If ``<EMPHASIS>FILE</>'' has protection 666 (for example),
the ZMailer internal function ``<COMPUTEROUTPUT>$(filepriv $filepath)</>''
returns ``<LITERAL>$nobody</>'' (userid of nobody), and function
``<COMPUTEROUTPUT>$(uid2login $nobody)</>'' fails, thus losing ``*-owner''
and  ``*-request'' features.
</PARA>

<PARA>
Also lists with filepriv ``nobody'' cannot be archived simply by having
an ``address'' of form ``<LITERAL>/file/path</>'' amongst the recipient
addresses.
</PARA>

<PARA>
This type of a mailing list is set up by creating a file in the 
<FILENAME CLASS="DIRECTORY">$<VARNAME>MAILVAR</>/lists/</> directory.
The file name is the list's name (LIST) in <EMPHASIS>all lowercase</>
(case-insensitive matching is done by converting to lowercase
before comparison).
</PARA>

<PARA>
The file contains a list of mail addresses (typically one per line)
which are parsed to pull out the destination addresses.
This means the users' full names can be given just as in any valid
RFC822 address.
</PARA>

<PARA>
The local account which owns the file will by default receive messages
sent to LIST-owner and LIST-request.
This can be explicitly overridden in the aliases file.
Mail to the list will go out with LIST-owner as the sender, so list
bounce messages will return to the LIST-owner address.
Archives of the list can be created by adding a file name address
(a local pathname starting with ``/'') to the LIST file.
The archive file is written with the ownership of the owner of the LIST file.
Forwarding the mailing list into a newsgroup can be done using a mail to news
script (two generations are provided in
<FILENAME>utils/distribute</> and <FILENAME>utils/mail2news</>
in the sources).
</PARA>

<SECT3 ID="zadm-rtr-aliases.cf-logic">
<TITLE><FILENAME>aliases.cf</> Logic</TITLE>

<PARA> FIXME! FIXME! -- VERIFY! UPDATE!
</PARA>

<ITEMIZEDLIST>
<LISTITEM><PARA>
If an <EMPHASIS>aliases</> database exists and local-part is found in it,
the list of addresses mapped to by the alias entry is substituted.
</PARA></LISTITEM>

<LISTITEM><PARA>
If an <EMPHASIS>mboxmap</> file exists and a mapping for the local-part is
found in it, the mapping (a ``<LITERAL>host!homedir!user</>'' value)
determines the remote recipient (<LITERAL>user@host</>) or recipient
mailbox (<FILENAME>homedir/../PObox/user</>) if host is local.
</PARA></LISTITEM>

<LISTITEM><PARA>
If local-part is a login name and a readable ``<FILENAME>~/.forward</>''
file  exists in the home directory, the list of addresses it contains is
substituted.
</PARA></LISTITEM>

<LISTITEM><PARA>
If local-part is a file basename in
the <FILENAME CLASS="DIRECTORY">$<VARNAME>MAILVAR</>/lists/</> directory,
the list of addresses contained in the file is substituted, and
the sender address set to local-part-owner.
</PARA></LISTITEM>

<LISTITEM><PARA>
If local-part is of the form ``file-<EMPHASIS>owner</>'' or
``file-<EMPHASIS>request</>'', where file is an entry in
the <FILENAME CLASS="DIRECTORY">$<VARNAME>MAILVAR</>/lists/</> directory,
the account name of the owner of the file is substituted.
(File-owner identity and correct file and directory protections are important.)
</PARA></LISTITEM>

<LISTITEM><PARA>
If the local-part is of format  ``user.name'', it is optionally mapped via
separate <EMPHASIS>fullnamemap</>.
</PARA></LISTITEM>

<LISTITEM><PARA>
If <VARNAME>PUNTHOST</> is defined (in <FILENAME>/etc/zmailer.conf</>)
the address <LITERAL>local-part@$PUNTHOST</> is substituted.
Note that in this case the <EMPHASIS>mboxmap</> mechanism should be used
to ensure local spool mailbox delivery for local users.
</PARA></LISTITEM>
</ITEMIZEDLIST>

</SECT3>

<SECT3 ID=zadm-rtr-aliases>
<TITLE>aliases</TITLE>

<PARA> FIXME! FIXME! -- aliases db regeneration methods have changed since
</PARA>


<PARA>
The file containing the actual aliasing data is automatically created by
the <COMMAND>router</> when asked to reconstruct the aliases database.
It does this based on a text file containing the alias definitions.
This text file, which corresponds to the <APPLICATION>sendmail</> aliases
file, consists of individual alias definitions, possibly separated by blank
lines or commentary.
Comments are introduced by a sharp sign (octothorp: ``<LITERAL>#</>'') at
any point where a token might start (for example the beginning of a line,
but not in the middle of an address), and extend to the end of the line. 
Each alias definition has the exact syntax of an RFC822 message header,
containing an address-list, except for comments.  The header field name
is the local-part being aliased to the address-list that is the header value.
</PARA>

<PARA>
The fact that an alias definition follows the syntax for an RFC822 message
header, introduces an incompatibility with <APPLICATION>sendmail</>.
The string ``<LITERAL>:include:</>'' at the start of a local-part
(a legacy of RFC733) has special semantics.
<APPLICATION>Sendmail</> would strip this prefix, and regard the rest of
the local-part as a path to a file containing a list of addresses to be
included in the alias expansion.
Indeed, the <COMMAND>router</> behaves in the same manner, but because some
of the characters in the prefix are RFC822 specials, the entire local-part
must be quoted.
Thus, whereas <APPLICATION>sendmail</>(8) allowed:
<PROGRAMLISTING>
<![CDATA[
  people: :include:/usr/lib/mail/lists/people
]]>
</PROGRAMLISTING>
the proper syntax with ZMailer is:
<PROGRAMLISTING>
<![CDATA[
  people: ":include:/usr/lib/mail/lists/people"
]]>
</PROGRAMLISTING>
</PARA>

<PARA>
Like <APPLICATION>sendmail</>, if a local-part is not found in
the aliases database, the <COMMAND>router</> also checks
``<FILENAME>~local-part/.forward</>'' (if such exists) for any
address expansion.
The <FILENAME>.forward</> file format is also an RFC822 address list,
similar to what <APPLICATION>sendmail</> expects.
</PARA>

<PARA>
As special cases, a local-part starting with a pipe character
(``<LITERAL>|</>'') is treated as mail destined for a program
(the rest of the local-part is any valid argument to a ``<LITERAL>sh -c</>''
command), and a local-part starting with a slash character (``<LITERAL>/</>')
is treated as mail destined for the <EMPHASIS>file</> named by the local-part.
</PARA>

<FIGURE PGWIDE=0 FLOAT=0 ID=fig.zadm-router-aliases-figure>
<TITLE>General format of Alias file entries:</TITLE>
<INFORMALTABLE FRAME=NONE>
<TGROUP COLS=2>
<THEAD>
<ROW ROWSEP=1>
  <ENTRY ALIGN=CENTER>``The Key''</ENTRY>
  <ENTRY ALIGN=CENTER>``The Data''</ENTRY>
</ROW>
</THEAD>
<TBODY>
<ROW>
  <ENTRY ALIGN=LEFT><USERINPUT>local-address-token:</></ENTRY>
  <ENTRY ALIGN=LEFT><USERINPUT>"replacement address" ,</></ENTRY>
</ROW>
<ROW>
  <ENTRY ALIGN=LEFT><USERINPUT>&nbsp;</></ENTRY>
  <ENTRY ALIGN=LEFT><USERINPUT>"extension line with another address"</></ENTRY>
</ROW>
</TBODY>
</TGROUP>
</INFORMALTABLE>
</FIGURE>

<PARA>
Protection of the aliases database must be at least 0644.
Protection of the <FILENAME CLASS=DIRECTORY>$<VARNAME>MAILVAR</>/db/</>
directory must be at least 03755, or stricter.
</PARA>

<PARA>
The following processing is done for (replacement) local-parts
(local mail addresses):  Note that <LITERAL>@</>'s are not allowed
in any local-part.
</PARA>

<PARA>
If the local-part starts with ``<LITERAL>|</>'' assume it is a command
specification:
<PROGRAMLISTING>
<![CDATA[
  prog-pipe: "|/path/to/program -args"
]]>
</PROGRAMLISTING>
</PARA>

<PARA>
If the local-part starts with ``<LITERAL>/</>'' assume it is a file pathname:
<PROGRAMLISTING>
<![CDATA[
  file-path: "/path/to/file"
]]>
</PROGRAMLISTING>
</PARA>

<PARA>
If the local-part starts with ``<LITERAL>:include:</>'' the rest should be
a file pathname of a list of mail addresses.  They are substituted:
<PROGRAMLISTING>
<![CDATA[
included-list: ":include:/path/to/address/file"
]]>
</PROGRAMLISTING>
</PARA>

<PARA>
After this point, all matches are case-insensitive by means of translating
the value to be looked up to lower-case, and then conducting a case-sensitive
lookup. <EMPHASIS>All keys in aliases et.al. must be in lower case &mdash; you
can achieve this with bundled ``<COMMAND>newaliases</>'' script, which calls
``<COMMAND>makedb</>'' with ``<OPTION>-l</>'' option to lowercasify keys.</>
(The hash functions inside  <EMPHASIS ROLE=STRONG>ndbm/gdbm/db/dbm</>  are
case sensitive, and as such, there is no way to avoid this requirement.)
</PARA>

</SECT3>

<SECT3 ID=zadm-rtr-memory-consumption>
<TITLE>About Large Lists, and Memory Consumption</TITLE>

<PARA> FIXME! FIXME! -- VERIFY! UPDATE!
(router internal memory management has improved dramatically at
2.99.51, and no serious bloat effect appears anymore.)
</PARA>

<PARA>
With old configuration scripts there used to be problems with list
expansion causing serious memory bloat (of $O(n^{2})$ kind of memory
consumption&hellip;)
{\em ZMailer-2.98} did introduce a working solution via the builtin
{\tt\$(listexpand ..)} function.
</PARA>

<PARA>
The rest of these notes apply to older config files using old style pipe of:
<PROGRAMLISTING>
<![CDATA[
  $(listaddresses &lt; file | maprrouter ...)
]]>
</PROGRAMLISTING>
where the maprrouter caused the actual memory bloat...
</PARA>

<PARA>
The primary reason behind this is that the router does necessary copies
of objects &mdash; it uses memory in a form of heap storage which is
allocated progressively and managed via ``<FUNCTION>mark</>()'', and
``<FUNCTION>release</>()'' type calls.
The ``<FUNCTION>release</>()'' calls are done when exiting
``<COMMAND>zmsh</>'' interpreter, and never during script running
(however calling builtin C functions which call scripts is another
story &mdash; there are ``<FUNCTION>release</>()'' checkpoints
available then.)
</PARA>

<PARA>
Especially bad thing for memory use to do is to use ``<FUNCTION>setf</>''
function.
</PARA>

<PARA>
Internal list expansion (through the
 <FILENAME>$<VARNAME>MAILVAR</>/lists/LISTNAME</> 
mechanism) is a sure way to expand <COMMAND>router</> process memory usage.
</PARA>

<PARA>
You can decrease the memory requirement dramatically, if you can feed all
the addresses in the envelope, or via <FILENAME>utils/listexpand.c</>
utility (alpha-test tool on 1-Sep-94).
You don't need to worry about it unless your list is 100+ recipients,
only then the memory usage starts to bloat seriously with the old-style
in-core <FUNCTION>$(listaddress ...)</> expander.
</PARA>

<PARA>
Although more interesting and useful models exist, the mail forwarding
functionality of ZMailer has been designed to generally emulate the
interface and behaviour of <COMMAND>sendmail</>(8).
The mechanisms that accomplish this are likely to be generalized in
a future version.
</PARA>

</SECT3>

<SECT3 ID=zadm-rtr-security-considerations>
<TITLE>Security Considerations</TITLE>

<PARA>
A LIST file must not be world writable, while most likely it can be
group-writable.
The <FILENAME CLASS="DIRECTORY">$<VARNAME>MAILVAR</>/lists/</> directory
must also not be group or world writable and must be owned by root or by
the owner of the LIST file.
Otherwise the file is declared insecure and all addresses in the file
get the least possible privilege associated (the ``nobody'' uid).
This can cause various things to break, for example mailing list archival,
or the -owner and -request features if ``nobody'' is not a valid account.
</PARA>

<PARA>
There is a mechanism to override using the modes on a
<FILENAME>file/directory</> as an indicator of its safeness.
</PARA>

<PARA>
Turning on the sticky bit on a file or directory tells the mailer to treat
it as if it was only owner writable independent of its actual modes.
</PARA>

<PARA>
This allows <FILENAME CLASS="DIRECTORY">$<VARNAME>MAILVAR</>/lists/</>
to be group or world writable and sticky-bitted if you want your general
user population (or special admin group) to be able to create mailing lists.
</PARA>

</SECT3>
</SECT2>
</SECT1>
</CHAPTER>
