E-mailing with Zeta

Main Menu

Installation

System

Preferences

Applications

Demos

Advanced

Related Topics
Beam BeMail BeMail - signature BeMail - writing an e-mail E-mail - attachments

Info
Mail User Agent: The mail program used by the end-user computer to create and read e-mail messages. The MUA passes the message to the local MTA (Mail Transfer Agent), see RFC 1711.

Info
Mail Transfer Agent: The application that uses SMTP to move an e-mail message to another MTA until message reaches the addressee's computer, where the message is delivered.

Alert
Rejecting a mail containing SPAM, tells the sender (spammer) that your e-mail address is active, and will most like mean that he will send you more spam and also sell your e-mail address to other spammers. You should concider using the discard syntax instead.

E-mailing with Zeta

Sieve is a language for filtering e-mail messages at time of final delivery. It is designed to be implementable on either a mail client or mail server, and can on Zeta be used with the e-mail client Beam.

The language has been made simple enough to allow many users to make use of it, rich enough that it can be used productively, but also limited in power in order to allow for a safe server-side filtering system. The intention is to make it impossible for users to do anything more complex (and dangerous) than write simple mail filters. Because of the expectation that users will make use of filtering if it is offered and easy to use, this language.

Scripts written in Sieve are executed during final delivery, when the message is moved to the user-accessible mailbox. Sieve filters might be used at a number of "final delivery" points in a mail system: by an SMTP server, by an IMAP or POP server filing into one or more mailboxes, or by a Mail User Agent (MUA or mail client) acting as a delivery agent (for instance, a POP or offline IMAP client.)

There are a number of reasons to use a filtering system. Mail traffic for most users has been increasing due both to increased usage of e-mail, the emergence of unsolicited email as a form of advertising, and increased usage of mailing lists.

The language consists of a set of commands. Each command consists of a set of tokens delimited by whitespace. The command identifier is the first token and it is followed by zero or more argument tokens. Arguments may be literal data, tags, blocks of commands, or test commands.

Two types of comments are offered:

1. Hash comments begin with a "#" character that is not contained within a string and continue until the next CRLF.

   Example: | if size :over 100K { # this is a comment
   | discard;
   }

2. Bracketed comments begin with the token "/*" and end with "*/" outside of a string. Bracketed comments may span multiple lines.

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

Scripts involve large numbers of strings as they are used for pattern matching, addresses, textual bodies, etc. Typically, short quoted strings suffice for most uses, but a more convenient form is provided for longer strings such as bodies of messages.

Match Types

There are three match types describing the matching used in this specification: ":is", ":contains", and ":matches".

Match type arguments are supplied to those commands which allow them to specify what kind of match is to be performed. These are used as tagged arguments to tests that perform string comparison.

The ":contains" match type describes a substring match. If the value argument contains the key argument as a substring, the match is true.

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.

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 "*".

All implementations must support the "i;octet" comparator (simply compares octets) and the "i;ascii-casemap" comparator (which treats uppercase and lowercase characters in the ASCII subset of UTF-8 as the same). If left unspecified, the default is "i;ascii-casemap". Some comparators may not be usable with substring matches; that is, they may only work with ":is". It is an error to try and use a comparator with ":matches" or ":contains" that is not compatible with it.

Example: | if header :contains :comparator "i;octet" "Subject"
| "MAKE MONEY FAST" {
| discard;
}

would discard any message with subjects like "You can MAKE MONEY FAST", but not "You can Make Money Fast", since the comparator used is case-sensitive.

Both ":matches" and ":contains" match types are compatible with the "i;octet" and "i;ascii-casemap" comparators and may be used with them.

Comparisons Against Addresses

Addresses are one of the most frequent things represented as strings. These are structured, and being able to compare against the local- part or the domain of an address is useful, so some tests that act exclusively on addresses take an additional optional argument that specifies what the test acts on.

These optional arguments are ":localpart", ":domain", and ":all", which act on the local-part (left-side), the domain part (right- side), and the whole address.

The semantics are similar to those of any of the many other programming languages these control commands appear in. When the interpreter sees an "if", it evaluates the test associated with it. If the test is true, it executes the block associated with it. If the test of the "if" is false, it evaluates the test of the first "elsif" (if any). If the test of "elsif" is true, it runs the elsif's block. An elsif may be followed by an elsif, in which case, the interpreter repeats this process until it runs out of elsifs. When the interpreter runs out of elsifs, there may be an "else" case. If there is, and none of the if or elsif tests were true, the interpreter runs the else case. This provides a way of performing exactly one of the blocks in the chain. In the following example, both Message A and B are dropped.

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

When the script below is run over message A, it redirects the message to acm@example.edu; message B, to postmaster@example.edu; any other message is redirected to field@example.edu.

Example: if header :contains ["From"] ["coyote"] { redirect "acm@example.edu"; } elsif header :contains "Subject" "$$$" { redirect "postmaster@example.edu"; } else { redirect "field@example.edu"; }

Control Structure Stop

Syntax: stop

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

Action Commands

This document supplies five actions that may be taken on a message: keep, fileinto, redirect, reject, and discard.

Implementations MUST support the "keep", "discard", and "redirect" actions. Implementations SHOULD support "reject" and "fileinto".

Action reject

Syntax: reject

The optional "reject" action refuses delivery of a message by sending back an [MDN] to the sender. It resends the message to the sender, wrapping it in a "reject" form, noting that it was rejected by the recipient. In the following script, message A is rejected and returned to the sender.

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

A reject message MUST take the form of a failure MDN as specified by [MDN]. The human-readable portion of the message, the first component of the MDN, contains the human readable message describing the error, and it SHOULD contain additional text alerting the original sender that mail was refused by a filter. This part of the MDN might appear as follows:

------------------------------------------------------------
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!
------------------------------------------------------------

The MDN action-value field as defined in the MDN specification MUST be "deleted" and MUST have the MDN-sent-automatically and automatic- action modes set.

Because some implementations can not or will not implement the reject command, it is optional. The capability string to be used with the require command is "reject".

Action fileinto

Syntax: fileinto

The "fileinto" action delivers the message into the specified folder. Implementations SHOULD support fileinto, but in some environments this may be impossible.
In the following script, message A is filed into folder "INBOX.harassment".

Example: require "fileinto"; if header :contains ["from"] "coyote" { fileinto "INBOX.harassment"; }

Action redirect

Syntax: redirect

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

Example: redirect "bart@example.edu";

Action keep

Syntax: keep

The "keep" action is whatever action is taken in lieu of all other actions, if no filtering happens at all; generally, this simply means to file the message into the user's main mailbox. This command provides a way to execute this action without needing to know the name of the user's main mailbox, providing a way to call it without needing to understand the user's setup, or the underlying mail system.

Example: if size :under 1M { keep; } else { discard; }

Action discard Syntax: discard Discard is used to silently throw away the message. It does so by simply canceling the implicit keep. If discard is used with other actions, the other actions still happen. Discard is compatible with all other actions. (For instance fileinto+discard is equivalent to fileinto.)

Example: if header :contains ["from"] ["idiot@example.edu"] { discard; }

Test Commands

Tests are used in conditionals to decide which part(s) of the conditional to execute.
Implementations must support these tests: "address", "allof", "anyof", "exists", "false", "header", "not", "size" and "true".
Implementations should support the "envelope" test.

Test envelope

Syntax: envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE]

The "envelope" test is true if the specified part of the SMTP (or equivalent) envelope matches the specified key.

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

If one of the envelope-part strings is (case insensitive) "to", then matching occurs against the TO address used in the SMTP RCPT command that resulted in this message getting delivered to this user.

Note that only the most recent TO is available, and only the one relevant to this user. The envelope-part is a string list and may contain more than one parameter, in which case all of the strings specified in the key-list are matched against all parts given in the envelope-part list.

Like address and header, this test returns true if any combination of the envelope-part and key-list arguments is true.

All tests against envelopes MUST drop source routes.

If the SMTP transaction involved several RCPT commands, only the data from the RCPT command that caused delivery to this user is available in the "to" part of the envelope.

If a protocol other than SMTP is used for message transport, implementations are expected to adapt this command appropriately.

The envelope command is optional. Implementations SHOULD support it, but the necessary information may not be available in all cases.
Example: require "envelope"; if envelope :all :is "from" "tim@example.com" { discard;

Test exists

Syntax: exists

The "exists" test is true if the headers listed in the 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.

Example: if not exists ["From","Date"] { discard; }

Test false

Syntax: false

The "false" test always evaluates to false.

Two bigger examples on how to write filters with sieve

# Handle messages from known mailing lists # Move messages from IETF filter discussion list to filter folder # if header :is "Sender" "owner-ietf-mta-filters@imc.org" { fileinto "filter"; # move to "filter" folder } # # Keep all messages to or from people in my company # elsif address :domain :is ["From", "To"] "example.com" { keep; # keep in "In" folder }

# Try and catch unsolicited email. If a message is not to me, # or it contains a subject known to be spam, file it away. # elsif anyof (not address :all :contains ["To", "Cc", "Bcc"] "me@example.com", header :matches "subject" ["*make*money*fast*", "*university*dipl*mas*"]) { # If message header does not contain my address, # it's from a list. fileinto "spam"; # move to "spam" folder } else { # Move all other (non-company) mail to "personal" # folder. fileinto "personal"; }

©2003-2004 by yellowTAB GmbH