We use Sieve for mail filtering. Unless you say otherwise, a default set of rules is in place, which takes care of recognition of spam and viruses.
You can define custom filter scripts by placing them in the ~/.config/sieve
directory (beware that the name of the script must be suffixed by .sieve
).
A ~/.sieve
symlink should point to the script you wish to run on
incoming mail. Before you use a script, run it through sievec
to compile it
and, more importantly, check its syntax.
In most situations, you want to combine your script with the default spam filtering rules. In order to do this, please add the following lines to the top of your script. (You can also add some of your tests before it, for example if you want to except some mail from spam filtering.)
require "include"; include :global "default";
For the complete documentation of the Sieve language, see the specification of the base language and the list of extensions supported by our server.
For the impatient, we present a brief introduction.
require ["include", "fileinto", "copy"]; include :global "default"; # This is a comment if header :contains "Subject" ["[DM]", "[ADS]"] { fileinto "students"; stop; } if address "From" "dekan@mff.cuni.cz" { redirect :copy "homemail@example.org"; } if address :matches "From" "*@mff.cuni.cz" { fileinto "mff"; }
The script consists of four sections:
[DM]
or [ADS]
and delivers
them to the students
folder. Then, the execution of the script
stops. (If a script stops without an explicit action on the message, the
message is delivered to the inbox as if no filtering took place.)
dekan@mff.cuni.cz
are
forwarded to homemail@example.org
. Since a :copy
modifier is given, the implicit delivery to the inbox is still performed.
mff.cuni.cz
domain
are filed into the mff
folder. (If there were no stop
in the second section, messages satisfying both conditions would be filed
to both folders.)
The script is a sequence of commands. Each command takes several arguments: some of them positional (their meaning depends on the position relative to the command), some of them introduced by a keyword starting with a colon. Keyword arguments must come before positional ones. Finally, some commands accept a block of other commands, which must be the last argument.
The if
command executes a block conditionally. The condition
takes the form of a test. It has a name (e.g., header
or address
) after one or more of arguments follow (again, they can
be either keyword arguments or positional ones).
For matching header fields, use the test
header
comparator match-type header-list value-list, where:
:comparator "i;octet"
performs byte-by-byte comparison
:comparator "i;ascii-casemap"
performs case-insensitive comparison on ASCII characters
i;ascii-casecmp
is used.
:is
for an exact match
:contains
if the header field should contain the value as a sub-string
:matches
if the value is a shell-like wildcard pattern (with ?
and *
wildcards) against which the field is matched
:is
is used.
To check if a field of a given name exists, use the test exists
header-list.
If the header field contains any MIME encoding of non-ASCII characters, it is automatically decoded and the test matches the UTF-8 form of the string.
Some header fields contain one or more e-mail addresses. Sieve knows how to parse them
and offers tests for matching individual addresses. The test is written as
address
comparator addr-part match-type header-list value-list,
where:
:all
is the whole address (default)
:localpart
is the part before @
:domain
is the part after @
header
test.
Addresses are present not only in the message header, but also in the envelope.
The envelope controls e-mail routing and it contains the final recipient (when forwarding
messages, the To
and Cc
header fields are left unmodified, but the
envelope recipient changes) and the real sender (which can differ from the From
header for example when the message passes through a mailing list).
To match addresses in the envelope, use the test
envelope
comparator addr-part match-type part-list value-list.
Most arguments work as usually, the part-list selects which parts of the
envelope should be matched:
"from"
is the envelope sender
"to"
is the envelope recipient
Our server supports local addresses of type user+
parameter@
domain.
For any value of the parameter, the message is delivered to the given user, but the parameter
can be tested in his Sieve script. When you require the subaddress
extension,
new choices of addr-part become available:
:user
matches the user part
:detail
matches the parameter part
Regardless of the status of the extension, :localpart
matches user+
parameter.
Generally, conditional statements have the following form:
if TEST { ... } elsif TEST { ... } else { ... }
Multiple tests can be combined to one using either
anyof(
test1,
test2,
...)
or
allof(
test1,
test2,
...)
.
There is also a test true
which always matches and false
which never does.
The following actions are available:
fileinto
mailbox – deliver the message to the given folder.
With :create
(mailbox
extension required), the folder is created
if it did not exist yet.
redirect
address – the message is forwarded to the specified address.
keep
– the message is delivered to the default folder (this is what happens
if no explicit action is given).
discard
– the message should be discarded: this disables the implicit keep
.
When the copy
extension is activated, a :copy
argument can be
given to fileinto
and redirect
, in which case the implicit keep
is not skipped.
If you want to remind message senders that you are currently on vacation,
you can use the vacation
extension. Here is an example script:
require ["include", "vacation"]; include :global "default"; vacation :days 7 :subject "On vacation until 2099" "I am currently on a long vacation. I will perhaps reply when I return.";
:days
specifies the minimum time between two vacation reminders
send to the same address. The two strings are the subject of the auto-reply and
the text in its body. Both can contain UTF-8 characters.
This example also shows that you can split a command to multiple lines.
When debugging tangled Sieve rules, it is useful to print debugging messages. You can use this:
require "vnd.dovecot.debug"; debug_log "I was here...";
The messages will appear in ~/.sieve.log
.
You can also log contents of header fields:
require ["variables", "envelope", "vnd.dovecot.debug"]; if envelope :matches "to" "*" { set "to" "${1}"; } if envelope :matches "from" "*" { set "from" "${1}"; } debug_log "Received message: TO=${to} FROM=${from}";
Unlike a paper letter, an e-mail can be delivered multiple times to the same destination. This frequently happens when a message is delivered to you both directly (as the sender CC'd you) and via a mailing list you are subscribed to.
It can be useful to set up filtering rules which deliver just one instance of the message. Sieve has a duplicate extension for that. Here is an example:
require "duplicate"; if duplicate :seconds 86400 { discard; }
When an e-mail is received, all messages with the same Message-ID received within the next 24 hours are discarded.
Running external programs: For security reasons, Sieve does not allow execution of arbitrary user-specified programs. If you need it, please ask the system administrators to include your program in the set of allowed binaries.
Variables: You can define your own variables and refer to them in commands and test using the variables extension.
Modifying headers: If you want to add new header fields or remove existing ones, you can use the editheader extension for that.
Working with MIME structure: The mime extension allows tests for structure of MIME parts of the message (e.g., presence of attachments of specific types). It can also remove parts and add new ones.