RFC 6609
Sieve Email Filtering: Include Extension
RFC 6609: Sieve Email Filtering: Include Extension π
Okay bestie, this is RFC 6609, and it's all about "Sieve Email Filtering: Include Extension" π Here's the title block so you know who's behind it:
- Authors: C. Daboo (Apple, Inc.) and A. Stone (Serendipity) π
- Category: Standards Track
- ISSN: 2070-1721
- Date: May 2012
- Obsoletes / Updates: the title block doesn't list anything this one obsoletes or updates π¦
Abstract β¨
Okay so the Sieve Email Filtering "include" extension lets users drop one Sieve script inside another one π§ This makes big scripts, or a bunch of scripts, way easier to manage. It also lets a site and its users build up libraries of scripts π Users can include their own personal scripts or site-wide scripts, iykyk π
Status of This Memo πΈ
This is an Internet Standards Track document, so it's the real deal β
It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more on Internet Standards, check out Section 2 of RFC 5741 π
For the current status, any errata, and how to give feedback, go to http://www.rfc-editor.org/info/rfc6609 π₯
Copyright Notice π
Copyright (c) 2012 IETF Trust and the persons identified as the document authors. All rights reserved π
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents, as they were on the publication date. Please read these carefully, because they spell out your rights and restrictions for this document π Code Components pulled out of this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. They come with no warranty, as described in the Simplified BSD License π
The source links are http://trustee.ietf.org/license-info for the license info.
Table of Contents πΊοΈ
1. Introduction and Overview .......................................2
2. Conventions Used in This Document ...............................2
3. Include Extension ...............................................3
3.1. General Considerations .....................................3
3.2. Control Structure "include" ................................4
3.3. Control Structure "return" .................................7
3.4. Interaction with the "variables" Extension .................8
3.4.1. Control Structure "global" ..........................8
3.4.2. Variables Namespace global .........................10
3.5. Interaction with Other Extensions .........................11
4. Security Considerations ........................................12
5. IANA Considerations ............................................12
6. References .....................................................13
6.1. Normative References ......................................13
6.2. Informative References ....................................13
Appendix A. Acknowledgments .......................................14
1. Introduction and Overview π¦
Okay, so it's super convenient to break Sieve [RFC5228] scripts into smaller parts that you can reuse in all kinds of situations π§© Say you have a default script, and you also want a special 'vacation' script that turns on when you go on vacation ποΈ The default actions should keep running, but a vacation command should run first. You could edit the default script every time to add or remove the vacation command, which is a lot ngl π Or you could make a vacation script that just has the vacation command and then includes the default script. Way cleaner, periodt β¨
This document defines the Sieve Email Filtering "include" extension. It lets users include one Sieve script inside another π
2. Conventions Used in This Document π
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] π
Conventions for notations are the same as in Sieve [RFC5228], Section 1.1 π
Here are the key phrases used to describe scripts and script execution π
- script: a valid Sieve script β¨
- script execution: an instance of a Sieve interpreter invoked for a given message delivery. It starts with the user's active script and keeps going through any included scripts until the message gets its final disposition (for example: delivered, forwarded, discarded, rejected, and so on) π¦
- immediate script: the individual Sieve script file that's being executed πΈ
- including script: the individual Sieve script file that had an include statement which included the immediate script π§
3. Include Extension π
3.1. General Considerations π
Sieve implementations that implement the "include", "return", and "global" commands (described below) have an identifier of "include" for use with the capability mechanism π₯ If any of the "include", "return", or "global" commands show up in a script, the "include" capability MUST be listed in the "require" statement in that script. No cap, this is a big one π
Sieve implementations need to keep track of actions used in included scripts. That way, implicit "keep" behavior can be figured out correctly, based on whether any actions ran in any script π«Ά
Sieve implementations are allowed to limit the total number of nested included scripts. But they MUST provide for a total of at least three levels of nested scripts, counting the top-level script too π If any nesting limit gets exceeded, an error MUST be generated either when the script is uploaded to the Sieve repository, or when the script is executed π If an error like that is detected while a Sieve script is being processed, an implicit "keep" action MUST be executed so no messages get lost π
Sieve implementations MUST NOT allow recursive script inclusion. That covers direct recursion, where script A includes script A (itself), and indirect recursion, where script A includes script B, which includes script A once again. Both are prohibited, no exceptions bestie π
Sieve implementations MUST generate an error at execution time if an included script is a recursive inclusion. Implementations MUST NOT generate errors for recursive includes at upload time, because that would force an upload ordering requirement on script authors and generators. Nobody wants that drama π ββοΈ
Sieve implementations MUST generate an error at execution time if an included script does not exist, except when the ":optional" parameter is specified πΈ Implementations MUST NOT generate errors for scripts that are missing at upload time, because that would also force an upload ordering requirement on script authors and generators. Same energy as before, it's giving consistency π
If the Sieve "variables" extension [RFC5229] is present, there's an issue with the "scope" of variables defined in scripts that might include each other π For example, say one script defines the variable "${status}" with one meaning, and another script defines "${status}" with a different meaning. If one script includes the other, then it's unclear which "${status}" is being referenced. To fix this, Sieve implementations MUST follow the scoping rules defined in Section 3.4 and support the "global" command defined there π«§ Okay, the plot thickens, and we get into it in the next part π¦β¨
3.2. Control Structure "include" π
Usage:
include [LOCATION] [":once"] [":optional"] <value: string>
LOCATION = ":personal" / ":global"
okay so include is the command that pulls in another script at that exact spot π§ It takes an optional location, an optional :once, an optional :optional, and one string naming the script you want to pull in. Implementations MUST restrict script names according to ManageSieve [RFC5804], Section 1.6. The script name argument MUST be a constant string as defined in [RFC5229], Section 3, and implementations MUST NOT expand variables in the script name argument. So no sneaky variable tricks in the name, bestie π
ββοΈ
The location MUST default to :personal if you don't specify one, and it MUST NOT be specified more than once. Here's the vibe for each one:
:personalmeans the script lives in your own private Sieve repository π:globalmeans the script lives in a site-wide Sieve repository that every user on the system can access π
The :once parameter is giving "only include this if it hasn't already been included somewhere else during this run" π
If it has already been included, processing just carries on right after the include command. Implementations MUST NOT generate an error if an include :once command names a script whose inclusion would be recursive. In that case the script MUST be considered previously included, so include :once won't pull it in again. No drama, no infinite loops π
Note: It is RECOMMENDED that script authors and generators use the:onceparameter only when including a script that performs general duties, such as declaring global variables and making sanity checks of the environment. Basically, save:oncefor the setup-y stuff π§Ή
The :optional parameter says the script might be missing, and that's totally fine π«Ά Normally, an implementation MUST generate an error during execution if an include command names a script that doesn't exist. But when :optional is there, implementations MUST NOT generate an error for a missing script, and MUST continue as if the include command had not been present. It just gets skipped, no tea spilled β
The included script MUST be a valid Sieve script. Implementations MUST validate that each script has its own require statements for all optional capabilities that script uses. The scope of a require statement is the script it immediately appears in. It doesn't get inherited or passed on to other scripts while things are running, so every script has to bring its own require receipts π§Ύ
A stop command inside an included script MUST stop all script processing, including the processing of the scripts that include the immediate one. Whole thing ends, period π The return command (more on that below) only stops the immediate script and lets the scripts that included it keep going. Very different energy, so pick wisely π
The include command MAY appear anywhere in a script where a control structure is legal, and MAY be used inside another control structure, like an if block. Main character placement, honestly π
Examples:
The user has four scripts stored in their personal repository:
The default script is the active one that includes several others:
require ["include"];
include :personal "always_allow";
include :global "spam_tests";
include :personal "spam_tests";
include :personal "mailing_lists";
The personal script always_allow special-cases some correspondent email addresses and makes sure any message from them is always kept π
if address :is "from" "boss@example.com"
{
keep;
}
elsif address :is "from" "ceo@example.com"
{
keep;
}
The personal script spam_tests (which uses "reject" [RFC5429]) runs some user-specific spam checks to catch the spam that the site-wide tests missed π«
require ["reject"];
if header :contains "Subject" "XXXX"
{
reject "Subject XXXX is unacceptable.";
}
elsif address :is "from" "money@example.com"
{
reject "Mail from this sender is unwelcome.";
}
The personal script mailing_lists looks for messages from different mailing lists and files each one into its own mailbox π¬
require ["fileinto"];
if header :is "List-ID" "sieve.ietf.org"
{
fileinto "lists.sieve";
}
elsif header :is "List-ID" "ietf-imapext.imc.org"
{
fileinto "lists.imapext";
}
There is one script stored in the global repository:
The site script spam_tests (which also uses "reject" [RFC5429]) does site-wide spam checks that any user at the site can include in their own scripts at a suitable point. The site administrator keeps the content up to date π§βπ»
require ["reject"];
if anyof (header :contains "Subject" "$$",
header :contains "Subject" "Make money")
{
reject "No thank you.";
}
3.3. Control Structure "return" π¦
Usage:
return
return stops processing of the immediately included script only, and hands control back to the script that included it. If you use it in the main script (so, not in an included one), it works exactly like stop, including the appropriate keep action if no other actions have run up to that point β¨
3.4. Interaction with the "variables" Extension πΈ
Okay, so variables getting "overwritten" by an included script is a whole mess we need to avoid π So this specification requires that all variables defined in a script MUST be kept "private" to the immediate script by default. In other words, they're not "visible" to other scripts. That way, two script authors can't accidentally wreck each other by picking the same variable name π
Sometimes, though, you actually want a variable from one script to be available to the others. This specification defines a new command, global, to declare that a variable is shared among scripts. Effectively, you get two namespaces: one local to the immediate script, and one shared among all scripts π Implementations MUST allow a non-global variable to have the same name as a global variable, with no interaction between the two.
3.4.1. Control Structure "global" π
Usage:
global <value: string-list>
The global command takes a string list argument that names one or more variables to store in the global variable space. Each name MUST be a constant string and conform to the syntax of variable-name as defined in the "variables" extension document [RFC5229], Section 3. Match variables can't be specified, and namespace prefixes aren't allowed. An invalid name MUST be detected as a syntax error π¨
The global command is only available when the script has both "include" and "variables" in its require line. If global shows up when only "include" or only "variables" has been required, an error MUST be generated when the script is uploaded. No half-required vibes allowed π
ββοΈ
If a global command is given the name of a variable that has already been defined in the immediate script with set, an error MUST be generated either when the script is uploaded or at execution time. Pick a lane, babe π£οΈ
If a global command lists a variable that hasn't been defined in the "global" namespace, the name of the variable is now marked as global. Any subsequent set command will then set the value of that variable in global scope π
A variable has global scope in all scripts that have declared it with global. If a script uses that variable name without declaring it global, the name points to a separate, non-global variable within that script. Two different variables, same name, zero drama π«§
Interpreting a string that contains a variable marked as global, but with no value set, SHALL behave as any other access to an unknown variable, as specified in the "variables" extension document [RFC5229], Section 3. In other words, it evaluates to an empty string π€
Example:
The active script:
The included script may contain repetitive code that is basically a subroutine you can factor out. In this script, the test that matches last leaves its value in the test_mailbox variable, and the top-level script files the message into that mailbox. If no tests matched, the message is implicitly kept in the INBOX π₯
require ["fileinto", "include", "variables", "relational"];
global "test";
global "test_mailbox";
set "test" "$$";
include "subject_tests";
set "test" "Make money";
include "subject_tests";
if string :count "eq" "${test_mailbox}" "1"
{
fileinto "${test_mailbox}";
stop;
}
The personal script subject_tests:
This script runs a bunch of tests against the message, sets the global test_mailbox variable to a folder to file the message into, and then falls back to the top-level script π
require ["include", "variables"];
global ["test", "test_mailbox"];
if header :contains "Subject" "${test}"
{
set "test_mailbox" "spam-${test}";
}
3.4.2. Variables Namespace global π§ββοΈ
In addition to the global command, this document defines the variables namespace "global", in accordance with the "variables" extension document [RFC5229], Section 3. The "global" namespace has no sub-namespaces. For example, set "global.data.from" "me@example.com"; is not allowed π
ββοΈ The variable-name part MUST be a valid identifier. For example, set "global.12" "value"; is not valid because "12" is not a valid identifier π’
Note that the "variables" extension document [RFC5229], Section 3 suggests that extensions should define a namespace that's the same as their capability string. In this case, that would be "include" rather than "global". Even so, references to the "global" namespace without a prior require statement for the "include" extension MUST cause an error π«
Example:
require ["variables", "include"];
set "global.i_am_on_vacation" "1";
Variables declared global and variables accessed via the "global" namespace MUST each be one and the same. In the following example, the variable i_am_on_vacation is used in a global command, and then again through the "global" namespace. Think of these as two syntaxes with identical meaning, iykyk π
require ["variables", "include", "vacation"];
global "i_am_on_vacation";
set "global.i_am_on_vacation" "1";
if string :is "${i_am_on_vacation}" "1"
{
vacation "It's true, I am on vacation.";
}
3.5. Interaction with Other Extensions π¦
okay so this bit is about what happens when "include" teams up with other Sieve extensions, and bestie it's giving drama πΏ
When "include" is used with the "editheader" extension [RFC5293], any changes made to headers in a script MUST be propagated both to and from included scripts. Like, if a script deletes one header and adds another, then includes a second script, the included script MUST NOT see the removed header, and MUST see the added header. And the reverse is just as strict: if the included script adds or removes a header, then once control is back in the including script, later actions MUST see the added headers and MUST NOT see the removed headers. Header changes are shared both ways, no cap π
When "include" is used with the MIME extension [RFC5703] "foreverypart" control structure, the included script MUST be presented with the current MIME part as though it were the entire message. π§ A script SHALL NOT have any special control over the control structure it was included from, so no sneaky boss moves. The "break" command in an included script is not valid on its own and may not terminate a "foreverypart" iteration in another script. The included script can use "return" to hand control back to the including script π, and a global variable can be used to pass results back too. A "stop" in an included script, even inside a "foreverypart" loop, still halts ALL script execution, per Section 3.2. So a stop is a stop, periodt π
When "include" is used with the "reject" extension [RFC5429], calling "reject" or "ereject" at any time sets the reject action on the message and keeps script execution going πΈ. Now the MIME part thing applies here too: if an included script only sees a piece of the message and calls a reject, the rejection covers the entire message, not just that one MIME part. Main character energy for the whole email, not just one part π
4. Security Considerations π
Okay this section is serious, so I'm keeping it all. π
Sieve implementations MUST ensure adequate security for the global script repository to prevent unauthorized changes to global scripts. For example, a site policy might enable only certain users with administrative privileges to modify the global scripts. Sites are advised against allowing all users to have write access to the sites' global scripts. Not everyone gets the keys to the global scripts, bestie ποΈ
Sieve implementations MUST ensure that script names are checked for validity and proper permissions prior to inclusion. The goal is to stop a malicious user from getting access to files that the mail server software can reach but that user should NOT be able to reach π¨
Sieve implementations MUST ensure that script names are safe for use with their storage system. An error MUST be generated either when the script is uploaded or at execution time for a script that includes a name which could be used as a vector to attack the storage system. For example, the following include commands should be considered hostile:
include "./../..//etc/passwd"
include "foo$(`rm star`)"
Yikes, those are giving villain arc π Don't let them in.
Beyond these, the "include" extension does not raise any security considerations that are not discussed in the base Sieve [RFC5228] document and the "variables" extension document [RFC5229]. So you've got the rest covered already, iykyk π«Ά
5. IANA Considerations π
The following template specifies the IANA registration of the Sieve extension specified in this document:
To: iana@iana.org
Subject: Registration of new Sieve extension
Capability name: include
Description: adds the "include" command to execute other Sieve
scripts, the "return" action from an included
script, and the "global" command and "global"
variables namespace to access variables shared
among included scripts.
RFC number: this RFC
Contact address: the Sieve discussion list <sieve@ietf.org>
This information has been added to IANA's "Sieve Extensions" registry (http://www.iana.org). It's officially on the list now, bestie β¨
6. References π
6.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC5228] Guenther, P., Ed., and T. Showalter, Ed., "Sieve: An Email Filtering Language", RFC 5228, January 2008.
[RFC5229] Homme, K., "Sieve Email Filtering: Variables Extension", RFC 5229, January 2008.
[RFC5804] Melnikov, A., Ed., and T. Martin, "A Protocol for Remotely Managing Sieve Scripts", RFC 5804, July 2010.
6.2. Informative References
[RFC5293] Degener, J. and P. Guenther, "Sieve Email Filtering: Editheader Extension", RFC 5293, August 2008.
[RFC5429] Stone, A., Ed., "Sieve Email Filtering: Reject and Extended Reject Extensions", RFC 5429, March 2009.
[RFC5703] Hansen, T. and C. Daboo, "Sieve Email Filtering: MIME Part Tests, Iteration, Extraction, Replacement, and Enclosure", RFC 5703, October 2009.
Appendix A. Acknowledgments π
Thanks to Stephan Bosch, Ned Freed, Arnt Gulbrandsen, Tony Hansen, Kjetil Torgrim Homme, Jeffrey Hutzelman, Barry Leiba, Alexey Melnikov, Ken Murchison, Marc Mutz, and Rob Siemborski, for comments and corrections. Literal hall of fame, the whole squad π
Authors' Addresses π¦
Cyrus Daboo
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
USA
EMail: cyrus@daboo.name
URI: http://www.apple.com/
Aaron Stone
Serendipity
1817 California St. #104
San Francisco, CA 94109
USA
EMail: aaron@serendipity.cx
π¦ where this page comes from
- ietf: rfc6609 2012-05
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms