OnWorks favicon

mailagent - Online in the Cloud

Run mailagent in OnWorks free hosting provider over Ubuntu Online, Fedora Online, Windows online emulator or MAC OS online emulator

This is the command mailagent that can be run in the OnWorks free hosting provider using one of our multiple free online workstations such as Ubuntu Online, Fedora Online, Windows online emulator or MAC OS online emulator



mailagent - an automatic mail-processing tool


mailagent [ -dhilqtFIVU ] [ -s{umaryt} ] [ -f file ] [ -e rule ] [ -c config ] [ -L
loglevel ] [ -r rulefile ] [ -o override ] [ mailfile ]


Mailagent allows you to process your mail automatically. Given a set of lex-like rules,
you are able to fill mails to specific folders, forward messages to a third person, pipe a
message to a command or even post the message to a newsgroup. It is also possible to
process messages containing some commands. The mailagent is not usually invoked manually
but is rather called via the filter program, which is in turn invoked by sendmail. That
means you must have sendmail on your system to use this. You also must have perl to run
the mailagent scripts.

There is a set of options which may be used when you invoke mailagent yourself. Please
refer to the OPTIONS section for a complete description. You may use the -h option to get
a cryptic usage reminder.

Product Overview
Mailagent has actually four distinct set of features, which can be used simultaneously or
one at a time. This involves:

· An @SH command processor, to remain compatible with the first implementation. In
this simplest usage, all the mail messages are left in your mailbox (or the catch all
folder required on Debian systems: Please see /usr/share/doc/mailagent/SECURITY for
details), with special processing raised on messages whose subject is Command.
Please refer to the section entitled USING THE DEFAULT RULES if you wish to use this

· A complete mail filter, which helps you sort your mail based on various sorting
criteria and actions. Filtering is specified in a rule file and supersedes the
default Command mail processing (which may be turned on again by explicitly setting
up a rule for it). This should be the most common use of mailagent and is fully
documented under the section entitled USING THE FILTER. You may deliver mail to
plain Unix-style folders but also to MMDF and MH ones.

· A replacement for the vacation program, which will automatically answer your mail
while you are not there. You only need to supply a message to be sent back and the
frequency at which this will occur. Some simple macro substitutions allow you to re-
use some parts of the mail header into your vacation message, for a more personalized
reply. See the VACATION MODE section for more details.

· A generic mail server, which will let you implement a real mail server without the
hassle of the lower-level concerns like error recovery, logging or command parsing.
The full documentation can be found in the section GENERIC MAIL SERVER at the end of
this manual page.

It is possible to extend the mailagent filtering commands by implementing them in perl and
then having them automagically loaded when used. Those extended commands will behave
exactly like built in ones, as documented in the EXTENDING FILTERING COMMANDS section.

Learning From Examples
It is quite possible that you will find this manual page too complex for you.
Unfortunately, it is not really meant to be a tutorial but rather a reference material. If
you wish, you may start by looking at the examples held in the distribution source tree
under agent/examples. This directory contains two examples of rule files (look at the
README file first) and are verbosely commented.


First, you need to install a minimum configuration and see how it works. It would be
useless to fully install the program and then discover that it does not work as

To start the installation, you have to set up a ~/.mailagent file which is the main
configuration file, and choose the right filter program.

Choosing The Filter Program
The distribution comes with two filter programs. One written in shell and one in C. The
shell version might be the one to use if you can receive your mail on many different
platforms where your home directory is NFS-mounted (i.e. shared among all those
platforms). The C version is safer and much faster, but you need to install it to a fixed

On some platforms, sendmail does not correctly reset its UID when processing mails in its
own queue. In that case, you need to get a private copy of the C filter program and make
it setuid to yourself. The filter will then correctly reset its UID if invoked with an
effective UID different from yours (it may also require the setgid bit to reset GID as
well). If this is indeed the case on your system, make sure you use the path
configuration variable to set a proper PATH, as the filter will spawn a perl process with
the '-S' option, looking for a mailagent script.

Even if you do not need to get a setuid copy of the filter program, it is wise to set up a
proper path: someone might break into your account by putting a mailagent Trojan horse in
the appropriate location. Also make sure the mailagent program is protected against
writing, as well as the directory which holds it, or someone might substitute his own
version of the script and break security. I believe the setuid filter program to be safe,
but overlooking is always possible so please report any security hole to me.

The filter script can be found in the Lib/mailagent directory. It needs some tailoring so
you should copy it into your home directory and edit it to suit your needs. Comments held
in it should be self explanatory. There is only a small section at the head of the script
which needs to be edited. You'll have to delete shell comments in the filter script by
yourself if your shell cannot deal with them.

As of version 3.0 PL44, I advise you to prefer the C version if you are concerned about
security. If you are in a position where multiple architectures can process your .forward,
then a shell wrapper selecting the proper executable based on the architecture will be

Configuring Mailagent
If mailagent is in your path, you may automatically configure a default installation by

mailagent -I

which will create a ~/.mailagent file from an existing template, customize some important
variables for your site, and make some basic sanity checks. Everything the command does is
output on the screen for checking purposes, and any problem found is reported.

Otherwise, you have to copy the mailagent.cf file held in the mailagent sub-directory
/usr/share/mailagent (hereafter named Lib) as a .mailagent in your home directory. Edit it
to configure the whole processing. In particular, you have to choose a spool directory
(hereafter named Spool) and a log directory (hereafter named Log).

Note that using the automatic installation procedure above does not prevent you from going
through the file and modifying it as you wish. In fact, you are greatly encouraged to do
this, especially for the home directory setting, the logging level and the path or p_host
variables. Once you are done, rerun the mailagent -I command to make sure everything is
fine. Still, you will have to plug in mailagent by creating a ~/.forward file, as
explained in a few sections.

Following is a description of each of the fields you will find in the ~/.mailagent file,
followed by a suggested value, when applicable. Fields marked as optional may not be
present in the configuration file. Some fields have a close relationship with others, and
that is given too.

agemax Period after which an entry in the database should be removed (suggested: 1y)
This field is optional, but needed if autoclean is on.
authfile Remote sending authorizations (not implemented yet).
autoclean Set to ON (case insensitively), mailagent will perform automatic cleaning of the
database entries under hash by removing all the items older than agemax. This is
an optional field, omitting it defaults to OFF. (suggested: OFF, unless you use
ONCE, UNIQUE or RECORD commands, or activate the vacation mode.)
biff Whether or not biffing is wanted when mailagent delivers mail to a folder. Set
it to ON (case insensitively) to allow local biffing if you are logged in.
(optional, defaults to: OFF)
biffhead When biffing is enabled, this variable lists which headers should be printed
out. Headers should be given in their normalized format and be separated with
commas. (optional, defaults to: From, To, Subject, Date).
bifflen The maximum length of the message body that should be printed when biffing.
(optional, defaults to 560).
bifflines The maximum number of lines of the message body that should be printed when
biffing. Actually, mailagent attempts to print that amount of lines, provided
the total amount of characters printed is less than bifflen. (optional,
defaults to 7).
biffmh When turned ON, the body of the message is compacted before biffing by removing
consecutive spaces and replacing newlines with a single space. The message
itself is not altered physically of course, only the output on the screen is
concerned. Since this may yield to a difficult-to-read message, I suggest you
also turn on biffnice when using this option. (optional, defaults to: OFF).
biffmsg The path to a file describing the format biffing should use. If not set, a
default hardwired format is used. Season to taste. (suggested: ~/.biffmsg).
biffnice Whether the message should be reformatted to nicely fit into the terminal.
(optional, defaults to OFF, suggested: ON when biffmh is also ON).
biffnl Controls whether "blank" body lines should be printed or not. By "blank" lines,
we mean lines not containing words. Set it to ON to print such blank lines, to
OFF if you wish to get a more compact view of the body within the limits fixed
by bifflen and bifflines. (optional, defaults to ON).
biffquote Controls whether the leading attribution line introducing a trimmed quotation
should be part of the biff message or not. When turned OFF, the attribution line
is trimmed along and this is reported in the trimming message, when bifftrim is
ON. (optional, defaults to ON).
bifftrim Controls whether trimmed lines within the biff message should be replaced by a
message stating how many of them were trimmed. Only used by the %-T biffing
macro. When turned OFF, it automatically turns off biffquote as well. (optional,
defaults to ON).
bifftrlen States how many lines long a leading quotation should be before performing any
trimming. Only used by the %-T biffing macro. (optional, defaults to 2).
callout The name of the callout queue file where batched jobs are kept. This parameter
must be defined when using the AFTER command. (suggested: $spool/callout)
cleanlaps Cleaning period for database entries. The value of the last clean up is saved
into the context file. This is optional, but needed if autoclean is on.
(suggested: 1M)
comfile Name of the file containing authorized commands. Needed when PROCESS is used.
(suggested: $spool/commands).
compress Name of the file containing the list of compressed folders. See section about
folder compression. This is an optional parameter. (suggested: ~/.compress).
compspecs Name of the file containing specifications for how to handle different types of
compression formats. See section about folder compression. This is an optional
parameter. (suggested: $spool/compressors).
comptag The default compression tag when creating new folders. If not specified, the
default is 'gzip'.
comserver Name of the file containing authorized SERVER commands and their definition.
This is an optional parameter if you don't plan to use the generic mail server.
(suggested: $spool/server).
context File holding the mailagent context. The context saves some variables which need
to be kept over the life of the process. Needed if auto cleaning is activated.
(suggested: $spool/context)
distlist A list of all the available distributions. See the sample held in
Lib/mailagent/distribs. Needed by PROCESS only. (suggested: $spool/distribs)
domain Your domain name, without the leading dot, as in example.com. The value is
appended to the value of email when that variable does not have any '@', to
construct a fully qualified e-mail address. See also the hidenet variable.
(optional, defaults to the domain name determined at build time).
email Your electronic mail address. If left unspecified, mailagent will try to guess
it. This address is used by mailagent when trying to send something to the user
(you!). (suggested: specify your e-mail address).
emergdir Name of the directory which should be used for dumps, preferably. This is
optional. (suggested: ~/tmp/lost+mail)
execsafe Whether to be strict before using exec() to launch a new process or not. The
value of this variable is used in place of secure when checking executable
files. (defaults to OFF, suggested: ON if possible).
execskip Whether to skip the exec() security checks alltogether. Don't turn this ON
unless you really trust all the users having access to your machine or file
server. (optional, default to OFF, suggested: OFF).
fromall Whether or not mailagent should escape all the From lines in the message, not
only those it thinks should appear dangerous (i.e. a From after a blank line).
This option only makes sense when fromesc is also activated. It is ignored
otherwise, and therefore is optional. By default, it is assumed to be OFF.
(suggested: OFF, until you have reasons to believe your mail user-agent is
confused in this mode: when it happens, your user agent will split mail for no
apparent reason).
fromesc Whether or not mailagent should escape potentially dangerous From lines in mail
messages. If you use MH or if your mail reader does not use those lines to
separate messages, then you may set it to OFF. (suggested: ON)
fromfake Whether or not mailagent should fake a From: line into the message header when
it is absent. Naturally, it requires a valid leading From line to operate!
(optional, defaults to ON, suggested: ON).
groupsafe If turned OFF, then group-writable files will be managed as if they were secure,
from a security point of view. Leave it to ON if possible, or you may pass by a
huge security hole without your noticing (optional, defaults to ON, suggested:
hash The directory used for name hashing by the built-in database used by ONCE,
UNIQUE and RECORD commands. Optional, unless you make use of those commands or
activate auto cleaning. The directory is placed in the spool area. (suggested:
helpdir Directory where help files for SERVER commands are kept. (suggested:
hidenet When set to ON, the value of the variable domain is the fully qualified name
used. When OFF, the hostname is prepended to the domain. If the hostname is
already fully qualified, then the value of domain is ignored. Assuuming domain
is set to example.com and the hostname is host, then the fully qualified name
will be host.example.com if hidenet is OFF, and example.com if ON. (optional,
defaults to whatever was determined at build time)
home Defines where the home directory is. This must be accurate.
level Log level, see below for a definition of available levels (suggested: 9).
linkdirs When set to ON, carefully checks symbolic links to directories when performing
security checks on sensitive files. This will (recursively) check for each
symbolic link level that the target directory is not world writable or group
writable and that the parent directory of each target link is not world
writable. If the secure option is OFF, this parameter is ignored. (optional,
defaults to: ON, suggested: ON when secure is also ON).
lockdekay The delay in seconds between two locking attempts. (optional, defaults to: 2).
lockhold The maximum delay in seconds for holding a lock. After that time, the lock will
be broken. (optional, defaults to: 3600).
lockmax Maximum number of locking attempts before giving up. (optional, defaults to:
locksafe When locking a file, mailagent normally makes lockmax attempts separated by
lockdelay seconds, and then gives up. When facing a delivery to a mailbox, it
may make sense to continue even if no lock was grabbed, or even if only a
partial locking was done (e.g. one of the .lock or flock()-style locking
succeeded). This variable controls how safe you want to be. Set it to OFF to let
mailagent continue its mailbox delivery even though no locking was done, to ON
if you want strict locking, to PARTIAL if you can live with partial locking.
Messages not saved in a folder are dumped to an emergency mailbox. (optional,
defaults to ON). On Debian systems, since mailagent can not grab locks,it should
always be left ON, or else mail garbling may occur. See
/usr/share/doc/mailagent/SECURITY for details.
lockwarn This variable controls the time after which mailagent should start emiting a
warning when busy trying to acquire a lock. It is a comma separated list of
values, in seconds. If two values are given, the first is the initial time
threshold, the second is the repeat period. For instance, a value of "15,60"
would cause a warning after 15 seconds, then every 60 seconds until the lock is
taken or the locking attempt time is expired (see lockmax and lockdelay). If
only one value is given, it is taken as being both the initial threshold and the
period. (optional, defaults to: 20,300).
log Name of the log file which will be put in Log directory. (suggested: agentlog).
logdir Logging directory. (suggested: ~/var/log).
mailbox The name of the system mailbox file, which by default is the value of the user
configuration variable. This is an optional parameter.
maildrop Location of the system mail spool directory. If none is provided, then the
mailagent will use the value determined by Configure.
mailopt Options to be passed to the mailer (see sendmail). (optional, suggested: -odq
-i, when using sendmail).
maxcmds Maximum number of commands that are allowed to be executed by a SERVER command
before flushing the remaining of the mail message. (suggested: 10).
maxerrors Maximum number of errors for the SERVER command before flushing the remaining of
the mail message. (suggested: 10).
maxsize Maximum size in bytes of files before using kit for sending files. This is used
by PROCESS. (suggested: 150000).
mboxlock The format to be used for locking mailboxes before delivering to them. This
string goes through a small macro substitution mechanism to make it more
general. The file name derived after macro substitution is the name of the lock
that will be used, given the name of the file that is to be locked. Available
macros are:

%D: the file directory name
%f: the file name to be locked (full path)
%F: the file base name (last path component)
%p: the current process pid number
%%: a plain % character

Common locking formats are "%f.lock" and "%D/.%F.lock". Of course, to be able to
use this feature, mailagent must not have been configured to use flock()-style
locking only. (optional, defaults to: %f.lock). This has no effect on Debian
systems, since mailagent can not get a lock anyway, since it is not sgid mail.
mhprofile The name of the MH profile to be used. This is needed only when attempting to
save in an MH folder. If this optional parameter is not set, the default value
~/.mh_profile is used.
mmdf Set this to ON if you wish to be able to save mail in MMDF-style mailboxes.
(suggested: OFF, unless you use MMDF or MH). This is invalid on a Debian system.
mmdfbox The value of this variable only matters when mmdf is on. If set to ON, then new
folders will be created as MMDF ones. This variable is not used when saving to
an existing folder, since in that case the mailagent will automatically
determine the type and save the message accordingly. (suggested: OFF, unless
you use MMDF or wish to use MH's mshf).
msgprefix Name of the file to put in directory folders, specifying the message prefix to
be used. Optional, defaults to .msg_prefix.
name First name of the user, used by mailagent when referring to you. This sets the
value of the %U macro.
newcmd Name of the file describing new filtering commands. See section Extending
Filtering Commands for more details. Leave this optional parameter out unless
you are a mailagent expert. (suggested: $spool/newcmd).
newsopt Options to be passed to the news posting program (see sendnews). (optional,
suggested: leave empty when using inews).
nfslock Set it to ON to ensure NFS-secure locks. The difference is that the hostname is
used in conjunction with the PID to obtain a lock. However, mailagent has to
fork/exec to obtain that information. This is an optional parameter which is set
to OFF by default. (suggested: OFF if you deliver mail from only one machine,
even though it's via NFS).
passwd File where SERVER power passwords are kept -- encrypted usually. (suggested:
path Minimum path to be used by C filter program. To set a specific path for a
machine host, set up a p_host variable. This will be prepended to the default
PATH variable supplied by other programs. (suggested: /bin:/usr/bin:/usr/ucb).
Note that the host name must be specified without any domain name appended to it
(e.g. for an host name of lyon.eiffel.com, use variable p_lyon). If your host
name contains an '-' in it, you must write it as a '_', since '-' is not a valid
character for a perl variable name.
perlib This variable may be used to change the perl search path for required files.
Directories should be separated using a ':' character, just like a shell PATH.
This path is prepended to the default perl search path. Any directory not
starting with a '/' (after ~name substitution) is taken relatively to the
mailagent private lib directory determined at configuration time.
plsave Name of the file used to save the patchlevels for archived distributions. This
is only used by the commands invoked via PROCESS. (suggested: $spool/plsave).
powerdir Directory listing user clearances for SERVER powers. (suggested:
powerlist Name of file containing SERVER power aliases. Since power names can be arbitrary
long but some filesystems still have a 14 character limitation on filename
length, internal aliases are created and maintained by mailagent. (suggested:
powerlog File where SERVER power requests are logged, in addition to the agentlog. Since
those are a security concern, it is a good idea to log them separately. If not
defined, log them only in agentlog. (suggested: $logdir/powerlog).
powers Directory for SERVER power administration. (suggested: $spool/powers)
proglist A small description for the available distributions. See the sample held in
Lib/mailagent/proglist. This is used by PROCESS only. (suggested:
queue Queue directory (messages waiting to be processed). Required, of course.
(suggested: $spool/queue)
queuehold Maximum number of seconds a mail can sit in the mailagent queue before being
actually processed. During that time, mailagent will not try to process the
message even when -q is used. (optional, defaults to: 1800).
queuelost Maximum number of seconds after which mailagent should flag messages still in
its queue as being old. (optional, defaults to: 86400, i.e. a day).
queuewait Time in seconds telling the C filter program how long it must wait before
launching mailagent. (optional, defaults to: 60, but can be lowered to 0 if you
don't want to wait to delay getting new messages).
rulecache The name of the file used to cache the latest compiled rules. Since usually
mailagent works mainly with one same rule file, this saves the overhead of
recompiling all the rules each time. (optional, suggested: $spool/rulecache).
rulemac Set this to ON to enable macro substitutions in rule patterns. (optional,
defaults to: OFF).
rules The name of the file holding the filtering rules (optional on non Debian
systems, suggested: ~/.rules). On Debian systems, one must have a minimal rules
file to prevent mailagent from trying to put messages into
/var/spool/mail/$USER, since mailagent can't lock that directory to prevent mail
from being garbled. This is because Debian policy requires all entities
attempting locks on that directory to be sgid mail, and making mailagent sgid
anything would be a security loophole.
{ SAVE incoming };
is the suggested minimal rules file.
runmax Timeout for RUN commands and friends. (optional, defaults to: 3600).
scriptcc Flag indicating whether a copy of the SERVER session transcript should be send
to the user running mailagent. (suggested: OFF).
secure When set to ON, mailagent and the C filter will perform extensive security
checks on sensitive files. This includes checks for group writability,
ownerships and protection testing on the directory where the file resides, and
checks on symbolic links to directories (mailagent only, when linkdirs is ON
too). Note that secure is assumed to be ON, whatever its real setting, when
running as super-user. (suggested: ON).
sendmail The name of the program used to send mail. That program must accept the mail
message with headers on its standard input and a list of recipients on the
command line. If not specified, will use the mailer chosen at configuration time
(sendmail usually). The command line used to mail a message will be sendmail
mailopt address(es). (optional, suggested: /usr/lib/sendmail).
sendnews The name of the program used to post news. That program must accept the news
article with headers on its standard input. If not specified, will use the news
posting program chosen at configuration time (inews usually). The command line
used to post an article will be sendnews -h newsopt. (optional, suggested:
seq File used to compute job numbers (suggested: .seq).
servdir The directory name where shell and perl server commands are stored. This is the
default lookup place. Optional parameter unless SERVER is used. (suggested:
servshell This is the name of the shell used to launch SERVER shell commands (actually to
process the wrapper file that will ultimately exec() the command). On some
systems like HPUX 10.x, this has to be set to /usr/old/bin/sh to get the plain
old Bourne shell, because /bin/sh is a braindead POSIX shell that closes file
descriptors greater than 2 upon exec(), whereas the Bourne shell does not.
(optional, suggested: /bin/sh unless you're on HPUX 10.x, as explained before).
spool Spool directory, required (suggested: ~/var/mailagent).
statfile File where statistics should be gathered. If no such file exists, no statistics
will be recorded (suggested: $spool/mailagent.st).
tofake Whether or not mailagent should fake a To: line into the message header when it
is absent, which will be used for filtering purposes (no physical alteration of
the header occur). It uses Alternate-To: headers if found, otherwise it assumes
the message was send to the user and takes the value from the user configuration
variable. (optional, defaults to ON, suggested: ON; turn it OFF only if you
want to identify missing To: lines to detect SPAM).
tome This optional variable may contain a comma separated list of alternate logins
that are also valid for the user (mail aliases). This is used in vacation mode
to check whether the mail was sent to the user or to a mailing list. Matching
is anchored on the login name, so saying "ro*" will match both root and rom.
track Set to on (case insensitively), this turns on the -t option which tracks all the
rule matches and the actions on standard output. This is optional (suggested:
timezone The time zone value for environment variable TZ (optional).
tmpdir Directory for temporary files. Required (suggested: /tmp).
umask Default umask which is reset by mailagent before processing a message. Assumed
to be decimal unless starting with '0' (for octal) or '0x' (for hexadecimal).
The octal format is the easiest way to specify it nonetheless. (optional,
defaults to: 077).
user Login name of the user who runs mailagent. This sets the value of the %u macro.
vacation A flag set to ON or OFF to switch the vacation mode accordingly.
vacfile The name of the file to be sent back in vacation mode (suggested: ~/.vacation).
vacfixed When ON, all changes to the vacation file (even locally) by means of the
VACATION command are forbidden. This is useful if you usually have many
customized vacation messages for different people but temporarily want to force
one unique message (optional, defaults to: OFF).
vacperiod The minimum time elapsed between two vacation messages to a given address
(suggested: 1d).

Available Logging Levels
The following log levels can be used while running mailagent:

0 No logging
1 Major problems only
2 Failed deliveries
3 Successful deliveries
4 Deferred messages
5 Successful filter actions
6 Unusual but benign incidents
7 Informative messages
8 Non-delivery filter actions
9 Mail reception
12 Debug
19 Verbose
20 Lot more verbose

Plugging Mailagent
Once you have configured mailagent in a ~/.mailagent (where ~ stands for your home
directory), you must tell sendmail how to invoke it. This is done by setting a ~/.forward
file which looks like this (leading and trailing double quotes are a mandatory part of

"| exec /users/ram/mail/filter >>/users/ram/.bak 2>&1"

This will pipe all your mails to the filter program, redirecting all unusual messages to
~/.bak. A sample filter shell script may be found in Lib/mailagent, as well as a C filter
program. On some systems, it may be necessary to move the '|' character before the leading
quote, but don't try this unless you have no other choice (i.e. only as a last resort).
Also, apparently Exim takes exeption to the exec, and even perhaps to the redirection --
which would be a pity.

It is very important to redirect error messages to some file within your home directory.
For one thing, that will get you out of trouble if strange things start to happen, but
more to the point, it makes your .forward file unique. Older sendmail program, in an
heroic attempt to "optimize" delivery, will silently remove duplicate recipients, and if a
recipient has a .forward, its literal content is used in place of his e-mail address.
Therefore, two local recipients with the same filtering string will be considered as one
unique recipient and only one of them will get the message...

If your system does not allow shell redirection from within the .forward, you can use this
instead (only supported by the C filter):

"| exec /users/ram/mail/filter -o /users/ram/.bak"

which in effect redirects stdout and stderr to the specified file for you, appending data
at the end of the file. If the filter runs setuid or setgid, you will not be allowed to
create the file, nor to append to it unless the owner of the file is the real uid invoking
the program (for security reasons).

Note that the .forward file only pipes the mail to the filter program and does not leave
any copy in the mailbox. It is up to you to decide in the rule file whether you want to
trash the mail away or leave it in the mailbox.(Note that on Debian systems mailagent can
not lock the spool directory, and letting it leave mail in mailbox may cause it to get
garbled). If you do not have a rule file (i.e. you left a blank entry in your
~/.mailagent, or you named a non-existent file, or your file is simply empty), the
default action is to leave the mail in the mailbox, which is not a good idea for Debian
machines. Please onstall a minimal rules file in any case,
{ SAVE incoming };
is the suggested minimal rules file.

Allowed Commands
The allowed command file (as specified by the comfile variable in your ~/.mailagent)
contains all the recognized and allowed commands. The file commands held in directory
Lib/mailagent should be copied as-is into your Spool directory.

Testing Your Installation
Now, assuming you have set a proper ~/.mailagent file and edited the configuration section
of the filter, it is time to test your installation. Make sure your .forward is world
readable and that the filter has the execution bits set (there is no reason to make the
filter world readable). Set a log-level of 20 and disable vacation mode (the vacation
entry in the ~/.mailagent should be OFF). Set the name of the rule file to an file
containing a catch-all rule:
{ SAVE incoming };
You are ready to proceed...

Send yourself a mail and give mailagent time to process your mail. The subject of the
message should be 'test' (in fact, anything but 'Command'). You may want to run a "tail
-f logfile" to see what's happening. At the end of the processing, the logfile should
contain something like the following (names of temporaries may -and will- of course
differ; timestamps have been removed):

got the right to process mail
building default rules
parsing mail
analyzing mail
in mode 'INITIAL' for ALL
selector 'All' on '<1,->', pattern '/^Subject: [Cc]ommand/'
matching '/^Subject: [Cc]ommand/' on 'All' (<1,->) was false
selector 'All' on '<1,->'
matching . on 'All' (<1,->) was true
saving in folder incoming
starting LEAVE
starting SAVE /home/ram/mail/incoming
SAVED [qm7831] in folder incoming
FILTERED [qm7831] from ram (Raphael Manfredi)
mailagent continues
mailagent exits

If you do not get that, there is a problem somewhere. Start by looking at the ~/.bak file
(or whatever file the .forward uses to redirect output of the filter). If you see
something like:

FATAL no valid queue directory
DUMPED in ~/mbox.filter

then it means the queue parameter in your ~/.mailagent does not point to a valid
directory. Your mail has been dumped in an emergency mailbox.

The ~/.bak file may also contain error messages stating that perl was not found. In that
case, there should be an error message in the logfile:

ERROR mailagent failed, [qm7886] left in queue

In that case, make sure the mail has correctly been queued in a file qm7886. The queue
will be processed again when another mail arrives or when the mailagent is invoked with -q
(however, to avoid race conditions, only mails which have remained for a while will be

Queuing of mail also happens when another mailagent is running. If the logfile says:

denied right to process mail

then remove the perl.lock file in the Spool directory. Old lock files are automatically
discarded by the mailagent anyway (after one hour).

If none of these occurs, then maybe sendmail did not process your ~/.forward at all or the
file has a syntax error. Check your mailbox, and if your mail is in there, your .forward
has not been processed. Otherwise, ask your system administrator to check sendmail's
logfile. A correct entry would appear as (with leading timestamps and syslog stamps

message-id=<[email protected]>
from=ram, size=395, class=0, received from local
to="| /york/ram/mail/filter >>/york/ram/.bak 2>&1", delay=00:00:05, stat=Sent

If you still cannot find why the mail was not correctly processed, you should make sure
you normally receive mail by removing (or renaming) your ~/.forward and sending yourself
another test mail. Also make sure your home directory is world readable and "executable".

If you are using the C filter, make sure it is running on the right platform. There may
be a low-level routing of all your mail to a mailhost machine, responsible for the final
delivery, and the filter program will run on that machine, which may be a different
platform than the one you compiled filter on. Also make sure your home directory is
mounted on that machine, or the mail transport agent will be unable to locate your
.forward file, less process it.

This kind of centralized mail delivery is good only when a few people have mail processing
hooks (i.e. .forward files piping mail to a program); otherwise it's better to route mail
to each user's workstation or machine, for local processing, to avoid an excessive
workload on the mailhost machine, especially if it is a dedicated NFS server. If you are a
system administrator installing mailagent and expect many people to use it, keep this in


There is a limited set of options which may be used when calling the mailagent directly.
Only one special option at a time may be specified. Invoking mailagent as mailqueue is
equivalent to using the -l option.

-c file Specify an alternate configuration file (~ substitution occurs). The
default is ~/.mailagent.

-d The mailagent parses the rule file, compiles the rules and dumps them on
the standard output. This option is mainly used to check the syntax of the
rule file and make sure the rules are what the user really thinks they are.

-e rule This option lets you specify some rules on the command line, which will
override those specified via the ~/.mailagent, if any. There may be as many
-e as necessary, all the rules being concatenated together as one happy
array, which is then parsed the same way a rule file is. If only one rule
is given and there is no action specified between {...} braces, then the
whole line is enclosed between braces. Hence saying -e 'SAVE foo' will be
understood as -e '{SAVE foo}', which will always match and be executed.
Using the -d option in conjunction with this one is a convenient way to
debug a set of rules.

-f mailfile Using mailfile as a UNIX-style mailbox (i.e. one where each mail is
preceded by a special From line stating the sender and the date the message
was issued), extract all its messages into the queue and process them as if
they were freshly arrived from the mail delivery subsystem.

-F Force processing on already seen messages. Usually, mailagent enters the
special _SEEN_ state when it detects an X-Filter: line issued by itself,
but this option will have it continue as usual (although vacation messages
are disabled). Use this option when post-processing mail already filtered.
Also look at the -U switch if you are using the RECORD or UNIQUE actions in
some rules.

-h Print out a usage message on the standard error and exit.

-i Interactive mode, directs mailagent to print a copy of all the log messages
on stderr.

-I Install a ~/.mailagent file from template, or merge new configuration
variables into an existing file; then perform sanity checks and create
mandatory files or directories. This option may be viewed as an help into
setting up mailagent's environment. In any case, the created/merged
~/.mailagent file should be manually verified before letting mailagent deal
with your mail by hooking it into ~/.forward.

-l List the mailagent queue. Recently queued mails which are waited for by the
filter are skipped for about half an hour, to avoid race conditions. This
may be configured via the queuehold variable. Really old messages (more
than queuelost seconds old) are flagged with a '#' character. Messages out
of the queue (queue variable) are flagged with a '*', whilst old messages
out of the queue are signaled by an '@'. Locked messages have a '*'
appended to their status.

-L level Override the log level specified in the configuration file.

-o override This option lets you override a specific configuration option. The option
must be followed by a valid configuration line, which will be parsed after
the configuration file itself. For instance, the -L 4 option is completely
equivalent to -o 'level: 4'. Note that any white space must be protected
against shell interpretation by using the appropriate quoting mechanism.
There may be as many -o options on the command line as necessary.

-q Force processing of mailagent's queue. Only the mails not tagged as skipped
by the -l option will be processed.

-r file Specify an alternate rule file.

-s {umaryt} Build a summary of all the statistics gathered so far. The output can be
controlled by appending one or more letters from the set {umaryt}. Using
-summary is a convenient way to get the whole history of the filter
actions. The u modifier will print only used rules. The m will merge all
the statistics at the end while a reports the mode the filter was in when
the command was executed. The r asks for rule-based statistics and the y is
pretty useless and is here only to get a nice mnemonic option. Note that
specifying an option more than once has no effect whatsoever on the option
itself (i.e. you may put three Uu and only one m, but you'll still get the
summary!). The t letter may be followed by digits specifying how many rule
file versions relative to the topmost (most recent) rule file we should
extract from the statistics, that amount defaulting to 1: using -surat will
print a complete statistics report for the last version of your rules,
while -surt12a would do the same for the last twelve versions of those same

-t Put mailagent in a special tracking mode where all the rule matches and
executed actions are printed on the standard output. This is mostly useful
for debugging a rule file. See also the track parameter in the
configuration file.

-V Print version number and exit.

-U Prevent the UNIQUE and RECORD commands from rejecting an already processed
Message-ID the first time they are run on a given message. This is useful
when processing messages that have been dropped in the emergdir directory
due to some abnormal (but transient) condition and you wish to reprocess
the message. Also see the -F switch if you are re-processing messages.

If you invoke mailagent without options and without any arguments, the program waits for a
mail on its standard input. If an argument is provided, it is the name of a file holding
one mail to be processed. This is the normal calling procedure from the filter, the
argument being the location of the queued mail.


If you do not want to use the filtering feature of mailagent, (NOTE: This may cause mail
to be garbled on Debian systems, since mailagent can not lock the spol directory under
Debian policy restrictions) then the default built-in rules will be used. Those are
really simple: all the mails are left in your mailbox and mails with a line "Subject:
Command" anywhere in the message will be processed. Commands are looked for on lines
starting with "@SH". The remaining of the line is then given to a shell for execution.

Available commands are read from a file (entry comfile in your configuration file), one
command name per line. Only those listed there will be executed, others will produce an
error message. The mailagent traps the exit status and will send an error report if a
command fails (provided that the command does not issue a message by itself, in which case
it should return a zero exit status).

If you do not want to use the default rules, you may skip the remaining of this section.

Configuring Help
The help text mailagent will send to people must be copied from Lib/mailagent/agenthelp
into your own spool directory, as specified in your ~/.mailagent. Two macros may be used:

=DEST= This will be expanded to the sender's address (the one who sent you the mail
currently processed by mailagent).

=MAXSIZE= This stands for the maximum size set before kit is used to send files back
(parameter maxsize in your ~/.mailagent file).

You may use the default help file or design one that will give even more details to the
poor user.

Distribution Files
The two files proglist and distribs held in Lib/mailagent describe the distributions your
mailagent will be able to distribute. The samples given show the expected syntax. In
order to clarify things, here is what the format should be:

File proglist contains a small description for programs. The name of the program appears
after a single star. It is followed by lines in free format. An optional three-dashes line
separates each program's description. Note that a leading tab will be added to each line
of description.

The distribs file holds lines of the following form:

progname version path archived compressed patches


progname is the program name (the same as the one mentioned in proglist).

version is the current version number. If none, a three-dashed line may be used.

path is the path where the distribution is stored. The ~ will be expanded into your
home directory. Note that if the distribution is stored in archived form, the
path name is the one of the archive without the ending extension (which may be
.cpio.Z or .tar.Z).

archived is either y or n depending on whether the distribution is archived or not.

is either y or n depending on whether the distribution is compressed or not.
This could be guessed from the extension's name, but we must think of file
systems with short names.

patches is y or n depending on whether the distribution is maintained or not by you. If
you put a p, this means official patches are available, although you do not
maintain the distribution. Finally, an o means that this is an old version,
where only patches are available, but maildist will not work. In that case,
assuming the version number is 1.0, old patches are expected in a bugs-1.0

You may include comments in both files: all lines starting with a leading # will be

Testing Your Mail Agent
It is now time to make sure your mailagent works. Send yourself the following mail:

Subject: Command
@SH mailhelp

You should receive back a mail from yourself with the subject set to: "How to use my
mailagent". If you don't, check the file ~/.bak (or whatever file you set in your
.forward). If it is empty, look at the log file. If the log file is not empty, then
perhaps the mail has been queued. Check the sendmail queue. Also make sure that you
removed the '#' comments in the filter script. On some systems, they cause some trouble.
If you are using the C filter, maybe your sendmail is broken and you need to make your own
setuid copy (or perl might complain that you have a kernel bug, etc...).

If you have done everything right but it still does not work properly, increase log level
to 20 and resend your command mail. Then check the log file. The diagnosis should be

Once this works, you should check your distribs and proglist files by sending yourself the
following mail:

Subject: Command
@SH maillist

If the list you have in return is incorrect, then your distribution files are wrongly
written. If you do not get the list, there is a problem with your mailagent's
configuration. Retry with a log level set to 20 and look at the issued log messages in
your Log directory. Make sure that the file listed in the plsave entry of your
~/.mailagent is correctly updated after a maillist has been run.


The mailagent can also be used as a filter: mail is parsed and some actions are taken
based on simple lex-like rules. Actions range from a simple saving in a folder, a
forwarding to another person, or even spawning of a shell command. Before going further,
here is a small example of a valid rule file:

From: root { FORWARD postmaster };
To: [email protected] { POST mail.gue };
Subject: /metaconfig/ { SAVE dist };
{ SAVE incoming };

There are three distinct rules. Rules are applied in sequence, until one matches (so the
order is important). Any mail coming from root will be forwarded to user postmaster. A
mail addressed to [email protected] is a mail coming from a mailing list. The mail is posted
on a local newsgroup mail.gue. Mails whose subject contains the word "metaconfig" will be
saved in a folder dist for delayed reading and will not appear in the main mailbox. If no
rule matched, the mail is left in the folder incoming.

Rule File Syntax
Here is a non-formal description of the rule file. Parsing of the file is done lexically,
hence the choice of non-ambiguous tokens like '{' or ';' which are easily parsed. This
introduces some limitations which are silently applied: for instance, no '{' may be used
as part of an address.

Comments are introduced by a leading '#' , which must be on the left margin. Unlike shell
comments, a '#' which is not left justified will not be understood as a comment. However,
spaces or tabs are allowed in front of '#'.

All the statements in the rule file must end with a ';'. There are mainly four parts in
each line. A list of comma separated modes, between '<' and '>', which give the set of
modes in which the rule applies. The special mode ALL will match everything. The filter
begins in the mode INITIAL. Omitting the mode defaults to "<ALL>". It is possible to guard
a rule against some specific mode by negating it, which is done by prefixing the mode with
'!'. Negated modes take precedence other plain modes, meaning "<!ALL>" will never be
matched, ever, and that "<MODE, !MODE>" is equivalent to "<!MODE>".

Then comes a list of selectors. Those selectors must be space separated and end with ':'.
They represent the names of header fields which must be looked at by the forthcoming
pattern. An empty selector list defaults to "Subject:". Special selectors "All:", "Body:"
and "Head:" apply to the whole message, its body or its header. A commonly used selector
list is "To Cc:" which tests the recipient fields of the header. If the selector name is
preceded by an exclamation mark '!', then the logical value of the test for that selector
is negated.

The list of selectors may end with an optional range specification, given as <min, max>,
before the final ':' character marking the end of the selector list. The minimum or the
maximum may be given as '-', in which case it is replaced with the minimal or maximal
possible value. Indices for selection begin at 1 (not 0), for instance: <3, 7>. If no
range selection is given, then the default <1, -> is used. Ranges normally select lines
within the matching buffer, unless the selector is expecting a list in which case it
operates on the list items. For instance, Body <3, 5>: would select lines #3 to #5
(included) from the mail body, whereas To Cc <1,3>: would focus on the first three
addresses on each To: or Cc: header lines. Negative values refer to that many lines or
addresses back from the end, i.e. Cc <-2,->: selects the last two addresses on the Cc:
line. A single number such as <2> is understood as <2, 2>, i.e. it select only one item
in the list, <-> meaning everything (and being therefore redundant).

The selector is then followed by a pattern within '/' or by a single name. In order to
ease the writing of the rules, the semantic of a single name varies depending on the
selector used. For the special selectors "From:", "To:", "Cc:", "Sender:", their
associated "Resent-" fields, "Reply-To:", "Envelope:" and "Apparently-To:", a single name
is understood as a match on the login name of the address. Note that if no "To:" field is
present in the header, one will be forged from the "Apparently-To:" for the purpose of
filtering only (i.e. no physical modification on the header is done). If the login name of
the address is a full name of the form First.Last, only the last name is kept, and is
lower-cased. If only a single name is given, only shell metacharacters * and ? are
allowed, as well as intervals [].

If the pattern is preceded by a single exclamation mark '!', then the matching status is
negated (i.e. it will succeed if the pattern is not found). If a single word is used for
non-special selectors, the same rules apply but the pattern is anchored at the beginning
and the end for an exact match. With a pattern starting with '/', any regular expression
understood by perl may be used and your pattern will not be modified in any way. The other
special selector "Newsgroups:" works as "To:", excepted that newsgroups names are expected
and a match is attempted on every item in the list. Every pattern match on a single name
for an address-type field (i.e. "Newsgroups:" excluded), are made in case-insensitive
mode. Otherwise, you can force a case-insensitive match by appending a trailing i option,
as in /pattern/i.

There is also a little magic involved when matching on an address field. Namely, if the
pattern is not a single word and is anchored at the beginning, then only the address part
of the field will be kept. For instance, if we have a From: field whose value is Raphael
Manfredi <[email protected]>, then the pattern /Raphael/ would match, but not /^Raphael/.
Instead, /^ram@.*$/ would match, but this is more easily done with a single word pattern
ram, for it only focuses on the login name of the address and would also match if the
address was written as eiffel.com!ram. A single address in Internet form, as in
[email protected] is implicitely matching on the address part of the field, and you must not
escape the '.' as you would have to in a regular expression.

This may sound a little complex, but this design is meant to make things easier for the
user. Here are some other examples:

# Match [email protected] as well as [email protected].
From: ram

# Match [email protected], ram but not [email protected]
From: r[oa]*

# Match [email protected] but not [email protected]
To Cc: /^gue@eiffel\.fr/

# This will match [email protected] as well as [email protected]
To Cc: /gue@eiffel/

# Match comp.lang.perl but not comp.lang.perl.poetry (?)
Newsgroups: comp.lang.perl

# Accept anything but messages coming from root
From: !root

When attempting a match on "To:", "Cc:" or "Apparently-To:", a list of addresses separated
by a comma is expected, whereas only one address is expected after "From:". If you omit
the pattern, it will be understood as * (recall that a single word uses shell meta-
characters), which will match anything.

Then comes the action to be taken when a match occurs. There are only a limited set of
valid actions which will be described soon in detail. The action is enclosed in curly
braces '{' and '}' and actions are separated or terminated (depending on your taste) by a
';'. Action names are spelled in upper-case for readability, but case is irrelevant. If
you want to put a ';' within the rule, it must be escaped by preceding it with a
backslash. A double backslash is translated into a single one, and any other escape
sequence involving the backslash character is ignored (i.e. \n would be kept verbatim).

Note that a rule should be ended by a single ';' after the last '}'. It is possible to
omit this final ';', but that single token is the re-synchronizing point for error
recovery. One could argue however that there should be no syntax error, and thus the ';'
ought to be safely omitted. Whenever in doubt, check your rule file with the -d option.

Here is a prototypical rule (using perl regular expressions; please refer to the
subsection Regular Expressions for more information):

<ROOT> From: /^\[email protected]$/ { SAVE eiffel };

That rule will only be taken into account when the filter is in the mode ROOT (recall that
the processing starts in mode INITIAL; use BEGIN to change the mode, as in lex). So in
mode ROOT, anything which comes from a user located in the eiffel.com site is saved in
folder eiffel for deferred reading. The mail will not appear in the mailbox.

It is possible to have more than one selection for a rule. Identical selectors are
logically or'ed while different ones are and'ed. The selections are comma separated. For

From: root, To: ram, From: ram, Subject: /\btest\b/ { DELETE };

will delete a mail from root or ram if it is sent to ram and has the word test in its
subject. It is also possible to write the previous rule as:

From: root, ram, To: ram, Subject: /\btest\b/ { DELETE };

because if no selector is given, the previous one is used (with the first selector being
"Subject:" by default).

Anywhere in the rule file, it is possible to define some variables. The list of recognized
variables is given later. For now, let's say that maildir is the default folder directory.
This variable is used by the SAVE command when the argument is not an absolute path.

maildir = ~/mail;

will direct the filter to use ~/mail as the folder directory (default is ~/Mail). Note the
~ substitution and the final ';'. It is not possible (currently) to modify the environment
by setting PATH for instance.

Finally, there is a special construct to load patterns from a file. A pattern enclosed in
double quotes means that the patterns to be applied should be taken from the specified
file. The file is expected to be in the directory mailfilter if it is not an absolute path
(~ substitution occurs). If the variable is not set maildir will be used. If by chance (!)
maildir is not set either, the home directory is used. The file should contain one pattern
per line, shell comments (#) being allowed at the beginning of each line.

An action may be followed by other rules. Hence the following is perfectly valid:

ram { SAVE ram }
/plc/i { SAVE plc }
root { SAVE ~/admin }
/xyz/ { DELETE }
"users" { LEAVE }

Note the use of the file inclusion: all the users listed in file users will have their
mail left in the system mailbox. The usual rules apply for these loaded patterns.

Selector Combination
A single rule may have a various set of selectors. For instance, in the following rule:

From: ram, To Cc: root, !Subject: /test/, From: raphael

we have the following set { From, To Cc, !Subject }. The first two selectors are called
direct selectors, !Subject: is called a negated selector. The To Cc: selector is a group
selector decomposing into two direct selectors, while From: is an atomic selector.
Finally, From: is also a selector with multiple occurrences. The value of a selector is
its matching status logical value.

Let D be the set of direct selectors and N the set of negated selectors, which form a
partition of R, the set of all the selectors in the rule. That is to say, R is the union
of D and N, and D intersected with N is the empty set (trivial proof: a selector is either
direct or negated). If either D or N is empty, then it's not a partition but in that case
we have either D = R or else N = R.

Let's define the logical value of a set S as being the logical value the filter would
return if those rules were actually written. Then the logical value of D is the logical
value of each of its item with the AND logical operator distributed among them, i.e. the
logical value of { a, b, c } is the value of (a AND b AND c). Let's write it AND(D). The
logical value of each of the items is the logical value of the selector itself if it is
not multiple, or it is the logical value of all the occurrences of the multiple selector
within the rule, with the logical OR operation distributed among them. That is to say, in
the above example, the value of From is true iff the From: fields contains ram OR raphael.
Let's write that OR[From].

To be sound, we have to apply De Morgan's Law on N, hence the following rules: the logical
value of N is OR(N) and given a negated selector s, its logical value is AND[s]. And
finally, the logical value of R is that of D AND N, with by convention having the logical
value of the empty set be true.

For those who do not know De Morgan's Law, here it is: given two logical propositions p
and q, then the following identities occur:

NOT (p AND q) <=> (NOT p) OR (NOT q)
NOT (p OR q) <=> (NOT p) AND (NOT q)

While we are in the logic of the propositions, note also that OR and AND are mutually
distributive, that is to say, given three logical propositions p, q and r, we have:

p AND (q OR r) <=> (p AND q) OR (p AND r)
p OR (q AND r) <=> (p OR q) AND (p OR r)

To be complete, OR and AND are associative with themselves and commutative. And the B set
{ 0, 1 } equipped with the set of operations (NOT, OR, AND) is an algebra (a Boolean one).
I will spare you the definition of an algebra, which really has nothing to do in this
manual page (which is for a mail agent, in case you don't remember :-).

The attentive reader will certainly have noted that I have not specified the logical value
of a group selector. Well, given a group selector G, we decompose it into a DG and NG
partition, DG being the subset of (atomic) direct selectors of G and NG being the subset
of (atomic) negated selectors. Then the logical value of DG is OR(DG) and the logical
value of NG is AND(NG); the global logical value of G being that of DG OR NG. In case
either DG or NG is empty, then we don't have a partition, but by convention the value of
the empty set is false, and one of the sets is equal to G. Note that within a group
selector, the rules are exactly the dual of the rules within R.

Now the only rule which is not logical is whether a group selector belongs to D or N. I've
chosen, for analogy reasons, to make the group selector belong to D if it does not start
by '!' and to N otherwise. That is, !To Cc: belongs to N whilst Cc !To: belongs to D.
Apart from that, order within the group selector is irrelevant: To Cc: is equivalent to Cc
To:, so the behavior in the quotient set is sound.

Here are some examples:

# Match anything: (not from ram OR not from root) is always true.
From: !ram, !root

# Match anything but reject mails coming from ram OR root
!From: ram, root

# Reject mails whose headers matching /^Re.*/ contain the word test
!^Re.*: /\btest\b/

# Keep mails whose subject contains test AND host
!Subject: !/test/, !/host/

# Matches if ram is listed in the To OR the Cc line
To Cc: ram

Minimal Header
A minimal set of selectors are guaranteed to be set, regardless of the actual header of
the message. This is for the purpose of filtering only, no physical alteration is

Envelope: This is the address found in the mail envelope, i.e. the address where the mail
seems to originate from. This can be different from the From: address field if
the mail originates from a trusted user, in sendmail's terminology. If you don't
know what that is, simply ignore it.
From: User who wrote the mail. If this line is missing, uses the address found in the
first From line.
Length: The physical length of the body, in bytes, once content-transfer-encoding (if
any) has been removed.
Lines: The amount of lines in the body (decoded, if necessary).
To: The main recipient(s) of the message. If this line is missing but a set of
Apparently-To: lines is found, then those addresses are used instead. If no such
line exists, then assume the mail was directed to the user (which seems a
reasonable assumption :-).
Sender: User who sent the mail. This may differ from the From: line. If no such field
exists, then the address in the first From line is used (mail envelope).
Relayed: This computed header is a comma-separated list of all the hosts where the
message was relayed, in the proper transmission order. Each item in this list
can be a machine name such as mail.hp.com or an IP address such as
[]. The list is derived from the Received: lines present in the
Reply-To: Where any reply should be sent. If no Reply-To: field is present, then the
Return-Path is used (with <> stripped out), or the From: line is parsed to
extract the e-mail address of the author.

The mailagent supports user-defined variables, which are globals. They are set via the
ASSIGN command and referred to with the %# macro. Assuming we set a variable host, then
%#host would be replaced by the actual value of the variable. This enables some variable
propagation across the rules.

For example, let's say the user receives cron outputs from various machines and wishes to
save them on a per-machine basis, differentiating between daily outputs and weekly ones.
Here is a solution:

Subject: /output for host (\w+)/ { ASSIGN host '%1'; REJECT };
Subject: /^Daily output/ { SAVE %#host/daily.%D };
Subject: /^Weekly output/ { SAVE %#host/weekly.%m-%d };

Besides variable interpolation via the %# escape, it is also possible to perform
substitutions and translations on the content of a variable (or a back-reference, i.e. a
number between 1 and 99). The two commands SUBST and TR will respectively perform in-place
substitutions and translations. In that case however, the name of the variable must be
preceded by a single #. This differentiates the back-reference 1 from the variable #1,
although 1 is a funny name for a variable. The need for # also prevents the common mistake
of writing %#, as mailagent will loudly complain if the first parameter of SUBST or TR is
not a digit between 1 and 99 or does not start with a #.

Here are some actions to canonicalize the host name into lower case and strip down the
domain name, if any:

{ TR #host /A-Z/a-z/; SUBST #host /^([^.]*)\..*/$1/ };

Those actions are directly translated into their perl equivalent, and any error in the
specification of the regular expression will be reported.

If the variable name begins with a colon ':', then the variable is made persistent. That
is to say it will keep its value across different mailagent invocations. The variable is
simply stored (with the leading ':' removed) in mailagent's database and is thus subject
to the aging policy set up in the ~/.mailagent.

Within PERL commands or mail hooks using perl (see the MAIL HOOKS section), you can
manipulate those (so-called) external variables via a set of interface functions located
in the extern package (i.e. you must prefix each of the function name with its package
name, set becoming extern'set). The following three interface functions are provided:

val(name) Return the value of the variable name (the leading ':' is not part of the name,
in any of these three interface functions).

set(name, value)
Set the external variable name to hold value. No interpretation is done by the
function on the actual content of the value you are providing.

age(name) Returns the age of the variable, i.e. the elapsed time in seconds since the last
modification made by set.

There is currently no way for erasing a variable from the database. But if you do not use
the variable any more, it will be removed when its age becomes greater than the maximum
age specified by the agemax configuration variable.

Regular Expressions
All the regular expressions follow the V8 syntax, as in perl, with all the perl
extensions. If a bracketing construct (...) is used inside a rule, then the %digit macro
matches the digit's substring held inside the bracket. All those back-references are
memorized on a per-rule basis, numbered from left to right. However, great care must be
taken when using a back-reference in multiply present selectors, as all the matches will
be performed up-to the first match, and back-references are computed on the fly while
doing pattern matching.

For instance:

To: /(.*)/, Subject: /Output from (\w+)/ { ASSIGN to '%1'; SAVE %2 };

will save the To: field in variable 'to' and save the mail in a folder derived from the
host name specified in the subject. However, if we say:

Subject: /host (\w+)/, /from (\w+)/ { ASSIGN match '%1' };

then there will be only one back-reference set, and it will come from the first pattern
matching if it succeeds, or from the second. Should the second or the first pattern have
no bracketing construct and still match, then the back-reference would not be recorded at
all, which means the following is probably not what you want:

Subject: /from/, /host (\w+)/, To: /(.*)/ { SAVE %1; REJECT };

as if the /from/ pattern matches then /host (\w+)/ will not be checked (identical
selectors are or'ed and that is optimized), then %1 would refer to the To: field whereas
if /host (\w+)/ matches, then %1 will be the host name.

However, this behavior can be used to selectively store a news article which has been
mailed to you in a folder whose name is the newsgroup name in dot form. Assuming we want
to give priority to comp.lang.perl, we could say:

/([^,]*)/ { SAVE %1 };

An article cross-posted to both comp.lang.perl and comp.mail.mh would be saved in a
comp.lang.perl folder, since this is what would match first. The last rules takes care of
other articles: the folder used being whatever newsgroup appears first.

There is also a special macro %&, which lists (it's a comma separated list) all the
selectors specified via a regular expression which indeed matched. For instance:

Re.*: /york/ { ASSIGN which '%&' };

would assign to which the list of all the fields matching the /Re.*/ pattern which
contained 'york', be it a Received: field or a Resent-From: field (as both match the
selector specification). Assuming both those fields contained the word york, the value of
%& would be 'Received,Resent-From;' (the fields are alphabetically sorted).

Should you have more than one such specified selector within a single rule, then it might
be worth knowing that all the set of matching selectors are recorded within %&, each set
terminated with a ';'. If a negated selector is used, then %& will record all the fields
which did not contain the pattern, assuming the selection succeeded (otherwise nothing is

Available Actions
The following actions are available as filtering commands. Case is irrelevant although the
recommended style is to spell them upper-cased. As explained later, most of the actions
record their exit status in a special variable which may be tested via the -t and -f
options of ABORT, REJECT and RESTART. For every command returning such an exit status, the
failure or success conditions are given at the end of each description. If nothing is
specified, then the command does not return a meaningful status.

ABORT [-tf] [mode]
Abort application of filtering rules immediately. See REJECT for the meaning of
the optional parameters. (Does not modify existing status)

AFTER [-sanc] (time) action
Records a callback for after the specified time, where action will be performed.
By default, a mailagent filtering action is assumed (-a option), on the current
mail message. A shell command (-c) may be given instead, receiving the current
mail message as standard input. Finally, a plain shell command may be run (with
no input) using the -s option. The option -n may be used when the current mail
message does not need to be kept for input. For instance:

AFTER -an (1 day) DO ~/process:proc'run(%u)

would call proc'run defined in the ~/process file in one day from now, without
giving any input (the action here does not require any).

When running mailagent commands, the initial working mode is set to _CALLOUT_.
This may matter if you call APPLY for instance. If the recorded time is less or
equal than the current time (which is now), the callback will occur when
mailagent is done with the messages in its queue, before exiting. This allows
for the following cute trick, found out by Randal Schwartz:

AFTER (now) # fork a copy I can mangle
STRIP Reply-To \; RESYNC \;
ANNOTATE -du Reply-To %2 \; RESYNC \;
NOTIFY message %r \; DELETE \;

Note that the command is not called AT because the call will only be performed
at the next mailagent invocation after the specified time has elapsed. Dates are
specified using the same format as in SELECT. (Fails if the action cannot be
recorded in the callout queue).

ANNOTATE [-du] field value
Annotate message by adding field into the mail header, with the supplied value.
This is like the MH command anno, but the annotation is performed at the end of
the header, whereas MH does it at the top. Normally, an extra field is added,
with the current date as field value.

This can be suppressed by using the -d option. If value is omitted, only the
date field is generated (hence it is an error to use the -d option without
supplying a value). As with all the commands which alter the header, a RESYNC is
necessary for the filter part to actually see the new header.

The -u option means "unique", and prevents ANNOTATE from executing if the
specified field is already present in the header. Don't forget to RESYNC between
successive ANNOTATE commands using this option if the field refers to a previous
ANNOTATE target. (Fails when no annotation takes place)

APPLY rulefile
Get the rules held in rulefile and apply them to the current message. The
filter will begin in whatever mode you were when using this command, but no feed
back will occur, i.e. any mode changing will be lost when returning from the

Variables (see the %# macro) are propagated back and forth through APPLY,
meaning you see variables set by the caller, and you may change their values or
create new variables for the caller to later use.

If mail is saved during the application of the rules, then the corresponding
flag is set in the main filter (the one that started the APPLY command). You may
nest them, of course. (Fails if mail is not saved by the rules held in

ASSIGN var value
Assign the value to the user-defined variable var, which may further be accessed
as '%#var' for macro substitution or #var in the TR and SUBST commands in place
of the variable name. Note that there is no leading # in front of the variable
name. The value you provide is first ran through perl to see if it contains some
arithmetic operations. If the evaluation is successful, the resulting value is
used instead. If an error occurs in this evaluation process, then the literal
value provided is used. To avoid the evaluation, you may enclose the whole
value in simple quotes. Those will be trimmed before the assignment takes place.
If you actually want simple quotes in the first AND last position, you have to
double each of them. (Does not modify existing status)

BACK command
Execute command and take its output as new actions to be performed on the mail
(hence performing something analogous to `command` in shell). If there is no
output, nothing is done. BACK commands can be nested, although this may lead to
surprises this manpage will not disclose (but I assure you it will be funny,
assuming we have the same sense of humor... :-). Note that both the standard
output and the standard error from the command are used.

If the command fails, the output is mailed back to the user and no action is
performed. Furthermore, normal feedback does not occur here: any output from the
command is taken as filter actions, which means the semantics of PASS, for
instance, is changed: we do not take a body back but commands. (The execution
status is that of the command)

BEEP [-l] count
This command may be used to tune the amount of beeps emitted when biffing on the
terminal, for each %a expansion. By default, that amount is set to 1. Using the
-l option alters the beep count locally for the rule. Otherwise, the default
amount is changed.

Note that this simply expands %a into the suitable amount of Ctrl-G characters.
Your terminal must be allowed to issue consecutive bells for this to work. Very
often, terminals are configured so that the first bell received disables further
beeps for some period, to avoid cascades of bells. If you use xterm for
instance, you should use:

xterm -xrm "XTerm*BellSuppressTime: 0"

to enable consecutive bells. Otherwise, xterm will swallow them during 200 ms,
hence making the BEEP command ineffective, apparently. (Does not modify
existing status)

BEGIN [-ft] state
Enter a new state. An explicit REJECT or RESTART is necessary to abort the
processing of the current rule. The processing begins in the state INITIAL. If
the -f (resp. -t) flag is specified, then the state change only occurs if the
last command status indicated a failure (resp. a success). A state name can
contain alphanumeric characters and underscores. (Does not modify existing

BIFF [-l] on|off|path
Allow or disallow biffing dynamically. When biffing is turned on via the
configuration file or via this command, a message is printed on some of the
terminals where the user is logged when mail is received, as explained under the

Instead of on or off, you can specify a file name (~ substitution allowed) being
the new path to be used for the biffing format template.

If you use the -l option, changes are made locally, for the duration of the rule
only. If you REJECT to go to some other rule, your changes will be lost. The
global value of the altered parameters is changed on the first local usage and
restored when a new rule is entered. (Does not alter execution status)

BOUNCE address(es)
Bounce the message to the specified address(es) and acts as if a save had been
done. The only difference with FORWARD is that no Resent-like lines are added to
the header. If an address is specified in double quotes, it is taken as the name
of a file to be loaded to get addresses (one address per line, shell comments
(#) allowed). The file name resolving is the same as the one used for pattern
loading. (Fails if mail cannot be resent)

DO routine [(arg1, arg2, ... , argn)]
Calls the perl routine, with the supplied arguments if any. This is a very low
level hook into mailagent's internal. The routine can be specified by itself
(package'name, package being main by default), or identified by a leading tag,
followed by a ':', then the routine name as before. The tag can be a path to a
file where the routine is defined, or a command name (for user-defined commands
which are loaded dynamically). For instance

DO UNKIT:newcmd'unkit('true')

would lookup the user-defined UNKIT command, load the file where it is defined
(in the newcmd package), then call the routine with 'true' as argument. The
package specified determines where the loading is done, so be sure it is
consistent with the definition in the file where the routine is defined. (Fails
if the routine cannot be located and executed)

DELETE Delete the current message. Actually, this does not do anything, it just marks
the mail as saved. If no further action involving saving is done, then the mail
will never show up in the mailbox. (Never fails)

FEED [-be] program
Feed the whole message to a program and get the output back as the new message.
Hence the program appears as a filter for the whole message. It does not tag
the message as having been saved. A RESYNC is automatically done upon return.
(Returns the status of program)

WARNING: Your program must be able to properly parse a MIME message and must
deal with transfer-encoded bodies by itself. To make the program task simpler,
you can supply the -b switch which will let mailagent decode the whole body for
you, suppressing any Content-Transfer-Encoding header (implying "binary"). This
is an invalid message format for sending the message, but it makes processing
easier. You still have to parse the MIME parts yourself though.

Using -b does not prevent your program from outputing a valid message back, one
that can be possibly sent on the network so you have two options: either you do
not supply any Content-Transfer-Encoding in the headers, and mailagent will
recode the body for you using the initial transfer encoding present in the
message (a relatively safe option if you make only changes in the body at well-
defined spots without introducing 8-bit chars), or you can supply the Content-
Transfer-Encoding yourself and perform the body encoding manually.

To be completely safe and minimize the work in your program, the -e switch will
let mailagent analyse the message body you are returning and select the proper
transfer encoding automatically. Since this will cause the whole body to be
analysed, and it can be potentially huge, that behaviour must be explicitly
asked for. If you need -e then you probably want -b as well (you can supply both
by saying -be naturally).

If you do not supply any switch, mailagent will give you the message as-is and
will get your message as-is without any additional magic.

FORWARD address(es)
Forward mail to the specified address(es). This acts as if a save had been done,
in order to avoid the DELETE. Usually when you forward a mail, you do not wish
to keep it. The command adds Resent-like lines in the header. As for BOUNCE,
file inclusion is possible (i.e. use an address "forward_list" to forward a mail
to all the users listed in the file forward_list). (Fails if mail cannot be

GIVE program
Give the body of the message to the specified program by feeding its standard
input. Any output is mailed to the user who runs the mailagent. Note that the
message is not tagged as having been saved. (Returns the status of program)

NOTE: If the message had a body that was encoded for transport (using one of the
base64 or quoted-printable transfer encoding), mailagent will transparently
decode it and supply a version that can be properly handled. In other words,
the program does not need to care about the body being encoded in the message,
as it will get a plain one. (Since no headers are supplied, this is the only
possible option).

Caution though for MIME messages: you should use PIPE for them to give a chance
to the program to properly handle the body, but then it needs to be fully MIME-

KEEP header_fields_list
Keeps only the corresponding lines in the header of the mail. For instance, a
"KEEP From To Cc Subject" will keep only the principal fields from the mail
message. This is suitable for archiving mailing lists messages. You may add a
':' after each header field name if you wish, but that is not strictly
necessary. Headers may be specified using shell-style regular expressions, and
file inclusion is allowed to get headers from a file. (Does not modify existing

LEAVE Leave incoming mail in the system mailbox. This is the default action if no rule
matched or if no saving occurred. This is not recommended on Debian systems.
(Fails if mail cannot be saved)

MACRO [-rdp] name [= (value, type)]
Lets you specify user-defined macros, of the form %-(name). See the paragraph on
user-defined macros for explanation about the available types (SCALAR, EXPR,
CONST, FN, PROG, PROGC). A perl interface to the underlying user macros is
available for your perl commands. The -r option is used to replace an existing
macro (instead of pushing a new instance on the stack), the -d is to delete all
the instances of a named macro (in that case it takes only the first argument),
and -p pops the last instance of the macro from the stack and reverts to the
previous definition, if any (otherwise, it acts as -d). If you wish to define a
simple SCALAR macro, you may omit the = (value, type) part and simply continue
with the macro value. (Does not modify existing status)

Send message file back to the sender of the message (as derived from the header
of the message). The text of the message is run through the macro substitution
mechanism (described later on). (Fails if message cannot be sent)

NOP [-ft] No operation. If this seems a bit odd, think of it in terms of a ONCE command.
(Does not alter existing status unless -f or -t is used, in which case it forces
a false --failure-- or true success status)

NOTIFY file address(es)
Send a notification message file to a given address list. The text of the
message is run through the macro substitution mechanism (described later on).
As with FORWARD, file inclusion for address specification is possible. (Fails
if message cannot be sent)

ON (day list) command
Execute the specified filter command only on the specified day list. That list
is a space-separated list of days, specified using the English names. Only the
first three characters are taken into account, case-insensitively. Therefore,
the shortest valid day specifications are Mon, Tue, Wed, Thu, Fri, Sat and Sun.

This command can be used in conjunction with SELECT to do time-based selective
bouncing of messages to, for instance, your home address:

ON (Mon Tue Wed Thu) SELECT (18:30 .. 23:00) BOUNCE [email protected];
ON (Fri) SELECT (18:30 .. 23:59) BOUNCE [email protected];
ON (Sat Sun) BOUNCE [email protected];

That would bounce messages only on week-ends and during the week, after 18:30,
and until 23:00 (assuming that's bed time, other messages will be seen at work
the next day). Note that on Fridays, we go as far as 23:59. (Propagates status
from command. If the command is not executed, always return success)

ONCE (name, tag, period) command
Execute the specified filter command once per period. The name and tag fields
are used to record timestamps of the last ONCE command. More on this later.
(Propagates status from command. If the command is not executed, always return

PASS program
Feed the body of the message to the specified program and get a new body back
from the output of the program. Note that the message is not tagged as having
been saved. (Returns the status of program)

NOTE: If the message had a body that was encoded for transport (using one of the
base64 or quoted-printable transfer encoding), mailagent will transparently
decode it and supply a version that can be properly handled. The body generated
by the program will then be automatically encoded back using the same transfer

Caution though for MIME messages: you should use FEED for them to give a chance
to the program to properly handle the body, but then it needs to be fully MIME-

PERL script [arguments]
Escape to a perl script to perform some actions on the message. This is fully
described further in the manpage, and is very different from a RUN perl script
command. (Returns failure if the script did not compile or returned a non-zero

PIPE [-b] program
Pipe the whole message to the specified program, but do not get anything back.
Any output is mailed to the user who runs the mailagent. The message is not
tagged as having been saved in any case, so you must explicitly DELETE it if
piping was enough and it did not fail: "REJECT -f" is your friend here to avoid
unwanted deletion. (Returns the status of program)

WARNING: Your program must be able to properly parse a MIME message and must
deal with transfer-encoded bodies by itself. To make the program task simpler,
you can supply the -b switch which will let mailagent decode the whole body for
you, suppressing any Content-Transfer-Encoding header (implying "binary"). This
is an invalid message format for sending the message, but it makes processing
easier. You still have to parse the MIME parts yourself though.

POST [-lb] newsgroup(s)
Post the message to the specified newsgroup(s) after having cleaned-up the
header: mail-related fields like Received: or In-Reply-To: are removed, a valid
From: line is generated, the original To: and Cc: are renamed with an X- prefix,
the References: line is updated/generated if necessary based on existing In-
Reply-To, and NNTP-specific fields are stripped so that the server can add its

Running POST successfully acts as a saving.

If the first name is -l as in "POST -l comp.mail.mh", then a "Distribution:
local" header is added to force a local delivery. Otherwise, the default inews
distribution will be used (world, usually).

When the -b switch is given, a successful POST will result in biffing being
activated (see section MAIL BIFFING) for the resulting news article.

If more than one newsgroup is specified, they should be space separated. It is
possible to get a newsgroup list via file inclusion. (Fails if message cannot
be posted)

PROCESS Run the mailagent processing which looks for @SH commands and executes them.
This was described before in the section dealing with default rules. The action
associated by default to a mail having [Cc]ommand as its subject is PROCESS.
(Always returns success)

PROTECT [-lu] mode
Sets the default protection mode that should be set on created folders (or
created files when saving into an MH folder or a directory). By default,
permissions are governed by the UMASK command, but this lets you override the
default. The specified mode should be preceded by a 0 as in 0644 to give the
familiar octal permissions. Otherwise, it is interpreted as a decimal number, so

The -l option may be used to specify a mode locally for one rule. Otherwise,
the protection mode is set globally. The -u option unsets the global (or local
when combined with -l) mode, reverting to the default behaviour where only the
umask is taken into account by the system.

Note that when saving into an MH folder, the PROTECT command takes precedence
over the Msg-Protect field from your ~/.mh_profile file. (Does not alter
execution status)

PURIFY program
Feed the header into a program and get new header back. RESYNC is done
automatically upon return. This may be used to indeed purify the header by
removing all the verbose stuff added by so many mail transport agents (X-400
like lines for instance). Obviously, this does not flag the message as having
been saved. (Returns the status of program)

If your program removes the Content-Transfer-Encoding header in a MIME message,
mailagent will properly transform the message to have a non-encoded body. If
you change the value of the Content-Transfer-Encoding header, mailagent will
also correctly recode the body for you. The only supported encodings are base64
and quoted-printable.

QUEUE Queue mail again. A successful queuing counts as if mail has been saved. Mail
queued that way will not be processed during the next 30 minutes. Note that
unless mailagent is invoked on a regular basis by cron, the mail will remain in
the queue until another mail arrives. (Fails when mail cannot be queued)

RECORD [-acr] [state] [(tag-list)]
Record message in the history and enters state _SEEN_ if the message was already
present there. If the message is recorded for the first time, processing
continues normally. Otherwise a REJECT is performed. This behavior may be
somewhat modified by using some options. See UNIQUE for a complete description
of the options and arguments. Naturally, when a state is specified, that
overrides the default _SEEN_. A state name can contain alphanumeric characters
and underscores.

When a tag-list (comma-separated list of names) is specified, the message is
only recorded and checked against all those tags, but only them. Not specifying
any tag list means any occurrence, whether it is tagged or not. See paragraph
Using Tags in Record and Unique for more information. (Returns a failure status
if mail was already recorded)

REJECT [-tf] [state]
Abort execution of current action, and continue matching. If -t is specified,
the reject will occur only if the previous action was successfully completed
(return status of true), whilst -f would cause the reject only when a failure
occurred. If a state is specified, we enter that state before rejection. REJECT
resets the matching flag, which means that if no further match occurs, the
default action will apply. A state name can contain alphanumeric characters and
underscores. (Does not alter execution status)

REQUIRE file [package]
Behaves like the perl require operator by loading a perl file into memory. By
default, the file is read in the newcmd package, but you may specify whatever
package you wish to load it in. This command will only perform the loading once
per (file, package) tuple. Unlike its perl equivalent, the file "value" is not
important, i.e. it does not have to end with a statement returning a true value.
(Fails if file cannot be loaded)

RESTART [-tf] [state]
Abort execution of current action and restart the matching process from the
beginning. To avoid loops, each rule may be walked through once in a given
state. See REJECT for the meaning of the optional parameters. RESTART resets the
matching flag, which means that the default action will apply, should no further
match occur. (Does not alter execution status)

RESYNC Re-synchronize header used for matching with the header of the mail. This is
probably useful only when a SUBST or ANNOTATE command was run. (Does not alter
execution status)

NOTE: At RESYNC time, mailagent will check whether the Content-Transfer-Encoding
header was changed and will transparently recode the body if required, so that
the whole message remains valid despite header mangling. It will also take care
of updating Content-Length if required. Whenever you do change these important
headers via SUBST or ANNOTATE, be sure to call RESYNC before disposing of the
message or you run the risk of saving a corrupted version that will not be
properly understood by your mail user agent.

RUN program
Run the specified program and mail any output to the user who runs mailagent.
This action does not flag the message as having been saved. (Returns the status
of program)

SAVE folder
Save message in the specified folder. If folder name starts with a '+', it is
handled as an MH-style folder and rcvstore is emulated to deliver the message
into that folder. If folder is a directory, message is delivered in a single
file within that directory. See the FOLDERS section. (Fails if message cannot
be saved)

SELECT (start .. end) command
Execute the command only within the time selection period specified. Dates can
be specified in a wide range of formats. The output of the date(1) command is an
example of a valid specification. If the date, the year or the month is missing,
then the current one is substituted in place of it. The following dates are
valid specifications: '10:04:25', 'now' ,'April 1 1992', 'Dec 25', 'July 14
1789, 07:40' (err... it's valid according to the grammar, but it's before the
Epoch so it does not mean anything). Other fancy dates like 'last month - 5
minutes' or '3 weeks ago' are also enabled. (Isn't that great to have a real
parser? The filtering rules could have been more elaborated if only I had known
about this Berkeley yacc producing a perl parser...). (Returns the status of
command, if run, otherwise returns true).

SERVER [-t] [-d disabled commands]
Activate server processing. The body of the message is interpreted as a list of
commands to execute. See section GENERIC MAIL SERVER for more information about
the server itself. The -t option turns the server into trusted mode, where
powers may be gained. The -d option must be followed by a list of disabled
commands, separated by commas with no intervening spaces between them.

SPLIT [-adeiw] folder
Split a mail in digest format into the specified folder (same naming conventions
as in SAVE). If no folder is specified, each digest item is queued and will be
analyzed as a single mail by itself. The -d option deletes the digest header.
The -i option means split is done in-place and the original mail is discarded.
All the options may be used simultaneously provided they are stuck together at
the beginning (option parsing being really rudimentary).

If the mail is not in digest format and a folder is specified, then it is saved
in that folder. Otherwise, the SPLIT action fails and nothing occurs (the filter
continues its processing though). The SPLIT command will correctly burst RFC-934
digest messages and will try to do its best otherwise. If the digest was not
RFC-934 compliant and there is a chance SPLIT might have produced something
incorrect, then the original message is also saved if -i, otherwise it is not
tagged as saved (so that the default LEAVE command may apply). The -w (watch)
requests special care and will detect every non RFC-934 digest, even when the
non-compliance is otherwise harmless; furthermore, any trailing garbage longer
that 100 bytes will be saved as a digest item by itself.

The -a option annotates every digest item with an X-Digest-To: header line,
which is the concatenation of the To: and Cc: fields of the original digest
message. This may be used for instance to burst the digest into the queue and
then re-process each of its items according to this added field. Finally, the
-e option will discard the digest header only if its body is empty (i.e. the
moderator did not include any leading comment). (Returns success if mail was in
digest format and correctly split without any error)

STORE folder
Save message in the specified folder and leave a copy in the system mailbox.
The folder parameter follows the same naming conventions as in SAVE. Again,
because of locking issues, leaving mail in the mailbox is not recommended on
Debian machines. (Fails if message cannot be saved either in the folder or in
the mailbox)

STRIP header_fields_list
Remove the corresponding lines in the header of the mail. For instance, a "STRIP
Newsgroups Apparently-To" will remove the appropriate lines to wipe out any
Newsgroups: or Apparently-To: header. You may add a ':' after each header field
name if you wish, but that is not strictly necessary. Headers may be specified
via shell-style regular expressions or via "file" inclusion. (Does not alter
execution status)

SUBST var/header expression
Substitutes the expression on the specified user-defined variable (name starting
with a #) or back-reference (digit), or header field (optionally ending with
':'). For instance

SUBST #foo /w/y/g

would substitute in user-defined variable foo all the w by y. See also ASSIGN
and TR.

For substitutions on header fields, like:

SUBST Subject: /\[foo\]\s+//;

matching header lines will be reformatted when the substitution is successful,
which likely means original continuations will not be preserved. The target of
the substitution is the whole header, with continuations normalized to one
space. You are therefore guaranteed to be independent from the actual header
formatting in the original.

Do not forget to issue a RESYNC after a header field SUBST, since some routines
(like POST) probe into the parsed header hash table to generate the saved

(Fails if error in expression)

TR var/header translation
Perform the translation on the specified variable, back-reference or header
field. For instance

TR 1 /A-Z/a-z/

would canonicalize content of reference 1 into lowercase. Successfully
transliterated headers are reformatted, even when their overall size is not
changed. See also ASSIGN and SUBST. (Fails if error in translation)

UMASK [-l] mode
Changes the process's umask to the specified mode, which can be decimal, octal
(if preceded by '0') or hexadecimal (starting with '0x'). The octal notation is
the clearest way to specify the umask anyway. Aren't rumors saying that octal
was invented for that purpose only? ;-) Use the -l option to change the umask
for the duration of the current action rule only. Note that the default umask
specified in your config file is used to reset mailagent's umask at the start of
each mail processing. (Does not alter execution status)

UNIQUE [-acr] [state] [(tag-list)]
Record message in the history and tag message as saved if it was already present
there. If the message is recorded for the first time, processing continues
normally. Otherwise a REJECT is performed. If -r was used, a RESTART is used
instead whilst -a would run an ABORT. For instance, to remove duplicate
messages from mailing lists, run a UNIQUE -a before saving the mail. The -c
option may be used alone to actually prevent the command from disturbing the
execution flow, and to later use the return status to see what happened: UNIQUE
returns a failure status if the message was already recorded. If an optional
state argument is given, then the automaton will enter that state if the mail
was previously in the database. See also RECORD, and the paragraph entitled
Using Tags in Record and Unique for more information about the tag-list. (Fails
if mail was already recorded)

VACATION [-l] on|off|path [period]
Allow or disallow a vacation message. When vacation mode is turned on via the
configuration file, a message is sent whenever the user receives a mail meeting
some requirements, as explained under the section VACATION MODE. One of the
conditions is that the vacation flag modified by this command be true. This
makes it easy to disallow vacation messages, ever, to a group of people for

Instead of on or off, you can specify a file name (~ substitution allowed) being
the new path to be used for locating the vacation file. Optionally, you may
specify a last parameter, which will be taken as the period to apply when
sending the vacation message. Changes to the vacation message path are
forbidden when the configuration variable vacfixed is set to ON.

If you use the -l option, changes are made locally, for the duration of the rule
only. If you REJECT to go to some other rule, your changes will be lost. The
global value of the altered parameters is changed on the first local usage and
restored when a new rule is entered. (Does not alter execution status)

WRITE folder
Write the message in the specified folder, removing any pre-existing folder with
the same name. Hence, successive WRITE commands will overwrite the previous one.
This is useful to store output of system commands ran by cron. Don't try to use
it with an MH folder or a directory folder or it will behave like SAVE. (Fails
if message cannot be written)

Execution Status
Almost all the actions modify a variable which keeps track of the execution status
(analogous to the $? variable in the shell). This variable can be tested via the -t or -f
option of the REJECT command for instance. To give but a single example, the SAVE action
would return failed if it could not save the mail in the specified folder. If that SAVE
command was followed by a "REJECT -f FAILED", then the execution of the current rule would
stop and the automaton would continue to analyze the mail in the FAILED state.

Some of the actions however do not modify this last execution status. Typically, those are
actions which make decisions based on that status, or simply actions which may never fail.

It is unfortunate that ONCE or SELECT commands cannot make the difference between a non-
execution and a successful execution of the specified command. There may be a change in
the way this scheme works, but it should remain backward compatible.

Perl Escape
By using the PERL command, you have the ability to perform filtering and other
sophisticated actions directly in perl. This is really different from what you could do by
feeding your mail to a perl script. First of all, no extra process is created: the script
is loaded directly into mailagent and compiled in a special package called mailhook.
Secondly, you have a perl interface to all the filtering commands: each filtering action
is associated to a perl function (spelled lower-cased). Finally, some pre-defined
variables are set for you by mailagent.

Before we go any further, please note that as there is no extra process created, you must
not call the perl exit function. Use &exit instead, so that the exit may be trapped. &exit
takes one argument, the exit code. If you use 0, this is understood as a success, any
other value meaning failure (i.e. the PERL command will return a failure status). Using
the perl exit function directly would kill mailagent and would probably incur some mail

The scripts used should remain simple. In particular, you should avoid the use of the
package directive or define functions with a package name other than mailhook (i.e. the
package where your script is loaded). Failure to do so may raise some name clashes with
mailagent's own routines. In particular, avoid the main package. Note that since the
compilation environment is set-up to mailhook, not specifying package names in your
variables and subroutine is fine (in fact, it's meant to work that way).

Your script is free to do whatever it wants to the mail. Most of the time however, you end
up using the mailagent primitives to save the mail or forward it (but you are free to
redesign your own and call them instead, of course). The interface is simple: each
function takes but one argument, a string, which is the arguments to the command, if any.
For instance, in a perl escape script, you would express:

{ SAVE list; FORWARD "users"; FEED ~/bin/newmail -tty; REJECT }


&feed('~/bin/newmail -tty');

The rule is simple: each command is replaced by a function call, with the remaining
parameters enclosed in a string, if any. Alternatively, you may specify parameters as a
list: all the arguments you provide are joined into a big happy string, using a space
character as separator. The macro substitution mechanism is then ran on this resulting
argument string.

Each function returns a boolean success status of the command (i.e. 1 means success). For
those functions which usually do not modify the filter's last execution status variable, a
success is always returned. This makes it possible to (intuitively) write:

&exit(0) if &save('uucp');
&bounce('root') || &save('emergency');

and get the expected result. The mail will be saved in the emergency folder only when
saving in uucp folder failed and the mail could not be bounced to root.

It is important to understand that these commands have exactly the same effect on the
filtering process when they are run from a perl escape script or from within the rule file
as regular actions. A &reject call will simply abandon the execution of the current perl
script and the filter automaton will regain control and attempt a new match. But perl
brings you much more power, in particular system calls, control structures like if and
for, raw regular expressions, etc...

The special perl @INC array (which controls the search path for require) is slightly
modified by prepending mailagent's own private library path. This leaves the door open for
future mailagent library perl scripts which may be required by the perl script.
Furthermore, the following special variables are set-up by perl before invoking your

@ARGV The arguments of the script, which were given by the PERL command. This
array is set up the exact same way you would expect it to be set up if you
invoked the command directly from the shell, excepted that @ARGV[0] is the
name of the script (since you cannot use perl's $0 to get at it; that would
give you mailagent's name).
$address The address part of the From: line.
$cc The raw content of the Cc: line.
@cc The list of addresses on the Cc: line, with comments suppressed.
$envelope The mail envelope, as computed using the first From line of the message.
$friendly The comment part of the From: line, if any.
$from The content of the From: line, with address and comment part.
%header This table, indexed by field name, returns the raw content on the
corresponding header line. See below.
$msgpath The full path name of the folder (or message within an MH folder) where the
last saving operation has occurred. This is intended to be used if you wish
to construct your own mail reception notification.
$length The message length, in bytes.
$lines The number of lines in the message.
$login The login name of the address on the From: line.
$precedence The content of the Precedence: line, if any at all.
@relayed The list of host names (possibly raw IP addresses if no DNS mapping) listed
in the (computed) Relayed: header line.
$reply_to The e-mail address where a reply should be sent to, with comment
$sender The sender of the message (may have a comment), derived in the same way the
Sender: line is computed by mailagent.
$subject The subject of the message.
$to The raw content of the To: line.
@to The list of addresses on the To: line, with comments suppressed.

The associative array %header gives you access to all the fields in the header of the
message. For instance, $to is really the value of $header{'To'}. The key is specified
using a normalized case, i.e. the first letter of each word is uppercased, the remaining
being lowercased. This is independent of the actual physical representation in the
message itself.

The pseudo keys Head, Body and All respectively gives you access to the raw header of the
message, the body and the whole message. The %header array is really a reference to the
mailagent's internal data structure, so modifying the values will influence the filtering
process. For instance, the SAVE command writes the Head, the X-Filter: line, the end of
header (a single newline) and then the Body (this is an example only, not a documented
feature :-). The =Body= key is special: it is a Perl reference to a scalar containing the
body with any content transfer encoding removed.

Note that the $msgpath variable holds only a snapshot of the folder path at the time where
the PERL escape was called. If you perform your own savings in perl, then you need to look
at the $main'folder_saved variable instead to get the up-to-date folder path value.

As a final note, resist the temptation of reading the internals of the mailagent and
directly calling the routines you need. If it is not documented in the manual page, it may
be changed without notice by any further patch. (And this does not say that documented
features may not change also... It's just more unlikely, and patches would clearly state
that, of course.)

Program Environment
All the programs started by mailagent via RUN and friends inherit the following
environment variables: HOME, USER and NAME, respectively set from the configuration
parameters home, user and name. If the mailagent is invoked by the filter, then the PATH
is also set according to the configuration file (if you are using the C filter) or to
whatever you set PATH (if you are using the shell filter).

All the programs are executed from within the home directory. This includes scripts
started via the PERL command and mail hooks. The latter will be described in detail
further down.

File inclusion
Some commands like FORWARD or KEEP allow you to specify a file name between double quotes
to actually load parameters from this file. Unless a full path is given, the following
method is used to locate the file: first in the location pointed to by the mailfilter
variable if set, otherwise in maildir and finally in the home directory. Note that this is
not a search path in the sense that if mailfilter is defined and the file is not there, an
error will be reported.

The file should list each parameter (be it an address, a header or a pattern) on a line by
itself. Shell-style comments (#) are allowed within that file and leading white spaces are
trimmed (but not trailing spaces).

Macros Substitutions
All the commands go through a macro substitution mechanism before being executed. The
following macros are available:

%% A real percent sign
%A The internet address extracted out of the From: field (a.b.c in [email protected]),
converted to lower-case.
%C CPU name on which mailagent runs. That is a fully qualified hostname with the
domain name, e.g. lyon.eiffel.com.
%D Day of the week (0-6)
%H Host name (name of the machine on which the mailagent runs), without any domain
name. Always in lower-case, regardless of the machine name.
%I The internet domain name extracted out of the From: field (b.c in [email protected]),
converted to lower-case.
%L Length of the body part, in bytes, with content-transfer-encoding removed.
%N Full name of the sender (login name if none)
%O The organization name extracted out of the From: field (b in [email protected]), converted
to lower-case.
%R Subject of the original message with leading Re: suppressed
%S Re: subject of original message
%T Time of the last modification on mailed file (commands MESSAGE and NOTIFY)
%U Full name of the user
%Y Full year, with four digits (so-called yyyy format)
%_ A white space (useful to put white spaces in single patterns)
%& List of selectors which incurred match (among those specified via a regular
expression such as 'X-*: /foo/i'. If we find the foo substring in the X-Mailer:
header line, then %& will be set to this value). Values in the list are comma
%~ A null character, wiped out from the resulting string.
%digit Value of the corresponding back reference from the last match.
%#var Value of user-defined variable var
%=var Value of the mailagent configuration variable var as specified in the
~/.mailagent file.
%d Day of the month (01-31)
%e The user's e-mail address (yours!).
%f Contents of the "From:" line, something like %N <%r> or %r (%N) depending on how
the mailer is configured.
%h Hour of the day (00-23)
%i Message ID, if available (otherwise, this is a null string)
%l Number of lines in the message, once content-transfer-encoding has been removed
%m Month of the year (01-12)
%n Lower-case login name of sender
%o Organization (where mailagent runs)
%r Return address of message
%s Subject of original message
%t Current hour and minute (in HH:MM format)
%u Login name of the user
%y Year (last two digits)
%[To] Value of the header field (here To:)

User-defined Macros
The mailagent lets you define your own macros in two ways: at the filter level via the
MACRO command, or at the perl level in your own commands or perl actions.

Once defined, a user macro (say foo) can be substituted by using %-(foo). In the case of a
single-letter macro, that can be optimized into %-f for instance, i.e. the parenthesis can
be omitted.

There are six types of macros:

SCALAR A scalar value is given, e.g: red. The macro's value is the literal scalar
value, no further interpretation is performed on the data.

EXPR A perl expression will be evaled to get the value, e.g: $red. Note that the
evaluation will be performed within the usrmac package, so if you are referring
to a variable in another package, it would be wise to specify it, as in

CONST It's really the same as EXPR, but the value is known to be a constant. So the
first time a substitution is made, the expression will be evaluated, and then
its result is cached.

FN A perl function name (without the leading &), such as main'do_this. The
function will be called with a single parameter: the name of the macro itself.
That leaves the door open for further user-defined conventions by forcing
evaluation through one single perl function.

PROG A program to run to get the actual value. Only trailing newline is chopped,
others are preserved. The program is forked each time. In the argument list
given to the program, %n is expanded as the macro name we are trying to
evaluate. If you specify that in the filtering rules, don't forget to escape the
first %.

PROGC Same as PROG really, but the program is forked only once and the value is cached
for later perusal.

At the perl level, four functions let you manipulate and define your macros (all part of
the usrmac package):

new(name, value, type)
Replace or create a %-(name) macro. For instance:

new('foo', "$mailhook'header{'X-Foo'}", 'EXPR');

would create a new macro foo that would expand into the value of an hypothetical
X-Foo header.

Delete all values recorded for the macro.

push(name, value, type)
Stack a new macro, creating it if necessary.

pop(name) Remove last macro definition on the stack.

One macro stack is allocated for each macro, so that some kind of crude dynamic scoping
may be implemented. Creating a macro via push is like taking a local variable in perl,
while creating one by new is simply assigning to a variable. Likely, pop is like exiting a
block with a local variable definition and delete frees all the macro bearing that name,
i.e. it deletes the whole stack.

At the filter level, the MACRO command has three options. By default, the command defines
a new macro by using push, and the other options each let you access one of the other
interface functions. Note that macro definitions persist across APPLY commands.

User-defined Logging
Most of the time when writing a new mailagent filtering command or an perl hook, you will
have a need for specific logging, either to report a problem or to keep track of what you
are performing.

Normally, logs are appended into the agentlog file by calling &main'add_log(string) (see
subsection General Purpose Routines). For plain mailagent actions, this is fine.

But mailagent lets you define alternate logging files, referred to by name. This generic
logging interface is defined in the usrlog package:

new(name, file, flag)
Records a new log file known as name and done in file. If the pathname given for
this file is not absolute, it is rooted under the logdir directory. If flag is
set to true, any logging done to this file will also be copied to the default
system-wide logfile. Nothing is done if a logfile with the same name has already
been defined.

Deletes the logfile known as name. Further logging done to that file is
redirected to the default logfile.

main'usr_log(name, string)
Adds an entry to the logfile name. The default logfile is known as default and
cannot be redefined nor deleted. Note that this function is available from the
main package. Calling it with name set to the string 'default' is mostly
equivalent to calling directly main'add_log with the notable exception that the
-i mailagent option will not be honored in that case. This may or may not be
useful to you.

If you call &main'usr_log with a non-existent logfile name, logging is redirected to the
default system-wide logfile defined in your ~/.mailagent.

Dynamically Loading New Code
In you perl routines (user-defined commands, perl hooks, etc...), you may feel the need to
dynamically load some new code into mailagent. You have direct access to the internal
routine used by mailagent to implement the REQUIRE command or load your new filtering
commands for example.

Using the so-called dynload interface buys you some extra features:

· The mailagent public library path is automatically prepended to the @INC array, which
lets you define your own system-wide or private perl library files (the private
library path is defined by the perlib configuration variable, the public library path
was defined at installation time).

· Like perl's require, mailagent keeps track of which files were loaded into which
packages and will not reload the same file in the same package twice.

· It is possible to make sure that a specific function be defined in the loaded file,
with an error reported if this is not the case.

· You benefit from the default logging done by dynload when some error occurs.

In order to do all this, you call:

&dynload'load(package, file, function)

specifying the package into which you wish to load the file, and optionally the name of a
function that must be defined once the file has been loaded (leave this field to undef if
you do not have such a constraint). The routine returns undef if the file cannot be
loaded (non-existent file, most probably), 0 if the file was loaded but contained a syntax
error or did not define the specified function, and 1 for success.

Using Once Commands
The ONCE constructs lets you specify a given command to be run once every period (day,
week...). The command is identified by a name and a tag, the combination of the two being
unique. Why not just a single identifier? Well, that would be fine, but assume you want to
send a message in reply to someone once every week. You could use the e-mail address of
the person as the command identifier. But what if you also want to send another message to
the same address, this time once a month?

Here is a prototypical usage of a ONCE, which acts like the vacation program, excepted
that it sends a reply only once a day for a given address:

{ ONCE (%r, message, 1d) MESSAGE ~/.message };

This relies on the macro substitution mechanism to send only once a day the message held
in ~/.message. Do not use the tag vacation, unless you know what you are doing: this is
the tag used internally by mailagent in vacation mode. Recall that no selector nor pattern
is understood as "Subject: *", hence the rule is always executed because that pattern
always matches.

The timestamps associated with each commands are kept in files under the Hash directory.
The name is used as a hashing key to compute the name of the file (the two first letters
are used). Inside the file, timestamps are sorted by name, then by tag. Of course, you
could say (inverting tag and name):

{ ONCE (message, %r, 1d) MESSAGE ~/.message };

but that would be likely to be less efficient, as the first hashing would be done on a
fixed word, hence all the timestamps would be located in the file Hash/m/e (where Hash is
the name of your hashing directory, which is the hash parameter in the configuration

Using Tags in Record and Unique
Both the RECORD and UNIQUE commands let you specify a comma-separated tag list between '('
and ')'. For each tag present in the list, there is a separate entry in the database
associated with the message ID. When the message is recorded for at least one of the tags,
the command "fails". Not specifying any tags means looking for any occurrence of that
message ID, whether it is tagged or not.

This is very useful when receiving mail cross-posted to distinct mailing lists and you
want to save one instance of the message in each folder, but still guard against
duplicates. You may say:

To Cc: unix-wizards {
UNIQUE (wizards);
SAVE wizards;
To Cc: majordomo-users {
UNIQUE (majordomo);
SAVE majordomo;

and only one instance of the message will end up in each folder. When you have folders
with conflicting interests, you might use a tag list, instead of a single tag. For
instance, assuming you wish to keep a single copy for messages cross-posted to both dist-
users and agent-users, but have a separate copy if also cross-posted to majordomo-users,
then say:

To Cc: majordomo-users {
UNIQUE (majordomo);
SAVE majordomo;
To Cc: dist-users {
UNIQUE (dist, agent);
SAVE dist-users;
To Cc: agent-users {
UNIQUE (dist, agent);
SAVE dist-users;

If you have some rule using UNIQUE without any tags, it will match when at least one
instance of the message has been recorded, no matter what tag (if any at all) was used in
the first place.

Specifying A Period
The period parameter of the ONCE commands or the vacperiod parameter of your configuration
file has the following format: a number followed by a modifier. The modifier is an atomic
period like a day or a week, the number is the number of atomic periods the final period
should be equal to. The available modifiers are:

m minute
h hour (60 minutes)
d day (24 hours)
w week (7 days)
M month (30 days)
y year (365 days)

All the periods are converted internally in seconds, although you do not really care...
Examples of valid periods range from "1m" to "136y" on a 32 bits machine (why ?).

In order to avoid having a mailagent waiting for a command forever, a maximum execution
time of one hour is allowed by default. Past that amount of time, the child is sent a
SIGTERM signal. If it does not die within the next 30 seconds, a SIGKILL is sent. Output
from the program, if any so far, is mailed back to the user. This default behaviour may
be altered by setting a proper runmax variable in your configuration file to allow more
time for the command to complete.

There is also a filter queue timeout. In order to moderate system load, the C filter
program waits 60 seconds by default (or whatever queuewait was set to in the config file)
before launching mailagent. To avoid conflicts, messages queued by the first filter (which
will then sleep for queuewait seconds) are not processed by mailagent's -q option until
they are at least queuehold seconds old. Another queue-related parameter is queuelost, the
amount of seconds after which mailagent will flag messages as "lost" when listing the

Finally, the locking timeout policy may also be configured. By default, a lock is broken
when it is one hour old (configured by the lockhold variable) and mailagent will only make
lockmax attempts, spaced by lockdelay seconds to acquire the lock. It will then proceed
whether or not it got that lock. If you want a secure locking policy, make sure lockmax
times lockdelay is greater than lockhold, that parameter being "large" enough.

Avoiding Loops
The mailagent leaves an "X-Filter:" header on each filtered message, which in turn is used
to detect loops. If a message already filtered is to be processed, the mailagent enters a
special state _SEEN_. This state is special in the sense it is built-in, it is not matched
by ALL, and some actions are not made available, namely: BACK, BOUNCE, FEED, FORWARD,
GIVE, NOTIFY, PASS, PIPE, POST, PURIFY, QUEUE and RUN. Also note that although the ONCE
and SELECT constructs are enabled, they will not let you execute disallowed commands.
Otherwise, the _SEEN_ state behaves like any other state you can select or negate, so a
<!_SEEN_> guard will not select the rule when we are in state _SEEN_.

The _SEEN_ state makes it easy to deal with mails which loop because of an alias loop you
have no control on. If no action is found in the _SEEN_ state, the mail is left in the
mailbox, as usual. Moreover, if no saving is done, a LEAVE is executed. This is the normal

The "X-Filter:" header is only added when the message is saved. Actions such as PIPE or
GIVE do not flag the message as being saved and therefore they do not add that header
line. You can add one via ANNOTATE if you wish to prevent loops, in case the program to
which you are feeding the message might return it to you in some strange way.

Message Files
The text of the message to be sent back (for MESSAGE or NOTIFY) is read from a file and
passed through the macro substitution mechanism. The special macro %T is set to the date
of last modification made on that file. The format is month/day, and the year is added
before the month only if it differs from the current year.

At the head of the message, you may put header lines. Those lines will overwrite the
default supplied lines. That may be useful to change the default subject or add some
additional fields like the name of your organization. The end of your header is given by
the first blank line encountered. If the top of the message you wish to send looks like a
mail header, you may protect it by adding a blank line at the very top of the file. This
dummy line will be removed from the message and the whole file will be sent as a body

Here is an example of a vacation file. We add a carbon copy as well as the name of our
organization in the header:

Cc: ram
Organization: %o
Precedence: bulk

[Last revision made on %T]

Dear %N:

I've received your mail regarding "%R".
It will be read as soon as I come back from vacation.

%U <%u@%C>


When it's time to take some vacation, it is possible to set up mailagent in vacation mode.
Every vacperiod, the message vacfile will be sent back to the user (with macros
substitutions) if the user is explicitly listed in the To or Cc field and if the sender is
not a special user (root, uucp, news, daemon, postmaster, newsmaster, usenet, Mailer-
Daemon, Mailer-Agent or nobody). Matches are done in a case insensitive manner, so
MAILER-DAEMON will also be recognized as a special user. Furthermore, any message tagged
with a Precedence: field set to bulk, list or junk will not trigger a vacation message.
This built-in behavior can of course be overloaded by suitable rules (by testing and
issuing the vacation message yourself via MESSAGE).

Internally, mailagent uses a ONCE command tagged (%r, vacation, $vacperiod). This implies
you must not use the vacation tag in your own ONCE commands, unless you know what you are

Besides, the vacation message is sent only if no "VACATION off" commands were issued, or
if another "VACATION on" overwrote the previous one. Note that whether a rule matched or
not is irrelevant to the algorithm. By default, of course, the vacation message is allowed
when the vacation configuration parameter is set to on.

If you are not pleased by the fact that a vacation message is sent to people who addressed
you a carbon copy only, then you may write at the top of your rule file:

Cc: ram { VACATION off; REJECT };

Of course, you have to substitute your own login name in place of ram. You cannot use the
same scheme to allow vacation messages to special users like root, because the test for
"specialness" occurs after the vacation mode flag. This is construed as a feature as it
prevents stupid mistakes, like using r* instead of ram in the previous rule.

You may also want to setup a different vacation message, meant only for people in your
organization given the sensitive nature of the information revealed ;-). A simple way of
doing that is:

From: /^\w+$/, /^\w+@\w+$/, /^[\w.-]+@.*\.hp\.com$/i
{ VACATION ~/.hp_vacation 1w; REJECT HP };

Assuming the domain of my organization is .hp.com and that messages not bearing any domain
are local messages, the above rule sets up the file ~/.hp_vacation, sent once a week, for
all HP employees.

The VACATION command will not let you change the message path (but will allow frequency
changes anyway) when the vacfixed configuration variable is set to ON. This is meant to be
used in emergency situations, when only one vacation message will fit. For instance, when
you are on a sick leave, a simple trigger message to your mailagent from home could change
your ~/.mailagent configuration to force the ~/.i_am_sick message, regardless of what the
various rules have to say. Actually, this is precisely why this feature was added,
amazing... :-)


The following variables are paid attention to: they may come from the environment or be
set in the rule file:

indicates where loaded patterns are to be looked for, if the name of the file is
not fully qualified. If it is not set, maildir will be used instead. If maildir
is not set either, the home directory is used.

maildir is the location of your mail folders. Any relative path is understood as
starting from maildir. If it is not set, ~/Mail is used.

Those variables remain active while in the scope of the rule file. Should an alternate
rule file be used (via rules hook or the APPLY command), the current values are propagated
to the new rule set unless overridden in the alternate rule file. In any case, the
previous value is restored when control is transferred back to the previous set of rules.
That is, those variables are dynamically instead of statically scoped.


Anywhere in the mail, there can be an @RR left-justified line which will send back an
acknowledgment to the sender of the mail. The @RR may optionally be followed by an
address, in which case the acknowledgment will be sent to that address instead. In fact
(but let's keep that a secret), this is a way for me to be able to see who runs my
mailagent program and who doesn't...

The sendmail program usually implements such a feature via a Return-Receipt-To: header
line, which sends the whole header back upon successful delivery. However, this is not
implemented on all mail transport agents, and @RR is a good alternative :-).


Throughout this manual page, I have always written header fields with the first letter of
each word uppercased, as in Return-Receipt-To. But RFC-822 does not impose this spelling
convention, and a mailer could legally rewrite the previous field as return-receipt-to
(and in fact so does sendmail in its own private mail queue files).

However, you must always specify the headers in what could be called a normalized case
(for headers anyway). The mailagent will correctly recognize cc:, CC: or Cc: in a mail
message and will allow you to select those fields via the normalized Cc: selector. In
fact, it operates the normalization for you, and a cc: selector would not be recognized as
such. Of course, no physical alteration is ever made on the header itself.

This is also true for headers specified in the STRIP or KEEP command. If you write STRIP
Cc, it will correctly remove any cc: line. Likewise, if you use regular expressions to
specify a selector, Re.*: would match both original received: and Return-path: fields,
internally known through their normalized representation.


The mail hooks allow mailagent to transparently invoke some scripts or perform further
processing on the message. Those hooks are activated via the SAVE, STORE or LEAVE
commands. Namely, saving in a folder whose executable bit is set will raise a special
processing. By default, the folder is taken as a program where the mail should be piped
to. If the "folder" program returns a zero status, then the message is considered saved by
the mailagent. Otherwise, all the processing attached to failed save commands is started
(including emergency saving attempts). Executable folders provide a transparent way (from
the rule file point of view) to deal with special kind of messages.

In fact, five different types of hooks are available. The first one is the plain
executable folder we have just spoken about. But in fact, here is what really happens when
a saving command detects an executable folder: the mailagent scans the first line of the
folder (in fact, the first 128 bytes) and looks for something starting with #: and
followed by a single word, describing a special kind of hook. This is similar in the way
the kernel deals with the #! hook in executable programs. If no #: is found or #: is
followed by some garbage, then mailagent decides it is a simple program and feeds the mail
message to this program. End of the story.

But if the #: token is followed (spaces allowed, case is irrelevant) by one of the
following words, then special actions are taken:

rules The file holds a set of mailagent rules which are to be applied. A new mailagent
process is created to actually deal with those and the exit status is propagated
back to the original mailagent.

audit This is similar in spirit to what Martin Streicher's audit.pl package does,
hence the name of this hook. The special variables which are set up by the PERL
filter commands are initialized and the script is loaded in the special mailhook
package name space, which also gives you an interface to the mailagent's own
routines. You may safely use the exit function here, since an extra fork is
done. This is the only difference between an audit and a perl hook.

deliver Same thing as for the audit hook, but the standard output of your script is
monitored by mailagent and understood as mailagent filtering commands. Upon
successful return, a mailagent process will be invoked to actually execute those
commands on the message. Again, this is similar in spirit to Chip Salzenberg's
deliver package and gave the name of this hook.

perl This hook is the same as audit but it is executed without forking a new
mailagent, and you have the perl interface to mailagent's filtering commands.
There is no difference with the PERL command, because it is implemented that
way, by calling a mailagent and forcing the PERL command to be executed. This is
similar in spirit to Larry Wall's famous perl language and it is responsible for
the name of this hook :-).

As mentioned earlier in this manual page, the hook is invoked from with the home directory
specified in your ~/.mailagent (which may differ from your real home directory, as far as
mailagent or mailhook are concerned).

For those hooks which are finally ran by perl, the special @INC array has mailagent's own
private library path prepended to it, so that require first looks in this place.


A folder is a file or a directory which can be the target of a delivery by the mailagent,
that is to say the argument of SAVE-like commands.

Folder Format
By default, mails are written into folders according to the standard UNIX-style mailbox
format: each mail starts with a leading From line bearing the sender's address and the
date. However, by setting the mmdf parameter from the ~/.mailagent to ON, the mailagent
will be able to save messages in MMDF format: each message is sandwiched between two lines
of four Ctrl-A characters (ASCII code 1) and the leading From line is removed.

When MMDF mode is activated, each folder will be scanned to see if it is a UNIX-style or
MMDF-style mailbox and the message will be saved accordingly. When saving to a new
folder, the default is to create a UNIX-style mailbox, unless the mmdfbox configuration
variable was set to ON, in which case the MMDF format prevails.

Note that the MMDF format is also the standard for MH packed folders, so by enabling the
MMDF mode, you can actually deliver directly to those packed folders. The MH command inc
is able to incorporate mail from either form anyway, i.e. it does not matter whether the
folder is in UNIX format (also called UUCP-style) or in MMDF format.

MH-style folders are also supported. It is mainly a directory in which messages are stored
in individual files. To save directly into an MH folder, simply prefix the folder name
with '+', just as you would do with MH commands. The unseen sequences specified in your
MH profile (the mhprofile parameter in your ~/.mailagent, default is ~/.mh_profile) will
be correctly updated, as rcvstore would.

When the target folder is a directory, mailagent attempts the delivery in an individual
numbered file. If a prefix file is present (config parameter msgprefix, default is
.msg_prefix), its first line is used to specify the base name of the message, then a
number is appended to give the name of the message file to use. That is, if there is no
such file, the folder will look like an MH one, without any MH sequence file though.

Folder Compression
If you have one or more of the widely available file compression utilities such as
compress or gzip in your PATH (as set up by ~/.mailagent), then you may wish to use folder
compression to save some disk space, especially when you are away for some time and do not
want to see your mail fill-up the filesystem.

To achieve folder compression, you have to set up a file, referred to by the compress
configuration variable. This file must list folder names, one per line, with blank lines
ignored and shell-style (#) comments allowed. You may use shell-style patterns to specify
the folders, and the match will be attempted on the full pathname of the folder (~
substitution occurs). If you do not specify a pattern starting with a leading '/'
character, then the match will be attempted on the basename of the folder (i.e. the last
component of the folder path). If you want to compress all your folders, then simply put a
single '*' inside this file.

Mailagent uses the filename extension to determine what compression scheme is used for a
particular folder. The file referred to by the compspecs configuration variable (default
is $spool/compressors) is used to define the commands that mailagent will use to perform
the compress, uncompress, and cat operations for a particular extension.

The compressors file holds lines of the following form:

tag extension compression_prog uncompress_prog cat_prog


tag is the logical name for the compression scheme. This is typically the same as
the name of the program used to provide the compression, but could be different
for some unforeseen reason. This must be unique across all records in the file.

extension is the extension to recognize as belonging to the specified tag. This must be
unique across all records in the file.

is the name of the command to run to compress a folder. The program must
replace the uncompressed file with the compressed one with the extension
appended to the filename (like compress or gzip).

is the name of the command to run to uncompress a folder. The program must
replace the compressed file with the uncompressed one without the extension
(like uncompress or gunzip).

cat_prog is the name of the command to output the uncompressed contents of a compressed
folder to stdout (like zcat or gzcat).

The fields are separated by TABS to allow for the use of space characters in the command

If the file referred to by the compspecs configuration variable cannot be accessed for
whatever reason, a default entry is hard-wired into mailagent (knows about both compress
and gzip programs):

compress <TAB> .Z <TAB> compress <TAB> uncompress <TAB> zcat
gzip <TAB> .gz <TAB> gzip <TAB> gunzip <TAB> gunzip -c

If you wish to add more compressors, you can copy the default compressors file from
mailagent's private library directory and setup a correct entry for your alternate
compressor. Keep in mind that the trailing extension needs to be unique amongst all the
listed programs, since that extension is used to determine the type of compression
performed on the folder.

If the folder is created without any existing compressed form around, a default compressor
is selected for you, as defined by the comptag configuration variable. That refers to the
tag name of the compspecs file, i.e. the first word on the line (usually the name of the
compression program, but not necessarily).

When attempting delivery, mailagent will check the folder name against the list of
patterns in the compress file. If there is a match, the folder is flagged as compressed.
Then mailagent attempts decompression if there is already a compressed form (ie. the file
has a recognized filename extension) and if no uncompressed form is present. Delivery is
then made to the uncompressed folder. However, re-compression is not done immediately,
since it is still possible to get messages to that folder in a single batch delivery.
Should disk space become so tight that decompression of other folders is impossible,
mailagent will re-compress the folders it has already uncompressed. Otherwise, it waits
until the last moment.

If for some reason there is a compressed folder which cannot be decompressed, mailagent
will deliver the mail to the plain folder. Further delivery to that folder will be faced
with both a compressed and a plain version of the folder, and that will get you a warning
in the log file, but delivery will be made automatically to the plain file.

On newly created folders the comptag configuration variable is referenced to determine the
compression type to use for the folder.


If you are receiving and processing mail on your own machine, then you have access to
local mail biffing where mailagent can warn you about new messages and tell you about
where they have been saved, printing a small subset of the header and the first few lines
of the body.

To use biffing, all you need is the setting of the few biff parameters in your
~/.mailagent and make sure biff is set to ON. Actually, this is the only parameter you
need to set to get minimal default biffing behaviour. Don't forget to run the shell
command "biff y" on the terminals where you want to get notification (you may do that on
several ttys, one for each virtual display for instance).

Upon mail reception and saving on a folder or posting to a newsgroup, mailagent locates
all the ttys where you are logged on, then selects those where biffing was requested,
finally emitting a message and making a beeping sound (if your terminal supports this and
you are using the standard format--see below).

Customizing Biffing Output
Should the default format not suit your needs, you may customize the biffing message
freely, setting the biffmsg parameter to point to the file where the format is stored.
Standard macros substitutions will be performed on your message, the following macro set
superseding and completing the standard set:

%-A Same as writing %-H, new line, %-B
%-B The body part of the biffing message, with content-transfer-encoding removed.
If the message is a MIME multipart one, the text/plain part is shown. If only a
text/html part is available, the HTML markup is stripped for biffing.
%-H The header part of the biffing message. If shows only From:, To: Subject: and
Date: headers, or whatever you have set the biffhead configuration variable to.
All headers are showed as one line of text, regardless of their actual length.
There will be three trailing dots at the end to signal that truncation occurred.
For a news article (biffing after a POST -b), the To: and Cc: fields are never
shown, even if specified in biffhead.
%-T Same as %-B, but trimming is activated. The purpose of trimming is to remove any
leading quotation in the message, to get only the most meaningful part. This
assumes the quoting character is a single non-alphanumeric character. The
leading attribution line that may introduce the quotation can be also removed,
and a minimum length for the quotation can be set in the configuration file.
%B The relative path under %d of the message folder, full path (%p) if not saved
under that directory. The newsgroup name for news articles.
%D The directory where the message is stored. If an MH folder, this is the folder
full path. The home directory is replaced by a ~. Empty for news articles.
%F The base name (last path component) of the message. For an MH message, this is
the message number. Empty for news articles.
%P The folder path. It has the correct semantics for MH and directory folders, i.e.
it points to the folder directory itself. Otherwise, the same as %p.
%a Alarm characters (^G). May expand to more than one under the control of the
BEEP filtering command. Use %b if you only want a single bell.
%b A beeping character (^G). As opposed to %a, this only expands to give one bell.
%d Full path where folders such as the one being saved into are stored if not
qualified (i.e. your MH path for MH folders, of something like ~/Mail for other
folders). Empty for news articles.
%f Folder where mail was saved, home replaced by ~ for short. The newsgroup when
article was posted for news.
%m A '+' sign if the folder is an MH one, empty otherwise.
%p The full path name (same as %f) of the message, but without any ~ shortcut. The
newsgroup name for news articles.
%t The type of message: usually "mail", but set to "article" for biffing after a
POST command.

You can get the standard macro expansion by using %:f for instance, since the %f macro is
superseded. The %: form lets you obtain the standard macro definition anyway, no matter
what, so you don't have to remember whether a given macro is superseded in this context or
not. Besides, it is safer since new macros may be added here without notice. Note that
macros related to the message content all start with %- and therefore are not conflicting
with standard one.

Here is the format you need to use to get the same behaviour as the default hardwired

New %t for %u has arrived in %f:

Note that the string ...more... appears at the end of the body when it has not been
completely printed out on the screen and the remaining lines are not blank or similar.

Trimming Leading Quotation
It is a standard practice, when replying to a message, to include an excerpt of the
sentences being replied-to, using a non-alphanumeric character such as '>' to prefix
quoted lines. Something like:

Quoting John Doe:
> This is quoted material.
> Another line from John's mail.

This is part of the reply to John.

The leading "Quoting ..." line, called the attribution line, is optional and may be
missing or take another free form.

However, when biffing, this may be seen as useless noise, especially nowadays where people
freely quote more and more in their replies. Since the biff message only shows the top
lines of the message, it may be desirable to automatically trim those quoted lines.

Via the %-T macro in the customized biff format, you may request trimming of the leading
quotation material, keeping the attribution line or not, and even replace trimmed material
with a notification that so many lines have been removed.

All this customization is done from the ~/.mailagent configuration file, using the
bifftrim, bifftrlen and biffquote variables.

You first need to turn trimming on by using a customized biff format using the %-T macro.
By setting bifftrlen to 3, you may request that only quotations of at least 3 lines be
trimmed. Turning bifftrim off will remove the trimming notification, whilst turning
biffquote off will also strip the attribution line, when present.

For instance, assuming the following settings:

bifftrim : ON
bifftrlen: 2
biffquote: OFF

then the above example would produce the following biffing output (header of the message
not withstanding):

[trimmed 3 lines starting with a leading '>' character & attribution line]
This is part of the reply to John.

because the blank line following the quoted material is counted as being part of the
quotation. The "[trimmed ..]" message can be turned off by setting bifftrim to OFF.

The trimming algorithm considers the first line of the body to see if it starts with a
non-alphanumeric character. If it does, then all the following lines starting with that
same character, or any blank line is removed, up to the first non-blank line starting with
another character. Optionally, the first line (and that line only) is skipped if the
second one starts with a non-alphanumeric character, and the first line is taken as being
the attribution line.

Using Compact MH-style Biffing
The so-called MH-style biffing is a way of presenting a compacted body where all the lines
are joined together into a big happy string with successive spaces turned into a single
space character. To enable it, you need to set the biffmh variable to ON.

Since this compacting is output verbatim on the tty, line breaks will occur randomly and
this may make reading difficult. You may request an automatic reformatting of the
compacted body by turning biffnice to ON and the biff output will fit nicely within the

Unfortunately, it is not possible to customize the amount of columns that should be used
for formatting: since you may biff to any tty you are logged on, that would force
mailagent to probe the tty for its column size, for each possible tty where output may go,
and there is no reliable portable way of doing that. Sorry.


Once you've reached the expert level, and provided you have a fair knowledge of perl, you
may feel the need for more advanced commands which are not part of the standard set. This
section explains how you can achieve this dynamically, without the need of diving deep
inside the source code.

Once you have extended the filtering command set, you may use those commands inside the
rule file as if they were built-in. You may even choose to redefine the standard commands
if they do not suit you (however, if you wish to do that, you should know exactly what you
are doing, or you may start losing some mail or get an unexpected behavior -- this also
voids your warranty :-).

The ability to provide external commands without actually modifying the main source code
is, I believe, a strong point in favor of having a program written in an interpreted
language like perl. This of course once you have convinced yourself that it is a Good
Thing to customize and extend a program in the same language as the one used for the core,
meaning usually a fairly low-level language with fewer user-friendly hooks.

In order to implement a new command, say FOLD, you will need to do the following:

· Write a perl subroutine to implement the FOLD action and put that into an external
file. Say we write the subroutine fold and we store that in a fold.pl file. This is
naturally the difficult part, where you need to know some basic things about
mailagent internals.

· Choose where you want to store your fold.pl file. Then check the syntax with perl -c,
just to be sure...

· Edit the newcmd file (as given by the configuration file) to record your new command.
Then make sure this file is tightly protected. You must own it, and it should not be
writable by any other individual but you.

· Additionally, you may want to specify whether FOLD is to modify the existing
execution status and whether or not it will be allowed within the special _SEEN_

· Write some rules using the new FOLD command. This is the easy part! Note that your
command may also be used within perl hooks as if it were a builtin command (this
means there is an interface function built for you within the mailhook package).

In the following sections, we're going to describe the syntax of the newcmd file, and
we'll then present some low-level internal variables which may be used when implementing
new commands.

New Command File Format
The newcmd file consists of a series of lines, each line describing one command. Blank
lines are ignored and shell-style comments introduced by the sharp (#) character are

Each line is formed by 3 principal fields and 2 optional ones; fields are separated by
spaces or tabs. Here is a skeleton:

<cmd_name> <path> <function> <status_flag> <seen_flag>

The cmd_name is the name of the command you wish to add. In our previous example, it would
be FOLD. The next field, path, tells mailagent where the file containing the command
implementation is located. Say we store it in ~/mail/cmds/fold.pl. The function field is
the name of the perl function implementing FOLD, which may be found in fold.pl. Here, we
named our function fold. Note that if your function has its name within the newcmd
package, which is the default behavior if you do not specify any, then there is no need to
prefix the function name with the package. Otherwise, you must use a fully qualified name.

The last two fields are optional, and are boolean values which may be specified by true or
yes to express truth, and false or no to express falsehood. If status_flag is set to true,
then the command will modify the last execution status variable. If seen_flag is true,
then the command may be used when the filter is in _SEEN_ state. The default values are
respectively true and false.

So in our example, we would have written:

FOLD ~/mail/cmds/fold.pl fold no yes

to allow FOLD even in _SEEN_ state and have it executed without modifying the current
value of the last-command-status variable.

Writing An Implementation
Your perl function will be loaded when needed into the special package newcmd, so that its
own name-space is protected and does not accidentally conflict with other mailagent
routines or variables. When you need to call the perl interface of some common mailagent
functions, you will have to remember to use the fully qualified routine name, for instance
&mailhook'leave to actually execute the LEAVE command.

(Normally, in PERL hooks, there is no need for this prefixing since the perl script is
loaded in the mailhook package. When you are extending your mailagent, you should be extra
careful however, and it does not really hurt to use this prefixing. You are free to use
the perl package directive within your function, hence switching to the mailhook package
in the body of the routine but leaving its name in the newcmd package.)

Since mailagent will dynamically load the implementation of your command the first time it
is run, by loading the specified perl script into memory and evaluating it, I suggest you
put each command implementation in a separate file, to avoid storing potentially unneeded
code in memory.

Each command is called with one argument, namely the full command string as read from the
filter rules. Additionally, the special @ARGV array is set by performing a shell-style
parsing of the command line (which will fail if quotes are mismatched, but then you can do
the parsing by yourself since you get the command line). At the end of your routine, you
must return a failure status, i.e. 0 for success and 1 to signal failure.

Those are your only requirements. You are free to do whatever you want inside the routine.
To ease your task however, some variables are pre-computed for you, the same ones that are
made available within mail hooks, only they are defined within the newcmd package this
time. There are also a few special variables which you need to know about, and a set of
standard routines you may want to call. Please avoid calling something which is not
documented here, since it may change without prior notice. If you would like to use one
routine and it is not documented in this manual page, please let me know.

Each command is called from within an eval construct, so you may safely use die or call
external library routines that use die. If you use require, be aware that mailagent is
setting up a special @INC array by putting its private library path first, so you may
place all your mailagent-related library files in this place.

Special Variables
The following special variables (some of them marked read-only, meaning you shouldn't
modify them, and indeed you can't) made available directly within the newcmd package, are
pre-set by the filter automaton, and are used to control the filtering process:

$mfile The base name of the mail file being processed. This variable is read-only.
It is mainly used in log messages, as in [$mfile] to tag each log, since a
single mailagent process may deal with multiple messages.

$ever_saved This is a boolean, which should be set to 1 once a successful saving
operation has been completed. If at the end of the filtering, this variable
is still 0, then the default LEAVE will be executed.

$folder_saved The value of that variable governs the $msgpath convenience variable set
for PERL escapes. It is updated whenever a message is written to a file, to
hold the path of the written file.

$cont This is the continuation status, a variable of the utmost importance when
dealing with the control flow. Four constants from the main package can be
used to specify whether we should continue with the current rule
($FT_CONT), abandon current rule ($FT_REJECT), restart filtering from the
beginning ($FT_RESTART) or simply abort processing ($FT_ABORT). More on
this later.

$lastcmd The last failure status recorded by the last command (among those which do
modify the execution status). You should not have to update this by
yourself unless you are implementing some encapsulation for other commands,
like BACK or ONCE, since by default $lastcmd will be set to the value you
return at the end of the command.

$wmode This records the current state of the filter automaton (working mode), in a
literal string form, typically modified by the BEGIN command or as a side
effect, as in REJECT for instance.

All the special variables set-up for PERL escapes are also installed within the newcmd
package. Those are $login, %header, etc... You may peruse them at will.

Other variables you might have a need for are configuration parameters, held in the
~/.mailagent configuration file. Well, the rule is simple. The value of each parameter
param from the configuration file is held in variable $cf'param. Variable $main'loglvl is
the copy of $cf'level, since it's always shorter to type in $'loglvl after each call to
the logging routine &add_log.

There is one more variable worth knowing about: $main'FILTER, which is the suitable X-
Filter line that should be appended in all the mail you send via mailagent, in order to
avoid loops. Also when you save mails to a folder, it's wise adding this line in case a
problem arises: you may then identify the culprit.

Rule Environment
An action might have a legitimate desire of altering the environment for the scope of one
rule only, reverting to the previous value when exiting the rule. Or you might want to
change the value forever.

When we speak about altering the environment, we refer to the one set up via the
configuration file, whose values end-up in the cf package. Well, some of those variables
are copied in the env package before filtering of a message starts (under the control of
the @env'Env array).

All rules should then refer to the version in the env package, and not in the cf package,
to see alterations. Global changes are made by affecting directly to the variable in the
env package, while local changes are requested by calling the &env'local routine.

For instance, the cf'umask value is copied as env'umask because umask is held in @env'Env.
Global changes are made by setting that copy directly, while local changes may be made

&env'local('umask', 0722);

to set-up a new local value. The first time &env'local is called on a variable, its value
is saved somewhere, and will be restored upon exiting the scope of the rule. Then the new
value is affected to the variable.

Variables requiring a side effect when their value is changed (such as the umask variable,
which requires a system call to let the kernel see the change) may specify it by accessing
the %env'Spec array, the key being the name of the variable requiring a side effect, the
value being interpreted as a bit of perl code ran once the original value is restored. For
instance, we say somewhere (in &env'init):

package env;
$Spec{'umask'} = 'umask($umask)';

to update the kernel view when leaving scope. Note that the side effect is evaluated once
the variable has recovered its original value, and within the env package.

Internally, the &analyze_mail routine calls &env'setup before starting its processing to
initialize the env package, and &env'cleanup at the end before returning. Before running
the actions specified on a rule match, &apply_rules calls &env'restore to ensure a
coherent view of the environment while running the actions for that particular rule.

Altering Control Flow
When you want to alter control flow to perform a REJECT, a RESTART or an ABORT, you have
three choices. If you wish to control that action via an option, the same way the standard
UNIQUE does (with -c, -r or -a), you may call &main'alter_execution(option, state) giving
it two parameters: the option letter and the state you wish to change to before altering
the control flow.

You may also want to directly alter the $wmode and $cont variables, but then you'll have
to do your own logging if you want some. Or you may call low-level routines
&main'do_reject, &main'do_restart and &main'do_abort to perform the corresponding
operation (with logging).

Remember that the _SEEN_ state is special and directly handled at the filter level, and
the filter begins in the INITIAL state. The default action is to continue with the current
rule, which is why there is no routine to perform this task.

The preferred way is to invoke the mailhook interface functions, &mailhook'begin,
&mailhook'reject, etc..., and that will work even if you redefine those functions
yourself. Besides, that's the only interface which is likely not to be changed by new

General Purpose Routines
The following is a list of all the general routines you may wish to call when performing
some low-level tasks. Note that this information is version-dependent. Since I document
them, I'll try to keep them in new versions, but I cannot guarantee I will not have to
slightly change some of their semantics. There is a good chance you will never have to
worry about that anyway.

Return a formatted RFC822 field to fit in 78 columns, with proper continuations
introduced by eight spaces.

Normalize case in RFC822 header and return the new header name with every first
letter uppercased.

This is part of an RFC822 header validation, mainly used when splitting a
digest. This resets the recognition automaton (see &header'valid).

Returns a boolean status, indicating if all the lines given so far to this
function since the last &header'reset are part of a valid RFC822 header. The
function understands the first From line which is part of UNIX mails. At any
time, the variable $header'maybe may be checked to see if so far we have found
at least one essential mail header field.

Perform a .lock locking on the file, returning 0 on success and -1 on failure.
If an old lock was present, it is removed (time limit set to one hour). Use
&main'free_file to release the lock.

Add the string to the logfile. The usual idiom is to postfix that call with the
if $'loglvl > value, where value is the logging level you wish to have before
emitting that kind of log ($'loglvl is a short form for $main'loglvl).

Remove a .lock on a file, obtained by &main'acs_rqst. It returns 0 if the lock
was successfully removed, -1 if it was a stale lock (obtained by someone else).

Scan the head of a file and try to determine whether there is a mail header at
the beginning or not. Return true if a header was found.

Record the message ID of the current message and return 0 if the message had not
been previously seen, 1 if it is a duplicate.

Return the value of the hostname, lowercased, with possible domain name appended
to it. The hostname is cached, since its value must initially be obtained by
forking. (see also &main'myhostname)

Parse an e-mail internet address and return a three-element array containing the
host, the domain and the country part of the internet host. For instance, if the
address is [email protected], it will return (c, b, a).

Parse the e-mail internet address and return the login name.

Perform in-place macro substitution (line passed as a type glob) using the
information currently held in the %main'Header array. Do not pass *_ as a
parameter, since internally macros_subst uses a local variable bearing that name
to perform the substitutions and you would end up with an unmodified version. If
you really want to pass *_, then you must use the returned value from
macros_subst which is the substituted text, but that's less efficient than
having it modified in place.

&main'makedir(pathname, mode)
Make directory, creating all the intermediate directories needed to make
pathname a valid directory. Has no effect if the directory already exists. The
mode parameter is optional, 0700 is used (octal number) if not specified.

Returns the hostname of the current machine, without any domain name. The
hostname is cached, since its value must initially be obtained by forking.

Execute the single filter command specified and return the continuation status,
which should normally be affected to the $cont variable. You will need this
routine when trying to implement commands which encapsulate other commands, like

Return the number of seconds in the period specified. See section Specifying A
Period to get valid period strings.

&main'shell_command(program, input, feedback)
Run a shell command and return a failure status (0 for OK). The input parameter
may be one of the following constants (defined in the main package): $NO_INPUT
to close standard input, $BODY_INPUT to pipe the body of the current message,
$MAIL_INPUT to pipe the whole mail as-is, $MAIL_INPUT_BINARY to pipe the whole
mail after having removed any content transfer-encoding and $HEADER_INPUT to
pipe the message header. The feedback parameter may be one of $FEEDBACK or
$NO_FEEDBACK depending whether or not you wish to use the standard output to
alter the corresponding part of the message. If no feedback is wanted, the
output of the command is mailed back to the user. The $FEEDBACK_ENCODING is
handled like $FEEDBACK but will tell mailagent to look at the best suitable body
encoding when the input is the whole message.

Parse an RFC822 e-mail address and return a two-elements array containing the
internet address and the comment part of that address.

Execute a series of actions separated by the ';' character, calling run_command
to actually perform the job. Return the continuation status. Note that
$FT_ABORT will never be returned, since mailagent usually stops after having
executed one set of actions, only continuing if it saw an RESTART or a REJECT.
What ABORT does is skipping the remaining commands on the line and exiting as if
all the commands had been run. You could say xeqte is the equivalent of the eval
function in perl, since it interprets a little filter script and returns control
to the caller once finished, and ABORT is perl's die.

You may also use the three functions from the extern package which manipulate persistent
variables (already documented in the section dealing with variables) as well as the user-
defined macro routines.

Writing your own commands is not easy, since it requires some basic knowledge regarding
mailagent internals. However, once you are familiar with that, it should be relatively

Here is a small example. We want to write a command to bounce back a mail message to the
original sender, the way sendmail does, with some leading text to explain what happened.
The command would have the following syntax:


and we would like that command to modify the existing status, returning a failure if the
mail cannot be bounced back. Since this command actually sends something back, we do not
want it to be executed in the _SEEN_ state. Here is my implementation (untested):

sub sendback {
local($cmd_line) = @_;
local($reason) = join(' ', @ARGV[1..$#ARGV]);
unless (open(MAILER, "|/usr/lib/sendmail -odq -t")) {
&'add_log("ERROR cannot run sendmail to send message")
if $'loglvl;
return 1;
print MAILER <<EOF;
From: mailagent
To: $header{'Sender'}
Subject: Returned mail: Mailagent failure

--- Transcript Of Session


--- Unsent Message Follows

close MAILER;
$ever_saved = 1; # Don't want it in mailbox
$? == 0 ? 0 : 1; # Failure status

Assuming this command is put into ~/mail/cmds/sendback.pl, the line describing it in the
newcmd file would be:

SENDBACK ~/mail/cmds/sendback.pl sendback yes no

Now this command may be used freely in any rule, and will be logged as a user-defined
command by the command dispatcher. Who said it was not easy to do? :-)

Note the use of the $ever_saved variable to mark the mail as saved once it has been
bounced. Indeed, should the SENDBACK action be the only one action to be run, we do not
want mailagent to LEAVE the mail in the mailbox because it has never been saved (this
default behavior being a precaution only -- better safe than sorry).

If along the way you imagine some useful commands which could be made part of the standard
command set, please e-mail them to me and I'll consider integrating them. In the future, I
would also like to provide a standard library of perl scripts to implement some weird
commands which could be needed in special cases.

Note that you may also use the information presented here inside the perl escape scripts.
Via the require operator, it is easy to get the new command implementation into your
script and perform the same task. You will maybe need to set up @ARGV by yourself if you
rely on that feature in your command implementation.

Command extension can also be viewed as a way to reuse some other perl code, the mailagent
providing a fixed and reliable frame and the external program providing the service. One
immediate extension would be mailing list handling, using this mechanism to interface with
some mailing list management software written in perl.


One nice thing about mailagent is that it provides you with the basic tools to implement a
generic mail server. Indeed, via the SERVER command, you can process a mail message,
extract and then execute some predefined commands. For instance, you may implement an
archive server, or a mailing list manager, etc...

The major limitation currently is that only plain commands are accepted, or commands
taking some additional info as standard input or equivalent. There is no notion of modes,
with separate command sets for each mode or limited name-space visibility, at least for
now, so it is not easy (albeit possible) to implement an ftpmail server, for instance,
since this implies the notion of mode.

In order to implement a mail server command (say send file, which would send an arbitrary
file from the file system in a separate mail message), you need to do the following:

· Think about the command from a security point of view. Here, the command we want to
implement is a potentially dangerous one since it can give access to any file on the
machine the individual running mailagent has access to. So we want to restrict that
command to a limited number of trusted people, who will be granted the power to run
this command. More on this later.

· Choose whether you want to implement the command in perl or in another programming
language. If you do the latter, your command will be known as a shell command (i.e. a
command runnable directly from a shell), while in the former case, you have the
choice of making it appear as a shell command, or have it hooked to the mailagent in
which case it is known as a perl command. In that last case, your command will be
dynamically loaded into mailagent with all the advantages that brings you. Here, we
are going to write our command as a shell script.

· Write the command itself. That's the most difficult part in this scheme. Later on,
we will see a straightforward implementation of the send command.

· Edit the comserver file (defined in your ~/.mailagent) to record your new command.
Then make sure this file is tightly protected. You must own it, and be the only one
allowed to modify it.

· Additionally, you may want to hide some of the arguments in the session transcript
(more on this later), allow the command to take a flow of data as its standard input,
assign a path to the command, etc... All those parameters take place in your
comserver file.

· Start using the command... which of course is the nicest part in this scheme!

In the following sections, we'll learn about the syntax of the comserver file, what powers
are, how the session transcript is built, what the command environment is, etc...

Builtin Commands Overview
The mail server has a limited set of builtin commands, dealing with user authentication
and command environment settings. User authentication is password based and is not
extremely strong since passwords are specified in clear within the mail message itself,
which could be easily intercepted.

The server maintains the notion of powers. One user may have more than one power at a
time, each power granting only a limited access to some sensitive area. A few powers are
hardwired in the server, but the user may create new ones when necessary. Those powers are
software-enforced, meaning the command must check for itself whether is has the necessary
power(s) to perform correctly.

Powers are protected by a password and a clearance file. Having the good password is not
enough, you have to be cleared in order to (ab)use it. The clearance file is a list of e-
mail address patterns, using the shell metacharacters scheme, someone being cleared if and
only if his e-mail address matches at least one of the patterns from the clearance file.
The more use you will make of metacharacters, the weaker this clearance scheme will be, so
be careful.

Your commands and the output resulting from their execution is normally mailed back to you
as a session transcript. For security reasons, passwords are hidden from the command line.
Likewise, failure to get a power will not indicate whether you lacked authorization or
whether your password was bad.

A user with the system power is allowed to create new powers, delete other powers, change
power passwords, and list, remove or change power clearances. This is somehow an important
power which should be detained by a small number of users with very strict clearance (no
meta-characters in the address, if possible). A good password should also protect that

However, a user with the system power is not allowed to directly get another power without
specifying its password and being allowed to do so by the associated clearance file. But
it would be possible to achieve that indirectly by removing the power and creating a new
one bearing the same name. In order to control people with the system power and also for
some tricky situation, there is another more god-like power: the root power.

A user with the root power can do virtually anything, since it instantly grants that
individual all the powers available on the server (but security). The only limitation is
that root cannot remove the root power alone. One needs to specify the security password
(another hardwired power) in order to proceed. Needless to say, only one individual should
have both root and security clearance, and only one individual should know the security
password and be listed in the clearance file. The system power cannot harm any of those
two powers. Eventually, more than one user could have the root power, but do not grant
that lightly...

Getting the root power is necessary when system has messed with the system configuration
in an hopeless way, or when a long atomic sequence of commands has to be issued: root is
not subject to the maximum number of command that can be issued in one single message.

In case you think this mailagent feature is dangerous for your account, do not create the
root and security powers, and do not write any sensitive commands.

Builtin Commands Definition
Now let's have a look at those builtin commands. Passwords of sensitive commands will be
concealed in the session transcript. Some commands accept input by reading the mail
message up to the EOF marker, which is a simple EOF string on a line by itself (analogous
with shell's here documents).

addauth power password
Add users to clearance file for power. If the power password is given, no
special power is needed, otherwise the system power is required. For root or
security powers, the corresponding power is required, or the password must be
specified. The command reads the standard input up to the EOF marker to get the
new users.

approve password command
Records the password in the command environment, then executes the command. If
a power is required and not yet obtained, the command will look for the password
in the environment and try to get the relevant power using that password. Hence,
approved command (with proper password) will transparently execute without the
hassle of requesting the power, issuing the command and then releasing the
power. It is up to the command to perform the approve password test by looking
at the approve variable in the command environment (see below). Since clearance
checks (such as those performed when requesting a power) are not performed, no
sensitive command should ever deal with the approve construct.

delpower power password [security]
Delete a power from the system, and its associated clearance list. The system
power is required to delete most powers except root and security. The security
power may only be deleted by itself and the root power may only be deleted when
the security password is also specified.

getauth power password
Get current clearance file for a given power. No special power required if the
password is given or the power is already detained. Otherwise, the system power
is needed for all powers but root or security where the corresponding power is

newpower power password [alias]
Add a new power to the system. The command then reads the standard mail input
until the EOF marker to get the power clearance list. The system power is
required to create a new power, unless it's root or security: The security power
is required to create root and the root power is required to create security.

passwd power old new
Change power password. It does not matter if you already hold the corresponding
power, you must give the proper old password. See also the password command.

password power new
Change power password. The corresponding power is required, or you have to get
the system power. To change the root or security passwords, you need the
corresponding power.

power name password
Ask for a new power. Of course, root does not need to request for any other
power but security, less give any password. This command is not honored when the
server is not in trusted mode, unbeknownst to the user: the error message in the
transcript file is no different from the one obtained with an invalid password.

powers regexp
List all the powers matching the perl regular expression, along with their
respective clearance file. The system power is required to get the list. The
root or security power are required to get access to the root or security
information, respectively. If no arguments are given, all the powers are

release power
Get rid of some power.

remauth power password
Remove users from clearance file, getting the list by reading the standard mail
input until the EOF marker. This command does not require any special power if
the proper password is given or if the power is already detained. Otherwise,
the system power is needed. For root and security clearance, the corresponding
power is needed as well.

set variable value
Set the variable to the corresponding value. Useful to alter internal variables
like the EOF marker value, or change some command environment. The user may
define his own variables for his commands. For flag-type variable, a value of
on, yes or true sets the variable to 1, any other string sets it to 0 (false).
Used all by itself as set, the list of all the defined variables along with
their respective values is returned.

setauth power password
Replace power clearance file with one obtained from standard mail input up to
the EOF mark. The system power is needed unless you specify the proper password
or the power is already yours. As usual, root or security clearances can only be
changed when the power is detained.

user [e-mail [command]]
Execute command by assuming the e-mail identity specified. Powers are lost while
executing the command. The e-mail identity may be checked by the command itself,
which may impose further restrictions on the execution, like getting user-
defined powers. Note that this command only modifies the global environment, and
that it's up to the command implementation to make use of that information. If
no command is specified, the new identity is assumed until changed by another
user command and all the powers currently held by the user are released. If no
e-mail address is given, the original user ID is restored.

Command Environment
There are six types of commands and variables that can be specified in server mode. Two of
them, end and help types are special and handled separately. Two types var and flag refer
to variables and the last two types perl and shell refer to commands.

Whenever mailagent fires a server command, it sets up an environment for that command: if
it is a perl-type command, then a set of perl variables are set before loading the
command; if it is a shell-type command, some environment variables are initialized and
file descriptor #3 is set up to point directly to the mailagent session transcript.

A shell-type command is forked, whilst a perl-type command is loaded directly in mailagent
within the cmdenv package. This operates much like the PERL filtering command, only the
target package differs and a distinct set of variables is preset.

Some commands collect additional data up to an end-of-file marker (by default the string
EOF on a line by itself) and those data are fed to shell commands via stdin and to perl
commands via the @buffer variable set up in the environment package named cmdenv (in which
the command is loaded and run).

If you define your own variables (types var or flag), you may use the builtin set command
to modify their values. Note that no default value can be provided when defining your
variable. A suitable default value must be set within commands making use of them, with
the advantage that different default values may be used by different commands.

The following environment variables are defined. Most are read-only, unless notified
otherwise, in which case the builtin set command may be used on them.

approve The approve password for approve commands, empty if not within a builtin approve

auth A flag set to true when a valid envelope was found in the mail message. When
this flag is false, the server cannot be put in trusted mode.

cmd The command line, as written in the message.

collect Internal flag set to true while collecting input from a here-document. It is
normally reset to false before calling the command.

debug True when debug mode is activated (may be set).

disabled A comma separated list of disabled commands, with no space between them. This
is initialized when the SERVER command is invoked and the -d option is used.

eof The current end-of-file marker for here-document commands. By default set to
'EOF' (may be changed).

errors Number of errors so far.

jobnum The job number assigned to the current mailagent.

log What was logged in the transcript, with some args possibly concealed.

name The command name.

pack Packing mode for file sending (may be set).

path Destination address for file sending or notification (may be set).

powers A colon (:) separated list of powers the user currently has successfully
requested and got.

requests Number of requests processed so far.

trace True when shell commands want to be traced in transcript (may be set).

trusted True when server is in trust mode, where powers may be gained. This is activated
by the -t option of the SERVER command, provided a valid mail envelope was

uid Address of the sender of the message, where transcript is to be sent. By
extension, the real user ID for the server, which is the base of the power
clearance mechanism.

user The effective user ID, originally the same as the uid, but may be changed via
the user builtin command.

Session Transcript
A session transcript is mailed back automatically to the user who requested a server
access. This transcript shows the commands ran by the user and their status: OK or FAILED.
Between those two lines, the transcript show any output explicitly made by the command to
the transcript. Typically, the transcript may be used to forward error messages back to
the user, but even commands executing correctly may want to issue an explicit message,
stating what has just been done.

A perl command may access the transcript via the MAILER file handle, defined in the cmdenv
package, whilst a shell command may access it via its file descriptor #3.

Note that the session transcript is mailed to the sender of the message, i.e. whoever the
envelope header line says it is. As far as the server is concerned, this e-mail address is
used as the user ID, just like a plain login name can be thought of as the user id. For
sensitive commands, authentication based on that information is really weak. A more
"secure" authentication is provided by the server powers, which is password-based.
Unfortunately, the clear password has to be transmitted in the message itself and could be

Recording New Commands and Variables
Server commands and variables are defined in the comserver file defined in your
~/.mailagent. The format of the file is that of a table with items on a row separated by
tabs characters. Each line defines one command or variable. Any irrelevant field may be
entered as a single '-' (minus) character. The format allows for shell-style (#) comments.

Each row has the following fields:

name type hide collect-data path extra


name is the name of the command or variable as recognized by the server.

type is one of perl, shell, var, flag, help or end.

hide indicates which arguments in the command are to be hidden (the command name
being argument zero) in the session transcript. Use '-' if no arguments
need to be hidden. Typically, this is used to hide clear passwords in
commands. If more than one argument has to be hidden, then a list of
numbers separated by a ',' (comma) may be specified, with no spaces between
them. For instance '2,4' would hide arguments 2 and 4 in the transcript.

collect-data is a flag (specify as either 'y' or 'n', but you may use complete words
'yes' or 'no') indicating whether the command collects additional data in a
here-document until the EOF marker. Alternatively, you may specify '-' in
place of 'n'.

path specifies the path of the command (~name substitution allowed). If not
relevant (e.g. when defining a variable) or when you want to leave it
blank, use '-'. If a blank path is specified for a perl or shell command,
then the implementation of that command is expected to be found in servdir,
as defined in ~/.mailagent. If the command name is cmd for instance, then
perl command are expected there in a file named cmd of cmd.pl, whereas
shell commands are expected to be found in a cmd of cmd.sh file. Note that
a command is disabled if it cannot be located at the time the comserver
file is parsed.

extra is any extra parameter needed for the command. Unlike other fields, this
should be left blank if not needed. Anything up to the end of the line is
grabbed by this field. Perl commands should specify the name of the perl
function to call to execute the command; if none is specified, the name of
the command itself is called. Shell commands may use that field to supply
additional options, which will be inserted right after the command name and
before any other user-supplied arguments. Others should leave this alone.

Special Command Types
There are currently two special command types.

The simplest is the end type. This is used to specify commands which may end the server
processing. By default, processing continues until the end of the file is reached or a
signature delimiter '--' is found. For instance, you may wish to define the command quit
and give it the end type. As soon as the server reaches that command, it aborts
processing and discards the remaining of the message.

The help type is usually attached to an help command and prints help on a command basis,
help for each command being stored under the helpdir variable (defined in your
~/.mailagent) in a file bearing the same name as the command itself. For example, assuming
a command shoot, its help file would be expected in helpdir/shoot. If no file is found
there, mailagent looks in its public library (/usr/share/mailagent) for an help file.
Help is provided only when the help file exists and is not zero-sized.

Creating the Root Power
In order to bootstrap the server, you need to create the root power. All the other powers
may then be created by using the server interface, which ensures consistency and logs your
actions. If you don't plan using powers at all, you may skip that section.

First, you need to pick up a good password for the root power. Someone with the root power
can do virtually anything with the server, so be careful. Let's assume you choose root-
pass as a password.

Edit passwd (defined in your ~/.mailagent) and add the following line:


i.e. enter the password in clear between '<' and '>'. It won't stay in that form for long,
but this is the easiest way to bootstrap it. Protect the passwd file tightly (read-write
permissions only for you). Then create a powerdir/root file, protect it the same way and
add your e-mail address to it, on a line by itself. That must be the address that will
show up in the From: line of your mails. Since clearance files support shell-style
patterns, you may use login@*domain.top to allow mails from your login from any machine in
your domain.

You are almost done. Now simply issue the following command:

mailagent -i -e 'SERVER -t'

and feed its standard input with:

From your e-mail address
From: your e-mail address

power root root-pass
password root root-pass

Note that the first From line is mandatory here, since it's the envelope on which
authentication is based. Since we're feeding mailagent with an handcrafted message, we
must provide a valid envelope or the server will not switch into trusted mode...

The side effect of re-instantiating your password will be to crypt it in the passwd file,
so that anybody looking at that file cannot guess your root password, hopefully.

Once you have a valid root power installed, you may create the system power by using
newpower. Further powers may then be created and deleted using the system power only.

You should also create the security power and give it a different password than the root
password. This is really needed only if you wish to remotely administrate the server. If
you have local access and things get corrupted, it's always possible to change the root
password manually by repeating this bootstrapping sequence.

Note that clearance checks are made using the envelope address of the message, which is a
little harder to forge than plain header fields like Sender:. The envelope is extracted
by looking at the first header line, which on Unix systems looks like:

From envelope-address send-date

and is inserted by the mail transport agent (MTA). If you are using sendmail as the MTA,
then only trusted users declared in the sendmail.cf file are able to create a "fake"
envelope address, a feature typically used by mailing list dispatchers, since that address
is then used as the bounce target in case the mail cannot be delivered. If that first
header line is absent, the sender is computed using the Sender: field if present, then the
From: field, but the auth variable is set to false and the server will not switch into
trusted mode; in other words, it will not be possible to gain powers in that session.

Moreover, since the session transcript is sent to that same envelope address used to
authenticate the eligibility for a power, the server feature can hardly be used to
retrieve confidential information held at the site where the mailagent is run since the
information would be sent to one of the users cleared for that power. It is the
responsibility of you, the user, to make sure this cannot happen or you could get into
legal troubles.

Finally, sensitive commands should be protected by a proper power, and great care should
be taken in writing the command implementation to ensure the security cannot be
circumvented. But no, this mailagent feature is not believed to be dangerous for the
system or site it is used on, since a determined user could implement one trivially via a
five line shell script. If security is really an issue, .forward files using the piping
feature should be prohibited and access to cron forbidden in order to avoid automatic mail
processing (since it would be possible to have cron invoke a mailagent process -or any
other program for that matter- to process the incoming mail in a comparable way).

Here is an example showing the steps involved in creating a shell command, which would
take a script by collecting lines until an EOF mark and feed it to a real shell for
execution. Since allowing this feature without any safeguards would be a real security
hole, we protect that by requesting the power shell before allowing the execution.

Here is my implementation of the shell command (available in the mailagent distribution
under misc/shell):


# Execute commands from stdin, as transmitted by the mailagent server.
# File descriptor #3 is a channel to the session transcript.

# Make sure we have the shell power.
# Don't even allow the root power to bypass that for security reasons.
case ":$powers:" in
*:shell:*) ;;
echo "Permission denied." >&3
exit 1

# Perhaps a shell was defined... Otherwise, use /bin/sh
case "$shell" in
'') shell='/bin/sh';;

# Normally, a shell command has its output included in the transcript only in
# case of error or when the user requests the trace. Here however, we need to
# see what happened, so everything is redirected to the session transcript.

exec $shell -x >&3 2>&3

Note how we make access to the $powers and $shell environment variable. That last one is
user-defined to allow dynamic set-up of a shell.

Assuming we store that command under servdir/shell.sh (don't forget to add the execution
bit on the file...), here is how we declare it and its variable in the comserver file.

shell shell - y -
shell var - - -

This example shows that there is a separate name-space for variables and commands.
Moreover, the command bears the same name as its type -- don't let that confuse you :-).

Now, assuming you have already created a system power and protected it with a password
(let's assume sys-pass for the purpose of this example), you need to create the shell
power. Although you could do it manually (like when you handcrafted the root power), it's
better to use the SERVER interface since it ensures consistency.

In order to create the shell power required to use the newly created shell command, you
need to add the following rule to your rule file:

Subject: Server { SAVE server; SERVER -t };

which will save all server mail in a dedicated folder and process them. Note the -t
option, which allows trusted mode, in which powers may be gained. Now send yourself the
following mail:

Subject: Server
power system sys-pass
newpower shell shell-pass
[email protected]

which requests for the system power (needed to created most powers), and then creates a
new power shell, assigning shell-pass as its password and clearing [email protected] for it.
Note the here-document fill-in for the newpower command, up to the EOF marker. Of course,
you need to replace the address by your real address.

You will receive a session transcript along these lines:

---- Mailagent session transcript for [email protected] ----

----> power system ********

====> newpower shell ********

====> --
End of processing (.signature)

---- End of mailagent session transcript ----

Note the concealed passwords, and the prompt change once the system power has been
granted. Since my mailer automatically appends a signature, the processing stops on it.

Now let's use this new command... Send yourself the following mail:

Subject: Server
set shell /bin/ksh
set eof END
ls -l /etc/passwd
power shell shell-pass
ls -l /etc/passwd

If you everything is right, you should receive back a transcript looking like this:

---- Mailagent session transcript for [email protected] ----

----> set shell /bin/ksh

----> set eof END

----> shell
Permission denied.
Command returned a non-zero status (1).

----> power shell ********

====> shell
+ ls -l /etc/passwd
-rw-r--r-- 1 root system 691 Oct 01 14:24 /etc/passwd

====> --
End of processing (.signature)

---- End of mailagent session transcript ----

The first invocation of the shell command fails since we lack the shell power. The string
"Permission denied." is echoed by the command itself into file descriptor #3 and makes it
to the transcript.

The generic mail server implemented in mailagent can be used to implement a mailing list
manager, a vote server, an archive server, etc... Unfortunately, it does not currently
have the notion of state, with a command set dedicated to each state, so it is not
possible to implement an intelligent archive server.

If you implement new simple server commands and feel they are generic enough to be
contributed, please send them to me and I will gladly integrate them.


Here are some examples of rule files. First, if you do not specify a rule file or if it is
empty, the following built-in rule applies:

All: /^Subject: [Cc]ommand/ { LEAVE; PROCESS };

Every mail is left in the mailbox. Besides, mail with "Subject: Command" anywhere in the
message are processed.

The following rule file is the one I am currently using:

maildir = ~/mail;

All: /^Subject: [Cc]ommand/ { SAVE cmds; PROCESS };

To: /^[email protected]/ { POST -l mail.gue };
Apparently-To: ram,
Newsgroups: mail.gue { BOUNCE [email protected] };

Apparently-To: ram,
Newsgroups: mail.gue { DELETE };

From: root, To: root { BEGIN ROOT; REJECT };
<ROOT> /^Daily run output/ { WRITE ~/var/log/york/daily.%D };
<ROOT> /^Weekly run output/ { WRITE ~/var/log/york/weekly };
<ROOT> /^Monthly run output/ { WRITE ~/var/log/york/monthly };

From: ram { BEGIN RAM; REJECT };
<RAM> To: ram { LEAVE };
<RAM> X-Mailer: /mailagent/ { LEAVE };

The folder directory is set to ~/mail. All command mails are saved in the folder
~/mail/cmds and processed. They do not show up in my mailbox. Mails directed to the gue
mailing list (French Eiffel's Users Group, namely Groupe des Utilisateurs Eiffel) are
posted on the local newsgroup mail.gue and do not appear in my mailbox either. Any follow-
up made on this group is mailed to me by inews (and not directly to the mailing list,
because those mails would get back to me again and be fed to the newsgroup, which in turn
would have them mailed back to the list, and so on, and so forth). Hence the next rule
which catches those follow-ups and bounces them to the mailing list. Those mails will
indeed come back, but the _SEEN_ rule will simply delete them.

On my machine, the mails for root are forwarded to me. However, everyday, the cron daemon
starts some processes to do some administration clean-up (rotating log files, etc...), and
mails the results back. They are redirected into specific folders with the WRITE command,
to ensure they do not grow up without limit. Note the macro substitution for the daily
output (on Mondays, the output is stored in daily.1 for instance).

The next group of rules prevents the mail system from sending back mails when I am in a
group alias expansion. This is a sendmail option which I disabled on my machine. Care is
taken however to keep mails coming from the mailagent which I receive as a blind carbon


In order to limit the load overhead on the system, only one mailagent process is allowed
to run the commands. If some new mail arrives while another mailagent is running, that
mail is queued and will be processed later by the main mailagent.

For the same reason, messages sent back by mailagent are queued by sendmail, to avoid the
cost of mail transfer while processing commands.


First, let me discuss what security means here. It does not mean system safety against
intruder attacks. If your system allows .forward hooks and/or cron jobs to be set by
regular users, then your system is not secure at all. Period. So we're not bothering with
security at the system level, but rather at your own account level where all sort of
precious data is held.

To avoid any pernicious intrusion via Trojan horses, the C filter will refuse to run if
the configuration file ~/.mailagent or the rule file specified are world writable or not
owned by the user. Those tests are enforced even if the filter does not run setuid,
because they compromise the security of your account. The mailagent will also perform
some of those checks, in case it is not invoked via the C filter.

Indeed, if someone can write into your ~/.mailagent file, then he can easily change your
rules configuration parameter to point to another faked rule file and then send you a
mail, which will trigger mailagent, running as you. Via the RUN command, this potential
intruder could run any command, using your privileges, and could set a Trojan horse for
later perusal. Applying the same logic, the rule file must also be protected tightly.

And, no surprise, the same rules apply for your newcmd file, which is used to describe
extended filtering commands. Otherwise it would allow someone to quietly redefine a
commonly used standard command like LEAVE and later be able to assume your identity.

Versions after 3.0 PL44 come with an improved (from a security point of view) C filter
that will not only perform the aforementionned checks but will also ensure that the perl
executable and the mailagent script it is about to exec are not loosely protected (when
execsafe is ON or when running with superuser privileges). Furthermore, if the filter is
set up in your .forward as described in this man page, it will be able to check itself for
safety and will warn you loundly if it can be tampered with, which could defeat all
security checks.

Mailagent was also extended so that all programs executed via RUN and friends, as well as
mail hooks, are checked for obvious protection flaws before being actually run Interpreted
scripts (starting with the #! magic token) and perl scripts following the magic "exec perl
if $under_shell" incantation are specially checked for further security of the relevant
interpretor. Those checks are performed systematically (when execsafe is ON or when
running with superuser privileges) even if the secure parameter was not set to ON. Also,
all files about to be exec()ed are checked using the same extended check method used when
secure is ON (ownership tests are skipped however when checking for exec()-ness of a

Use mailagent online using onworks.net services

Free Servers & Workstations

Download Windows & Linux apps

  • 1
    Sardi is a complete restyling and
    optimisation of svg code. 6 choices for
    your applications and 10 kind of folders
    to use in your file manager. The sardi
    Download Sardi
  • 2
    LMMS Digital Audio Workstation
    LMMS Digital Audio Workstation
    LMMS is a free cross-platform software
    which allows you to produce music with
    your computer. If you like this project
    consider getting involved in the project
    Download LMMS Digital Audio Workstation
  • 3
    FreeRTOS Real Time Kernel (RTOS)
    FreeRTOS Real Time Kernel (RTOS)
    FreeRTOS is a market-leading real-time
    operating system (RTOS) for
    microcontrollers and small
    microprocessors. Distributed freely
    under the MIT open source lice...
    Download FreeRTOS Real Time Kernel (RTOS)
  • 4
    Avogadro is an advanced molecular
    editor designed for cross-platform use
    in computational chemistry, molecular
    modeling, bioinformatics, materials
    science and ...
    Download Avogadro
  • 5
    XMLTV is a set of programs to process
    TV (tvguide) listings and help manage
    your TV viewing, storing listings in an
    XML-based format. There are utilities to
    Download XMLTV
  • 6
    Strikr Free Software project. Artifacts
    released under a 'intent based'
    dual license: AGPLv3 (community) and
    CC-BY-NC-ND 4.0 international
    Download strikr
  • More »

Linux commands