RFC 3028

Sieve: A Mail Filtering Language

๐Ÿ“… published January 2001, from ietf

RFC 3028: Sieve: A Mail Filtering Language ๐Ÿ’Œ

Who wrote it: T. Showalter from Mirapoint, Inc. ๐Ÿงš Category: Standards Track โœจ (the Internet community is invited to discuss it and suggest improvements) Date: January 2001 ๐Ÿ“… Obsoletes / Updates: This document doesn't list any RFCs it obsoletes or updates in its header. ๐ŸŽ€


Status of this Memo ๐Ÿ“‹

This document is an Internet standards track protocol for the Internet community, and it asks for discussion and suggestions for improvements. For the current standardization state and status of this protocol, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited. ๐ŸŒธ

Copyright (C) The Internet Society (2001). All Rights Reserved. ๐Ÿ’œ

Abstract ๐Ÿฆ‹

Okay so this doc describes a language for filtering e-mail messages right at final delivery. It's designed to work on either a mail client or a mail server, and it's meant to be extensible, simple, and independent of access protocol, mail architecture, and operating system. It's especially good for mail servers where users aren't allowed to run arbitrary programs, like on black box Internet Message Access Protocol (IMAP) servers. Why? Because it has no variables, no loops, and no way to shell out to external programs. Safe and iconic ๐Ÿ’…

Table of Contents ๐Ÿ“š

   1.      Introduction ...........................................   3
   1.1.     Conventions Used in This Document .....................   4
   1.2.     Example mail messages .................................   4
   2.      Design .................................................   5
   2.1.     Form of the Language ..................................   5
   2.2.     Whitespace ............................................   5
   2.3.     Comments ..............................................   6
   2.4.     Literal Data ..........................................   6
   2.4.1.   Numbers ...............................................   6
   2.4.2.   Strings ...............................................   7
   2.4.2.1. String Lists ..........................................   7
   2.4.2.2. Headers ...............................................   8
   2.4.2.3. Addresses .............................................   8
   2.4.2.4. MIME Parts ............................................   9
   2.5.     Tests .................................................   9
   2.5.1.   Test Lists ............................................   9
   2.6.     Arguments .............................................   9
   2.6.1.   Positional Arguments ..................................   9
   2.6.2.   Tagged Arguments ......................................  10
   2.6.3.   Optional Arguments ....................................  10
   2.6.4.   Types of Arguments ....................................  10
   2.7.     String Comparison .....................................  11
   2.7.1.   Match Type ............................................  11
   2.7.2.   Comparisons Across Character Sets .....................  12
   2.7.3.   Comparators ...........................................  12
   2.7.4.   Comparisons Against Addresses .........................  13
   2.8.     Blocks ................................................  14
   2.9.     Commands ..............................................  14
   2.10.    Evaluation ............................................  15
   2.10.1.  Action Interaction ....................................  15
   2.10.2.  Implicit Keep .........................................  15
   2.10.3.  Message Uniqueness in a Mailbox .......................  15
   2.10.4.  Limits on Numbers of Actions ..........................  16
   2.10.5.  Extensions and Optional Features ......................  16
   2.10.6.  Errors ................................................  17
   2.10.7.  Limits on Execution ...................................  17
   3.      Control Commands .......................................  17
   3.1.     Control Structure If ..................................  18
   3.2.     Control Structure Require .............................  19
   3.3.     Control Structure Stop ................................  19
   4.      Action Commands ........................................  19
   4.1.     Action reject .........................................  20
   4.2.     Action fileinto .......................................  20
   4.3.     Action redirect .......................................  21
   4.4.     Action keep ...........................................  21
   4.5.     Action discard ........................................  22
   5.      Test Commands ..........................................  22
   5.1.     Test address ..........................................  23
   5.2.     Test allof ............................................  23
   5.3.     Test anyof ............................................  24
   5.4.     Test envelope .........................................  24
   5.5.     Test exists ...........................................  25
   5.6.     Test false ............................................  25
   5.7.     Test header ...........................................  25
   5.8.     Test not ..............................................  26
   5.9.     Test size .............................................  26
   5.10.    Test true .............................................  26
   6.      Extensibility ..........................................  26
   6.1.     Capability String .....................................  27
   6.2.     IANA Considerations ...................................  28
   6.2.1.   Template for Capability Registrations .................  28
   6.2.2.   Initial Capability Registrations ......................  28
   6.3.     Capability Transport ..................................  29
   7.      Transmission ...........................................  29
   8.      Parsing ................................................  30
   8.1.     Lexical Tokens ........................................  30
   8.2.     Grammar ...............................................  31
   9.      Extended Example .......................................  32
   10.     Security Considerations ................................  34
   11.     Acknowledgments ........................................  34
   12.     Author's Address .......................................  34
   13.     References .............................................  34
   14.     Full Copyright Statement ...............................  36

1. Introduction โœจ

This memo is all about a language you can use to make filters for electronic mail. It isn't tied to any particular operating system or mail architecture ๐ŸŒˆ. It does require [IMAIL]-compliant messages, but otherwise it should generalize to lots of systems.

The language is powerful enough to be useful, but it's intentionally limited so a server-side filtering system stays safe ๐Ÿ”’. The goal is that users literally can't do anything more complex (and dangerous) than write simple mail filters. It also makes it easier to use GUIs for creating and managing filters ๐Ÿซถ. Real talk: the language is not Turing-complete. There's no way to write a loop or a function, and there are no variables. Bestie, that's the vibe ๐Ÿ’€.

Sieve scripts run during final delivery, which is when the message gets moved into the user-accessible mailbox ๐Ÿ“ฌ. In systems where the MTA does the final delivery, like traditional Unix mail, it's totally reasonable to sort messages when the MTA drops them into the user's mailbox ๐Ÿ“.

There are a bunch of reasons people want a filtering system ๐Ÿ™ˆ. Mail traffic for most users has been climbing because more people are using e-mail, unsolicited email has become a form of advertising, and mailing lists are more popular than ever.

Carnegie Mellon's experience showed that when a filtering system is available, lots of people use it to file messages from specific users or mailing lists ๐ŸŽ€. But plenty of others didn't use the Andrew system's FLAMES filtering language [FLAMES] because it was hard to set up ๐Ÿ˜ญ.

Since people will totally use filtering if it's offered and easy to use, this language is simple enough for lots of users to pick up, but rich enough to be used productively ๐Ÿ’…. That said, GUI-based editors are expected to be the preferred way to edit filters for a large number of users ๐Ÿฆ‹.

1.1. Conventions Used in This Document ๐Ÿ“

In the sections that talk about the requirements of various keywords and operators, these conventions are used.

The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in this document are to be interpreted as defined in [KEYWORDS].

Each section on a command (test, action, or control structure) has a line labeled "Syntax:". This line describes the command's syntax, including its name and its arguments ๐Ÿงš. Required arguments go inside angle brackets ("<" and ">"). Optional arguments go inside square brackets ("[" and "]"). Each argument is followed by its type, so "<key: string>" means an argument called "key" that is a string. Literal strings are written as double-quoted strings. Alternatives are separated with slashes, and parentheses group things together, kind of like [ABNF] ๐ŸŒธ.

In the "Syntax" line there are three special pieces of syntax that keep coming up: MATCH-TYPE, COMPARATOR, and ADDRESS-PART. They're covered in sections 2.7.1, 2.7.3, and 2.7.4 respectively โœจ.

The formal grammar for these commands is in section 10 and it's the authoritative reference for how to build commands. But the formal grammar doesn't specify the order, semantics, number or types of arguments to commands, or the legal command names. That's on purpose, so the language can be extended without changing the grammar ๐Ÿ”ฅ.

1.2. Example mail messages ๐Ÿ’Œ

The following mail messages get used all through this document in examples ๐Ÿ“.

   Message A
   -----------------------------------------------------------
   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

   Look, I'm sorry about the whole anvil thing, and I really
   didn't mean to try and drop it on you from the top of the
   cliff.  I want to try to make it up to you.  I've got some
   great birdseed over here at my place--top of the line
   stuff--and if you come by, I'll have it all wrapped up
   for you.  I'm really sorry for all the problems I've caused
   for you over the years, but I know we can work this out.
   --
   Wile E. Coyote   "Super Genius"   coyote@desert.example.org
   -----------------------------------------------------------

   Message B
   -----------------------------------------------------------
   From: youcouldberich!@reply-by-postal-mail.invalid
   Sender: b1ff@de.res.example.com
   To: rube@landru.example.edu
   Date:  Mon, 31 Mar 1997 18:26:10 -0800
   Subject: $$$ YOU, TOO, CAN BE A MILLIONAIRE! $$$

   YOU MAY HAVE ALREADY WON TEN MILLION DOLLARS, BUT I DOUBT
   IT!  SO JUST POST THIS TO SIX HUNDRED NEWSGROUPS!  IT WILL
   GUARANTEE THAT YOU GET AT LEAST FIVE RESPONSES WITH MONEY!
   MONEY! MONEY! COLD HARD CASH!  YOU WILL RECEIVE OVER
   $20,000 IN LESS THAN TWO MONTHS!  AND IT'S LEGAL!!!!!!!!!
   !!!!!!!!!!!!!!!!!!111111111!!!!!!!11111111111!!1  JUST
   SEND $5 IN SMALL, UNMARKED BILLS TO THE ADDRESSES BELOW!
   -----------------------------------------------------------

2. Design ๐ŸŽจ

2.1. Form of the Language ๐ŸŒˆ

Okay so the whole language is built out of commands, and each command is just a bunch of tokens separated by whitespace โœจ. The first token is the command identifier, and it's followed by zero or more argument tokens ๐ŸŽ€. Arguments can be literal data, tags, blocks of commands, or test commands ๐Ÿ’….

The language is written in UTF-8, as specified in [UTF-8] ๐ŸŒˆ.

Tokens in the ASCII range are case-insensitive, so you can type them however you want ๐Ÿ’–.

2.2. Whitespace ๐Ÿซง

Whitespace is how we separate tokens ๐ŸŒธ. It's made up of tabs, newlines (CRLF, never just CR or LF ๐Ÿ™…โ€โ™€๏ธ), and the space character. How much whitespace you use doesn't matter at all, so go wild ๐Ÿฆ‹.

2.3. Comments ๐Ÿ’ฌ

There are two kinds of comments, and they're basically whitespace in disguise ๐Ÿฅน. You can put them anywhere whitespace goes, with one exception inside multi-line strings, which the grammar explains ๐Ÿงš.

Hash comments start with a # that isn't inside a string, and they keep going until the next CRLF ๐Ÿ“.

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

Bracketed comments start with the token /* and end with */ outside of a string ๐Ÿซถ. They can stretch across multiple lines, but they do not nest, so no comments-inside-comments drama ๐Ÿ™ˆ.

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

2.4. Literal Data ๐ŸŽ

Literal data is data that doesn't get executed. It just gets evaluated "as is" and handed over as an argument to a command ๐Ÿง. Literal data is limited to numbers and strings, and that's the whole list, bestie ๐Ÿ‘‘.

2.4.1. Numbers ๐Ÿ”ข

Numbers are plain old decimal numbers ๐Ÿผ. But the really big ones, like message sizes, MAY have a "K", "M", or "G" tacked on the end to mean a power-of-two multiple ๐ŸŒŸ. That's so they line up with the power-of-two SI-style units computers love to use ๐Ÿค“. K means kibi-, which is 1,024 (2^10) times the number. M means mebi-, which is 1,048,576 (2^20) times the number. G means tebi-, which is 1,073,741,824 (2^30) times the number [BINARY-SI] ๐Ÿงฎ.

Implementations MUST provide 31 bits of magnitude in numbers, but they MAY provide more ๐Ÿ’œ. Only positive integers are permitted by this spec, so no negatives in this house ๐Ÿšซ.

2.4.2. Strings ๐Ÿงถ

Scripts use LOTS of strings for pattern matching, addresses, bodies of text, and more ๐Ÿ’Œ. Short quoted strings do the job for most things, but there's a fancier form for longer strings like message bodies ๐Ÿ“œ.

A quoted string starts and ends with a single double quote (the " character, ASCII 34) ๐ŸŽ€. Inside a quoted string, a backslash (\, ASCII 92) has to be followed by either another backslash or a double quote. That two-character combo stands for one single backslash or one double quote inside the string, respectively ๐Ÿฆ‹.

No other characters should be escaped with a single backslash ๐Ÿ™…โ€โ™€๏ธ.

An undefined escape sequence (like \a where "a" doesn't have any special meaning) gets treated as if the backslash was never there. So \a is just "a" ๐Ÿซ .

Non-printing characters like tabs, CR, LF, and other control characters are allowed in quoted strings ๐ŸŒˆ. Quoted strings MAY span multiple lines ๐Ÿ’–. NUL (ASCII 0) is not allowed in strings, so leave it out ๐Ÿšซ.

When you need to enter a bigger chunk of text, like an email message, there's a multi-line form ๐Ÿ“จ. It starts with the keyword text:, then a CRLF. It ends with a CRLF, a single period, and another CRLF ๐Ÿ”š. To let the message contain lines that are just a single dot, lines get dot-stuffed. When you're composing a message body, you add an extra . in front of each line that starts with a . ๐Ÿช„. When the server interprets the script, those extra dots get removed โœจ. A line that starts with a dot followed by a non-dot character isn't dot-stuffed, so .foo is read as .foo ๐Ÿง. Since that's kind of ambiguous, scripts SHOULD be properly dot-stuffed so those lines never show up ๐Ÿซถ.

A hashed comment or whitespace can sit between text: and the CRLF, but not inside the string itself. Bracketed comments aren't allowed here at all ๐Ÿ™…โ€โ™€๏ธ.

2.4.2.1. String Lists ๐Ÿ“š

When you're matching patterns, it's super handy to match against a whole group of strings instead of just one ๐Ÿ—‚๏ธ. So lots of tests let you pass a list of strings. If the test is true for any one of them, the whole test is true ๐Ÿ’ฏ. Implementations are encouraged to use short-circuit evaluation in these cases, so they can stop as soon as they've got their answer ๐Ÿƒโ€โ™€๏ธ.

For example, the test `header :contains ["To", "Cc"] ["me@example.com", "me00@landru.example.edu"]' is true if either the To header or the Cc header of the input message contains either of the email addresses "me@example.com" or "me00@landru.example.edu" ๐Ÿ“ฌ.

Also, anywhere a list of strings is expected, a single string works fine on its own. It's the same as a list with one member ๐Ÿฅฐ. So exists "To" is equivalent to exists ["To"] ๐Ÿ’….

2.4.2.2. Headers ๐Ÿชช

Headers are a subset of strings ๐Ÿ“‡. The Internet Message Specification [IMAIL] [RFC1123] says each header line can have whitespace nearly anywhere, including after the field name and before the colon that follows it ๐ŸŒท. So extra spaces between a header name and the ":" get ignored ๐Ÿซง.

A header name never contains a colon ๐Ÿ™…โ€โ™€๏ธ. The "From" header refers to a line that begins "From:" (or "From :", and so on). No header will ever match the string "From:" because of that trailing colon ๐Ÿ’€.

Folding of long header lines (as described in [IMAIL] 3.4.8) is removed before the data gets interpreted ๐Ÿงน. The folding syntax is the CRLF that ends a line, plus any leading whitespace at the start of the next line. Both of those get treated as if they were a single space ๐Ÿชถ.

2.4.2.3. Addresses ๐Ÿ“ฎ

A bunch of commands want email addresses, which are also a subset of strings ๐Ÿ’Œ. When addresses are used in outbound contexts, they must comply with [IMAIL], but they're also further constrained ๐Ÿ”’. Using the symbols defined in section 6.1 of [IMAIL], the syntax of an address is:

sieve-address = addr-spec                ; simple address
              / phrase "<" addr-spec ">" ; name & addr-spec

Routes and group syntax are not permitted, so no fancy stuff ๐Ÿšซ. If you need multiple addresses, use a string list ๐Ÿ“‹. Named groups aren't used here either ๐Ÿ™…โ€โ™€๏ธ.

Implementations MUST ensure that addresses are syntactically valid, but they don't need to make sure the addresses actually belong to a real email recipient ๐Ÿ“ญ.

2.4.2.4. MIME Parts ๐Ÿงฉ

In a few places, [MIME] body parts are represented as strings ๐ŸŽจ. These parts include MIME headers and the body. This is a way to embed typed data inside a Sieve script, so that character sets other than UTF-8 can be used for output messages ๐ŸŒ.

2.5. Tests ๐Ÿงช

Tests are given as arguments to commands so they can control what the commands do ๐ŸŽ›๏ธ. In this document, tests get handed to if/elsif/else to decide which block of code runs ๐Ÿƒโ€โ™€๏ธ.

Tests MUST NOT have side effects. In other words, a test can't change the state of the filter or the message ๐Ÿ™…โ€โ™€๏ธ. No tests in this spec have side effects, and side effects are forbidden in extension tests too ๐Ÿ›‘.

The reason is that tests with side effects make scripts harder to read and maintain, and they're hard to show in a graphical interface for generating scripts ๐Ÿ–ผ๏ธ. Side effects are kept over in the actions, where they're much clearer ๐Ÿซถ.

2.5.1. Test Lists ๐Ÿ“

Some tests ("allof" and "anyof", which do logical "and" and logical "or", respectively) can need more than one test as an argument ๐Ÿง . The test-list syntax element is how you group tests together ๐Ÿงบ.

Example:

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

2.6. Arguments ๐ŸŽ€

To say what a command should do, most commands take arguments ๐Ÿ—ฃ๏ธ. There are three types: positional, tagged, and optional ๐ŸŒธ.

2.6.1. Positional Arguments ๐Ÿ”ข

Positional arguments are given to a command, which figures out what they mean from their order ๐Ÿ“. When a command takes positional arguments, all of them must be supplied, and they must be in the prescribed order ๐Ÿ“.

2.6.2. Tagged Arguments ๐Ÿท๏ธ

This document has tagged arguments, in the style of CommonLISP ๐Ÿง. They're kind of like the flags you'd pass to a command in most command-line tools ๐Ÿ’ป.

A tagged argument is an argument that begins with ":" followed by a tag naming it, like ":contains" ๐Ÿท๏ธ. It means that zero or more of the next tokens have some particular meaning, depending on the argument ๐Ÿงฉ. Those next tokens can be numbers or strings, but they are never blocks ๐Ÿ™…โ€โ™€๏ธ.

Tagged arguments are a lot like positional arguments, except the meaning comes from the tag instead of from the command ๐ŸŽฏ.

Tagged arguments must appear before positional arguments, but they can show up in any order with other tagged arguments ๐Ÿ”€. The syntax definitions with commands don't spell this out, just to keep the spec simpler. But they can still be reordered however you like, as long as they come before positional arguments ๐Ÿฆ‹. Tagged arguments can be mixed with optional arguments too ๐Ÿฅณ.

To keep things simple, tagged arguments SHOULD NOT take tagged arguments as their own arguments ๐Ÿ™…โ€โ™€๏ธ.

2.6.3. Optional Arguments โœจ

Optional arguments are exactly like tagged arguments, except they can be left out, and then a default value gets implied ๐Ÿ’ซ. Because optional arguments tend to make scripts shorter, they've been used way more than tagged arguments ๐Ÿ“‰.

One especially noteworthy case is the ":comparator" argument ๐Ÿ‘€. It lets the user pick which [ACAP] comparator gets used to compare two strings, since different languages can put UTF-8 [UTF-8] characters in different orders ๐ŸŒ.

2.6.4. Types of Arguments ๐Ÿงฌ

In the abstract, arguments can be literal data, tests, or blocks of commands ๐ŸŒŠ. That's how an "if" control structure works: it's just a command that happens to take a test and a block as arguments, and it may run the block of code ๐Ÿƒโ€โ™€๏ธ.

But this abstraction is ambiguous when it comes to parsing ๐Ÿค”. The grammar in section 9.2 gives a parsable version of it. Arguments are string-lists, numbers, and tags, which may be followed by a test or a test-list, which may be followed by a block of commands ๐Ÿงฉ. No more than one test or test list can be used, and no more than one block of commands either ๐Ÿ™…โ€โ™€๏ธ. Commands that end with blocks of commands don't end with semicolons ๐Ÿซถ.

2.7. String Comparison ๐Ÿ”

When you match one string against another, there are a few ways to do it ๐Ÿ•ต๏ธโ€โ™€๏ธ. There are three types of matches: an exact match, a substring match, and a wildcard glob-style match ๐Ÿงธ. They're described below ๐Ÿ‘‡.

To handle matches between character sets and case insensitivity, Sieve borrows ACAP's comparator registry ๐Ÿค.

Header names are the exception. When a string represents the name of a header, the comparator is never user-specified ๐Ÿ™…โ€โ™€๏ธ. Header comparisons are always done with the "i;ascii-casemap" operator, which means case-insensitive comparisons, because that's how the message specification [IMAIL] defines things ๐Ÿ’–.

2.7.1. Match Type ๐ŸŽฏ

There are three match types in this spec ๐Ÿ’…: ":is", ":contains", and ":matches". They're tagged arguments that tell a command what kind of matching to do, and they only show up in tests that compare strings ๐ŸŽ€

Here's the syntax, bestie:

Syntax:   ":is" / ":contains" / ":matches"

:contains is the substring match โœจ. If the value contains the key as a substring, it's a match. So "frobnitzm" contains "frob" and "nit", but not "fbm". The null key ("") is contained in every value ๐ŸŒธ

:is is the absolute match ๐Ÿ’–. Both strings have to be exactly the same to count. Only "frobnitzm" is "frobnitzm". The null key ":is" and only ":is" the null value ๐Ÿ™ˆ

:matches is the wildcard match ๐Ÿฆ‹. * matches zero or more characters and ? matches exactly one character. If you want a literal ? or * in a string, escape it as \\? or \\*. The first backslash escapes the second backslash, and together they escape the *. Ngl that's awkward, but it's super common in languages that use globs and regular expressions, so you'll recognize it ๐Ÿงš

Commands that support matching take the optional tagged arguments ":matches", ":is", and ":contains". If you don't pass a match type, the command defaults to ":is" ๐Ÿ’…. Also, these modifiers can interact with comparators. Some comparators don't work with :contains or :matches, and using one that isn't compatible is an error ๐Ÿ’€

You can only give one of these arguments to a command. Giving more than one is an error ๐Ÿšซ


2.7.2. Comparisons Across Character Sets

Every Sieve script is written in UTF-8, but messages can use lots of different character sets ๐ŸŒˆ So if you want comparisons to work across charsets, implementations SHOULD do the following ๐Ÿซถ

  • Implementations decode header charsets to UTF-8. Two strings count as equal if their UTF-8 representations are identical. Implementations should decode charsets in the forms specified by [MIME], for both message headers and bodies. Implementations must be able to decode US-ASCII, ISO-8859-1, the ASCII subset of the ISO-8859-* character sets, and UTF-8.

If implementations don't support the behavior above, they MUST follow this instead ๐Ÿ˜ญ

  • No two strings can be considered equal if one of them contains octets greater than 127.

2.7.3. Comparators

Comparators let you do language-independent, case-independent matches ๐Ÿ“ The match type can be paired with a comparator name. Comparators are described in [ACAP], which defines a registry for them, and this spec uses that registry ๐Ÿ“š

ACAP defines multiple comparator types, but this spec only uses the equality types ๐Ÿ‘‘

All implementations MUST support the "i;octet" comparator, which just compares octets ๐Ÿ”ฅ. They also MUST support the "i;ascii-casemap" comparator, which treats uppercase and lowercase characters in the ASCII subset of UTF-8 as the same โœจ. If you don't specify one, the default is "i;ascii-casemap" ๐Ÿ’–

Some comparators can't be used with substring matches. They might only work with :is. Trying to use a comparator with :matches or :contains when it isn't compatible is an error ๐Ÿ’€

You specify a comparator with the :comparator option on commands that support matching ๐ŸŽ€ That option is followed by a string with the comparator's name. Here's the syntax, abbreviated as COMPARATOR, which repeats in several tests:

Syntax:   ":comparator" <comparator-name: string>

Now the example ๐Ÿงš

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

This would discard any message with a subject like "You can MAKE MONEY FAST", but NOT "You can Make Money Fast", because the comparator is case-sensitive ๐Ÿ™ˆ (Keep in mind the RFC writes "NOT" in plain caps here, but it's not a keyword in this section, so it's just emphasis from me ๐Ÿ˜Œ)

Comparators other than i;octet and i;ascii-casemap have to be declared with require, since they're extensions ๐Ÿฆ‹ If a comparator is declared with require but isn't known, that's an error and execution fails ๐Ÿ˜ญ If a comparator isn't declared with require, it's also an error, even if the implementation supports it ๐Ÿ’€ (See 2.10.5.)

Both the ":matches" and ":contains" match types work with the "i;octet" and "i;ascii-casemap" comparators, so you can use them with those ๐Ÿซถ

Again, you can only give one of these arguments to a given command. More than one is an error ๐Ÿšซ

2.7.4. Comparisons Against Addresses

Addresses are one of the most common things represented as strings ๐Ÿ’… They're structured, so it's really useful to compare just the local-part or just the domain. That's why some tests that only act on addresses take an extra optional argument saying what part they look at ๐ŸŒธ

Those optional arguments are ":localpart", ":domain", and ":all". They act on the local-part (the left side), the domain part (the right side), and the whole address ๐Ÿฆ‹

The kind of comparison, such as whether it's case-insensitive, is set with a comparator argument on the test ๐ŸŽ€

If you leave out the address-part argument, the default is ":all" ๐Ÿ’–

You can only give one of these arguments to a given command. More than one is an error ๐Ÿšซ

For convenience, the ADDRESS-PART syntax element is defined like this:

Syntax:   ":localpart" / ":domain" / ":all"

2.8. Blocks

Blocks are sets of commands wrapped in curly braces ๐ŸŽ€ They get passed to commands so those commands can run control structures ๐Ÿงš

A control structure is a command that takes a test and a block as arguments. Depending on whether the test passes, it runs the code in the block some number of times ๐Ÿ’…

In this spec there are no loops ๐Ÿ™…โ€โ™€๏ธ The control structures here (if, elsif, and else) run a block either once or not at all. So there are two arguments: the test and the block โœจ

2.9. Commands

Sieve scripts are sequences of commands ๐Ÿ“œ Commands can take any of the tokens above as arguments, and arguments can be tagged or positional. Not every command takes every argument ๐ŸŒˆ

There are three kinds of commands: test commands, action commands, and control commands ๐Ÿ‘‘

The simplest is the action command ๐Ÿ’– It's an identifier followed by zero or more arguments, and it ends with a semicolon. Action commands can't take tests or blocks as arguments ๐Ÿซถ

A control command is similar, but it takes a test as an argument and ends with a block instead of a semicolon ๐Ÿฆ‹

A test command gets used inside a control command. It decides whether the block given to that control command runs ๐Ÿ”ฅ

2.10. Evaluation

2.10.1. Action Interaction

Some actions can't be used together because the result would be absurd ๐Ÿ™ˆ Those restrictions are noted throughout this spec ๐Ÿ“š

Extension actions MUST say how they interact with the actions defined in this specification ๐Ÿ’…

2.10.2. Implicit Keep

Experience with filtering systems shows that cases tend to get missed in scripts ๐Ÿ˜ญ To prevent mistakes, Sieve has an "implicit keep" ๐Ÿซถ

An implicit keep is a keep action (see 4.4) that gets performed when there's no action canceling it ๐ŸŒธ

An implicit keep is performed if a message isn't written to a mailbox, redirected to a new address, or explicitly thrown out. In other words, if a fileinto, a keep, a redirect, or a discard runs, the implicit keep doesn't happen ๐Ÿฆ‹

Some actions may be defined so they don't cancel the implicit keep. These actions don't directly affect delivery of the message and are only used for their side effects. None of the actions in this document fit that description, but extension actions will ๐Ÿงš

For example, with any of the short messages above, the following script produces no actions ๐Ÿ’–

if size :over 500K { discard; }

So the implicit keep is taken ๐ŸŽ€

2.10.3. Message Uniqueness in a Mailbox

Implementations SHOULD NOT deliver a message to the same folder more than once, even if a script explicitly asks for the message to be written to a mailbox twice ๐Ÿ™…โ€โ™€๏ธ

How two messages are tested for equality is implementation-defined ๐Ÿคทโ€โ™€๏ธ

If a script asks for a message to be written to a mailbox twice, that MUST NOT be treated as an error ๐ŸŒˆ

2.10.4. Limits on Numbers of Actions

Site policy MAY limit the number of actions taken and MAY restrict which actions can be used together ๐Ÿ‘‘ If a script hits a policy limit on actions for a particular message, an error occurs ๐Ÿ’€

Implementations MUST prohibit more than one reject ๐Ÿ”ฅ

Implementations MUST allow at least one keep or one fileinto. If fileinto isn't implemented, implementations MUST allow at least one keep ๐Ÿซถ

Implementations SHOULD prohibit reject when it's used with other actions ๐Ÿ’…

2.10.5. Extensions and Optional Features

Mail systems differ a lot in what they can do, so many features in this spec are optional ๐ŸŽ€ Before any of these extensions can run, they must be declared with the "require" action โœจ

If an extension isn't enabled with "require", implementations MUST treat it as if they don't support it at all ๐Ÿ™ˆ

If a script doesn't understand an extension declared with require, the script must not be used at all. Implementations MUST NOT execute scripts that require unknown capability names ๐Ÿ’€

Note: The reason for this restriction is that experience with languages like LISP and Tcl suggests it's a workable way to note that a script uses an extension ๐Ÿงš Experience with PostScript suggests that mechanisms letting a script work around missing extensions aren't really used in practice ๐Ÿ’…

Extensions that define actions MUST state how they interact with the actions discussed in the base specification ๐Ÿซถ

2.10.6. Errors ๐Ÿšจ

Okay bestie, so in any programming language there are two kinds of errors: compile-time and run-time ๐Ÿ’€. Compile-time errors are the ones hiding in your syntax that a syntax check can catch. Run-time errors stay hidden until the script actually runs, and that includes stuff like a full disk ๐Ÿ’พ and weird combos of actions that don't play nicely together.

When an error happens in a Sieve script, all processing stops. Full stop, no cap ๐Ÿ›‘.

Implementations MAY choose to do a full parse, then evaluate the script, then do all actions. Some implementations might even go so far as to make execution atomic, meaning either all the actions run or none of them do ๐Ÿง.

Other implementations may choose to parse and run at the same time. Those are simpler, but they have issues with partial failure, where some actions happen and others don't ๐Ÿ˜ฌ.

Some implementations might even go so far as to make sure scripts can never run an invalid set of actions (for example, reject + fileinto) before anything executes. That could involve solving the Halting Problem ๐Ÿคฏ.

This specification allows any of these approaches. Solving the Halting Problem is considered extra credit ๐Ÿ†.

When an error happens, implementations MUST notify the user that an error occurred, which actions (if any) were taken, and do an implicit keep ๐Ÿซถ.

2.10.7. Limits on Execution โณ

Implementations may limit certain constructs. However, this specification places a lower bound on some of these limits, so there are minimums everyone has to meet ๐Ÿ’….

Implementations MUST support fifteen levels of nested blocks.

Implementations MUST support fifteen levels of nested test lists.


3. Control Commands ๐ŸŽ€

Control structures are needed so you can do multiple and conditional actions. Think of them as the decision-making brain of your script ๐Ÿง .

3.1. Control Structure If ๐ŸŒธ

There are three pieces to if: "if", "elsif", and "else". Each one is technically a separate command in the grammar ๐Ÿ“. An elsif MUST only follow an if, and an else MUST follow only either an if or an elsif. If those conditions aren't met, an error occurs ๐Ÿ™…โ€โ™€๏ธ.

Syntax:   if <test1: test> <block1: block>

Syntax:   elsif <test2: test> <block2: block>

Syntax:   else <block>

The semantics are similar to the many other programming languages where these control commands show up ๐Ÿ’œ. When the interpreter sees an "if", it evaluates the test attached to it. If the test is true, it runs the block attached to it โœจ.

If the test of the "if" is false, the interpreter checks the test of the first "elsif" (if there is one). If that elsif test is true, it runs the elsif's block. An elsif can be followed by another elsif, and then the interpreter just keeps repeating this process until it runs out of elsifs ๐Ÿ”.

Once the interpreter runs out of elsifs, there may be an "else" case. If there is one, and none of the if or elsif tests were true, the interpreter runs the else case ๐Ÿฆ‹.

Basically this gives you a way to run exactly one of the blocks in the chain. Only one, periodt ๐Ÿ’….

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 runs over message A, it redirects the message to acm@example.edu. Message B gets redirected to postmaster@example.edu, and 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";
          }

Note that this definition prohibits the "... else if ..." sequence used by C ๐Ÿ™…โ€โ™€๏ธ. This is intentional, because that construct produces a shift-reduce conflict, and nobody wants that drama ๐Ÿ˜ญ.

3.2. Control Structure Require ๐Ÿ“š

Syntax:   require <capabilities: string-list>

The require action notes that a script makes use of a certain extension. That declaration is required to use the extension, as discussed in section 2.10.5 ๐Ÿ”. You can declare multiple capabilities with a single require, which is super efficient ๐Ÿงš.

The require command, if present, MUST be used before anything other than a require can be used. If a require shows up after a command other than require, an error occurs ๐Ÿšซ.

Example:  require ["fileinto", "reject"];
Example:  require "fileinto";
          require "vacation";

3.3. Control Structure Stop ๐Ÿ›‘

Syntax:   stop

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


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

Implementations MAY limit the number of certain actions taken (see section 2.10.4) ๐Ÿงฎ.

4.1. Action reject ๐Ÿ™…โ€โ™€๏ธ

Syntax:   reject <reason: string>

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 and 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" ๐Ÿงฉ.

4.2. Action fileinto ๐Ÿ“

Syntax:   fileinto <folder: string>

The "fileinto" action delivers the message into the specified folder ๐Ÿ—‚๏ธ. Implementations SHOULD support fileinto, but in some environments this may be impossible ๐Ÿฅฒ.

The capability string for use with the require command is "fileinto" ๐Ÿ’–.

In the following script, message A is filed into folder "INBOX.harassment" ๐Ÿšจ.

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

4.3. Action redirect โžก๏ธ

Syntax:   redirect <address: string>

The "redirect" action is used to send the message to another user at a supplied address, kind of like 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 ๐Ÿ“ญ.

The redirect command performs an MTA-style "forward", which 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. (This is not an MUA-style forward, which creates a new message with a different sender and message ID, wrapping the old message in a new one. Different vibe entirely ๐Ÿ™…โ€โ™€๏ธ.)

A simple script can be used for redirecting all mail ๐ŸŒˆ:

Example:  redirect "bart@example.edu";

Implementations SHOULD take measures to implement loop control, possibly including adding headers to the message or counting received headers. If an implementation detects a loop, it causes an error ๐Ÿ”„.

4.4. 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 filing the message into the user's main mailbox ๐Ÿ“ฅ. This command gives you a way to execute that action without needing to know the name of the user's main mailbox, so you can call it without needing to understand the user's setup or the underlying mail system ๐Ÿงธ.

For instance, in an implementation where the IMAP server is running scripts on behalf of the user at time of delivery, a keep command is equivalent to a fileinto "INBOX" โœจ.

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

Note that the above script is identical to the one below ๐Ÿ‘ฏ.

Example:  if not size :under 1M { discard; }

4.5. 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) ๐Ÿงƒ.

Discard MUST be silent. That is, it MUST NOT return a non-delivery notification of any kind ([DSN], [MDN], or otherwise) ๐Ÿ™Š.

In the following script, any mail from "idiot@example.edu" is thrown out ๐Ÿ‘‹.

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

While an important part of this language, "discard" has the potential to create serious problems for users ๐Ÿ˜ฑ. Students who leave themselves logged in to an unattended machine in a public computer lab may find their script changed to just "discard" ๐Ÿ’€. In order to protect users in this situation (along with similar situations), implementations MAY keep messages destroyed by a script for an indefinite period, and MAY disallow scripts that throw out all mail ๐Ÿ›ก๏ธ.


5. 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 ๐Ÿ’ซ.

5.1. Test address ๐Ÿ“ฌ

Syntax:   address [ADDRESS-PART] [COMPARATOR] [MATCH-TYPE]
          <header-list: string-list> <key-list: string-list>

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 the comparator and the match keyword ๐ŸŽฏ.

Like envelope and header, this test returns true if any combination of the header-list and key-list arguments match ๐Ÿงฉ.

Internet email addresses [IMAIL] have the somewhat awkward characteristic that the local-part to the left of the at-sign is considered case sensitive, and the domain-part to the right of the at-sign is case insensitive ๐Ÿ™ƒ. The "address" command does not deal with this itself, but it provides the ADDRESS-PART argument so users can deal with it ๐Ÿง.

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

Okay bestie, the address test has to stay in its lane ๐Ÿ’… Implementations MUST keep it to headers that actually hold addresses, but they MUST at least cover From, To, Cc, Bcc, Sender, Resent-From, and Resent-To, and they SHOULD also cover any other header with an "address-list" body ๐ŸŽ€

if address :is :all "from" "tim@example.com" {
    discard;

5.2. Test allof โœจ

Syntax: allof <tests: test-list>

The allof test is basically a logical AND on whatever tests you hand it ๐Ÿงš

allof (false, false)  =>   false
allof (false, true)   =>   false
allof (true,  true)   =>   true

The allof test takes as its argument a test-list. ๐Ÿ’–

5.3. Test anyof ๐Ÿฆ‹

Syntax: anyof <tests: test-list>

The anyof test is a logical OR, so one true test is enough to make it true ๐ŸŒˆ

anyof (false, false)  =>   false
anyof (false, true)   =>   true
anyof (true,  true)   =>   true

5.4. Test envelope ๐Ÿ’Œ

Syntax: envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE] <envelope-part: string-list> <key-list: string-list>

The "envelope" test is true if the chosen part of the SMTP (or equivalent) envelope matches the chosen key ๐Ÿ’œ

If one of the envelope-part strings is (case insensitive) "from", then matching happens against the FROM address used in the SMTP MAIL command ๐Ÿ“ฎ

If one of the envelope-part strings is (case insensitive) "to", then matching happens against the TO address used in the SMTP RCPT command that got this message 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 can hold more than one parameter. When it does, all of the strings in the key-list get matched against all parts 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. No exceptions, bestie ๐Ÿ’€

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

5.5. Test exists ๐ŸŒท

Syntax: exists <header-names: string-list>

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 ๐Ÿ˜ญ

This example tosses out mail that doesn't have a From header and a Date header ๐Ÿ—‘๏ธ

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

5.6. Test false โŒ

Syntax: false

The "false" test always evaluates to false. Never true, not ever ๐Ÿ™…โ€โ™€๏ธ

5.7. Test header ๐ŸŽ€

Syntax: header [COMPARATOR] [MATCH-TYPE] <header-names: string-list> <key-list: string-list>

The "header" test evaluates to true if any header name matches any key. The type of match comes from the optional match argument, which defaults to ":is" if you don't specify one, as covered in section 2.6 ๐Ÿฆ‹

Like address and envelope, this test returns true if any combination of the string-list and key-list arguments match ๐Ÿ’…

If a header listed in the header-names argument exists, it contains the null key (""). But if the named header isn't there at all, it does not contain the null key. So if a message had this header:

X-Caffeine: C8H10N4O2

these tests on that header go like this:

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

5.8. Test not ๐Ÿ™ƒ

Syntax: not <test>

The "not" test takes some other test as an argument and flips the result. "not false" evaluates to "true" and "not true" evaluates to "false" ๐Ÿ”„

5.9. Test size ๐Ÿ“

Syntax: size <":over" / ":under"> <limit: number>

The "size" test is all about how big a message is ๐Ÿ˜ It takes either a tagged argument of ":over" or ":under", followed by a number that represents the size of the message.

If the argument is ":over" and the size of the message is greater than the number provided, the test is true. Otherwise, it's false.

If the argument is ":under" and the size of the message is less than the number provided, the test is true. Otherwise, it's false.

Exactly one of ":over" or ":under" must be specified, and anything else is an error ๐Ÿšจ

The size of a message is defined as the number of octets from the initial header until the last character in the message body ๐Ÿงฎ

Note that for a message that is exactly 4,000 octets, the message is neither ":over" 4000 octets nor ":under" 4000 octets. Math is hard, bestie ๐Ÿซ 

5.10. Test true ๐ŸŒž

Syntax: true

The "true" test always evaluates to true. Main character energy โญ

6. Extensibility โœจ

New control structures, actions, and tests can be added to the language ๐Ÿงš Sites have to make these features known to their users, and this document doesn't define a way to discover the list of extensions a server supports ๐Ÿ”

Any extension to this language MUST define a capability string that uniquely identifies that extension. If a new version of an extension changes what a previously defined extension does, it MUST use a different name ๐Ÿท๏ธ

In a situation where there's a submission protocol and an extension advertisement mechanism that knows the details of this language, submitted scripts can be checked against the mail server. That way nobody gets to use an extension the server doesn't support ๐Ÿ™…โ€โ™€๏ธ

Extensions MUST state how they interact with the constraints defined in section 2.10. For example, they should say whether they cancel the implicit keep, and which actions they're compatible and incompatible with ๐Ÿ“

6.1. Capability String ๐Ÿซง

Capability strings are typically short strings that describe what capabilities a server supports ๐Ÿฌ

Capability strings beginning with "vnd." are vendor-defined extensions. These aren't defined by Internet standards or RFCs, but they still get registered with IANA so nobody collides. Extensions starting with "vnd." SHOULD be followed by the name of the vendor and product, like "vnd.acme.rocket-sled" ๐Ÿš€

The following capability strings are defined by this document:

envelope    The string "envelope" indicates that the implementation
            supports the "envelope" command.

fileinto    The string "fileinto" indicates that the implementation
            supports the "fileinto" command.

reject      The string "reject" indicates that the implementation
            supports the "reject" command.

comparator- The string "comparator-elbonia" is provided if the
            implementation supports the "elbonia" comparator.
            Therefore, all implementations have at least the
            "comparator-i;octet" and "comparator-i;ascii-casemap"
            capabilities.  However, these comparators may be used
            without being declared with require.

6.2. IANA Considerations ๐Ÿ›๏ธ

In order to provide a standard set of extensions, IANA provides a registry. Capability names may be registered on a first-come, first-served basis. Extensions designed for interoperable use SHOULD be defined as standards track or IESG approved experimental RFCs ๐Ÿค

6.2.1. Template for Capability Registrations ๐Ÿ“‹

The following template is what you use to register new Sieve extensions with IANA ๐Ÿ’–

To: iana@iana.org
Subject: Registration of new Sieve extension

Capability name:
Capability keyword:
Capability arguments:
Standards Track/IESG-approved experimental RFC number:
Person and email address to contact for further information:

6.2.2. Initial Capability Registrations ๐ŸŒŸ

These are the entries that go into the IANA registry for Sieve extensions as its starting contents ๐ŸŒฑ

Capability name:        fileinto
Capability keyword:     fileinto
Capability arguments:   fileinto <folder: string>
Standards Track/IESG-approved experimental RFC number:
        RFC 3028 (Sieve base spec)
Person and email address to contact for further information:
        Tim Showalter
        tjs@mirapoint.com

Capability name:        reject
Capability keyword:     reject
Capability arguments:   reject <reason: string>
Standards Track/IESG-approved experimental RFC number:
        RFC 3028 (Sieve base spec)
Person and email address to contact for further information:
        Tim Showalter
        tjs@mirapoint.com

Capability name:        envelope
Capability keyword:     envelope
Capability arguments:
        envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE]
        <envelope-part: string-list> <key-list: string-list>
Standards Track/IESG-approved experimental RFC number:
        RFC 3028 (Sieve base spec)
Person and email address to contact for further information:
        Tim Showalter
        tjs@mirapoint.com

Capability name:        comparator-*
Capability keyword:
        comparator-* (anything starting with "comparator-")
Capability arguments:   (none)
Standards Track/IESG-approved experimental RFC number:
        RFC 3028, Sieve, by reference of
        RFC 2244, Application Configuration Access Protocol
Person and email address to contact for further information:
        Tim Showalter
        tjs@mirapoint.com

6.3. Capability Transport ๐Ÿšš

The range of mail systems this document applies to is pretty varied, so a way to advertise which capabilities an implementation supports is tricky, given how many different implementations exist ๐ŸŒ Still, such a mechanism should have the property that the implementation can advertise the complete set of extensions it supports ๐Ÿ“ฃ

7. Transmission ๐Ÿ“จ

The MIME type for a Sieve script is "application/sieve" ๐Ÿ’•

The registration of this type for RFC 2048 requirements looks like this:

 Subject: Registration of MIME media type application/sieve

 MIME media type name: application
 MIME subtype name: sieve
 Required parameters: none
 Optional parameters: none
 Encoding considerations: Most sieve scripts will be textual,
    written in UTF-8.  When non-7bit characters are used,
    quoted-printable is appropriate for transport systems
    that require 7bit encoding.

 Security considerations: Discussed in section 10 of RFC 3028.
 Interoperability considerations: Discussed in section 2.10.5
    of RFC 3028.
 Published specification: RFC 3028.
 Applications which use this media type: sieve-enabled mail servers
 Additional information:
   Magic number(s):
   File extension(s): .siv
   Macintosh File Type Code(s):
 Person & email address to contact for further information:
    See the discussion list at ietf-mta-filters@imc.org.
 Intended usage:
    COMMON
 Author/Change controller:
    See Author information in RFC 3028.

8. Parsing ๐Ÿงฉ

The Sieve grammar gets split into tokens and a separate grammar, just like most programming languages do ๐ŸŽ€

8.1. Lexical Tokens ๐Ÿ”ค

Sieve scripts are encoded in UTF-8. The following assumes a valid UTF-8 encoding, and special characters in Sieve scripts are all ASCII ๐ŸŒˆ

The following are tokens in Sieve:

  • identifiers
  • tags
  • numbers
  • quoted strings
  • multi-line strings
  • other separators

Blanks, horizontal tabs, CRLFs, and comments ("white space") get ignored โœจ except when they separate tokens. Some white space is required to separate tokens that would otherwise run together, and it's also needed in specific places inside multi-line strings ๐Ÿซถ.

The other separators are single individual characters, and the grammar mentions each one explicitly ๐Ÿ’….

The lexical structure of sieve is defined in the following BNF (as described in [ABNF]) ๐ŸŒธ:

bracket-comment = "/*" *(CHAR-NOT-STAR / ("*" CHAR-NOT-SLASH)) "*/"
        ;; No */ allowed inside a comment.
        ;; (No * is allowed unless it is the last character,
        ;; or unless it is followed by a character that isn't a
        ;; slash.)

CHAR-NOT-DOT = (%x01-09 / %x0b-0c / %x0e-2d / %x2f-ff)
        ;; no dots, no CRLFs

CHAR-NOT-CRLF = (%x01-09 / %x0b-0c / %x0e-ff)

CHAR-NOT-SLASH = (%x00-57 / %x58-ff)

CHAR-NOT-STAR = (%x00-51 / %x53-ff)

comment = bracket-comment / hash-comment

hash-comment = ( "#" *CHAR-NOT-CRLF CRLF )

identifier = (ALPHA / "_") *(ALPHA DIGIT "_")

tag = ":" identifier

number = 1*DIGIT [QUANTIFIER]

QUANTIFIER = "K" / "M" / "G"

quoted-string = DQUOTE *CHAR DQUOTE
        ;; in general, \ CHAR inside a string maps to CHAR
        ;; so \" maps to " and \\ maps to \
        ;; note that newlines and other characters are all allowed
        ;; strings

multi-line          = "text:" *(SP / HTAB) (hash-comment / CRLF)
                      *(multi-line-literal / multi-line-dotstuff)
                      "." CRLF
multi-line-literal  = [CHAR-NOT-DOT *CHAR-NOT-CRLF] CRLF
multi-line-dotstuff = "." 1*CHAR-NOT-CRLF CRLF
        ;; A line containing only "." ends the multi-line.
        ;; Remove a leading '.' if followed by another '.'.

white-space = 1*(SP / CRLF / HTAB) / comment

8.2. Grammar โœจ

The following is the grammar of Sieve after it has been lexically interpreted. No white space or comments appear below ๐Ÿ™ˆ. The start symbol is "start" ๐Ÿ’–.

argument = string-list / number / tag

arguments = *argument [test / test-list]

block = "{" commands "}"

command = identifier arguments ( ";" / block )

commands = *command

start = commands

string = quoted-string / multi-line

string-list = "[" string *("," string) "]" / string         ;; if
there is only a single string, the brackets are optional

test = identifier arguments

test-list = "(" test *("," test) ")"

9. Extended Example ๐Ÿงš

Okay, so here's an extended example of a Sieve script ๐ŸŽ€. Heads up: it does not make use of the implicit keep ๐Ÿ™ˆ.

#
# Example Sieve Filter
# Declare any optional features or extension used by the script
#
require ["fileinto", "reject"];

#
# Reject any large messages (note that the four leading dots get
# "stuffed" to three)
#
if size :over 1M
        {
        reject text:
Please do not send me large attachments.
Put your file on a server and send me the URL.
Thank you.
.... Fred
.
;
        stop;
        }
#
# 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";
        }

10. Security Considerations ๐Ÿ”

Users must get their mail ๐Ÿ“ฌ. It's imperative that whatever method implementations use to store the user-defined filtering scripts is secure ๐Ÿ”’.

It's equally important that implementations sanity-check the user's scripts, and don't let users create on-demand mailbombs ๐Ÿ’ฃ. For instance, an implementation that lets a user reject or redirect multiple times to a single message might also let a user create a mailbomb triggered by mail from a specific user ๐Ÿ™€. Site- or implementation-defined limits on actions are useful for this ๐Ÿ’œ.

Several commands, such as "discard", "redirect", and "fileinto" allow for actions to be taken that are potentially very dangerous ๐Ÿ’€.

Implementations SHOULD take measures to prevent languages from looping. ๐ŸŒ€

11. Acknowledgments ๐Ÿซถ

I'm so thankful to Chris Newman for his support and his ABNF syntax checker ๐Ÿ’–. Big shoutout to John Myers and Steve Hole for outlining the requirements for the original drafts ๐Ÿ™Œ. Larry Greenfield gets a crown for nagging about the grammar and finally fixing it ๐Ÿ‘‘, and Greg Sereda gets one for repeatedly fixing things and providing examples ๐Ÿ’…. Ned Freed fixed everything else ๐Ÿ”ง, and Rob Earhart built an early implementation and helped a ton ๐Ÿฆ‹. Randall Gellens did endless amounts of proofreading ๐Ÿ“–, which is iconic. Carnegie Mellon University is where most of the work on this document got done ๐ŸŽ“, and I'm also indebted to all of the readers of the ietf-mta-filters@imc.org mailing list ๐ŸŒˆ.

12. Author's Address ๐Ÿ’Œ

Tim Showalter
Mirapoint, Inc.
909 Hermosa Court
Sunnyvale, CA 94085

EMail: tjs@mirapoint.com

13. References ๐Ÿ“š

[ABNF]      Crocker, D. and P. Overell, "Augmented BNF for Syntax
            Specifications: ABNF", RFC 2234, November 1997.

[ACAP]      Newman, C. and J. G. Myers, "ACAP -- Application
            Configuration Access Protocol", RFC 2244, November 1997.

[BINARY-SI] "Standard IEC 60027-2: Letter symbols to be used in
            electrical technology - Part 2: Telecommunications and
            electronics", January 1999.

[DSN]       Moore, K. and G. Vaudreuil, "An Extensible Message Format
            for Delivery Status Notifications", RFC 1894, January
            1996.

[FLAMES]    Borenstein, N, and C. Thyberg, "Power, Ease of Use, and
            Cooperative Work in a Practical Multimedia Message
            System", Int. J.  of Man-Machine Studies, April, 1991.
            Reprinted in Computer-Supported Cooperative Work and
            Groupware, Saul Greenberg, editor, Harcourt Brace
            Jovanovich, 1991.  Reprinted in Readings in Groupware and
            Computer-Supported Cooperative Work, Ronald Baecker,
            editor, Morgan Kaufmann, 1993.

[KEYWORDS]  Bradner, S., "Key words for use in RFCs to Indicate
            Requirement Levels", BCP 14, RFC 2119, March 1997.

[IMAP]      Crispin, M., "Internet Message Access Protocol - version
            4rev1", RFC 2060, December 1996.

[IMAIL]     Crocker, D., "Standard for the Format of ARPA Internet
            Text Messages", STD 11, RFC 822, August 1982.

[MIME]      Freed, N. and N. Borenstein, "Multipurpose Internet Mail
            Extensions (MIME) Part One: Format of Internet Message
            Bodies", RFC 2045, November 1996.

[MDN]       Fajman, R., "An Extensible Message Format for Message
            Disposition Notifications", RFC 2298, March 1998.

[RFC1123]   Braden, R., "Requirements for Internet Hosts --
            Application and Support", STD 3, RFC 1123, November 1989.

[SMTP]      Postel, J., "Simple Mail Transfer Protocol", STD 10, RFC
            821, August 1982.

[UTF-8]     Yergeau, F., "UTF-8, a transformation format of Unicode
            and ISO 10646", RFC 2044, October 1996.

Copyright (C) The Internet Society (2001). All Rights Reserved. ๐ŸŒˆ

This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of developing Internet standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English ๐ŸŒ.

The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns โ™พ๏ธ.

This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.

Acknowledgement ๐ŸŒธ

Funding for the RFC Editor function is currently provided by the Internet Society ๐Ÿ’–.

๐Ÿ“ฆ where this page comes from