How to filter your mail

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.

A simple example

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:

Syntax

Basic structure

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).

Matching header fields

For matching header fields, use the test header comparator match-type header-list value-list, where:

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.

Matching addresses

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:

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:

Parametrized addresses

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:

Regardless of the status of the extension, :localpart matches user+parameter.

Conditional execution

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.

Actions

The following actions are available:

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.

Vacation auto-reply

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.

Debug logging

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}";

Dropping duplicate messages

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.

Advanced topics

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.