diff --git a/Makefile.am b/Makefile.am
index 6e91416918e6a8ffd0a8b3f45522fc8a01748b8c..a4d359fa604f40653d0674a1986178508c2e5bce 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,6 +76,7 @@ test_cases = \
 	tests/extensions/include/variables.svtest \
 	tests/extensions/include/once.svtest \
 	tests/extensions/include/twice.svtest \
+	tests/extensions/include/optional.svtest \
 	tests/extensions/include/rfc.svtest \
 	tests/extensions/include/execute.svtest \
 	tests/extensions/imap4flags/basic.svtest \
diff --git a/README b/README
index 4e3c00ab01e256e4b16b79c8b7c8aac36c5bccc6..8d9f345ce10bb34ce1dd3981da57c3e0b418f3a9 100644
--- a/README
+++ b/README
@@ -109,8 +109,7 @@ following list outlines the implementation status of each supported extension:
     relational (RFC 5231): fully supported.
     imap4flags (RFC 5232): fully supported.
     subaddress (RFC 5233): fully supported, but with limited configurability.
-    spamtest and virustest (RFC 5235): fully supported (v0.1.16+), but
-        currently considered experimental.
+    spamtest and virustest (RFC 5235): fully supported (v0.1.16+).
     date (RFC 5260; Section 4): fully supported (v0.1.12+).
     editheader (RFC 5293): fully supported (v0.3.0+).
     reject (RFC 5429; Section 2.2): fully supported.
@@ -121,8 +120,7 @@ following list outlines the implementation status of each supported extension:
     ihave (RFC 5463): fully supported (v0.2.4+).
     mailbox (RFC 5490; Section 3): fully supported (v0.1.10+), but ACL
         permissions are not verified for mailboxexists.
-    include (draft v05; not latest version): almost fully supported, but
-        interaction with ManageSieve is not in accordance with specification.
+    include (RFC 6609): fully supported (v0.4.0+)
     regex (draft v08; not latest version): almost fully supported, but
         UTF-8 is not supported.
 
diff --git a/TODO b/TODO
index 392b856aefd27eb0c417481c44ec4ad17b6df8af..4f9684c9a9ed9c9b2eb3d0b23aa0b0dde50ac9f0 100644
--- a/TODO
+++ b/TODO
@@ -21,8 +21,6 @@ Next (mostly in order of descending priority/precedence):
   of script storage like LDAP or SQL database.
 	- Implement read/write script storages for using ManageSieve with dict
 	  database
-* Update include extension to latest draft (v13 currently):
-	- Implement :optional tag.
 * Implement index extension
 * Add normalize() method to comparators to normalize the string before matching
   (for efficiency).
diff --git a/doc/rfc/draft-ietf-sieve-include-05.txt b/doc/rfc/draft-ietf-sieve-include-05.txt
deleted file mode 100644
index ada0bc86c8d4f36d296dda6612ef08a2a2e1eb9a..0000000000000000000000000000000000000000
--- a/doc/rfc/draft-ietf-sieve-include-05.txt
+++ /dev/null
@@ -1,784 +0,0 @@
-
-
-
-Network Working Group                                           C. Daboo
-Internet-Draft                                                  A. Stone
-Intended status: Standards Track                           July 12, 2010
-Expires: January 13, 2011
-
-
-                Sieve Email Filtering: Include Extension
-                      draft-ietf-sieve-include-05
-
-Abstract
-
-   The Sieve Email Filtering "include" extension permits users to
-   include one Sieve script inside another.  This can make managing
-   large scripts or multiple sets of scripts much easier, and allows a
-   site and its users to build up libraries of scripts.  Users are able
-   to include their own personal scripts or site-wide scripts.
-
-Status of this Memo
-
-   This Internet-Draft is submitted in full conformance with the
-   provisions of BCP 78 and BCP 79.
-
-   Internet-Drafts are working documents of the Internet Engineering
-   Task Force (IETF).  Note that other groups may also distribute
-   working documents as Internet-Drafts.  The list of current Internet-
-   Drafts is at http://datatracker.ietf.org/drafts/current/.
-
-   Internet-Drafts are draft documents valid for a maximum of six months
-   and may be updated, replaced, or obsoleted by other documents at any
-   time.  It is inappropriate to use Internet-Drafts as reference
-   material or to cite them other than as "work in progress."
-
-   This Internet-Draft will expire on January 13, 2011.
-
-Copyright Notice
-
-   Copyright (c) 2010 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
-   (http://trustee.ietf.org/license-info) in effect on the date of
-   publication of this document.  Please review these documents
-   carefully, as they describe your rights and restrictions with respect
-   to this document.  Code Components extracted from this document must
-   include Simplified BSD License text as described in Section 4.e of
-   the Trust Legal Provisions and are provided without warranty as
-   described in the Simplified BSD License.
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 1]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-   This document may contain material from IETF Documents or IETF
-   Contributions published or made publicly available before November
-   10, 2008.  The person(s) controlling the copyright in some of this
-   material may not have granted the IETF Trust the right to allow
-   modifications of such material outside the IETF Standards Process.
-   Without obtaining an adequate license from the person(s) controlling
-   the copyright in such materials, this document may not be modified
-   outside the IETF Standards Process, and derivative works of it may
-   not be created outside the IETF Standards Process, except to format
-   it for publication as an RFC or to translate it into languages other
-   than English.
-
-
-Table of Contents
-
-   1.  Introduction and Overview  . . . . . . . . . . . . . . . . . .  5
-   2.  Conventions Used in This Document  . . . . . . . . . . . . . .  5
-   3.  Include Extension  . . . . . . . . . . . . . . . . . . . . . .  5
-     3.1.  General Considerations . . . . . . . . . . . . . . . . . .  5
-     3.2.  Control Structure include  . . . . . . . . . . . . . . . .  6
-     3.3.  Control Structure return . . . . . . . . . . . . . . . . . 10
-     3.4.  Interaction with Variables . . . . . . . . . . . . . . . . 10
-       3.4.1.  Control Structure global . . . . . . . . . . . . . . . 10
-       3.4.2.  Variables Namespace global . . . . . . . . . . . . . . 12
-   4.  Security Considerations  . . . . . . . . . . . . . . . . . . . 12
-   5.  IANA Considerations  . . . . . . . . . . . . . . . . . . . . . 13
-     5.1.  "include" Extension Registration . . . . . . . . . . . . . 13
-   6.  References . . . . . . . . . . . . . . . . . . . . . . . . . . 13
-     6.1.  Normative References . . . . . . . . . . . . . . . . . . . 13
-     6.2.  Informative References . . . . . . . . . . . . . . . . . . 13
-   Appendix A.  Acknowledgments . . . . . . . . . . . . . . . . . . . 13
-   Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 13
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 2]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-Change History (to be removed prior to publication as an RFC)
-
-   Changes from ietf-04 to ietf-05:
-
-   a.  Integrate review from Barry Leiba.
-
-   Changes from ietf-03 to ietf-04:
-
-   a.  No changes.
-
-   Changes from ietf-02 to ietf-03:
-
-   a.  Setting a variable then calling global on it is an error
-       (something like 'use strict').
-
-   b.  Specify that the 'global' keyword is only available when
-       'variables' has also been required.
-
-   c.  Uploading a script that includes a nonexistent script is not an
-       error at upload time.
-
-   Changes from ietf-01 to ietf-02:
-
-   a.  Require that script names must be constant strings, not subject
-       to variable expansion.
-
-   b.  Try the phrase immediate script instead of current script.
-
-   c.  Clarify that "global 'varname'" and "global.varname" refer to the
-       same variable.
-
-   d.  Drop the requirement the global keywords come after require and
-       before anything else.
-
-   Changes from ietf-00 to ietf-01:
-
-   a.  Replaced import/export with global.
-
-   b.  Added :once modifier to include.
-
-   c.  Added global namespace to see if it holds water.
-
-   Changes from daboo-06 to ietf-00:
-
-   a.  None
-
-   Changes from -05 to -06:
-
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 3]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-   a.  Aaron Stone joins as author.
-
-   b.  Removed | characters from the script examples.
-
-   c.  Updated draft references to published RFCs.
-
-   Changes from -04 to -05:
-
-   a.  Fixed examples.
-
-   b.  Relaxed requirement that imported/exported variables be set
-       before being used.
-
-   Changes from -03 to -04:
-
-   a.  Fixed missing 2119 definitions.
-
-   b.  Defined interaction with variables through use of import and
-       export commands.
-
-   Changes from -02 to -03:
-
-   a.  Refreshing expired draft (updated for nits).
-
-   b.  Syntax -> Usage.
-
-   c.  Updated to 3028bis reference.
-
-   Changes from -01 to -02:
-
-   a.  Minor formatting changes only - refreshing expired draft.
-
-   Changes from -00 to -01:
-
-   a.  Added IPR boiler plate.
-
-   b.  Re-ordered sections at start to conform to RFC style.
-
-   c.  Moved recursion comment into General Considerations section.
-
-   d.  Switched to using optional parameter to indicate personal vs
-       global.
-
-   e.  Explicitly state that an error occurs when a missing script is
-       included.
-
-
-
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 4]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-1.  Introduction and Overview
-
-   It's convenient to be able to break SIEVE [RFC5228] scripts down into
-   smaller components which can be reused in a variety of different
-   circumstances.  For example, users may want to have a default script
-   and a special 'vacation' script, the latter being activated when the
-   user goes on vacation.  In that case the default actions should
-   continue to be run, but a vacation command should be executed first.
-   One option is to edit the default script to add or remove the
-   vacation command as needed.  Another is to have a vacation script
-   that simply has a vacation command and then includes the default
-   script.
-
-
-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 as in SIEVE [RFC5228] Section 1.1.
-
-   The following key phrases are 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, starting with the user's active script and continuing
-      through any included scripts until the message is delivered.
-
-   immediate script
-      the individual Sieve script file 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",
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 5]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-   "return", or "global" commands are used in a script, the "include"
-   capability MUST be listed in the "require" statement in that script.
-
-   Sieve implementations need to track the use of actions in included
-   scripts so that implicit "keep" behavior can be properly determined
-   based on whether any actions have executed in any script.
-
-   Sieve implementations are allowed to limit the total number of nested
-   included scripts, but MUST provide for a total of at least three
-   levels of nested scripts including the top-level script.  An error
-   MUST be generated either when the script is uploaded to the Sieve
-   repository, or when the script is executed, if any nesting limit is
-   exceeded.  If such an error is detected whilst processing a Sieve
-   script, an implicit "keep" action MUST be executed to prevent loss of
-   any messages.
-
-   Sieve implementations MUST ensure that recursive includes are not
-   possible.  For example, if script "A" includes script "B", and script
-   "B" includes script "A" an error MUST be generated either when the
-   script is uploaded to the Sieve repository, or when the script is
-   executed.  If such an error is detected whilst processing a Sieve
-   script, an implicit "keep" action MUST be executed to prevent loss of
-   any messages.
-
-   Sieve implementations MUST generate an error at execution time if an
-   included script does not exist.  Implementations MUST NOT generate
-   errors for scripts missing at upload time, as this would force an
-   upload ordering requirement upon script authors / generators.
-
-   If the Sieve "variables" extension [RFC5229] is present, an issue
-   arises with the "scope" of variables defined in scripts that may
-   include each other.  For example, if a script defines the variable
-   "${status}" with one particular meaning or usage, and another defines
-   "${status}" with a different meaning, then if one script includes the
-   other there is an issue as to which "${status}" is being referenced.
-   To solve this problem, Sieve implementations MUST follow the scoping
-   rules defined in Section 3.4 and support the "global" command defined
-   there.
-
-3.2.  Control Structure include
-
-      Usage:   include [LOCATION] [ONCE] <value: string>
-
-               LOCATION = ":personal" / ":global"
-
-               ONCE = ":once"
-
-   The "include" command takes an optional "location" parameter, an
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 6]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-   optional ":once" parameter, and a single string argument representing
-   the name of the script to include for processing at that point.  It
-   is RECOMMENDED that implementations restrict script names according
-   to [I-D.ietf-sieve-managesieve] Section 1.7.  Implementations MUST
-   NOT allow variables to be expanded into the names of Sieve scripts;
-   in other words, the value MUST be a constant string as defined in
-   VARIABLES [RFC5229], Section 3.
-
-   The "location" parameter MUST default to ":personal" if not
-   specified.  The "location" has the following meanings:
-
-   :personal
-      Indicates that the named script is stored in the user's own
-      personal (private) Sieve repository.
-
-   :global
-      Indicates that the named script is stored in a site-wide Sieve
-      repository, accessible to all users of the Sieve system.
-
-   The ":once" parameter tells the interpreter only to include the named
-   script if it has not already been included at any other point during
-   script execution.  If the script has already been included,
-   processing continues immediately following the include command.
-   Implementations MUST NOT generate an error if an "include :once"
-   command names a script whose inclusion would be recursive; in this
-   case, the script MUST be considered previously included and therefore
-   "include :once" will not include it again.
-
-   Note: It is RECOMMENDED that script authors / generators use this
-   parameter only when including a script that performs general duties
-   such as declaring global variables and making sanity checks of the
-   environment.
-
-   The included script MUST be a valid Sieve script, including having
-   necessary "require" statements for all optional capabilities used by
-   the script.  The scope of a "require" statement in an included script
-   is for the immediate script only, not the including script.  For
-   example, if script "A" includes script "B", and script "B" uses the
-   "fileinto" extension, script "B" must have a "require" statement for
-   "fileinto", irrespective of whether script "A" has one.  In addition,
-   if script "A" does not have a "require" statement for "fileinto",
-   "fileinto" cannot be used anywhere in script "A", even after
-   inclusion of script "B".
-
-   A "stop" command in an included script MUST stop all script
-   processing, including the processing of the scripts that include the
-   immediate one.  The "return" command (described below) stops
-   processing of the immediate script only, and allows the scripts that
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 7]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-   include it to continue.
-
-   Examples:
-
-   The user has four scripts stored in their personal repository:
-
-   "default"
-
-      This is the default active script that includes several others.
-
-
-      require ["include"];
-
-      include :personal "always_allow";
-      include :global "spam_tests";
-      include :personal "spam_tests";
-      include :personal "mailing_lists";
-
-   Personal script "always_allow"
-
-      This script special-cases some correspondent email addresses and
-      makes sure any message containing those addresses are always kept.
-
-
-      if header :is "From" "boss@example.com"
-      {
-          keep;
-      }
-      elsif header :is "From" "ceo@example.com"
-      {
-          keep;
-      }
-
-   Personal script "spam_tests"
-
-      This script does some user-specific spam tests to catch spam
-      messages not caught by the site-wide spam tests.
-
-
-      require ["reject"];
-
-      if header :contains "Subject" "XXXX"
-      {
-          reject;
-      }
-      elsif header :is "From" "money@example.com"
-      {
-          reject;
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 8]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-      }
-
-   Personal script "mailing_lists"
-
-      This script looks for messages from different mailing lists and
-      files each into a mailbox specific to the mailing list.
-
-
-      require ["fileinto"];
-
-      if header :is "List-ID" "owner-ietf-mta-filters@imc.org"
-      {
-          fileinto "lists.sieve";
-      }
-      elsif header :is "List-ID" "owner-ietf-imapext@imc.org"
-      {
-          fileinto "lists.imapext";
-      }
-
-   There is one script stored in the global repository:
-
-   Site script "spam_tests"
-
-      This script does some site-wide spam tests which any user at the
-      site can include in their own scripts at a suitable point.  The
-      script content is kept up to date by the site administrator.
-
-
-      require ["reject"];
-
-      if anyof (header :contains "Subject" "$$",
-                header :contains "Subject" "Make money")
-      {
-          reject;
-      }
-
-   The "include" command may appear anywhere in the script where a
-   control structure is legal.
-
-   Example:
-
-      require ["include"];
-
-      if anyof (header :contains "Subject" "$$",
-                header :contains "Subject" "Make money")
-      {
-          include "my_reject_script";
-      }
-
-
-
-Daboo & Stone           Expires January 13, 2011                [Page 9]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-3.3.  Control Structure return
-
-      Usage: return
-
-   The "return" command stops processing of the immediately included
-   script only and returns processing control to the script which
-   includes it.  If used in the main script (i.e., not in an included
-   script), it has the same effect as the "stop" command, including the
-   appropriate "keep" action if no other actions have been executed up
-   to that point.
-
-3.4.  Interaction with Variables
-
-   In order to avoid problems of variables in an included script
-   "overwriting" those from the script that includes it, this
-   specification requires that all variables defined in a script MUST be
-   kept "private" to the immediate script by default - that is, they are
-   not "visible" to other scripts.  This ensures that two script authors
-   cannot inadvertently cause problems by choosing the same name for a
-   variable.
-
-   However, sometimes there is a need to make a variable defined in one
-   script available to others.  This specification defines the new
-   command "global" to declare that a variable is shared among scripts.
-   Effectively, two namespaces are defined: one local to the immediate
-   script, and another shared among all scripts.  Implementations MUST
-   allow a non-global variable to have the same name as a global
-   variable but have no interaction between them.
-
-3.4.1.  Control Structure global
-
-      Usage:   global <value: string-list>
-
-   The "global" command contains a string list argument that defines one
-   or more names of variables to be stored in the global variable space.
-
-   The "global" command is only available when the script has both
-   "include" and "variables" in its require line.  If the "global"
-   command appears when only "include" or only "variables" has been
-   required, an error MUST be generated when the script is uploaded.
-
-   If a "global" command is given the name of a variable that has
-   previously been defined in the immediate script with "set", an error
-   MUST be generated either when the script is uploaded or at execution
-   time.
-
-   If a "global" command lists a variable that has not been defined in
-   the global namespace, the name of the variable is now marked as
-
-
-
-Daboo & Stone           Expires January 13, 2011               [Page 10]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-   global, and any subsequent "set" command will set the value of the
-   variable in global scope.
-
-   A variable has global scope in all scripts that have declared it with
-   the "global" command.  If a script uses that variable name without
-   declaring it global, the name specifies a separate, non-global
-   variable within that script.
-
-   Interpretation of a string containing a variable marked as global,
-   but without any value set, SHALL behave as any other access to an
-   unknown variable, as specified in VARIABLES [RFC5229], Section 3
-   (i.e., evaluates to an empty string).
-
-   Example:
-
-      require ["variables", "include"];
-      global "test";
-      global "test-mailbox";
-
-      # The included script may contain repetitive code that is
-      # effectively a subroutine that can be factored out.
-      set "test" "$$"
-      include "spam_filter_script";
-
-      set "test" "Make money"
-      include "spam_filter_script";
-
-      # Message will be filed according to the test that matched last.
-      if string :count "${test-mailbox}" "1"
-      {
-          fileinto "INBOX${test-mailbox}";
-          stop;
-      }
-
-      # If nothing matched, the message is implicitly kept.
-
-                               Active script
-
-
-      require ["variables", "include"];
-      global ["test", "test-mailbox"];
-
-      if header :contains "Subject" "${test}"
-      {
-          set "test-mailbox" "spam-${test};
-      }
-
-                            spam_filter_script
-
-
-
-Daboo & Stone           Expires January 13, 2011               [Page 11]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-3.4.2.  Variables Namespace global
-
-   In addition to the "global" command, this document defines the
-   variables namespace "global", as specified in VARIABLES [RFC5229],
-   Section 3.
-
-   Example:
-
-      require ["variables", "include"];
-
-      set "global.i_am_on_vacation" "1";
-
-   Variables declared global and variables accessed via the global
-   namespace MUST be one and the same.  In the following example script,
-   we see the variable "i_am_on_vacation" used in a "global" command,
-   and again with the "global." namespace.  Consider these as two
-   syntaxes with identical meaning.
-
-   Example:
-
-      require ["variables", "include"];
-      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."
-      }
-
-
-4.  Security Considerations
-
-   Sieve implementations MUST ensure adequate security for the global
-   script repository to prevent unauthorized changes to global scripts.
-
-   Sieve implementations MUST ensure that script names are checked for
-   validity and proper permissions prior to inclusion, in order to
-   prevent a malicious user from gaining acess to files accessible to
-   the mail server software that should not be accessible to the user.
-
-   Beyond these, the "include" extension does not raise any security
-   considerations that are not present in the base SIEVE [RFC5228]
-   document and the VARIABLES [RFC5229] extension.
-
-
-
-
-
-
-
-Daboo & Stone           Expires January 13, 2011               [Page 12]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-5.  IANA Considerations
-
-   The following template specifies the IANA registration of the Sieve
-   extension specified in this document:
-
-5.1.  "include" Extension Registration
-
-   Capability name: include
-   Description:     adds the "include" command to execute other Sieve
-                    scripts, 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 <ietf-mta-filters@imc.org>
-
-
-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. and T. Showalter, "Sieve: An Email Filtering
-              Language", RFC 5228, January 2008.
-
-   [RFC5229]  Homme, K., "Sieve Email Filtering: Variables Extension",
-              RFC 5229, January 2008.
-
-6.2.  Informative References
-
-   [I-D.ietf-sieve-managesieve]
-              Melnikov, A. and T. Martin, "A Protocol for Remotely
-              Managing Sieve Scripts", draft-ietf-sieve-managesieve-09
-              (work in progress), January 2009.
-
-
-Appendix A.  Acknowledgments
-
-   Thanks to Ken Murchison, Rob Siemborski, Alexey Melnikov, Marc Mutz,
-   Kjetil Torgrim Homme, Stephan Bosch, Arnt Gulbrandsen, Barry Leiba,
-   and Jeffrey Hutzelman for comments and corrections.
-
-
-
-
-
-
-
-
-
-
-Daboo & Stone           Expires January 13, 2011               [Page 13]
-
-Internet-Draft          Sieve Extension: Include               July 2010
-
-
-Authors' Addresses
-
-   Cyrus Daboo
-
-   Email: cyrus@daboo.name
-
-
-   Aaron Stone
-
-   Email: aaron@serendipity.cx
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Daboo & Stone           Expires January 13, 2011               [Page 14]
-
diff --git a/doc/rfc/include.rfc6609.txt b/doc/rfc/include.rfc6609.txt
new file mode 100644
index 0000000000000000000000000000000000000000..08620a5b2e86741154c2c1f96008611783ab9c6b
--- /dev/null
+++ b/doc/rfc/include.rfc6609.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF)                          C. Daboo
+Request for Comments: 6609                                   Apple, Inc.
+Category: Standards Track                                       A. Stone
+ISSN: 2070-1721                                              Serendipity
+                                                                May 2012
+
+
+                Sieve Email Filtering: Include Extension
+
+Abstract
+
+   The Sieve Email Filtering "include" extension permits users to
+   include one Sieve script inside another.  This can make managing
+   large scripts or multiple sets of scripts much easier, and allows a
+   site and its users to build up libraries of scripts.  Users are able
+   to include their own personal scripts or site-wide scripts.
+
+Status of This Memo
+
+   This is an Internet Standards Track document.
+
+   This document is a product of the Internet Engineering Task Force
+   (IETF).  It represents the consensus of the IETF community.  It has
+   received public review and has been approved for publication by the
+   Internet Engineering Steering Group (IESG).  Further information on
+   Internet Standards is available in Section 2 of RFC 5741.
+
+   Information about the current status of this document, any errata,
+   and how to provide feedback on it may be obtained at
+   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
+   (http://trustee.ietf.org/license-info) in effect on the date of
+   publication of this document.  Please review these documents
+   carefully, as they describe your rights and restrictions with respect
+   to this document.  Code Components extracted from this document must
+   include Simplified BSD License text as described in Section 4.e of
+   the Trust Legal Provisions and are provided without warranty as
+   described in the Simplified BSD License.
+
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 1]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+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
+
+   It's convenient to be able to break Sieve [RFC5228] scripts down into
+   smaller components that can be reused in a variety of different
+   circumstances.  For example, users may want to have a default script
+   and a special 'vacation' script, the latter being activated when the
+   user goes on vacation.  In that case, the default actions should
+   continue to be run, but a vacation command should be executed first.
+   One option is to edit the default script to add or remove the
+   vacation command as needed.  Another is to have a vacation script
+   that simply has a vacation command and then includes the default
+   script.
+
+   This document defines the Sieve Email Filtering "include" extension,
+   which permits users to 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 as in Sieve [RFC5228], Section 1.1.
+
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 2]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   The following key phrases are 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, starting with the user's active script and continuing
+      through any included scripts until the final disposition of the
+      message (e.g., delivered, forwarded, discarded, rejected, etc.).
+
+   immediate script
+      the individual Sieve script file being executed.
+
+   including script
+      the individual Sieve script file that had an include statement
+      that 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 are used in a script, the "include"
+   capability MUST be listed in the "require" statement in that script.
+
+   Sieve implementations need to track the use of actions in included
+   scripts so that implicit "keep" behavior can be properly determined
+   based on whether any actions have executed in any script.
+
+   Sieve implementations are allowed to limit the total number of nested
+   included scripts, but MUST provide for a total of at least three
+   levels of nested scripts including the top-level script.  An error
+   MUST be generated either when the script is uploaded to the Sieve
+   repository, or when the script is executed, if any nesting limit is
+   exceeded.  If such an error is detected whilst processing a Sieve
+   script, an implicit "keep" action MUST be executed to prevent loss of
+   any messages.
+
+   Sieve implementations MUST NOT allow recursive script inclusion.
+   Both direct recursion, where script A includes script A (itself), and
+   indirect recursion, where script A includes script B which includes
+   script A once again, are prohibited.
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 3]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   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, as this would
+   force an upload ordering requirement upon script authors and
+   generators.
+
+   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
+   missing at upload time, as this would force an upload ordering
+   requirement upon script authors and generators.
+
+   If the Sieve "variables" extension [RFC5229] is present, an issue
+   arises with the "scope" of variables defined in scripts that may
+   include each other.  For example, if a script defines the variable
+   "${status}" with one particular meaning or usage, and another defines
+   "${status}" with a different meaning, then if one script includes the
+   other there is an issue as to which "${status}" is being referenced.
+   To solve this problem, Sieve implementations MUST follow the scoping
+   rules defined in Section 3.4 and support the "global" command defined
+   there.
+
+3.2.  Control Structure "include"
+
+      Usage:  include [LOCATION] [":once"] [":optional"] <value: string>
+
+              LOCATION = ":personal" / ":global"
+
+   The "include" command takes an optional "location" parameter, an
+   optional ":once" parameter, an optional ":optional" parameter, and a
+   single string argument representing the name of the script to include
+   for processing at that point.  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; implementations MUST NOT expand variables in the script
+   name argument.
+
+   The "location" parameter MUST default to ":personal" if not
+   specified.  The "location" parameter MUST NOT be specified more than
+   once.  The "location" has the following meanings:
+
+   :personal
+      Indicates that the named script is stored in the user's own
+      personal (private) Sieve repository.
+
+   :global
+      Indicates that the named script is stored in a site-wide Sieve
+      repository, accessible to all users of the Sieve system.
+
+
+
+Daboo & Stone                Standards Track                    [Page 4]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   The ":once" parameter tells the interpreter only to include the named
+   script if it has not already been included at any other point during
+   script execution.  If the script has already been included,
+   processing continues immediately following the "include" command.
+   Implementations MUST NOT generate an error if an "include :once"
+   command names a script whose inclusion would be recursive; in this
+   case, the script MUST be considered previously included, and
+   therefore "include :once" will not include it again.
+
+   Note: It is RECOMMENDED that script authors and generators use the
+   ":once" parameter only when including a script that performs general
+   duties such as declaring global variables and making sanity checks of
+   the environment.
+
+   The ":optional" parameter indicates that the script may be missing.
+   Ordinarily, an implementation MUST generate an error during execution
+   if an "include" command specifies a script that does not exist.  When
+   ":optional" is specified, implementations MUST NOT generate an error
+   for a missing script, and MUST continue as if the "include" command
+   had not been present.
+
+   The included script MUST be a valid Sieve script.  Implementations
+   MUST validate that each script has its own "require" statements for
+   all optional capabilities used by that script.  The scope of a
+   "require" statement is the script in which it immediately appears,
+   and neither inherits nor passes on capabilities to other scripts
+   during the course of execution.
+
+   A "stop" command in an included script MUST stop all script
+   processing, including the processing of the scripts that include the
+   immediate one.  The "return" command (described below) stops
+   processing of the immediate script only, and allows the scripts that
+   include it to continue.
+
+   The "include" command MAY appear anywhere in a script where a control
+   structure is legal, and MAY be used within another control structure,
+   e.g., an "if" block.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 5]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   Examples:
+
+   The user has four scripts stored in their personal repository:
+
+   "default"
+
+      This is the default active script that includes several others.
+
+      require ["include"];
+
+      include :personal "always_allow";
+      include :global "spam_tests";
+      include :personal "spam_tests";
+      include :personal "mailing_lists";
+
+   Personal script "always_allow"
+
+      This script special-cases some correspondent email addresses and
+      makes sure any message containing those addresses is always kept.
+
+      if address :is "from" "boss@example.com"
+      {
+          keep;
+      }
+      elsif address :is "from" "ceo@example.com"
+      {
+          keep;
+      }
+
+   Personal script "spam_tests" (uses "reject" [RFC5429])
+
+      This script does some user-specific spam tests to catch spam
+      messages not caught by the site-wide spam tests.
+
+      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.";
+      }
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 6]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   Personal script "mailing_lists"
+
+      This script looks for messages from different mailing lists and
+      files each into a mailbox specific to the mailing list.
+
+      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:
+
+   Site script "spam_tests" (uses "reject" [RFC5429])
+
+      This script does some site-wide spam tests that any user at the
+      site can include in their own scripts at a suitable point.  The
+      script content is kept up to date by the site administrator.
+
+      require ["reject"];
+
+      if anyof (header :contains "Subject" "$$",
+                header :contains "Subject" "Make money")
+      {
+          reject "No thank you.";
+      }
+
+3.3.  Control Structure "return"
+
+      Usage:  return
+
+   The "return" command stops processing of the immediately included
+   script only and returns processing control to the script that
+   includes it.  If used in the main script (i.e., not in an included
+   script), it has the same effect as the "stop" command, including the
+   appropriate "keep" action if no other actions have been executed up
+   to that point.
+
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 7]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+3.4.  Interaction with the "variables" Extension
+
+   In order to avoid problems of variables in an included script
+   "overwriting" those from the script that includes it, this
+   specification requires that all variables defined in a script MUST be
+   kept "private" to the immediate script by default -- that is, they
+   are not "visible" to other scripts.  This ensures that two script
+   authors cannot inadvertently cause problems by choosing the same name
+   for a variable.
+
+   However, sometimes there is a need to make a variable defined in one
+   script available to others.  This specification defines the new
+   command "global" to declare that a variable is shared among scripts.
+   Effectively, two namespaces are defined: one local to the immediate
+   script, and another shared among all scripts.  Implementations MUST
+   allow a non-global variable to have the same name as a global
+   variable but have no interaction between them.
+
+3.4.1.  Control Structure "global"
+
+      Usage:  global <value: string-list>
+
+   The "global" command accepts a string list argument that defines one
+   or more names of variables to be stored 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 cannot be specified, and
+   namespace prefixes are not 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 the "global"
+   command appears when only "include" or only "variables" has been
+   required, an error MUST be generated when the script is uploaded.
+
+   If a "global" command is given the name of a variable that has
+   previously been defined in the immediate script with "set", an error
+   MUST be generated either when the script is uploaded or at execution
+   time.
+
+   If a "global" command lists a variable that has not been defined in
+   the "global" namespace, the name of the variable is now marked as
+   global, and any subsequent "set" command will set the value of the
+   variable in global scope.
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 8]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   A variable has global scope in all scripts that have declared it with
+   the "global" command.  If a script uses that variable name without
+   declaring it global, the name specifies a separate, non-global
+   variable within that script.
+
+   Interpretation of a string containing a variable marked as global,
+   but without any value set, SHALL behave as any other access to an
+   unknown variable, as specified in the "variables" extension document
+   [RFC5229], Section 3 (i.e., evaluates to an empty string).
+
+   Example:
+
+   The active script
+
+      The included script may contain repetitive code that is
+      effectively a subroutine that can be factored out.  In this
+      script, the test that matches last will leave its value in the
+      test_mailbox variable, and the top-level script will file the
+      message into that mailbox.  If no tests matched, the message will
+      be 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;
+      }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                    [Page 9]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   Personal script "subject_tests"
+
+      This script performs a number of tests against the message, sets
+      the global test_mailbox variable with 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 (e.g., 'set "global.data.from" "me@example.com";'
+   is not allowed).  The variable-name part MUST be a valid identifier
+   (e.g., '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 is the same
+   as its capability string (in this case, "include" rather than
+   "global").  Nevertheless, 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
+   script, we see the variable "i_am_on_vacation" used in a "global"
+   command, and again with the "global" namespace.  Consider these as
+   two syntaxes with identical meaning.
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                   [Page 10]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+   Example:
+
+      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
+
+   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.  By way of example, 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.  Likewise, if the included script adds or removes a header,
+   upon returning to the including script, subsequent actions MUST see
+   the added headers and MUST NOT see the removed headers.
+
+   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.  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 transfer control back to the including script.  A
+   global variable can be used to convey results to the including
+   script.  A "stop" in an included script, even within a "foreverypart"
+   loop, still halts all script execution, per Section 3.2.
+
+   When "include" is used with the "reject" extension [RFC5429], calling
+   "reject" or "ereject" at any time sets the reject action on the
+   message, and continues script execution.  Apropos of the MIME
+   extension, if an included script sees only a portion of the message
+   and calls a reject, it is the entire message and not the single MIME
+   part that carries the rejection.
+
+
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                   [Page 11]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+4.  Security Considerations
+
+   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.
+
+   Sieve implementations MUST ensure that script names are checked for
+   validity and proper permissions prior to inclusion, in order to
+   prevent a malicious user from gaining access to files accessible to
+   the mail server software that should not be accessible to the user.
+
+   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 including a
+   name that could be used as a vector to attack the storage system.  By
+   way of example, the following include commands should be considered
+   hostile: 'include "./../..//etc/passwd"', 'include "foo$(`rm
+   star`)"'.
+
+   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].
+
+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).
+
+
+
+
+
+
+Daboo & Stone                Standards Track                   [Page 12]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                   [Page 13]
+
+RFC 6609                Sieve Extension: Include                May 2012
+
+
+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.
+
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Daboo & Stone                Standards Track                   [Page 14]
+
diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c
index 516cb6844a91dd003ce32faa60db25b2ec1c56fd..f9e54ec579d7e5db6016b4741302be92a71bc222 100644
--- a/src/lib-sieve/plugins/include/cmd-include.c
+++ b/src/lib-sieve/plugins/include/cmd-include.c
@@ -23,7 +23,7 @@
  * Include command
  *
  * Syntax:
- *   include [LOCATION] <value: string>
+ *   include [LOCATION] [":once"] [":optional"] <value: string>
  *
  * [LOCATION]:
  *   ":personal" / ":global"
@@ -74,11 +74,11 @@ const struct sieve_operation_def include_operation = {
 
 struct cmd_include_context_data {
 	enum ext_include_script_location location;
-	bool location_assigned;
-
-	bool include_once;
 
 	struct sieve_script *script;
+	enum ext_include_flags flags;
+
+	unsigned int location_assigned:1;
 };
 
 /*
@@ -103,17 +103,25 @@ static const struct sieve_argument_def include_global_tag = {
 	NULL, NULL, NULL
 };
 
-static bool cmd_include_validate_once_tag
+static bool cmd_include_validate_boolean_tag
 	(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
 		struct sieve_command *cmd);
 
 static const struct sieve_argument_def include_once_tag = {
 	"once",
 	NULL,
-	cmd_include_validate_once_tag,
+	cmd_include_validate_boolean_tag,
+	NULL, NULL, NULL
+};
+
+static const struct sieve_argument_def include_optional_tag = {
+	"optional",
+	NULL,
+	cmd_include_validate_boolean_tag,
 	NULL, NULL, NULL
 };
 
+
 /*
  * Tag validation
  */
@@ -147,14 +155,17 @@ static bool cmd_include_validate_location_tag
 	return TRUE;
 }
 
-static bool cmd_include_validate_once_tag
+static bool cmd_include_validate_boolean_tag
 (struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_ast_argument **arg,
 	struct sieve_command *cmd)
 {
 	struct cmd_include_context_data *ctx_data =
 		(struct cmd_include_context_data *) cmd->data;
 
-	ctx_data->include_once = TRUE;
+	if ( sieve_argument_is(*arg, include_once_tag) )
+		ctx_data->flags |= EXT_INCLUDE_FLAG_ONCE;
+	else
+		ctx_data->flags |= EXT_INCLUDE_FLAG_OPTIONAL;
 
 	/* Delete this tag (for now) */
 	*arg = sieve_ast_arguments_detach(*arg, 1);
@@ -173,6 +184,7 @@ static bool cmd_include_registered
 	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_personal_tag, 0);
 	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_global_tag, 0);
 	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_once_tag, 0);
+	sieve_validator_register_tag(valdtr, cmd_reg, ext, &include_optional_tag, 0);
 
 	return TRUE;
 }
@@ -204,7 +216,7 @@ static bool cmd_include_validate
 	struct sieve_script *script;
 	const char *script_location, *script_name;
 	enum sieve_error error = SIEVE_ERROR_NONE;
-	bool include = TRUE;
+	int ret;
 
 	/* Check argument */
 	if ( !sieve_validate_positional_argument
@@ -251,33 +263,47 @@ static bool cmd_include_validate
 		(this_ext->svinst, script_location, script_name,
 			sieve_validator_error_handler(valdtr), &error);
 
-	if ( script == NULL ) {
+	ret = 0;
+	if ( script != NULL )
+		ret = sieve_script_open(script, &error);
+
+	if ( script == NULL || ret < 0 ) {
 		if ( error != SIEVE_ERROR_NOT_FOUND ) {
+			if ( script != NULL )
+				sieve_script_unref(&script);
 			return FALSE;
+
+		/* Not found */
 		} else {
 			enum sieve_compile_flags cpflags =
 				sieve_validator_compile_flags(valdtr);
 
-			if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) {
+			if ( (ctx_data->flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0 ) {
+				/* :optional */
+
+			} else if ( (cpflags & SIEVE_COMPILE_FLAG_UPLOADED) != 0 ) {
+				/* Script is being uploaded */
 				sieve_argument_validate_warning(valdtr, arg,
 					"included %s script '%s' does not exist (ignored during upload)",
 					ext_include_script_location_name(ctx_data->location),
 					str_sanitize(script_name, 80));
-				include = FALSE;
+				ctx_data->flags |= EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD;
+
 			} else {
+				/* Should have existed */
 				sieve_argument_validate_error(valdtr, arg,
 					"included %s script '%s' does not exist",
 					ext_include_script_location_name(ctx_data->location),
 					str_sanitize(script_name, 80));
+				if ( script != NULL )
+					sieve_script_unref(&script);
 				return FALSE;
 			}
 		}
 	}
 
-	if ( include ) {
-		ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script);
-		ctx_data->script = script;
-	}
+	ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script);
+	ctx_data->script = script;
 
 	(void)sieve_ast_arguments_detach(arg, 1);
 	return TRUE;
@@ -293,26 +319,20 @@ static bool cmd_include_generate
 	struct cmd_include_context_data *ctx_data =
 		(struct cmd_include_context_data *) cmd->data;
 	const struct ext_include_script_info *included;
-	unsigned int flags = ctx_data->include_once;
 	int ret;
 
-	/* Upon upload ctx_data->script may be NULL if the script was not found. We
-	 * don't emit any code for this include command in that case.
+	/* Compile (if necessary) and include the script into the binary.
+	 * This yields the id of the binary block containing the compiled byte code.
 	 */
-	if ( ctx_data->script != NULL ) {
-		/* Compile (if necessary) and include the script into the binary.
-		 * This yields the id of the binary block containing the compiled byte code.
-		 */
-		if ( (ret=ext_include_generate_include
-			(cgenv, cmd, ctx_data->location, ctx_data->script, &included,
-				ctx_data->include_once)) < 0 )
-	 		return FALSE;
-
-		if ( ret > 0 ) {
-		 	(void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation);
-			(void)sieve_binary_emit_unsigned(cgenv->sblock, included->id);
-			(void)sieve_binary_emit_byte(cgenv->sblock, flags);
-		}
+	if ( (ret=ext_include_generate_include
+		(cgenv, cmd, ctx_data->location, ctx_data->flags, ctx_data->script,
+			&included)) < 0 )
+ 		return FALSE;
+
+	if ( ret > 0 ) {
+	 	(void)sieve_operation_emit(cgenv->sblock, cmd->ext, &include_operation);
+		(void)sieve_binary_emit_unsigned(cgenv->sblock, included->id);
+		(void)sieve_binary_emit_byte(cgenv->sblock, ctx_data->flags);
 	}
 
 	return TRUE;
@@ -371,7 +391,8 @@ static int opc_include_execute
 		return SIEVE_EXEC_BIN_CORRUPT;
 	}
 
-	return ext_include_execute_include(renv, include_id, flags & 0x01);
+	return ext_include_execute_include
+		(renv, include_id, (enum ext_include_flags)flags);
 }
 
 
diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c
index acf2e32a26cbd461508540a73eb8e04d8f75c534..38a893625eb2cadac6cf944e00b5eec067b93fe8 100644
--- a/src/lib-sieve/plugins/include/ext-include-binary.c
+++ b/src/lib-sieve/plugins/include/ext-include-binary.c
@@ -122,16 +122,18 @@ struct ext_include_binary_context *ext_include_binary_init
  */
 
 const struct ext_include_script_info *ext_include_binary_script_include
-(struct ext_include_binary_context *binctx, struct sieve_script *script,
-	enum ext_include_script_location location, struct sieve_binary_block *inc_block)
+(struct ext_include_binary_context *binctx, 
+	enum ext_include_script_location location, enum ext_include_flags flags,
+	struct sieve_script *script,	struct sieve_binary_block *inc_block)
 {
 	pool_t pool = sieve_binary_pool(binctx->binary);
 	struct ext_include_script_info *incscript;
 
 	incscript = p_new(pool, struct ext_include_script_info, 1);
 	incscript->id = array_count(&binctx->include_index)+1;
-	incscript->script = script;
 	incscript->location = location;
+	incscript->flags = flags;
+	incscript->script = script;
 	incscript->block = inc_block;
 
 	/* Unreferenced on binary_free */
@@ -220,9 +222,15 @@ static bool ext_include_binary_save
 	for ( i = 0; i < script_count; i++ ) {
 		struct ext_include_script_info *incscript = scripts[i];
 
-		sieve_binary_emit_unsigned(sblock, sieve_binary_block_get_id(incscript->block));
+		if ( incscript->block != NULL ) {
+			sieve_binary_emit_unsigned
+				(sblock, sieve_binary_block_get_id(incscript->block));
+		} else {
+			sieve_binary_emit_unsigned(sblock, 0);
+		}
 		sieve_binary_emit_byte(sblock, incscript->location);
 		sieve_binary_emit_cstring(sblock, sieve_script_name(incscript->script));
+		sieve_binary_emit_byte(sblock, incscript->flags);
 		sieve_script_binary_write_metadata(incscript->script, sblock);
 	}
 
@@ -267,17 +275,19 @@ static bool ext_include_binary_open
 	/* Read dependencies */
 	for ( i = 0; i < depcount; i++ ) {
 		unsigned int inc_block_id;
-		struct sieve_binary_block *inc_block;
-		unsigned int location;
+		struct sieve_binary_block *inc_block = NULL;
+		unsigned int location, flags;
 		string_t *script_name;
 		const char *script_location;
 		struct sieve_script *script;
+		enum sieve_error error;
 		int ret;
 
 		if (
 			!sieve_binary_read_unsigned(sblock, &offset, &inc_block_id) ||
 			!sieve_binary_read_byte(sblock, &offset, &location) ||
-			!sieve_binary_read_string(sblock, &offset, &script_name) ) {
+			!sieve_binary_read_string(sblock, &offset, &script_name) ||
+			!sieve_binary_read_byte(sblock, &offset, &flags) ) {
 			/* Binary is corrupt, recompile */
 			sieve_sys_error(svinst,
 				"include: failed to read included script "
@@ -286,7 +296,8 @@ static bool ext_include_binary_open
 			return FALSE;
 		}
 
-		if ( (inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) {
+		if ( inc_block_id != 0 &&
+			(inc_block=sieve_binary_block_get(sbin, inc_block_id)) == NULL ) {
 			sieve_sys_error(svinst,
 				"include: failed to find block %d for included script "
 				"from dependency block %d of binary %s", inc_block_id, block_id,
@@ -304,23 +315,59 @@ static bool ext_include_binary_open
 			return FALSE;
 		}
 
-		/* Can we find/open the script dependency ? */
+		/* Can we find the script dependency ? */
 		script_location = ext_include_get_script_location
 			(ext, location, str_c(script_name));
-		if ( script_location == NULL || (script=sieve_script_create
-			(ext->svinst, script_location, str_c(script_name), NULL, NULL)) == NULL )
-			{
+		if ( script_location == NULL ) {
 			/* No, recompile */
 			return FALSE;
 		}
 
-		if ( (ret=sieve_script_binary_read_metadata(script, sblock, &offset))
-			< 0 ) {
+		/* Can we open the script dependency ? */
+		script = sieve_script_create
+			(ext->svinst, script_location, str_c(script_name), NULL, &error);
+		if ( script == NULL ) {
+			/* No, recompile */
+			return FALSE;
+		}
+		if ( sieve_script_open(script, &error) < 0 ) {			
+			if ( error != SIEVE_ERROR_NOT_FOUND ) {
+				/* No, recompile */
+				return FALSE;
+			}
+
+			if ( (flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0 &&
+				 (flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) == 0) {
+				/* Not supposed to be missing, recompile */
+				if ( svinst->debug ) {
+					sieve_sys_debug(svinst,
+						"include: script '%s' contained in binary %s is now missing, "
+						"so recompile", str_c(script_name), sieve_binary_path(sbin));
+				}
+				return FALSE;
+			}
+
+		} else if (inc_block == NULL) {
+			/* Script exists, but it is missing from the binary, recompile no matter
+			 * what.
+			 */
+			if ( svinst->debug ) {
+				sieve_sys_debug(svinst,
+					"include: script '%s' is missing in binary %s, but is now available, "
+					"so recompile", str_c(script_name), sieve_binary_path(sbin));
+			}
+			return FALSE;
+		}
+
+		/* Can we read script metadata ? */
+		if ( (ret=sieve_script_binary_read_metadata
+			(script, sblock, &offset))	< 0 ) {
 			/* Binary is corrupt, recompile */
 			sieve_sys_error(svinst,
 				"include: dependency block %d of binary %s "
 				"contains invalid script metadata for script %s",
 				block_id, sieve_binary_path(sbin), sieve_script_location(script));
+			sieve_script_unref(&script);
 			return FALSE;
 		}
 
@@ -328,7 +375,7 @@ static bool ext_include_binary_open
 			binctx->outdated = TRUE;
 
 		(void)ext_include_binary_script_include
-			(binctx, script, location, inc_block);
+			(binctx, location, flags, script, inc_block);
 
 		sieve_script_unref(&script);
 	}
@@ -363,7 +410,8 @@ static void ext_include_binary_free
 
 	/* Release references to all included script objects */
 	hctx = hash_table_iterate_init(binctx->included_scripts);
-	while ( hash_table_iterate(hctx, binctx->included_scripts, &script, &incscript) )
+	while ( hash_table_iterate
+		(hctx, binctx->included_scripts, &script, &incscript) )
 		sieve_script_unref(&incscript->script);
 	hash_table_iterate_deinit(&hctx);
 
@@ -391,23 +439,31 @@ bool ext_include_binary_dump
 		return FALSE;
 
 	hctx = hash_table_iterate_init(binctx->included_scripts);
-	while ( hash_table_iterate(hctx, binctx->included_scripts, &script, &incscript) ) {
-		unsigned int block_id = sieve_binary_block_get_id(incscript->block);
+	while ( hash_table_iterate
+		(hctx, binctx->included_scripts, &script, &incscript) ) {
 
-		sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)",
-			ext_include_script_location_name(incscript->location),
-			sieve_script_name(incscript->script), block_id);
+		if ( incscript->block == NULL ) {
+			sieve_binary_dump_sectionf(denv, "Included %s script '%s' (MISSING)",
+				ext_include_script_location_name(incscript->location),
+				sieve_script_name(incscript->script));
 
-		denv->sblock = incscript->block;
-		denv->cdumper = sieve_code_dumper_create(denv);
+		} else {
+			unsigned int block_id = sieve_binary_block_get_id(incscript->block);
 
-		if ( denv->cdumper == NULL )
-			return FALSE;
+			sieve_binary_dump_sectionf(denv, "Included %s script '%s' (block: %d)",
+				ext_include_script_location_name(incscript->location),
+				sieve_script_name(incscript->script), block_id);
 
-		sieve_code_dumper_run(denv->cdumper);
-		sieve_code_dumper_free(&(denv->cdumper));
-	}
+			denv->sblock = incscript->block;
+			denv->cdumper = sieve_code_dumper_create(denv);
 
+			if ( denv->cdumper == NULL )
+				return FALSE;
+
+			sieve_code_dumper_run(denv->cdumper);
+			sieve_code_dumper_free(&(denv->cdumper));
+		}
+	}
 	hash_table_iterate_deinit(&hctx);
 
 	return TRUE;
diff --git a/src/lib-sieve/plugins/include/ext-include-binary.h b/src/lib-sieve/plugins/include/ext-include-binary.h
index 448ae9673b68a8940bbde77a0b990cc9c96fa412..0d9ca0b099a525212786ca47c7c0c33aebc5fb49 100644
--- a/src/lib-sieve/plugins/include/ext-include-binary.h
+++ b/src/lib-sieve/plugins/include/ext-include-binary.h
@@ -30,18 +30,19 @@ struct sieve_variable_scope_binary *ext_include_binary_get_global_scope
  */
 
 struct ext_include_script_info {
-    unsigned int id;
+	unsigned int id;
 
-    struct sieve_script *script;
-    enum ext_include_script_location location;
+	struct sieve_script *script;
+	enum ext_include_flags flags;
+	enum ext_include_script_location location;
 
-    struct sieve_binary_block *block;
+	struct sieve_binary_block *block;
 };
 
 const struct ext_include_script_info *ext_include_binary_script_include
-	(struct ext_include_binary_context *binctx, struct sieve_script *script,
-		enum ext_include_script_location location,
-		struct sieve_binary_block *block);
+	(struct ext_include_binary_context *binctx, 
+		enum ext_include_script_location location, enum ext_include_flags flags,
+		struct sieve_script *script, struct sieve_binary_block *inc_block);
 bool ext_include_binary_script_is_included
 	(struct ext_include_binary_context *binctx, struct sieve_script *script,
 		const struct ext_include_script_info **script_info_r);
diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c
index 28e044cf4983eec3c4c84adf9f344c0c74f5729f..abdab4631d6be403e2106fd64ee8c1888f44030d 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.c
+++ b/src/lib-sieve/plugins/include/ext-include-common.c
@@ -454,8 +454,9 @@ struct sieve_variable_storage *ext_include_interpreter_get_global_variables
 
 int ext_include_generate_include
 (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd,
-	enum ext_include_script_location location, struct sieve_script *script,
-	const struct ext_include_script_info **included_r, bool once)
+	enum ext_include_script_location location, 	enum ext_include_flags flags,
+	struct sieve_script *script,
+	const struct ext_include_script_info **included_r)
 {
 	const struct sieve_extension *this_ext = cmd->ext;
 	struct ext_include_context *ext_ctx =
@@ -489,7 +490,7 @@ int ext_include_generate_include
 	}
 
 	/* Check for circular include */
-	if ( !once ) {
+	if ( (flags & EXT_INCLUDE_FLAG_ONCE) == 0 ) {
 		pctx = ctx;
 		while ( pctx != NULL ) {
 			if ( sieve_script_equals(pctx->script, script) ) {
@@ -517,7 +518,6 @@ int ext_include_generate_include
 	/* Is the script already compiled into the current binary? */
 	if ( !ext_include_binary_script_is_included(binctx, script, &included) )
 	{
-		struct sieve_binary_block *inc_block;
 		const char *script_name = sieve_script_name(script);
 		enum sieve_compile_flags cpflags = cgenv->flags;
 
@@ -532,55 +532,65 @@ int ext_include_generate_include
 
 		/* No, allocate a new block in the binary and mark the script as included.
 		 */
-		inc_block = sieve_binary_block_create(sbin);
-		included = ext_include_binary_script_include
-			(binctx, script, location, inc_block);
-
-		/* Parse */
-		if ( (ast = sieve_parse(script, ehandler, NULL)) == NULL ) {
-	 		sieve_command_generate_error(gentr, cmd,
-	 			"failed to parse included script '%s'", str_sanitize(script_name, 80));
-	 		return -1;
-		}
+		if ( !sieve_script_is_open(script) ) {
+			i_assert((flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) != 0 ||
+				(flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0);
+			included = ext_include_binary_script_include
+				(binctx, location, flags, script, NULL);
+			result = 0;
+
+		}	else {
+			struct sieve_binary_block *inc_block = sieve_binary_block_create(sbin);
+
+			included = ext_include_binary_script_include
+				(binctx, location, flags, script, inc_block);
+
+			/* Parse */
+			if ( (ast = sieve_parse(script, ehandler, NULL)) == NULL ) {
+		 		sieve_command_generate_error(gentr, cmd,
+		 			"failed to parse included script '%s'", str_sanitize(script_name, 80));
+		 		return -1;
+			}
 
-		/* Included scripts inherit global variable scope */
-		(void)ext_include_create_ast_context(this_ext, ast, cmd->ast_node->ast);
+			/* Included scripts inherit global variable scope */
+			(void)ext_include_create_ast_context(this_ext, ast, cmd->ast_node->ast);
 
-		if ( location == EXT_INCLUDE_LOCATION_GLOBAL )
+			if ( location == EXT_INCLUDE_LOCATION_GLOBAL )
 				cpflags &= ~SIEVE_RUNTIME_FLAG_NOGLOBAL;
-		else
+			else
 				cpflags |= SIEVE_RUNTIME_FLAG_NOGLOBAL;
 
-		/* Validate */
-		if ( !sieve_validate(ast, ehandler, cpflags, NULL) ) {
-			sieve_command_generate_error(gentr, cmd,
-				"failed to validate included script '%s'",
-				str_sanitize(script_name, 80));
-	 		sieve_ast_unref(&ast);
-	 		return -1;
-	 	}
+			/* Validate */
+			if ( !sieve_validate(ast, ehandler, cpflags, NULL) ) {
+				sieve_command_generate_error(gentr, cmd,
+					"failed to validate included script '%s'",
+					str_sanitize(script_name, 80));
+		 		sieve_ast_unref(&ast);
+		 		return -1;
+		 	}
+
+			/* Generate
+			 *
+			 * FIXME: It might not be a good idea to recurse code generation for
+			 * included scripts.
+			 */
+		 	subgentr = sieve_generator_create(ast, ehandler, cpflags);
+			ext_include_initialize_generator_context(cmd->ext, subgentr, ctx, script);
+	
+			if ( sieve_generator_run(subgentr, &inc_block) == NULL ) {
+				sieve_command_generate_error(gentr, cmd,
+					"failed to generate code for included script '%s'",
+					str_sanitize(script_name, 80));
+		 		result = -1;
+			}
 
-		/* Generate
-		 *
-		 * FIXME: It might not be a good idea to recurse code generation for
-		 * included scripts.
-		 */
-	 	subgentr = sieve_generator_create(ast, ehandler, cpflags);
-		ext_include_initialize_generator_context(cmd->ext, subgentr, ctx, script);
+			sieve_generator_free(&subgentr);
 
-		if ( sieve_generator_run(subgentr, &inc_block) == NULL ) {
-			sieve_command_generate_error(gentr, cmd,
-				"failed to generate code for included script '%s'",
-				str_sanitize(script_name, 80));
-	 		result = -1;
+			/* Cleanup */
+			sieve_ast_unref(&ast);
 		}
-
-		sieve_generator_free(&subgentr);
-
-		/* Cleanup */
-		sieve_ast_unref(&ast);
 	}
-
+	
 	if ( result > 0 ) *included_r = included;
 
 	return result;
diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h
index 20763ed07acb2216419fa4e8f47b84d366e7bf90..53b1131f03d51e8ff11809f0bd2e973b4d3b5e76 100644
--- a/src/lib-sieve/plugins/include/ext-include-common.h
+++ b/src/lib-sieve/plugins/include/ext-include-common.h
@@ -21,6 +21,12 @@ struct ext_include_binary_context;
  * Types
  */
 
+enum ext_include_flags { // stored in one byte
+	EXT_INCLUDE_FLAG_ONCE = 0x01,
+	EXT_INCLUDE_FLAG_OPTIONAL = 0x02,
+	EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD = 0x04
+};
+
 enum ext_include_script_location {
 	EXT_INCLUDE_LOCATION_PERSONAL,
 	EXT_INCLUDE_LOCATION_GLOBAL,
@@ -144,8 +150,9 @@ void ext_include_register_generator_context
 
 int ext_include_generate_include
 	(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd,
-		enum ext_include_script_location location, struct sieve_script *script,
-		const struct ext_include_script_info **included_r, bool once);
+		enum ext_include_script_location location,
+		enum ext_include_flags flags, struct sieve_script *script,
+		const struct ext_include_script_info **included_r);
 
 /* Interpreter context */
 
diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c
index 6471edda3add2c9b4620cb3febfcdf94c07a91d1..9172d5d0106e35190806eb18d44f3b48237b2be1 100644
--- a/src/lib-sieve/plugins/include/ext-include.c
+++ b/src/lib-sieve/plugins/include/ext-include.c
@@ -64,6 +64,8 @@ static bool ext_include_binary_load
 
 const struct sieve_extension_def include_extension = {
 	.name = "include",
+	.version = 1,
+
 	.load = ext_include_load,
 	.unload = ext_include_unload,
 	.validator_load = ext_include_validator_load,
@@ -72,6 +74,7 @@ const struct sieve_extension_def include_extension = {
 	.binary_load = ext_include_binary_load,
 	.binary_dump = ext_include_binary_dump,
 	.code_dump = ext_include_code_dump,
+
 	SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations)
 };
 
diff --git a/src/lib-sieve/sieve-lexer.c b/src/lib-sieve/sieve-lexer.c
index 20af2c83fc8f6f757da1a36b98140982c58f2b7d..e7527fe7e358a55df41eb40a949d89295f8d8e0d 100644
--- a/src/lib-sieve/sieve-lexer.c
+++ b/src/lib-sieve/sieve-lexer.c
@@ -71,8 +71,7 @@ const struct sieve_lexer *sieve_lexer_create
 	const struct stat *st;
 
 	/* Open script as stream */
-	stream = sieve_script_open(script, error_r);
-	if ( stream == NULL )
+	if ( sieve_script_get_stream(script, &stream, error_r) < 0 )
 		return NULL;
 
 	/* Check script size */
@@ -121,7 +120,6 @@ void sieve_lexer_free(const struct sieve_lexer **lexer)
 
 	i_stream_unref(&scanner->input);
 
-	sieve_script_close(scanner->script);
 	sieve_script_unref(&scanner->script);
 
 	sieve_error_handler_unref(&scanner->ehandler);
diff --git a/src/lib-sieve/sieve-script-dict.c b/src/lib-sieve/sieve-script-dict.c
index 489c6bbabb421a777250c289565c289866345a75..1ea1fd22425c0b86acc065770de3bee76eb77f63 100644
--- a/src/lib-sieve/sieve-script-dict.c
+++ b/src/lib-sieve/sieve-script-dict.c
@@ -49,7 +49,18 @@ static struct sieve_script *sieve_dict_script_alloc(void)
 	return &script->script;
 }
 
-static int sieve_dict_script_create
+static void sieve_dict_script_free(struct sieve_script *_script)
+{
+	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
+
+	if ( script->dict != NULL )
+		dict_deinit(&script->dict);
+
+	if ( script->data_pool != NULL )
+		pool_unref(&script->data_pool);
+}
+
+static int sieve_dict_script_open
 (struct sieve_script *_script, const char *data, const char *const *options,
 	enum sieve_error *error_r)
 {
@@ -154,19 +165,9 @@ static int sieve_dict_script_create
 	return 0;
 }
 
-static void sieve_dict_script_destroy(struct sieve_script *_script)
-{
-	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
-
-	if ( script->dict != NULL )
-		dict_deinit(&script->dict);
-
-	if ( script->data_pool != NULL )
-		pool_unref(&script->data_pool);
-}
-
-static struct istream *sieve_dict_script_open
-(struct sieve_script *_script, enum sieve_error *error_r)
+static int sieve_dict_script_get_stream
+(struct sieve_script *_script, struct istream **stream_r,
+	enum sieve_error *error_r)
 {
 	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
 	struct sieve_instance *svinst = _script->svinst;
@@ -194,21 +195,11 @@ static struct istream *sieve_dict_script_open
 				"not found at path %s",	script->data_id, name, path);
 		}
 		*error_r = SIEVE_ERROR_TEMP_FAIL;
-		return NULL;
+		return -1;
 	}
 
-	return i_stream_create_from_data(script->data, strlen(script->data));
-}
-
-static void sieve_dict_script_close(struct sieve_script *_script)
-{
-	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
-
-	pool_unref(&script->data_pool);
-
-	script->data_pool = NULL;
-	script->data_id = NULL;
-	script->data = NULL;
+	*stream_r = i_stream_create_from_data(script->data, strlen(script->data));
+	return 0;
 }
 
 static int sieve_dict_script_binary_read_metadata
@@ -270,18 +261,18 @@ static int sieve_dict_script_binary_save
 static bool sieve_dict_script_equals
 (const struct sieve_script *_script, const struct sieve_script *_other)
 {
-        struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
-        struct sieve_dict_script *other = (struct sieve_dict_script *)_other;
+	struct sieve_dict_script *script = (struct sieve_dict_script *)_script;
+	struct sieve_dict_script *other = (struct sieve_dict_script *)_other;
 
-        if ( script == NULL || other == NULL )
-                return FALSE;
+	if ( script == NULL || other == NULL )
+		return FALSE;
 
-        if ( strcmp(script->dict_uri, other->dict_uri) != 0 )
+	if ( strcmp(script->dict_uri, other->dict_uri) != 0 )
 		return FALSE;
 
 	i_assert( _script->name != NULL && _other->name != NULL );
 
-        return ( strcmp(_script->name, _other->name) == 0 );
+	return ( strcmp(_script->name, _other->name) == 0 );
 }
 
 
@@ -289,11 +280,11 @@ const struct sieve_script sieve_dict_script = {
 	.driver_name = SIEVE_DICT_SCRIPT_DRIVER_NAME,
 	.v = {
 		sieve_dict_script_alloc,
-		sieve_dict_script_create,
-		sieve_dict_script_destroy,
+		sieve_dict_script_free,
 
 		sieve_dict_script_open,
-		sieve_dict_script_close,
+
+		sieve_dict_script_get_stream,
 
 		sieve_dict_script_binary_read_metadata,
 		sieve_dict_script_binary_write_metadata,
diff --git a/src/lib-sieve/sieve-script-file.c b/src/lib-sieve/sieve-script-file.c
index d7c184f81e81c496940086dab40591ea418af623..2e3bf60d7eb4d5dc1fa8dd1f1a115e05945bdef6 100644
--- a/src/lib-sieve/sieve-script-file.c
+++ b/src/lib-sieve/sieve-script-file.c
@@ -113,7 +113,7 @@ static struct sieve_script *sieve_file_script_alloc(void)
 	return &script->script;
 }
 
-static int sieve_file_script_create
+static int sieve_file_script_open
 (struct sieve_script *_script, const char *path, const char *const *options,
 	enum sieve_error *error_r)
 {
@@ -248,8 +248,9 @@ static int sieve_file_script_create
 	return ( success ? 0 : -1 );
 }
 
-static struct istream *sieve_file_script_open
-(struct sieve_script *_script, enum sieve_error *error_r)
+static int sieve_file_script_get_stream
+(struct sieve_script *_script, struct istream **stream_r,
+	enum sieve_error *error_r)
 {
 	struct sieve_file_script *script = (struct sieve_file_script *)_script;
 	struct sieve_instance *svinst = _script->svinst;
@@ -261,7 +262,7 @@ static struct istream *sieve_file_script_open
 
 	if ( (fd=open(script->path, O_RDONLY)) < 0 ) {
 		sieve_file_script_handle_error(_script, script->path, name, error_r);
-		return NULL;
+		return -1;
 	}
 
 	if ( fstat(fd, &st) != 0 ) {
@@ -292,7 +293,8 @@ static struct istream *sieve_file_script_open
 		}
 	}
 
-	return result;
+	*stream_r = result;
+	return 0;
 }
 
 static int sieve_file_script_get_size
@@ -356,11 +358,11 @@ const struct sieve_script sieve_file_script = {
 	.driver_name = SIEVE_FILE_SCRIPT_DRIVER_NAME,
 	.v = {
 		sieve_file_script_alloc,
-		sieve_file_script_create,
 		NULL,
 
 		sieve_file_script_open,
-		NULL,
+
+		sieve_file_script_get_stream,
 
 		sieve_file_script_binary_read_metadata,
 		NULL,
diff --git a/src/lib-sieve/sieve-script-private.h b/src/lib-sieve/sieve-script-private.h
index e317ff0d76b48d5e518bd8e4a3c9e372bd527399..955885226c678413250ee3d35772659885bcec05 100644
--- a/src/lib-sieve/sieve-script-private.h
+++ b/src/lib-sieve/sieve-script-private.h
@@ -12,17 +12,16 @@
 
 struct sieve_script_vfuncs {
 	struct sieve_script *(*alloc)(void);
-	int (*create)
-		(struct sieve_script *script, const char *data, const char *const *options,
-			enum sieve_error *error_r);
-	void (*destroy)
-		(struct sieve_script *script);
+	void (*destroy)(struct sieve_script *script);
 
-	struct istream *(*open)
-		(struct sieve_script *script, enum sieve_error *error_r);
-	void (*close)
-		(struct sieve_script *script);
+	int (*open)
+		(struct sieve_script *script, const char *data,
+			const char *const *options, enum sieve_error *error_r);
 
+	int (*get_stream)
+		(struct sieve_script *script, struct istream **stream_r,
+			enum sieve_error *error_r);
+	
 	int (*binary_read_metadata)
 		(struct sieve_script *_script, struct sieve_binary_block *sblock,
 			sieve_size_t *offset);
@@ -53,18 +52,20 @@ struct sieve_script {
 	struct sieve_error_handler *ehandler;
 
 	const char *name;
+	const char *data;
 	const char *location;
 	const char *bin_dir;
 
 	/* Stream */
 	struct istream *stream;
+
+	unsigned int open:1;
 };
 
-struct sieve_script *sieve_script_init
+void sieve_script_init
 	(struct sieve_script *script, struct sieve_instance *svinst,
 		const struct sieve_script *script_class, const char *data,
-		const char *name, struct sieve_error_handler *ehandler,
-		enum sieve_error *error_r);
+		const char *name, struct sieve_error_handler *ehandler);
 
 int sieve_script_setup_bindir
 	(struct sieve_script *script, mode_t mode);
diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c
index 82558674f17cc1bf89d78029a2f31b95794dc301..e627125f2f0fd38fb3fd78289c22aea1cc1a2c6a 100644
--- a/src/lib-sieve/sieve-script.c
+++ b/src/lib-sieve/sieve-script.c
@@ -180,51 +180,19 @@ static bool sieve_script_location_parse
 	return TRUE;
 }
 
-struct sieve_script *sieve_script_init
+void sieve_script_init
 (struct sieve_script *script, struct sieve_instance *svinst,
-	const struct sieve_script *script_class, const char *data, const char *name,
-	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
+	const struct sieve_script *script_class, const char *data,
+	const char *name, struct sieve_error_handler *ehandler)
 {
-	enum sieve_error error;
-	const char *const *options = NULL;
-	const char *location = NULL, *parse_error = NULL;
-
-	if ( error_r != NULL )
-		*error_r = SIEVE_ERROR_NONE;
-
 	script->script_class = script_class;
 	script->refcount = 1;
 	script->svinst = svinst;
-
 	script->ehandler = ehandler;
-
+	script->data = p_strdup_empty(script->pool, data);
 	script->name = p_strdup_empty(script->pool, name);
 
-	if ( !sieve_script_location_parse
-		(script, data, &location, &options, &parse_error) ) {
-		sieve_critical(svinst, ehandler, NULL,
-			"failed to access sieve script", "failed to parse script location: %s",
-			parse_error);
-		if ( error_r != NULL )
-			*error_r = SIEVE_ERROR_TEMP_FAIL;
-		return NULL;
-	}
-
-	if ( script->v.create(script, location, options, &error) < 0 ) {
-
-		if ( error_r == NULL ) {
-			if ( error == SIEVE_ERROR_NOT_FOUND )
-				sieve_error(ehandler, script->name, "sieve script does not exist");
-		} else {
-			*error_r = error;
-		}
-		return NULL;
-	}
-
-	i_assert( script->location != NULL );
-
 	sieve_error_handler_ref(ehandler);
-	return script;
 }
 
 struct sieve_script *sieve_script_create
@@ -257,36 +225,109 @@ struct sieve_script *sieve_script_create
 			else
 				script_class = NULL;
 
-			if ( script_class == NULL )
-				i_error("Unknown sieve script driver module: %s", driver);
+			if ( script_class == NULL ) {
+				sieve_sys_error(svinst,
+					"Unknown sieve script driver module: %s", driver);
+			}
 		} T_END;
 	}
 
-	if ( script_class == NULL )
+	if ( script_class == NULL ) {
+		if ( error_r != NULL )
+			*error_r = SIEVE_ERROR_TEMP_FAIL;
 		return NULL;
+	}
 
 	script = script_class->v.alloc();
-	if ( sieve_script_init
-		(script, svinst, script_class, data, name, ehandler, error_r) == NULL ) {
-		pool_unref(&script->pool);
-		return NULL;
+	sieve_script_init(script, svinst, script_class, data, name, ehandler);
+	return script;
+}
+
+int sieve_script_open
+(struct sieve_script *script, enum sieve_error *error_r)
+{
+	struct sieve_instance *svinst = script->svinst;
+	struct sieve_error_handler *ehandler = script->ehandler;
+	enum sieve_error error;
+	const char *const *options = NULL;
+	const char *location = NULL, *parse_error = NULL;
+
+	if ( error_r != NULL )
+		*error_r = SIEVE_ERROR_NONE;
+
+	if ( script->open )
+		return 0;
+
+	if ( !sieve_script_location_parse
+		(script, script->data, &location, &options, &parse_error) ) {
+		sieve_critical(svinst, ehandler, NULL,
+			"failed to access sieve script", "failed to parse script location: %s",
+			parse_error);
+		if ( error_r != NULL )
+			*error_r = SIEVE_ERROR_TEMP_FAIL;
+		return -1;
+	}
+
+	if ( script->v.open(script, location, options, &error) < 0 ) {
+		if ( error_r == NULL ) {
+			if ( error == SIEVE_ERROR_NOT_FOUND )
+				sieve_error(ehandler, script->name, "sieve script does not exist");
+		} else {
+			*error_r = error;
+		}
+		return -1;
 	}
 
+	i_assert( script->location != NULL );
+	i_assert( script->name != NULL );
+	script->open = TRUE;
+	return 0;
+}
+
+int sieve_script_open_as
+(struct sieve_script *script, const char *name, enum sieve_error *error_r)
+{
+	if ( sieve_script_open(script, error_r) < 0 )
+		return -1;
+
+	/* override name */
+	script->name = p_strdup(script->pool, name);
+	return 0;
+}
+
+struct sieve_script *sieve_script_create_open
+(struct sieve_instance *svinst, const char *location, const char *name,
+	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
+{
+	struct sieve_script *script;
+
+	script = sieve_script_create(svinst, location, name, ehandler, error_r);
+	if ( script == NULL )
+		return NULL;
+
+	if ( sieve_script_open(script, error_r) < 0 ) {
+		sieve_script_unref(&script);
+		return NULL;
+	}
+	
 	return script;
 }
 
-struct sieve_script *sieve_script_create_as
+struct sieve_script *sieve_script_create_open_as
 (struct sieve_instance *svinst, const char *location, const char *name,
 	struct sieve_error_handler *ehandler, enum sieve_error *error_r)
 {
 	struct sieve_script *script;
 
-	if ( (script=sieve_script_create(svinst, location, NULL, ehandler, error_r))
-		== NULL )
+	script = sieve_script_create(svinst, location, name, ehandler, error_r);
+	if ( script == NULL )
 		return NULL;
 
-	/* override name */
-	script->name = p_strdup(script->pool, name);
+	if ( sieve_script_open_as(script, name, error_r) < 0 ) {
+		sieve_script_unref(&script);
+		return NULL;
+	}
+	
 	return script;
 }
 
@@ -338,6 +379,7 @@ struct sieve_instance *sieve_script_svinst(const struct sieve_script *script)
 
 int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r)
 {
+	struct istream *stream;
 	int ret;
 
 	if ( script->v.get_size != NULL ) {
@@ -346,31 +388,42 @@ int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r)
 	}
 
 	/* Try getting size from the stream */
-	if ( script->stream == NULL && sieve_script_open(script, NULL) == NULL )
+	if ( script->stream == NULL &&
+		sieve_script_get_stream(script, &stream, NULL) < 0 )
 		return -1;
 
 	return i_stream_get_size(script->stream, TRUE, size_r);
 }
 
+bool sieve_script_is_open(const struct sieve_script *script)
+{
+	return script->open;
+}
+
 /*
  * Stream management
  */
 
-struct istream *sieve_script_open
-(struct sieve_script *script, enum sieve_error *error_r)
+int sieve_script_get_stream
+(struct sieve_script *script, struct istream **stream_r,
+	enum sieve_error *error_r)
 {
 	enum sieve_error error;
+	int ret;
 
 	if ( error_r != NULL )
 		*error_r = SIEVE_ERROR_NONE;
 
-	if ( script->stream == NULL ) {
-		T_BEGIN {
-			script->stream = script->v.open(script, &error);
-		} T_END;
+	if ( script->stream != NULL ) {
+		*stream_r = script->stream;
+		return 0;
 	}
 
-	if ( script->stream == NULL ) {
+	T_BEGIN {
+		ret = script->v.get_stream(script, &script->stream, &error);
+	} T_END;
+
+	if ( ret < 0 ) {
 		if ( error_r == NULL ) {
 			if ( error == SIEVE_ERROR_NOT_FOUND ) {
 				sieve_error(script->ehandler, script->name,
@@ -379,23 +432,11 @@ struct istream *sieve_script_open
 		} else {
 			*error_r = error;
 		}
+		return -1;
 	}
 
-	return script->stream;
-}
-
-void sieve_script_close(struct sieve_script *script)
-{
-	if ( script->stream != NULL )
-		return;
-
-	i_stream_unref(&script->stream);
-
-	if ( script->v.close != NULL ) {
-		T_BEGIN {
-			script->v.close(script);
-		} T_END;
-	}
+	*stream_r = script->stream;
+	return 0;
 }
 
 /*
@@ -425,6 +466,8 @@ bool sieve_script_equals
 
 unsigned int sieve_script_hash(const struct sieve_script *script)
 {
+	i_assert( script->name != NULL );
+
 	return str_hash(script->name);
 }
 
diff --git a/src/lib-sieve/sieve-script.h b/src/lib-sieve/sieve-script.h
index 93a3d93d4369bba1c47041673b275db47992a728..1d9a42518086ec79f5f21d0fa41b3b48107dace2 100644
--- a/src/lib-sieve/sieve-script.h
+++ b/src/lib-sieve/sieve-script.h
@@ -26,13 +26,22 @@ ARRAY_DEFINE_TYPE(sieve_scripts, struct sieve_script *);
 struct sieve_script *sieve_script_create
 	(struct sieve_instance *svinst, const char *location, const char *name,
 		struct sieve_error_handler *ehandler, enum sieve_error *error_r);
-struct sieve_script *sieve_script_create_as
-	(struct sieve_instance *svinst, const char *location, const char *name,
-		struct sieve_error_handler *ehandler, enum sieve_error *error_r);
 
 void sieve_script_ref(struct sieve_script *script);
 void sieve_script_unref(struct sieve_script **script);
 
+int sieve_script_open
+	(struct sieve_script *script, enum sieve_error *error_r);
+int sieve_script_open_as
+	(struct sieve_script *script, const char *name, enum sieve_error *error_r);
+
+struct sieve_script *sieve_script_create_open
+	(struct sieve_instance *svinst, const char *location, const char *name,
+		struct sieve_error_handler *ehandler, enum sieve_error *error_r);
+struct sieve_script *sieve_script_create_open_as
+	(struct sieve_instance *svinst, const char *location, const char *name,
+		struct sieve_error_handler *ehandler, enum sieve_error *error_r);
+
 /*
  * Accessors
  */
@@ -41,6 +50,8 @@ const char *sieve_script_name(const struct sieve_script *script);
 const char *sieve_script_location(const struct sieve_script *script);
 struct sieve_instance *sieve_script_svinst(const struct sieve_script *script);
 
+bool sieve_script_is_open(const struct sieve_script *script);
+
 /*
  * Saving/loading Sieve binaries
  */
@@ -61,10 +72,9 @@ int sieve_script_binary_save
  * Stream management
  */
 
-struct istream *sieve_script_open
-	(struct sieve_script *script, enum sieve_error *error_r);
-void sieve_script_close(struct sieve_script *script);
-
+int sieve_script_get_stream
+	(struct sieve_script *script, struct istream **stream_r,
+		enum sieve_error *error_r);
 int sieve_script_get_size(struct sieve_script *script, uoff_t *size_r);
 
 /*
diff --git a/src/lib-sieve/sieve.c b/src/lib-sieve/sieve.c
index 3d60dc7aaca64abf2e7061bfee50b5a49fc16700..8e310c685d8eb87263e53b5d36618848aa208df5 100644
--- a/src/lib-sieve/sieve.c
+++ b/src/lib-sieve/sieve.c
@@ -255,7 +255,7 @@ struct sieve_binary *sieve_compile
 	struct sieve_script *script;
 	struct sieve_binary *sbin;
 
-	if ( (script = sieve_script_create
+	if ( (script = sieve_script_create_open
 		(svinst, script_location, script_name, ehandler, error_r)) == NULL )
 		return NULL;
 
@@ -375,10 +375,8 @@ struct sieve_binary *sieve_open
 	struct sieve_binary *sbin;
 
 	/* First open the scriptfile itself */
-	script = sieve_script_create
-		(svinst, script_location, script_name, ehandler, error_r);
-
-	if ( script == NULL ) {
+	if ( (script=sieve_script_create_open
+		(svinst, script_location, script_name, ehandler, error_r)) == NULL ) {
 		/* Failed */
 		return NULL;
 	}
diff --git a/src/lib-sievestorage/sieve-storage-script.c b/src/lib-sievestorage/sieve-storage-script.c
index 7314addcb7d1f442fe1bc44c65807cac51f97759..70583c180043df0bee63c43f4f4ce5d154d2394a 100644
--- a/src/lib-sievestorage/sieve-storage-script.c
+++ b/src/lib-sievestorage/sieve-storage-script.c
@@ -63,18 +63,18 @@ struct sieve_script *sieve_storage_script_init_from_path
 	st_script->file.script.pool = pool;
 	st_script->storage = storage;
 
-	if ( sieve_script_init
+	sieve_script_init
 		(&st_script->file.script, storage->svinst, &sieve_file_script, path,
-			scriptname,	sieve_storage_get_error_handler(storage), &error) != NULL ) {
-		return &st_script->file.script;
-	}
-
-	pool_unref(&pool);
+			scriptname,	sieve_storage_get_error_handler(storage));
 
-	if ( error == SIEVE_ERROR_NOT_FOUND )
-		sieve_storage_set_error(storage, error, "Script does not exist.");
+	if ( sieve_script_open(&st_script->file.script, &error) < 0 ) {
+		if ( error == SIEVE_ERROR_NOT_FOUND )
+			sieve_storage_set_error(storage, error, "Script does not exist.");
+		pool_unref(&pool);
+		return NULL;
+	}
 
-	return NULL;
+	return &st_script->file.script;
 }
 
 struct sieve_script *sieve_storage_script_init
diff --git a/src/managesieve/cmd-getscript.c b/src/managesieve/cmd-getscript.c
index d5aae5c8e6e2551081fd944d749c4bbb6a06b9e2..232fe1a9065040776b85f1ee11359d2afaa2ea8d 100644
--- a/src/managesieve/cmd-getscript.c
+++ b/src/managesieve/cmd-getscript.c
@@ -105,9 +105,8 @@ bool cmd_getscript(struct client_command_context *cmd)
 		return cmd_getscript_finish(ctx);
 	}
 
-	ctx->script_stream = sieve_script_open(ctx->script, &error);
-
-	if ( ctx->script_stream == NULL ) {
+	if ( sieve_script_get_stream
+		(ctx->script, &ctx->script_stream, &error) < 0 ) {
 		if ( error == SIEVE_ERROR_NOT_FOUND )
 			sieve_storage_set_error(client->storage, error, "Script does not exist.");
 		ctx->failed = TRUE;
diff --git a/src/plugins/lda-sieve/lda-sieve-plugin.c b/src/plugins/lda-sieve/lda-sieve-plugin.c
index d8f3c5b86dff8a89b41a0d4bee8f951d80293a71..797acd2bd6f45fb61d70ebfe42e99296b96008db 100644
--- a/src/plugins/lda-sieve/lda-sieve-plugin.c
+++ b/src/plugins/lda-sieve/lda-sieve-plugin.c
@@ -605,7 +605,7 @@ static int lda_sieve_deliver_mail
 
 		user_location = lda_sieve_get_personal_location(svinst, mdctx->dest_user);
 		if ( user_location != NULL ) {
-			srctx.user_script = sieve_script_create_as
+			srctx.user_script = sieve_script_create_open_as
 				(svinst, user_location, "main script", master_ehandler, &error);
 
 			if ( srctx.user_script == NULL ) {
@@ -629,7 +629,7 @@ static int lda_sieve_deliver_mail
 		if ( srctx.user_script == NULL ) {
 			default_location = lda_sieve_get_default_location(mdctx->dest_user);
 			if ( default_location != NULL ) {
-				srctx.main_script = sieve_script_create_as
+				srctx.main_script = sieve_script_create_open_as
 					(svinst, default_location, "main script", master_ehandler, &error);
 
 				if ( srctx.main_script == NULL && error == SIEVE_ERROR_NOT_FOUND &&
diff --git a/tests/extensions/include/execute/optional.sieve b/tests/extensions/include/execute/optional.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..a6ad479fe0f81ce816ca9d48a616864cd7d5e4ea
--- /dev/null
+++ b/tests/extensions/include/execute/optional.sieve
@@ -0,0 +1,5 @@
+require "include";
+
+include :optional "optional-1";
+include :optional "optional-2";
+include :optional "optional-3";
diff --git a/tests/extensions/include/included/optional-1.sieve b/tests/extensions/include/included/optional-1.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..288d1412c4f12932401fbe157876a0465852726f
--- /dev/null
+++ b/tests/extensions/include/included/optional-1.sieve
@@ -0,0 +1,9 @@
+require "include";
+require "variables";
+
+global "result";
+
+set "result" "${result} ONE";
+
+return;
+
diff --git a/tests/extensions/include/included/optional-2.sieve b/tests/extensions/include/included/optional-2.sieve
new file mode 100644
index 0000000000000000000000000000000000000000..11920f5e4600d57c9212664114b6861395dce20a
--- /dev/null
+++ b/tests/extensions/include/included/optional-2.sieve
@@ -0,0 +1,9 @@
+require "include";
+require "variables";
+
+global "result";
+
+set "result" "${result} TWO";
+
+keep;
+
diff --git a/tests/extensions/include/optional.svtest b/tests/extensions/include/optional.svtest
new file mode 100644
index 0000000000000000000000000000000000000000..345f8309b389da4178f637e1262278fb2a4ebd8f
--- /dev/null
+++ b/tests/extensions/include/optional.svtest
@@ -0,0 +1,40 @@
+require "vnd.dovecot.testsuite";
+require "include";
+require "variables";
+
+global "result";
+set "result" "";
+
+test "Included Optional" {
+	include :optional "optional-1";
+	include :optional "optional-2";
+
+	if not string "${result}" " ONE TWO" {
+		test_fail "unexpected result value: ${result}";
+	}
+
+	# missing
+	include :optional "optional-3";
+
+	if not string "${result}" " ONE TWO" {
+		test_fail "unexpected result value after missing script: ${result}";
+	}
+}
+
+
+test "Included Optional - Binary" {
+ 	if not test_script_compile "execute/optional.sieve" {
+		test_fail "failed to compile sieve script";
+	}
+
+	test_binary_save "optional";
+	test_binary_load "optional";
+
+	if not test_script_run {
+		test_fail "failed to execute sieve script";
+	}
+
+	if not string "${result}" " ONE TWO" {
+		test_fail "unexpected result value: ${result}";
+	}
+}