GNU Mailutils Manual (split by chapter):   Section:   Chapter:FastBack: Libraries   Up: Top   FastForward: Reporting Bugs   Contents: Table of ContentsIndex: Function Index

5 Sieve Language

The input language understood by the GNU Sieve Library is a superset of the Sieve language as described in RFC 3028.

5.1 Lexical Structure

Whitespace and Comments

Comments are semantically equivalent to whitespace and can be used anyplace that whitespace is (with one exception in multi-line strings, as described below).

There are two kinds of comments: hash comments, that begin with a ‘#’ character that is not contained within a string and continue until the next newline, and C-style or bracketed comments, that are delimited by ‘/*’ and ‘*/’ tokens. The bracketed comments may span multiple lines. E.g.:

if size :over 100K
  { # this is a comment
    discard;
  }
  
if size :over 100K
  { /* this is a comment
       this is still a comment */ discard /* this is a comment again
     */ ;
  }

Like in C, bracketed comments do not nest.

Lexical Tokens

The basic lexical entities are identifiers and literals.

An identifier is a sequence of letters, digits and underscores, that begins with a letter or underscore. For example, header and check_822_again are valid identifiers, whereas 1st is not. A special form of identifier is tag: it is an identifier prefixed with a colon (‘:’), e.g.: :comparator.

A literal is a data that is not executed, merely evaluated “as is”, to be used as arguments to commands. There are four kinds of literals:

5.2 Syntax

Being designed for the sole purpose of filtering mail, Sieve has a very simple syntax.

5.2.1 Commands

The basic syntax element is a command. It is defined as follows:

command-name [tags] args

where command-name is an identifier representing the name of the command, tags is an optional list of optional or tagged arguments and args is a list of required or positional arguments.

Positional arguments are literals delimited with whitespace. They provide the command with the information necessary to its proper functioning. Each command has a fixed number of positional arguments. It is an error to supply more arguments to the command or to give it fewer arguments than it accepts.

Optional arguments allow to modify the behaviour of the command, like command line options in UNIX do. They are a list of tags (see Lexical Structure) separated by whitespace. An optional argument may have at most one parameter.

Each command understands a set of optional arguments. Supplying it tags that it does not understand results in an error.

For example, consider the following command

header :mime :comparator "i;octet" ["to", "from"] "bug-mailutils@gnu.org"

Here, given that header takes two positional arguments: header is command name, the list ["to", "from"] is first positional argument and the string "bug-mailutils@gnu.org" is second positional argument. There are two optional arguments: :mime and :comparator. The latter has a string "i;octet" as its parameter.

5.2.2 Actions Described

An action is a Sieve command that performs some operation over a message. Actions do the main job in any Sieve program. Syntactically, an action is a command terminated with semicolon, e.g.:

keep;

fileinto "mbox";

GNU Sieve provides the full set of actions described in RFC 3028. It also allows to extend this set using loadable actions. See Actions, for detailed discussion of actions.

5.2.3 Control Flow

The only control flow statement Sieve has is if statement. In its simplest form it is:

if condition { … }

The effect of this statement is that the sequence of actions between the curly braces is executed only if the condition evaluates to true.

A more elaborate form of this statement allows to execute two different sets of actions depending on whether the condition is true or not:

if condition { … } else { … }

The most advanced form of the “if” statement allows to select an action depending on what condition from the set of conditions is met.

if cond1 { … } elsif cond2 { … } else { … }

There may be any number of “elsif” branches in an “if” statement. However it may have at most one “else” branch. Notes for C programmers:

  1. The braces surrounding each branch of an “if” statement are required.
  2. The “else if” construct is disallowed. Use “elsif” keyword instead.

Here’s an example of “if” statement:

if header :contains "from" "coyote"
  {
    discard;
  }
elsif header :contains ["subject"] ["$$$"]
  {
    discard;
  }
else
  {
    fileinto "INBOX";
  }

The following section describes in detail conditions used in “if” statements.

5.2.4 Tests and Conditions

Tests are Sieve commands that return boolean value. E.g. the test

header :contains "from" "coyote"

returns true only if the header “From” of the current message contains substring “coyote”.

The tests shipped with the GNU Sieve are described in Tests.

Condition is a Sieve expression that evaluates to true or false. In its simplest form, condition is just a Sieve test.

To reverse the sense of a condition use keyword not, e.g.:

not header :contains "from" "coyote"

The results of several conditions may be joined together by logical and and or operations. The special form allof takes several tests as its arguments and computes the logical and of their results. Similarly, the form anyof performs logical or over the results of its arguments. E.g.:

if anyof (not exists ["From", "Date"],
          header :contains "from" "fool@example.edu")
  {
    discard;
  }

5.3 Preprocessor

Preprocessor statements are a GNU extension to the Sieve language. The syntax for a preprocessor statement is similar to that used in C programming language, i.e. a pound character (‘#’) followed by a preprocessor directive and its arguments. Any amount of whitespace can be inserted between the ‘#’ and the directive. Currently implemented directives are include and searchpath.

5.3.1 Sieve #include directive

The #include directive reads in the contents of the given file. The contents is “inserted” into the text being parsed starting at the line where the directive appears. The directive takes two forms:

#include "filename"

The filename is taken relative to the current directory.

#include <filename>"

The filename is searched in the list of include directories as specified by the -I command line options.

If filename starts with a directory separator character (‘/’) both forms have the same effect.

5.3.2 Sieve #searchpath directive

The #searchpath directive adds its argument to the list of directories searched for loadable modules. It has the same effect as library-path Sieve configuration statement (see library-path).

5.4 Require Statement

Syntax:   require string;
          require string-list;

The require statement informs the parser that a script makes use of a certain extension. Multiple capabilities can be declared using the second form of the statement. The actual handling of a capability name depends on its suffix.

If the name starts with ‘comparator-’, it is understood as a request to use the specified comparator. The comparator name consists of the characters following the suffix.

If the name starts with ‘test-’, it means a request to use the given test. The test name consists of the characters following the suffix.

Otherwise, the capability is understood as a name of an action to be used.

The require statement, if present, must be used before any other statement that is using the required capability. As an extension, the GNU sieve allows the require and any other statements to be interspersed.

By default the following actions and comparators need not be explicitly required:

Example:

require ["fileinto", "reject"];

require "fileinto";

require "comparator-i;ascii-numeric";

When processing arguments for require statement, GNU libmu_sieve uses the following algorithm:

  1. Look up the name in a symbol table. If the name begins with ‘comparator-’ it is looked up in the comparator table. If it begins with ‘test-’, the test table is used instead. Otherwise the name is looked up in the action table.
  2. If the name is found, the search is terminated.
  3. Otherwise, transform the name. First, any ‘comparator-’ or ‘test-’ prefix is stripped. Then, any character other than alphanumeric characters, ‘.’ and ‘,’ is replaced with dash (‘-’). The name thus obtained is used as a file name of an external loadable module.
  4. Try to load the module. The module is searched in the following search paths (in the order given):
    1. Mailutils module directory. By default it is $prefix/lib/mailutils.
    2. Sieve library path as given with the -L options in the command line
    3. Additional search directories specified with the #searchpath directive.
    4. The value of the environment variable LTDL_LIBRARY_PATH.
    5. System library search path: The system dependent library search path (e.g. on Linux it is set by the contents of the file /etc/ld.so.conf and the value of the environment variable LD_LIBRARY_PATH).

    The value of LTDL_LIBRARY_PATH and LD_LIBRARY_PATH must be a colon-separated list of absolute directories, for example, ‘"/usr/lib/mypkg:/lib/foo"’.

    In any of these directories, libmu_sieve first attempts to find and load the given filename. If this fails, it tries to append the following suffixes to the file name:

    1. the libtool archive extension ‘.la
    2. the extension used for native dynamic libraries on the host platform, e.g., ‘.so’, ‘.sl’, etc.
  5. If the module is found, libmu_sieve executes its initialization function (see below) and again looks up the name in the symbol table. If found, search terminates successfully.
  6. If either the module is not found, or the symbol wasn’t found after execution of the module initialization function, search is terminated with an error status. libmu_sieve then issues the following diagnostic message:
    source for the required action NAME is not available
    

5.5 Comparators

GNU libmu_sieve supports the following built-in comparators:

i;octet

This comparator simply compares the two arguments octet by octet

i;ascii-casemap

It treats uppercase and lowercase characters in the ASCII subset of UTF-8 as the same. This is the default comparator.

i;ascii-numeric

Treats the two arguments as ASCII representation of decimal numbers and compares their numeric values. This comparator must be explicitly required prior to use.

5.6 Tests

This section describes the built-in tests supported by GNU libmu_sieve. In the discussion below the following macro-notations are used:

match-type

This tag specifies the matching type to be used with the test. It can be one of the following:

:is

The :is match type describes an absolute match; 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. This is the default match-type.

: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 ‘*’.

:regex

The :regex version specifies a match using POSIX Extended Regular Expressions.

:value relation

The :value match type does a relational comparison between strings. Valid values for relation are:

"eq"

Equal

"ne"

Not Equal

"gt"

Greater Than

"ge"

Greater than or Equal

"lt"

Less Than

"le"

Less than or Equal

:count relation

This match type first determines the number of the specified entities (headers, addresses, etc.) in the message and does a relational comparison of the number of entities to the values specified in the test expression. The test expression must be a list of one element.

comparator

A comparator syntax item is defined as follows:

:comparator "comparator-name"

It instructs sieve to use the given comparator with the test. If comparator-name is not one of ‘i;octet’, ‘i;ascii-casemap’ it must be required prior to using it. For example:

require "comparator-i;ascii-numeric";

if header :comparator "i;ascii-numeric" :is "X-Num" "10"
  {
    ...
address-part

This syntax item is used when testing structured Internet addresses. It specifies which part of an address must be used in comparisons. Exactly one of the following tags may be used:

:all

Use the whole address. This is the default.

:localpart

Use local part of the address.

:domain

Use domain part of the address.

Notice, that match-type modifiers interact with comparators. Some comparators are not suitable for matching with :contains or :matches. If this occurs, sieve issues an appropriate error message. For example, the statement:

if header :matches :comparator "i;ascii-numeric"

would result in the following error message:

comparator `i;ascii-numeric' is incompatible with match type `:matches'
in call to `header'

GNU Sieve supports two kinds of tests. Built-in tests are defined within the library and do not require any external files. External tests are loadable modules that can be linked in at run time using the require statement (see Require Statement).

5.6.1 Built-in Tests

Test: false

This test always evaluates to “false”.

Test: true

This test always evaluates to “true”.

Test: address [address-part] [comparator] [match-type] header-names key-list

Tagged arguments:

address-part

Selects the address part to compare. Default is the whole email address (:all).

comparator

Specifies the comparator to be used instead of the default i;ascii-casemap.

match-type

Specifies the match type to be used instead of the default :is.

Required arguments:

header-names

A list of header names.

key-list

A list of address values.

The address test matches Internet addresses in structured headers that contain addresses. It returns true if any header contains any key in the specified part of the address, as modified by comparator and match-type optional arguments.

This test returns true if any combination of the header-names and key-list arguments match.

The address primitive never acts on the phrase part of an email address, nor on comments within that address. Use the header test instead. It also never acts on group names, although it does act on the addresses within the group construct.

Example:

if address :is :all "from" "tim@example.com"
  {
     discard;
  } 
Test: size [:over | :under] limit(number)

The size test deals with the size of a message. The required argument limit represents the size of the message in bytes. It may be suffixed with the following quantifiers:

k
K

The number is expressed in kilobytes.

m
M

The number is expressed in megabytes.

g
G

The number is expressed in gigabytes.

If the tagged argument is ‘:over’, and the size of the message is greater than number, the test is true; otherwise, it is false.

If the argument is ‘:under’, and the size of the message is less than the number, the test is true; otherwise, it is false.

Otherwise, the test is true only if the size of the message equals exactly number. This is a GNU extension.

The size of a message is defined to be the number of octets from the initial header until the last character in the message body.

Test: envelope [address-part] [comparator] [match-type] envelope-part(string-list) key-list(string-list)

Tagged arguments:

address-part

Selects the address part to compare. Default is the whole email address (:all).

comparator

Specifies the comparator to be used instead of the default i;ascii-casemap.

match-type

Specifies the match type to be used instead of the default :is.

Required arguments:

envelope-parts

A list of envelope parts to operate upon.

key-list

A list of address values.

The envelope test is true if the specified part of the SMTP envelope matches the specified key.

If the envelope-part strings is (case insensitive) ‘from’, then matching occurs against the FROM address used in the SMTP MAIL command.

Notice, that due to the limitations imposed by SMTP envelope structure the use of any other values in envelope-parts header is meaningless.

Test: exists header-names(string-list)

Required arguments:

header-names

List of message header names.


The exists test is true if the headers listed in header-names argument exist within the message. All of the headers must exist or the test is false.

The following example throws out mail that doesn’t have a From header and a Date header:

if not exists ["From","Date"]
  {
     discard;
  }
Test: header [comparator] [match-type] [:mime] header-names(string-list) key-list(string-list)

Tagged arguments:

comparator

Specifies the comparator to be used instead of the default i;ascii-casemap.

match-type

Specifies the match type to be used instead of the default :is.

:mime

This tag instructs header to search through the mime headers in multipart messages as well.


Required arguments:

header-names

A list of header names.

key-list

A list of header values.


The header test evaluates to true if any header name matches any key. The type of match is specified by the optional match argument, which defaults to ":is" if not explicitly given.

The test returns true if any combination of the header-names and key-list arguments match.

If a header listed in header-names exists, it contains the null key (‘""’). However, if the named header is not present, it does not contain the null key. So if a message contained the header

X-Caffeine: C8H10N4O2

these tests on that header evaluate as follows:

header :is ["X-Caffeine"] [""] ⇒ false
header :contains ["X-Caffeine"] [""] ⇒ true

5.6.2 External Tests

Test: numaddr [:over | :under] header-names(string-list) count(number)


Synopsis:

require "test-numaddr";
…
if numaddr args
  {
    …
  }

Description: This test is provided as an example of loadable extension tests. You must use ‘require "test-numaddr"’ statement before actually using it.

The numaddr test counts Internet addresses in structured headers that contain addresses. It returns true if the total number of addresses satisfies the requested relation.

If the tagged argument is ‘:over’ and the number of addresses is greater than count, the test is true; otherwise, it is false.

If the tagged argument is ‘:under’ and the number of addresses is less than count, the test is true; otherwise, it is false.

If the tagged argument is not given, ‘:over’ is assumed.

Test: pipe [:envelope] [:header] [:body] [:exit code(number)] [:signal code(number)] command(string)

Synopsis:

require "test-pipe";

if pipe command
  {
    …
  }

Description: The pipe test executes a shell command specified by its argument and pipes the entire message (including envelope) to its standard input. When given, tags :envelope, :header, and :body control what parts of the message to pipe to the command.

In the absence of the :exit tag, the test returns true if the command exits with code 0. If :exit is given, the test returns true if the command exits with code equal to its argument.

The :signal tag determines the result of the test in case if the program exits on signal. By default, the test returns false. If :signal is given and the number of signal which caused the program to terminate matches its argument, the test returns true.

Test: spamd [:host tcp-host(string)] [:port tcp-port(number)] [:socket unix-socket(string)] [:user name(string)] [:over | :under limit(string)]

Synopsis:

require "test-spamd";
…
if spamd args
  {
    # This is spam
    …
  }

Description: This test is an interface to SpamAssassin filter. It connects to the spamd daemon using connection parameters specified by tagged arguments :host and :port (if the daemon is listening on an INET socket), or :socket (if the daemon is listening on a UNIX socket) and returns true, if SpamAssassin qualifies the message as spam. Tagged argument limit alters the default behavior. Its value is a string representation of a floating point number. If the tag :over is used, then the test returns true if the spam score returned from SpamAssassin is greater than limit. Otherwise, if :under is used, the test returns true if the spam score is less than limit. The comparison takes into account three decimal digits.

Tagged argument :user allows to select a specific user profile. If it is not given, the user name is determined using the effective UID.

Before returning, the spamd test adds the following headers to the message:

X-Spamd-Status

YES’ or ‘NO’, depending on whether the message is qualified as spam or ham.

X-Spamd-Score

Actual spam score value.

X-Spamd-Threshold

Spam score threshold, as configured in SpamAssassin settings.

X-Spamd-Keywords

Comma-separated list of keywords, describing the spam checks that succeeded for this message.

Example:

request "test-spamd";

if spamd :host 127.0.0.1 :port 3333
  {
     discard;
  }
Test: list [comparator] [match-type] [ :delim delimiters(string) ] headers(string-list) keys(string-list)

Synopsis:

require "test-list";
if list args
  {
     … 
  }

Description: The list test evaluates to true if any of headers matches any key from keys. Each header is regarded as containing a list of keywords. By default, comma is assumed as list separator. This can be overridden by specifying the :delim tag, whose value is a string consisting of valid list delimiter characters.

Example:

This test can be used in conjunction with the spamd test described above:

require ["fileinto", "test-spamd", "test-list"];

if spamd :host 127.0.0.1 :port 3333
  {
     if list :matches :delim " ,"
             "X-Spamd-Keywords" [ "HTML_*", "FORGED_*" ]
       {      
          fileinto "~/mail/spam";
       }
     else
       {
          discard;
       }
  }
Test: timestamp [:before | :after] header(string) date(string)

Synopsis:

require "test-timestamp";

if timestamp arg
  {
     …
  }

Description: The timestamp test compares the value of a structured date header field (header) with the given date (date).

If the tagged argument is :after and the date from the header is after the specified date the result is true, otherwise, if the header date is before the given date, the result is false.

If the tagged argument is :before and the date from the header is before the specified date the result is true, otherwise, if the header date is after the given date, the result is false.

If no tagged argument is supplied, :after is assumed.

Almost any date format is understood. See Date Input Formats, for a detailed information on date formats.

Example:

The test below succeeds if the date in ‘X-Expire-Timestamp’ header is more than 5 days older than the current date:

require "test-timestamp";

if timestamp :before "X-Expire-Timestamp" "now - 5 days"
  {
     discard;
  }

5.7 Actions

There are two groups of GNU Sieve actions: built-in actions, which are defined within the library, and external actions, i.e. loadable modules that can be linked in at run time using the require statement (see Require Statement).

5.7.1 Built-in Actions

The GNU libmu_sieve supports the following built-in actions:

Among them the first three actions do not need to be explicitly required by a require statement, while the others do.

These actions are described in detail below.

Action: stop

The stop action ends all processing. If no actions have been executed, then the keep action is taken.

Action: keep

The effect of this action is to preserve the current message in the mailbox. This action is executed if no other action has been executed.

Action: discard

Discard silently throws away the current message. No notification is returned to the sender, the message is deleted from the mailbox.

Example:

if header :contains ["from"] ["idiot@example.edu"]
  {
    discard;
  }
Action: fileinto [:permissions mode] folder

Required arguments:

folder

A string representing the folder name

Tagged arguments:

:permissions mode

Specifies the permissions to use, if the mailbox is created.

The fileinto action delivers the message into the specified folder. If the folder is local, it is created using permissions ‘0600’, for regular files, and ‘0700’ for directories. This default can be changed by using the :permissions tag. Its argument is a mode specification, similar to that used by chmod shell utility. It is a list of permissions settings separated by commas. Each setting begins with one of the following letters:

g

Set permissions for the users in the file group.

o

Set permissions for users not in the file’s group.

This letter must be followed by either ‘+’ or ‘=’ and the list of permissions to be set. This latter list is a string containing any one or both of the following characters:

r

Grant permission to read.

w

Grant permission to write.

For example, the following instruction creates the mailbox ~/shared which will be world readable and writable for the group:

  fileinto :permissions "g=rw,o=r" "~/shared"

Notice that:

  1. The :permissions setting are affected by the current umask value.
  2. Only r and w permissions can be set, since other permissions do not seem to be useful for mailboxes. However, for mailboxes that have a directory structure (such as maildir and MH), any settings in ‘g’ and ‘o’ sets imply setting the executable bit.
  3. Owner’s permissions cannot be set. The owner always has all permissions on the mailbox he created.
  4. The :permissions settings apply only to local mailboxes. They are ignored for remote mailboxes.
Action: reject reason

The optional reject action refuses delivery of a message by sending back a message delivery notification to the sender. It resends the message to the sender, wrapping it in a “reject” form, noting that it was rejected by the recipient. The required argument reason is a string specifying the reason for rejecting the message.

Example:

If the message contained

Date: Tue, 1 Apr 1997 09:06:31 -0800 (PST)
From: coyote@desert.example.org
To: roadrunner@acme.example.com
Subject: I have a present for you

I've got some great birdseed over here at my place.
Want to buy it?

and the user’s script contained:

if header :contains "from" "coyote@desert.example.org"
  {
    reject "I am not taking mail from you, and I don't want
            your birdseed, either!";
  }

then the original sender <coyote@desert.example.org> would receive the following notification:

To: <coyote@desert.example.org>
X-Authentication-Warning: roadrunner set sender using -f flag
Content-Type: multipart/mixed; boundary=----- =_aaaaaaaaaa0
MIME-Version: 1.0
----- =_aaaaaaaaaa0
The original message was received at
Tue, 1 Apr 1997 09:07:15 -0800 from
coyote@desert.example.org.
Message was refused by recipient's mail filtering program.
Reason given was as follows:

I am not taking mail from you, and I don't want your
birdseed, either!

----- =_aaaaaaaaaa0
Content-Type: message/delivery-status

Reporting-UA: sieve; GNU Mailutils 0.1.3
Arrival-Date: Tue, 1 Apr 1997 09:07:15 -0800
Final-Recipient: RFC822; roadrunner@acme.example.com
Action: deleted
Disposition: automatic-action/MDN-sent-automatically;deleted
Last-Attempt-Date: Tue, 1 Apr 1997 09:07:15 -0800

----- =_aaaaaaaaaa0
Content-Type: message/rfc822

From: coyote@desert.example.org
To: roadrunner@acme.example.com
Subject: I have a present for you

I've got some great birdseed over here at my place.
Want to buy it?
----- =_aaaaaaaaaa0

If the reason argument is rather long, the common approach is to use the combination of the text: and #include keywords, e.g.:

if header :mime :matches "Content-Type"
          [ "*application/msword;*", "*audio/x-midi*" ]
  {
    reject text:
#include "nomsword.txt"
    .
    ;
  }
Action: redirect address

The redirect action is used to send the message to another user at a supplied address, as a mail forwarding feature does. This action makes no changes to the message body or existing headers, but it may add new headers. It also modifies the envelope recipient.

The redirect command performs an MTA-style “forward” — that is, what you get from a .forward file using sendmail under UNIX. The address on the SMTP envelope is replaced with the one on the redirect command and the message is sent back out. Notice, that it differs from the MUA-style forward, which creates a new message with a different sender and message ID, wrapping the old message in a new one.

5.7.2 External Actions

GNU Mailutils is shipped with a set of external Sieve actions. These actions are compiled as loadable modules and must be required prior to use (see Require Statement).

Action: moderator [:keep] [:address address(string)] [:source sieve-file(string)] [:program sieve-text(string)]

Synopsis:

require "moderator"
moderator args;

Description: This action is a moderator robot for Mailman-driven mail archives. A Mailman moderation request is a MIME message consisting of the following three parts:

NContent-TypeDescription
1text/plainIntroduction for the human reader.
2message/rfc822Original submission.
3message/rfc822Mailman control message.

Replying to part 3 (keeping the subject intact) instructs Mailman to discard the original submission.

Replying to part 3 while adding an ‘Approved:’ header with the list password in it approves the submission.

The moderator action spawns an inferior Sieve machine and filters the original submission (part 2) through it. If the inferior machine marks the message as deleted, the action replies to the control message, thereby causing the submission to be discarded. The ‘From:’ address of the reply can be modified using :address tag. After discarding the message, moderator marks it as deleted, unless it is given :keep tag.

If the :source tag is given, its argument specifies a Sieve source file to be used on the message. Otherwise, if :program is given, its argument supplies a Sieve program to be used on this message. At most one of these tags may be specified. Supplying them both, or supplying several instances of the same tag, is an error. The behavior of the action in this case is undefined.

If neither :program nor :source is given, moderator will create a copy of the existing Sieve machine and use it on the message.

The action checks the message structure: it will bail out if the message does not have exactly 3 MIME parts, or if parts 2 and 3 are not of ‘message/rfc822’ type. It is the responsibility of the caller to make sure the message is actually a valid Mailman moderation request (see the example below).


Example:

if allof(header :is "Sender" "mailman-bounces@gnu.org",
         header :is "X-List-Administrivia" "yes")
  {
     moderator :source "~/.sieve/mailman.sv";
  }
Action: pipe [:envelope] [:header] [:body] command(string)

Synopsis:

require "pipe";

pipe command

Description: The pipe action executes a shell command specified by its argument and pipes the entire message (including envelope) to its standard input. When given, tags :envelope, :header, and :body control what parts of the message to pipe to the command.


Example: The example below uses the putmail utility (see putmail) to forward the message to user ‘gray’ on the machine ‘mail.gnu.org’.

require "pipe";

pipe "/usr/bin/putmail smtp://gray@mail.gnu.org"
Action: vacation [:days ndays(number)] [:subject subject(string)] [:aliases addrlist(string-list)] [:noreply noreply-address(string-list)] [:reply_regex expr(string)] [:reply_prefix prefix(string)] [:sender email(string)] [:database path(string)] [:return_address email(string)] [:header headers(string-list)] [:mime] [:always_reply] [:rfc2822] [:file] text(string)

Syntax:

require "vacation";
vacation args;

Description: The vacation action returns a message with text to the sender. It is intended to inform the sender that the recipient is not currently reading his mail.

If the :file tag is present, text is treated as the name of the file to read the body of the reply message from. When used together with tag :rfc2822, the file should be formatted as a valid RFC 2822 message, i.e. headers followed by empty line and body. Headers may not contain ‘To’, ‘From’, and ‘Subject’, as these will be generated automatically.

If the :subject tag is given, its argument sets the subject of the message. Otherwise, the subject is formed by prefixing original subject with ‘Re:’, or the prefix given with the :reply_prefix tag. Before prefixing, any original prefixes matching extended regular expression expr (:reply_regex tag) are stripped from the subject line. If :reply_regex is not specified, the default regexp is ‘^re: *’.

Another headers can be added using the :header tag. Its argument is a list of header strings, each one having the form ‘"name:value"’. Additional whitespace is allowed on both sides of the colon.

The :aliases tag instructs vacation to handle messages for any address in addrlist in the same manner as those received for the user’s principal email.

Before processing, vacation compares the sender address with its address exclusion list. Elements of this list are extended case-insensitive regular expressions. If the sender address matches any of these expressions, the message will not be replied. The default exclusion list is:

    .*-REQUEST@.*
    .*-RELAY@.*
    .*-OWNER@.*
    ^OWNER-.*
    ^postmaster@.*
    ^UUCP@.*
    ^MAILER@.*
    ^MAILER-DAEMON@.*

New entries can be added to this list using :noreply tag.

The :days tag sets the reply interval. A reply is sent to each sender once in ndays days. GNU Sieve keeps track of sender addresses and dates in file .vacation stored in the user’s home directory. The file name can be changed using the :database tag.

The tag :always_reply instructs vacation to respond to the message regardless of whether the user email is listed as a recipient for the message.

5.8 Extensions

The following extensions are implemented

5.8.1 The encoded-character extension

The ‘encoded-character’ extension complies with RFC 5228, part 2.4.2.4. It provides a way of incorporating multibyte sequences in a Sieve script using only ASCII characters. This is a built-in extension. It is enabled using the following statement:

require "encoded-character";

When this extension is enabled, the sequences ‘${hex: ...}’, and ‘${unicode: ...}’ can appear inside of quoted strings.

The sequence

${hex: XX}

where XX is a sequence of one or two-digit hex numbers separated by any amount of whitespace, is replaced with the octets with the hexadecimal values given by each hex number. For example,

"${hex: 24 24}" ⇒ "$$"

Thus, the following script will discard any message containing three contiguous dollar signs in its ‘Subject’ header:

require "encoded-character";

if header :contains "Subject" "$${hex:24 24}" {
     discard;
}

The ‘hex:’ keyword is case-insensitive. If XX contains invalid hex numbers, the entire sequence is left verbatim. This is illustrated by the following example:

"$${hex:40}"         ⇒ "$@"
"${hex: 40 }"        ⇒ "@"
"${HEX: 40}"         ⇒ "@"
"${hex:40"             ⇒ "${hex:40"
"${hex:400}"         ⇒ "${hex:400}"
"${hex:4${hex:30}}"  ⇒ "${hex:40}"

The sequence

${unicode: HEXNUM}

where HEXNUM is a list of hexadecimal numbers separated with whitespace, will be replaced by the UTF-8 encoding of the specified Unicode characters, which are identified by the hexadecimal value of HEXNUM. For example, the following string represents a single ‘@’ sign:

"${UNICODE:40}"

Similarly to ‘hex:’, the ‘unicode:’ indicator is case insensitive. The following examples demonstrate the handling of several valid and invalid encodings:

"${unicode:40}"      ⇒ "@"
"${ unicode:40}"     ⇒ "${ unicode:40}"
"${UNICODE:40}"      ⇒ "@"
"${UnICoDE:0000040}" ⇒ "@"
"${Unicode:40}"      ⇒ "@"
"${Unicode:Cool}"    ⇒ "${Unicode:Cool}"
"${unicode:200000}"  ⇒ error
"${Unicode:DF01}     ⇒ error

5.8.2 The relational extension

The ‘relational’ extension complies with RFC 3431. It is a built-in extension. When enabled, the two new match types become available: :count and :value. Both keywords take a single argument defining the relational operator to use:

"gt"greater than (‘>’)
"ge"greater than or equal (‘>=’)
"lt"less than (‘<’)
"le"less than or equal (‘<=’)
"eq"equal to (‘==’)
"ne"not equal to (‘!=’)

The :value keyword requires a relational comparison between strings. The left side of the relation is formed by the value from the message. The right side of the relation is the value from the test expression. If there are multiple values on either side or both sides, the test is considered true if any pair is true. For example,

require ["relational", "fileinto"];

if header :value "gt" :comparator "i;ascii-numeric"
                ["x-spam-level] ["5"]
{
  fileinto "spam";
}  

The :count keyword counts the specified entities in the message and compares their number with the value given in the test expression. The latter must be a list of one element. This match type can only be used with numeric comparators. For example, the following script will discard any message with 10 or more recipient addresses in the ‘To’ and ‘Cc’ headers:

require "relational";

if address :count "ge" :comparator "i;ascii-numeric"
                      ["to", "cc"] ["10"]
{
    discard;
}

5.8.3 The variables extension

The ‘variables’ extension is defined in RFC 5229. It is a built-in extension. It introduces support for variables in Sieve scripts.

There are two kind of variables: user-defined and match variables.

A user-defined variable is initialized using the set action:

Action: set [modifiers] name(string) value(string)

Stores the specified value in the variable identified by name. Optional modifiers are applied on value before it is stored in the variable.

The following modifiers are available:

:lower

Convert value to lower case letters.

:upper

Convert value to upper case letters.

:lowerfirst

Convert the first character in value to lower case.

:upperfirst

Convert the first character in value to upper case.

:quotewildcard

Quote wildcard characters (‘*’, ‘?’, ‘\’) by prefixing each occurrence with a backslash (‘\’). This can be used to ensure that the variable will only match a literal occurrence if used as a parameter to :matches.

:length

The value is the decimal number of characters in the expansion, converted to a string.

When several modifiers are present, they are applied in the following order of precedence (largest value first):

precedencemodifiers
40:lower or :upper
30:lowerfirst or :upperfirst
20:quotewildcard
10:length

Modifiers having the same precedence (i.e. listed on the same row in the above table) cannot be used together.

Variables are referenced within text strings using the construct ‘${name}’, where name is the name of the variable as it appeared in the first parameter to the set statement. For example:

require "variables";

set "sender" "root
":

if envelope :matches "${sender}"
{
   ...
}

Match variables refer to parts of the most recently evaluated successful match of type :matches or :regex. They have names consisting entirely of decimal digits. The variable ‘${0}’ refers to the entire matched expression. The variable ‘${1}’ refers to the substring matching the first occurrence of the wildcard (‘?’ and ‘*’), ‘${2}’ refers to the second occurrence and so on. The wildcards match as little as possible (non-greedy matching). For example:

require ["variables", "fileinto"];

if header :matches "List-ID" "*<*
" {
   fileinto "INBOX.lists.${2}";
   stop;
}

If :regex match is used, the match variables starting from ‘${1}’ refer to the substrings of the argument value matching subsequent parenthesized groups of the regular expression.

Test: string [comparator] [match-type] source(string-list) keys(string-list)

The string test compares two strings according to the selected comparator and match type. The test evaluates to ‘true’ if any two strings from source and keys match.

The ‘:count’ match used in ‘string’ counts each empty string as 0, and each non-empty one as 1. The count of a string list is the sum of the counts of the member strings.

5.8.4 environment

The ‘environment’ extension complies with RFC 5183. It is a built-in extension. It introduces the following test:

Test: environment [comparator] [match-type] name(string) keys(string-list)

The environment test evaluates to ‘true’ if the value of the environment items name matches any string from keys.

The following environment items are defined:

domain

The primary DNS domain of the machine where the Sieve script is executing.

host

The fully-qualified domain name of the host where the Sieve script is executing.

location

Type of service that is evaluating the script. Depending on the utility that is evaluating the script it is:

UtilityLocation
sieve"MUA"’, or set with the --environment option.
maidag"MDA"
inc"MUA"
name

The string ‘GNU Mailutils

phase

The point relative to final delivery where the Sieve script is being evaluated. Depending on the utility that is evaluating the script it is:

UtilityLocation
sievepost’ unless set with the --environment option.
maidag"during"
inc"post"
version

Mailutils version string (e.g. ‘3.14’).

5.8.5 The numaddr extension

This is an example loadable extension. numaddr.

5.8.6 The editheader extension

The editheader extension complies with RFC 5293. It provides the following actions:

Action: addheader [:last] field-name(string) value(string)

Adds a header field to the existing message header. By default the header is inserted at the beginning of the header list. If the tag :last is specified, it is appended at the end.

Action: deleteheader" [:index fieldno(number) :last] [comparator] [match-type] field-name(string) [value-patterns(string-list)]

Deletes occurrences of the header field matching the criteria.

The value-patterns, if specified, determines which occurrences of the header fielde to delete. If not supplied, comparator and match-type are silently ignored.

If ‘:index fieldno’ is specified, only the numbered occurrence of the named header field will be matched (header numbering begins at 1), If :last is specified, the count is backwards; 1 denotes the last named header field, 2 the second to last, and so on. The counting happens before the value-patterns match, if any. Thus, e.g. the action

deleteheader :index 1 :contains "Delivered-To" "bob@example.com";

would delete the first ‘Delivered-To’ header field if it contains the string ‘bob@example.com’.

5.8.7 The list extension

list.

5.8.8 The moderator extension

A loadable extension implementing a moderator robot for Mailman-driven mail archives. moderator.

5.8.9 The pipe extension

A loadable extension for external command execution. It provides the pipe action (see pipe) and test (see pipe).

5.8.10 The spamd extension

Implements a test which interfaces to SpamAssassin filter. This is a loadable extension. see spamd.

5.8.11 The timestamp extension

The loadable extension timestamp implements a test for comparing the value of a structured date header field with the given date.

Note: this extension will probably phase away in favor of the date Sieve extension (RFC 5260).

5.8.12 The vacation extension

The loadable extension vacation provides the action intended to inform the sender that the recipient is not currently reading his mail.

See vacation.

5.9 GNU Extensions

This section summarizes the GNU extensions to the sieve language

  1. Multiline strings syntax

    GNU libmu_sieve understands the following multiline string syntax:

    text:[-][delimiter]
    ....
    delimiter
    

    The meaning of optional flags is the same as in shell “here document” construct: the dash strips all leading tab characters from the string body, thus allowing it to be indented in a natural fashion; delimiter introduces the new end-of-text delimiter instead of the default dot. If delimiter starts with a backslash, no preprocessing will be performed within a string.

  2. Handling of the require statement.
  3. header test

    The header takes an optional argument :mime, meaning to scan the headers from each part of a multipart message.

  4. size test

    The size test allows to omit the optional argument (:over|:under). In this case exact equality is assumed.

  5. envelope test

    The only value that can be meaningfully used as the first required argument of an envelope test is ‘from’. This limitation may disappear from the subsequent releases.

  6. fileinto action

    The fileinto action allows to specify permissions on the mailbox, in case it will be created (see fileinto).

  7. Match type optional argument.

    Along with the usual :is, :matches and :contains matching type, GNU sieve library understands :regex type. This matching type toggles POSIX Extended Regular Expression matching.

GNU Mailutils Manual (split by chapter):   Section:   Chapter:FastBack: Sieve Language   Up: Sieve Language   FastForward: Reporting Bugs   Contents: Table of ContentsIndex: Function Index