I stopped copying to try to work out the a structure in which to understand this foolish language. The order in which Ed's document explains things doesn't quite make sense. The official spec for Sieve rfc3028 isn't much better on this point. My difficulty is that there are two special tests: anyof and allof. These two require a test list. These, however, are the only two which act that way and I therefore believe they should be handled separately and after the regular tests have been discussed.

NOTE : it appears that the CMU sieve implementation is CASE SENSITIVE with respect to the names of the tests... or at least sieveshell is. Something rejected my script when I made the tests upper-cased (as they appear below).

Tests

EXISTS
Tests for the existance of a specified header
Tests only for the existance of a header with the same name as one of the names in the given list. The list of header names is given as a string list. A string list is represented by a left square bracket, one or more quoted strings separated by commas, and a right square bracket.

HEADER
Compares the value of a specified header to a given string
Comparison against the values of an arbitrary header. Takes a list of header names and a list of strings to compare against the values for those headers. The string comparison may be an absolute match (:is), a sub-string match (:contains), or a simple regular expression (:matches). The comparison may also be case insensitive or require strict case matching. The default is an absolute, case insensitive match.

ADDRESS
Compares an email address in a specified header to a given string
A specialized version of HEADER that allows comparisons against specific parts of email addresses. Email addresses have two parts: a local part and a domain. In the address tim@example.com "tim" is the local part and "example.com" is the domain. The parts of the address that may be checked are the whole addres (:all), the local part (:localpart), or the domain (:domain).

ENVELOPE
Compares the email addresses given to the SMTP server to a given string
A specialized version of ADDRESS which has special functions to read the to and from values used when the mail was transmitted to the receiving server (as opposed to value which may appear in the headers). NOTE: this feature is an optional part of the RFC so it may not be implemented on all servers.

SIZE
Compares the size of the message to the given size
This test may be used to check for sizes larger than or smaller than the given size. It appears that the size may be specified as octets, kilobytes (K), or megabytes (M). I have not seen a definitive spec on how to represent sizes.

TRUE
This test always evaluates to true. That's it.

FALSE
This test always evaluates to false. That's it.

NOT
This inverts the result of the test which follows it. As such, I think of it as far more of an operator than a test. However according to the documentation it is classified as a test. This is where I want to claim that the sieve language is lisp like (following in the footsteps of the AMS Flames files)

ALLOF
Performs a logical AND operation between all of the tests in the list of tests which follow it. A list of tests is represented by an open paren, one or more tests separated by commas, and a close paren. This is even more lisp like than NOT. Effectively what we have here is a function call with the results of multiple tests as its arguments.

ANYOF
Performs a logical OR operation between all of the tests in the list of tests which follow it. This is just like the ALLOF test.

This hierarchy is more representative of the way I order these tests. At the top, we have the simplest useful tests. We proceed through the more specialized versions. Next comes the one odd attribute test, size. After that we have the two token constants and then finally, the operators which are implemented as tests. Operators come last because they are of no use until you have mastered the more basic tests.

One of the things that I would like to see for my own understanding is a claim that a bunch of these things are equivalent. My first reaction was that ENVELOPE and ADDRESS were identical. Further reading has convinced me that ENVELOPE is special in that it deals with the values given to the SMTP server rather than what appears in the header. Moving up the chain, ADDRESS is just a specialized version of HEADER. It is specialized to look at email addresses. I believe a nearly equivalent test could be performed by each. For example: looking for a domain could use the domain flag to the ADDRESS or you could just write "@domain" in the match string used for HEADER. Beyond this, it appears from the RFC that EXISTS may be simulated by using HEADER and checking to to see if the specified header will substring match against a zero length string (HEADER :contains ["X-Caffeine"] [""]).

Optional Arguments

MATCH-TYPE
Controls the type of string matching performed. This may be used with the header, address, and envelope tests. The possible values are :
:is The ":is" match type describes an absolute match; This is the default type of matching. If the contents of the first string are absolutely the same as the contents of the second string, they match. Only the string "frobnitzm" is the string "frobnitzm". The null key ":is" and only ":is" the null value. (default)
:contains The ":contains" match type describes a substring match. If the value argument contains the key argument as a substring, the match is true. For instance, the string "frobnitzm" contains "frob" and "nit", but not "fbm". The null key ("") is contained in all values.
:matches The ":matches" version specifies a wildcard match using the characters "*" and "?". "*" matches zero or more characters, and "?" matches a single character. "?" and "*" may be escaped as "\\?" and "\\*" in strings to match against themselves. The first backslash escapes the second backslash; together, they escape the "*". This is awkward, but it is commonplace in several programming languages that use globs and regular expressions.

ADDRESS-PART
Controls which part of email addresses is looked at. This may be used for the address and evelope tests. The possible values are :
:all Match against the complete email address. (default)
:localpart Match against the recipient or userid part of the email address. This is the part before the at (@) sign.
:domain Match against just the domain of the email address. This is the part after the at (@) sign.

COMPARATOR
Controls how the comparison between two strings is handled (case insensitive, etc). This can be also be used for cross character set comparison implementations. Only two standard comparators are defined. Note that this argument has a sub-arguments of its own (unlike match-type and address-part). This may be used with the header, address, or envelope tests. The possible values are :
:comparator "i;ascii-casemap" Perform case insensitive, ASCII based comparison. (default)
:comparator "i;octet" Perform a direct octet (binary) comparison without any form of conversion.

To do : commands. Note that commands end with semi-colons.

NOTE : every line MUST end with CRLF. No other line ending is acceptable. sieveshell appears to be quite cranky and unwilling to help on this point (say by fixing up a script it doesn't like or giving you an option to force fix).

Actions

Actions specify how the message is handled. Each action statement is terminated by a semi-colon.

There are two special actions : "keep" and "discard". When processing start, the keep flag is automatically set. This is known as "implicit keep" in the sieve RFC. The discard action turns off the implicit keep action. However, if any other action (reject, redirect, or fileinto) is taken then the value of the keep flag is ignored / over-ridden. In practice this means that if a message is given a discard action, but a later rule in the script causes the message to be filed into a folder, then the message is filed rather than discarded! The work around for this is to use and if-else block for the discard and non-discard rules and actions.

KEEP
Set the keep flag to true -- this is the default value. If no other action is taken on the message it will be kept.

DISCARD
Set the keep flag to false. If no other action is taken on the message it will discarded silently. However, if any other action (such as reject, redirect, or fileinto) is called, the discard action will be ignored.

FILEINTO
Files the message in a mailbox other than the default. The name of the mailbox must be given after the fileinto keyword. For this action to work, the script must declare that it requires the fileinto module. This is done by including the line require "fileinto"; at the top of the sieve script.

REDIRECT
Forwards the message to a given email address. The message body is not altered.

REJECT
Sends a notification to the originating address stating that the message was rejected. A string stating the reason for rejection must be included. It is not clear if the reject module must be specified at the top of the script or not.


string list = [<string>, <string>, ...]
string      = "<chars>"

test list   = (<test> , <test> , ...)
invert      = not <test>

if test { block } else { block }
if test { block } elsif test { block } else { block }

Match Type -- Control used in header, address, and envelope.
     :is | :contains | :matches    --  type of match to perform
     

EXISTS <header list>
        header list  = string list of header names

HEADER [comparator] [match-type] <header list> <key list>
        comparator   = :comparator "i;ascii-casemap" | :comparator "i;octet"
        match-type   = :is | :contains | :matches
        header list  = string list of header names
        key list     = string list of values to look for

ADDRESS [address part] [comparator] [match-type] <header list> <key list>
        address part = :all | :localpart | :domain
        comparator   = :comparator "i;ascii-casemap" | :comparator "i;octet"
        match-type   = :is | :contains | :matches
        header list  = string list of header names
        key list     = string list of values to look for

ENVELOPE [address part] [comparator] [match-type] <header list> <key list>
        address part = :all | :localpart | :domain
        comparator   = :comparator "i;ascii-casemap" | :comparator "i;octet"
        match-type   = :is | :contains | :matches
        header list  = string list of header names -- only "to" and "from"
                       are likely supported.
        key list     = string list of values to look for

SIZE <limit-type> <limit value>
        limit-type   = :over | :under
        limit value  = the number of octets.  May be suffixed 
                       by "K", "M", or "G"
                       representing a kilobyte, megabyte, or gigabyte
                       (1024, etc) size.

NOT <test>
ALLOF (<test>, <test>, ..)
ANYOF (<test>, <test>, ..)
        test         = any test (exists, header, size, etc...)


ACTIONS --
reject <string>
fileinto <string>
redirect <string>
keep
discard

MISC --
require <string>

NOTE : it looks like the spec for the ENVELOPE test in rfc3028 is wrong. See http://www.faqs.org/rfcs/rfc3028.html. The syntax says "comparator address-part" but the example has them in the reverse order.... I'm going to assume that it should match the ADDRESS test.


copied from the MSWord document by edwardm. I've left it here for now.

Using Sieve

Introduction

Sieve is a language that can be used to create filters for email. CMU is currently using sieve on the Cyrus IMAP server.

Scripts written in Sieve are executed during final delivery, when the message is moved to the user-accessible mailbox. All Sieve scripts execute under the users access rights.

Currently, CMU has implemented a web-based GUI for creating and installing Sieve vacation notice scripts. In the future, CMU will offer web-based filtering for messages. Until then, you can create and install your own Sieve scripts to take advantage of the power Sieve offers today.

Basic Syntax

In general, a script consists of one or more actions or more usually a conditional statement with the following syntax:
if test { block of actions }
For example :
if address :is "from" "tim@example.com" { discard; }

Here the test is address :is "from" "tim@example.com", and the action block is a single action discard.

Arguments

There are several arguments used in Sieve scripts. Two discussed here are tagged and comparor.

Tagged

A tagged argument is an argument for a command that begins with ":" followed by a tag naming the argument, such as ":contains". This argument means that zero or more of the next tokens have some particular meaning depending on the argument. These next tokens may be numbers or strings but they are never blocks.



Last modified: Thu Oct 9 14:59:05 EDT 2003