From dfc1ce0984b26b0bab985d78ab6ee7416ffe8b9e Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sun, 12 Apr 2009 12:09:18 +0200
Subject: [PATCH] Implemented core support for the environment extension.

---
 Makefile.am                                   |   1 +
 TODO                                          |   1 -
 configure.in                                  |   1 +
 doc/rfc/environment.rfc5183.txt               | 563 ++++++++++++++++++
 src/lib-sieve/Makefile.am                     |   3 +-
 src/lib-sieve/plugins/Makefile.am             |   3 +-
 src/lib-sieve/plugins/environment/Makefile.am |  20 +
 .../environment/ext-environment-common.c      | 163 +++++
 .../environment/ext-environment-common.h      |  54 ++
 .../plugins/environment/ext-environment.c     |  53 ++
 .../environment/sieve-ext-environment.h       |  14 +
 .../plugins/environment/tst-environment.c     | 223 +++++++
 src/lib-sieve/sieve-extensions.c              |  20 +-
 tests/extensions/environment/basic.svtest     |  21 +
 14 files changed, 1128 insertions(+), 12 deletions(-)
 create mode 100644 doc/rfc/environment.rfc5183.txt
 create mode 100644 src/lib-sieve/plugins/environment/Makefile.am
 create mode 100644 src/lib-sieve/plugins/environment/ext-environment-common.c
 create mode 100644 src/lib-sieve/plugins/environment/ext-environment-common.h
 create mode 100644 src/lib-sieve/plugins/environment/ext-environment.c
 create mode 100644 src/lib-sieve/plugins/environment/sieve-ext-environment.h
 create mode 100644 src/lib-sieve/plugins/environment/tst-environment.c
 create mode 100644 tests/extensions/environment/basic.svtest

diff --git a/Makefile.am b/Makefile.am
index 02835571f..294e2326a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -83,6 +83,7 @@ test_cases = \
 	tests/extensions/enotify/errors.svtest \
 	tests/extensions/enotify/execute.svtest \
 	tests/extensions/enotify/mailto.svtest \
+	tests/extensions/environment/basic.svtest \
 	tests/multiscript/basic.svtest
 
 if HAVE_DOVECOT_LIBS
diff --git a/TODO b/TODO
index 02389df35..2d7640566 100644
--- a/TODO
+++ b/TODO
@@ -38,7 +38,6 @@ Next (in order of descending priority/precedence):
 	- Improve handling of invalid addresses in headers (requires Dovecot changes)
 * Implement namespace support for variables extension (to complete include 
   extension)
-* Implement environment extension
 * Add normalize() method to comparators to normalize the string before matching
   (for efficiency).
 * Implement mechanism for implicitly including an account's aliases in the vacation
diff --git a/configure.in b/configure.in
index 93a725a62..f817f9b41 100644
--- a/configure.in
+++ b/configure.in
@@ -113,6 +113,7 @@ src/lib-sieve/plugins/include/Makefile
 src/lib-sieve/plugins/body/Makefile
 src/lib-sieve/plugins/variables/Makefile
 src/lib-sieve/plugins/enotify/Makefile
+src/lib-sieve/plugins/environment/Makefile
 src/lib-sieve-tool/Makefile
 src/plugins/Makefile
 src/plugins/lda-sieve/Makefile
diff --git a/doc/rfc/environment.rfc5183.txt b/doc/rfc/environment.rfc5183.txt
new file mode 100644
index 000000000..ee14e08ce
--- /dev/null
+++ b/doc/rfc/environment.rfc5183.txt
@@ -0,0 +1,563 @@
+
+
+
+
+
+
+Network Working Group                                           N. Freed
+Request for Comments: 5183                              Sun Microsystems
+Category: Standards Track                                       May 2008
+
+
+              Sieve Email Filtering: Environment Extension
+
+Status of This Memo
+
+   This document specifies an Internet standards track protocol for the
+   Internet community, and requests discussion and suggestions for
+   improvements.  Please refer to the current edition of the "Internet
+   Official Protocol Standards" (STD 1) for the standardization state
+   and status of this protocol.  Distribution of this memo is unlimited.
+
+Abstract
+
+   This document describes the "environment" extension to the Sieve
+   email filtering language.  The "environment" extension gives a Sieve
+   script access to information about the Sieve interpreter itself,
+   where it is running, and about any transport connection currently
+   involved in transferring the message.
+
+1.  Introduction
+
+   Sieve [RFC5228] is a language for filtering email messages at or
+   around the time of final delivery.  It is designed to be
+   implementable on either a mail client or mail server.  It is suitable
+   for running on a mail server where users may not be allowed to
+   execute arbitrary programs, such as on black box Internet Message
+   Access Protocol [RFC3501] servers, as it has no user-controlled loops
+   or the ability to run external programs.
+
+   Although Sieve is intended to be independent of access protocol, mail
+   architecture, and operating system, in some cases it is useful to
+   allow scripts to access information about their execution context.
+   The "environment" extension provides a new environment test that can
+   be used to implement scripts that behave differently when moved from
+   one system to another, when messages arrive from different remote
+   sources or when otherwise operated in different contexts.
+
+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].
+
+
+
+
+
+Freed                       Standards Track                     [Page 1]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+   The terms used to describe the various components of the Sieve
+   language are taken from Section 1.1 of [RFC5228].
+
+   This document refers to the ABNF productions IPv4-address-literal,
+   IPv6-address-literal, and General-address-literal defined in Section
+   4.1.3 of [RFC2821].
+
+   The location item makes use of standard terms for email service
+   components.  Additional information and background on these terms can
+   be found in [EMAIL-ARCH].
+
+3.  Capability Identifiers
+
+   The capability string associated with the extension defined in this
+   document is "environment".
+
+4.  Environment Test
+
+   Usage:   environment [COMPARATOR] [MATCH-TYPE]
+                        <name: string>
+                        <key-list: string-list>
+
+   The environment test retrieves the item of environment information
+   specified by the name string and matches it to the values specified
+   in the key-list argument.  The test succeeds if a match occurs.  The
+   type of match defaults to ":is" and the default comparator is
+   "i;ascii-casemap".
+
+   The current message is not a direct source of information for the
+   environment test; the item of information specified by the name
+   string is extracted from the script's operating environment and the
+   key-list argument comes from the script.
+
+   The environment test MUST fail unconditionally if the specified
+   information item does not exist.  A script MUST NOT fail with an
+   error if the item does not exist.  This allows scripts to be written
+   that handle nonexistent items gracefully.  In particular, the test:
+
+     if environment :contains "item" "" { ... }
+
+   only succeeds if "item" is known to the implementation, and always
+   succeeds if it is.
+
+   The "relational" extension [RFC5231] adds a match type called
+   ":count".  The count of an environment test is 0 if the environment
+   information returned is the empty string, or 1 otherwise.
+
+
+
+
+
+Freed                       Standards Track                     [Page 2]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+   Environment items can be standardized or vendor-defined.  An IANA
+   registry is defined for both types of items.  Extensions designed for
+   interoperable use SHOULD be defined in standards track or
+   experimental RFCs.
+
+4.1.  Initial Standard Environment Items
+
+   The initial set of standardized environment items is as follows:
+
+    "domain"  => The primary DNS domain associated with the Sieve
+                 execution context, usually but not always a proper
+                 suffix of the host name.
+
+    "host"    => The fully-qualified domain name of the host where
+                 the Sieve script is executing.
+
+    "location"
+              => Sieve evaluation can be performed at various
+                 different points as messages are processed.  This item
+                 provides additional information about the type of
+                 service that is evaluating the script.  Possible values
+                 are "MTA", meaning the Sieve is being evaluated by a
+                 Message Transfer Agent, "MDA", meaning evaluation is
+                 being performed by a Mail Delivery Agent, "MUA",
+                 meaning evaluation is being performed by a Mail User
+                 Agent, and "MS", meaning evaluation is being performed
+                 by a Message Store.  Additional information and
+                 background on these terms can be found in
+                 [EMAIL-ARCH].
+
+    "name"    => The product name associated with the Sieve interpreter.
+
+    "phase"   => The point relative to final delivery where the
+                 Sieve script is being evaluated.  Possible values are
+                 "pre", "during", and "post", referring respectively to
+                 processing before, during, and after final delivery
+                 has taken place.
+
+    "remote-host"
+              => Host name of remote SMTP/LMTP/Submission client
+                 expressed as a Fully Qualified Domain Name (FQDN),
+                 if applicable and available.  The empty string will be
+                 returned if for some reason this information cannot be
+                 obtained for the current client.
+
+
+
+
+
+
+
+Freed                       Standards Track                     [Page 3]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+    "remote-ip"
+              => IP address of remote SMTP/LMTP/Submission client, if
+                 applicable and available.  IPv4, IPv6, and other types
+                 of addresses are respectively represented in the
+                 formats defined by the IPv4-address-literal,
+                 IPv6-address-literal, and General-address-literal
+                 productions defined in Section 4.1.3 of [RFC2821].
+
+    "version" => The product version associated with the Sieve
+                 interpreter.  The meaning of the product version string
+                 is product-specific and should always be considered
+                 in the context of the product name given by the
+                 "name" item.
+
+   Implementations SHOULD support as many of the items on this initial
+   list as possible.  Additional standardized items can only be defined
+   in standards-track or experimental RFCs.
+
+4.2.  Vendor-defined Environment Items
+
+   Environment item names beginning with "vnd." represent vendor-defined
+   extensions.  Such extensions are not defined by Internet standards or
+   RFCs, but are still registered with IANA in order to prevent
+   conflicts.
+
+4.3.  IANA Registration of Environment Items
+
+   A registry of environment items is provided by IANA.  Item names may
+   be registered on a first-come, first-served basis.
+
+   Groups of items defined in a standards track or experimental RFC MAY
+   choose to use a common name prefix of the form "name.", where "name"
+   is a string that identifies the group of related items.
+
+   Items not defined in a standards track or experimental RFC MUST have
+   a name that begins with the "vnd." prefix, and this prefix is
+   followed by the name of the vendor or product, such as
+   "vnd.acme.rocket-sled-status".
+
+
+
+
+
+
+
+
+
+
+
+
+
+Freed                       Standards Track                     [Page 4]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+4.3.1.  Template for Environment Registrations
+
+   The following template is to be used for registering new Sieve
+   environment item names with IANA.
+
+      To: iana@iana.org
+      Subject: Registration of new Sieve environment item
+
+      Item name: [the string for use in the 'environment' test]
+      Description:     [a brief description of the semantics of the
+                        value the item returns]
+
+      RFC number:      [for extensions published as RFCs]
+      Contact address: [email and/or physical address to contact for
+                        additional information]
+
+   Multiple items and descriptions MAY be specified in a single
+   registration request.  Both standardized and vendor-defined items use
+   this form.
+
+5.  Security Considerations
+
+   The environment extension may be used to obtain information about the
+   system the Sieve implementation is running on.  This information in
+   turn may reveal details about service provider or enterprise
+   infrastructure.
+
+   An implementation can use any technique to determine the remote-host
+   environment item defined in this specification, and the
+   trustworthiness of the result will vary.  One common method will be
+   to perform a PTR DNS lookup on the client IP address.  This
+   information may come from an untrusted source.  For example, the
+   test:
+
+     if environment :matches "remote-host" "*.example.com" { ... }
+
+   is not a good way to test whether the message came from "outside"
+   because anyone who can create a PTR record can create one that refers
+   to whatever domain they choose.
+
+   All of the security considerations given in the base Sieve
+   specification also apply to this extension.
+
+
+
+
+
+
+
+
+
+Freed                       Standards Track                     [Page 5]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+6.  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: environment
+      Description:     The "environment" extension provides a new
+                       environment test that can be used to implement
+                       scripts that behave differently when moved
+                       from one system to another or otherwise
+                       operated in different contexts.
+      RFC number:      RFC 5183
+      Contact address: Sieve discussion list <ietf-mta-filters@imc.org>
+
+   This specification also defines a new IANA registry for Sieve
+   environment item names.  The specifics of this registry are given in
+   Section 4.3.  The initial contents of the registry are given in the
+   following section.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Freed                       Standards Track                     [Page 6]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+6.1.  Initial Environment Item Registrations
+
+   The following template specifies the initial IANA registrations for
+   the environment items defined in this document:
+
+      To: iana@iana.org
+      Subject: Registration of new Sieve environment items
+
+      Capability name: domain
+      Description:     The primary DNS domain associated with the Sieve
+                       execution context, usually but not always a
+                       proper suffix of the host name.
+
+      Capability name: host
+      Description:     The fully-qualified domain name of the host
+                       where the Sieve script is executing.
+
+      Capability name: location
+      Description:     Type of service executing the Sieve script.
+
+      Capability name: name
+      Description:     The product name associated with the Sieve
+                       interpreter.
+
+      Capability name: phase
+      Description:     Point relative to final delivery at which the
+                       Sieve script is being evaluated.
+
+      Capability name: remote-host
+      Description:     Host name of remote SMTP client, if applicable
+                       and available.
+
+      Capability name: remote-ip
+      Description:     IP address of remote SMTP client, if applicable
+                       and available.
+
+      Capability name: version
+      Description:     The product version associated with the Sieve
+                       interpreter.
+
+      RFC number:      RFC 5183
+      Contact address: Sieve discussion list <ietf-mta-filters@imc.org>
+
+
+
+
+
+
+
+
+
+Freed                       Standards Track                     [Page 7]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+7.  References
+
+7.1.  Normative references
+
+   [RFC2119]     Bradner, S., "Key words for use in RFCs to Indicate
+                 Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+   [RFC2821]     Klensin, J., "Simple Mail Transfer Protocol", RFC 2821,
+                 April 2001.
+
+   [RFC5228]     Guenther, P. and T. Showalter, "Sieve: An Email
+                 Filtering Language", RFC 5228, January 2008.
+
+   [RFC5231]     Segmuller, W. and B. Leiba, "Sieve Email Filtering:
+                 Relational Extension", RFC 5231, January 2008.
+
+7.2.  Informative references
+
+   [EMAIL-ARCH]  Crocker, D., "Internet Mail Architecture", Work
+                 in Progress, February 2008.
+
+   [RFC3501]     Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL -
+                 VERSION 4rev1", RFC 3501, March 2003.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Freed                       Standards Track                     [Page 8]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+Appendix A.  Acknowledgements
+
+   Brian Carpenter, Dave Crocker, Cyrus Daboo, Philip Guenther, Kjetil
+   Torgrim Homme, John Klensin, Mark Mallett, Alexey Melnikov, and
+   Dilyan Palauzo provided helpful suggestions and corrections.
+
+Author's Address
+
+   Ned Freed
+   Sun Microsystems
+   3401 Centrelake Drive, Suite 410
+   Ontario, CA  92761-1205
+   USA
+
+   Phone: +1 909 457 4293
+   EMail: ned.freed@mrochek.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Freed                       Standards Track                     [Page 9]
+
+RFC 5183              Sieve Environment Extension               May 2008
+
+
+Full Copyright Statement
+
+   Copyright (C) The IETF Trust (2008).
+
+   This document is subject to the rights, licenses and restrictions
+   contained in BCP 78, and except as set forth therein, the authors
+   retain all their rights.
+
+   This document and the information contained herein are provided on an
+   "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+   OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+   THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+   THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+   WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+   The IETF takes no position regarding the validity or scope of any
+   Intellectual Property Rights or other rights that might be claimed to
+   pertain to the implementation or use of the technology described in
+   this document or the extent to which any license under such rights
+   might or might not be available; nor does it represent that it has
+   made any independent effort to identify any such rights.  Information
+   on the procedures with respect to rights in RFC documents can be
+   found in BCP 78 and BCP 79.
+
+   Copies of IPR disclosures made to the IETF Secretariat and any
+   assurances of licenses to be made available, or the result of an
+   attempt made to obtain a general license or permission for the use of
+   such proprietary rights by implementers or users of this
+   specification can be obtained from the IETF on-line IPR repository at
+   http://www.ietf.org/ipr.
+
+   The IETF invites any interested party to bring to its attention any
+   copyrights, patents or patent applications, or other proprietary
+   rights that may cover technology that may be required to implement
+   this standard.  Please address the information to the IETF at
+   ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Freed                       Standards Track                    [Page 10]
+
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index 68ea38da9..b7c2b210f 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -54,7 +54,8 @@ plugins = \
 	./plugins/include/libsieve_ext_include.la \
 	./plugins/body/libsieve_ext_body.la \
 	./plugins/variables/libsieve_ext_variables.la \
-	./plugins/enotify/libsieve_ext_enotify.la
+	./plugins/enotify/libsieve_ext_enotify.la \
+	./plugins/environment/libsieve_ext_environment.la
 
 libsieve_la_DEPENDENCIES = $(plugins)
 libsieve_la_LIBADD = $(plugins)
diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am
index d5786fe76..c75e88566 100644
--- a/src/lib-sieve/plugins/Makefile.am
+++ b/src/lib-sieve/plugins/Makefile.am
@@ -9,5 +9,6 @@ SUBDIRS = \
 	include \
 	body \
 	variables \
-	enotify
+	enotify \
+	environment
 
diff --git a/src/lib-sieve/plugins/environment/Makefile.am b/src/lib-sieve/plugins/environment/Makefile.am
new file mode 100644
index 000000000..0acdbdf6b
--- /dev/null
+++ b/src/lib-sieve/plugins/environment/Makefile.am
@@ -0,0 +1,20 @@
+noinst_LTLIBRARIES = libsieve_ext_environment.la
+
+AM_CPPFLAGS = \
+	-I../../ \
+	-I$(dovecot_incdir) \
+	-I$(dovecot_incdir)/src/lib \
+	-I$(dovecot_incdir)/src/lib-mail \
+	-I$(dovecot_incdir)/src/lib-storage 
+
+tests = \
+	tst-environment.c
+
+libsieve_ext_environment_la_SOURCES = \
+	$(tests) \
+	ext-environment-common.c \
+	ext-environment.c
+
+noinst_HEADERS = \
+	ext-environment-common.h \
+	sieve-ext-environment.h
diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.c b/src/lib-sieve/plugins/environment/ext-environment-common.c
new file mode 100644
index 000000000..367c64e1e
--- /dev/null
+++ b/src/lib-sieve/plugins/environment/ext-environment-common.c
@@ -0,0 +1,163 @@
+#include "lib.h"
+#include "hash.h"
+
+#include "ext-environment-common.h"
+
+static struct hash_table *environment_items;
+
+/*
+ * Core environment items
+ */
+
+static const struct sieve_environment_item *core_env_items[] = {
+	&domain_env_item, 
+	&host_env_item, 
+	&location_env_item, 
+	&phase_env_item, 
+	&name_env_item, 
+	&version_env_item
+};
+
+static unsigned int core_env_items_count = N_ELEMENTS(core_env_items);
+
+/*
+ * Initialization
+ */
+
+bool ext_environment_init(void) 
+{
+	unsigned int i;
+
+	environment_items = hash_table_create
+		(default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp);
+
+	for ( i = 0; i < core_env_items_count; i++ ) {
+		sieve_ext_environment_item_register(core_env_items[i]);
+	}
+
+	return TRUE;
+}
+
+void ext_environment_deinit(void)
+{
+	hash_table_destroy(&environment_items);
+}
+
+/*
+ * Registration
+ */
+
+void sieve_ext_environment_item_register
+(const struct sieve_environment_item *item)
+{
+	hash_table_insert
+		(environment_items, (void *) item->name, (void *) item);
+}
+
+/*
+ * Retrieval
+ */
+
+const char *ext_environment_item_get_value
+(const char *name, const struct sieve_script_env *senv)
+{
+	const struct sieve_environment_item *item = 
+		(const struct sieve_environment_item *) 
+			hash_table_lookup(environment_items, name);
+
+	if ( item == NULL )
+		return NULL;
+
+	if ( item->value != NULL )
+		return item->value;
+
+	if ( item->get_value != NULL ) 
+		return item->get_value(senv);
+
+	return NULL; 
+}
+
+/*
+ * Default environment items
+ */
+
+/* "domain":
+ *
+ *   The primary DNS domain associated with the Sieve execution context, usually 
+ *   but not always a proper suffix of the host name.
+ */
+const struct sieve_environment_item domain_env_item = {
+	"domain",
+	NULL,
+	NULL,
+};
+
+/* "host":
+ *
+ *   The fully-qualified domain name of the host where the Sieve script is 
+ *   executing.
+ */
+
+const struct sieve_environment_item host_env_item = {
+	"host",
+	NULL,
+	NULL,
+};
+
+/* "location":
+ *
+ *   Sieve evaluation can be performed at various different points as messages 
+ *   are processed. This item provides additional information about the type of
+ *   service that is evaluating the script.  Possible values are:
+ *    "MTA" - the Sieve script is being evaluated by a Message Transfer Agent 
+ *    "MDA" - evaluation is being performed by a Mail Delivery Agent 
+ *    "MUA" - evaluation is being performed by a Mail User Agent
+ *    "MS"  - evaluation is being performed by a Message Store
+ */
+const struct sieve_environment_item location_env_item = {
+	"location",
+	NULL,
+	NULL,
+};
+
+/* "phase":
+ *
+ *   The point relative to final delivery where the Sieve script is being
+ *   evaluated.  Possible values are "pre", "during", and "post", referring 
+ *   respectively to processing before, during, and after final delivery has 
+ *   taken place.
+ */
+
+const struct sieve_environment_item phase_env_item = {
+	"phase",
+	NULL,
+	NULL,
+};
+
+/* "name":
+ *
+ *  The product name associated with the Sieve interpreter.
+ */
+const struct sieve_environment_item name_env_item = {
+	"name",
+	SIEVE_NAME,
+	NULL,
+};
+
+/* "version":
+ *
+ * The product version associated with the Sieve interpreter. The meaning of the 
+ * product version string is product-specific and should always be considered
+ * in the context of the product name given by the "name" item.
+ */
+
+const struct sieve_environment_item version_env_item = {
+	"version",
+	SIEVE_VERSION,
+	NULL,
+};
+
+
+
+
+
diff --git a/src/lib-sieve/plugins/environment/ext-environment-common.h b/src/lib-sieve/plugins/environment/ext-environment-common.h
new file mode 100644
index 000000000..96010f673
--- /dev/null
+++ b/src/lib-sieve/plugins/environment/ext-environment-common.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#ifndef __EXT_ENVIRONMENT_COMMON_H
+#define __EXT_ENVIRONMENT_COMMON_H
+
+#include "sieve-common.h"
+
+#include "sieve-ext-environment.h"
+
+/*
+ * Extension
+ */
+
+extern const struct sieve_extension environment_extension;
+
+/* 
+ * Commands 
+ */
+
+extern const struct sieve_command tst_environment;
+
+/*
+ * Operations
+ */
+
+extern const struct sieve_operation tst_environment_operation;
+
+/*
+ * Environment items
+ */
+
+extern const struct sieve_environment_item domain_env_item;
+extern const struct sieve_environment_item host_env_item;
+extern const struct sieve_environment_item location_env_item;
+extern const struct sieve_environment_item phase_env_item;
+extern const struct sieve_environment_item name_env_item;
+extern const struct sieve_environment_item version_env_item;
+
+/*
+ * Initialization
+ */
+
+bool ext_environment_init(void);
+void ext_environment_deinit(void);
+
+/*
+ * Environment item retrieval
+ */
+
+const char *ext_environment_item_get_value
+	(const char *name, const struct sieve_script_env *senv);
+
+#endif /* __EXT_VARIABLES_COMMON_H */
diff --git a/src/lib-sieve/plugins/environment/ext-environment.c b/src/lib-sieve/plugins/environment/ext-environment.c
new file mode 100644
index 000000000..f53ff057c
--- /dev/null
+++ b/src/lib-sieve/plugins/environment/ext-environment.c
@@ -0,0 +1,53 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+/* Extension variables 
+ * -------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5183
+ * Implementation: full
+ * Status: experimental, not thoroughly tested
+ *
+ */
+ 
+#include "lib.h"
+#include "str.h"
+#include "unichar.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+
+#include "sieve-validator.h"
+
+#include "ext-environment-common.h"
+
+/* 
+ * Extension 
+ */
+
+static bool ext_environment_validator_load(struct sieve_validator *validator);
+
+static int ext_my_id = -1;
+	
+const struct sieve_extension environment_extension = { 
+	"environment", 
+	&ext_my_id,
+	ext_environment_init, 
+	ext_environment_deinit,
+	ext_environment_validator_load,
+	NULL, NULL, NULL, NULL, NULL,
+	SIEVE_EXT_DEFINE_OPERATION(tst_environment_operation), 
+	SIEVE_EXT_DEFINE_NO_OPERANDS
+};
+
+static bool ext_environment_validator_load
+	(struct sieve_validator *validator)
+{
+	sieve_validator_register_command(validator, &tst_environment);
+	
+	return TRUE;
+}
+
diff --git a/src/lib-sieve/plugins/environment/sieve-ext-environment.h b/src/lib-sieve/plugins/environment/sieve-ext-environment.h
new file mode 100644
index 000000000..9bd4cc8de
--- /dev/null
+++ b/src/lib-sieve/plugins/environment/sieve-ext-environment.h
@@ -0,0 +1,14 @@
+#ifndef __SIEVE_EXT_ENVIRONMENT_H
+#define __SIEVE_EXT_ENVIRONMENT_H
+
+struct sieve_environment_item {
+	const char *name;
+	
+	const char *value;
+	const char *(*get_value)(const struct sieve_script_env *senv);
+};
+
+void sieve_ext_environment_item_register
+	(const struct sieve_environment_item *item);
+
+#endif
diff --git a/src/lib-sieve/plugins/environment/tst-environment.c b/src/lib-sieve/plugins/environment/tst-environment.c
new file mode 100644
index 000000000..205026251
--- /dev/null
+++ b/src/lib-sieve/plugins/environment/tst-environment.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-environment-common.h"
+
+/* 
+ * Environment test 
+ *
+ * Syntax:
+ *   environment [COMPARATOR] [MATCH-TYPE]
+ *      <name: string> <key-list: string-list>
+ */
+
+static bool tst_environment_registered
+	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg);
+static bool tst_environment_validate
+	(struct sieve_validator *validator, struct sieve_command_context *tst);
+static bool tst_environment_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);
+
+const struct sieve_command tst_environment = { 
+	"environment", 
+	SCT_TEST, 
+	2, 0, FALSE, FALSE,
+	tst_environment_registered, 
+	NULL,
+	tst_environment_validate, 
+	tst_environment_generate, 
+	NULL 
+};
+
+/* 
+ * Environment operation
+ */
+
+static bool tst_environment_operation_dump
+	(const struct sieve_operation *op, 
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_environment_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation tst_environment_operation = { 
+	"ENVIRONMENT",
+	&environment_extension, 
+	0, 
+	tst_environment_operation_dump, 
+	tst_environment_operation_execute 
+};
+
+/* 
+ * Test registration 
+ */
+
+static bool tst_environment_registered
+	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
+{
+	/* The order of these is not significant */
+	sieve_comparators_link_tag(validator, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+	sieve_match_types_link_tags(validator, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+	return TRUE;
+}
+
+/* 
+ * Test validation 
+ */
+
+static bool tst_environment_validate
+	(struct sieve_validator *validator, struct sieve_command_context *tst) 
+{ 		
+	struct sieve_ast_argument *arg = tst->first_positional;
+	
+	if ( !sieve_validate_positional_argument
+		(validator, tst, arg, "name", 1, SAAT_STRING) ) {
+		return FALSE;
+	}
+	
+	if ( !sieve_validator_argument_activate(validator, tst, arg, FALSE) )
+		return FALSE;
+	
+	arg = sieve_ast_argument_next(arg);
+
+	if ( !sieve_validate_positional_argument
+		(validator, tst, arg, "key list", 2, SAAT_STRING_LIST) ) {
+		return FALSE;
+	}
+	
+	if ( !sieve_validator_argument_activate(validator, tst, arg, FALSE) )
+		return FALSE;
+
+	/* Validate the key argument to a specified match type */
+	return sieve_match_type_validate
+		(validator, tst, arg, &is_match_type, &i_octet_comparator);
+}
+
+/* 
+ * Test generation 
+ */
+
+static bool tst_environment_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx) 
+{
+	sieve_operation_emit_code(cgenv->sbin, &tst_environment_operation);
+
+ 	/* Generate arguments */
+	if ( !sieve_generate_arguments(cgenv, ctx, NULL) )
+		return FALSE;
+	
+	return TRUE;
+}
+
+/* 
+ * Code dump 
+ */
+
+static bool tst_environment_operation_dump
+(const struct sieve_operation *op ATTR_UNUSED,
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	int opt_code = 0;
+
+	sieve_code_dumpf(denv, "ENVIRONMENT");
+	sieve_code_descend(denv);
+
+	/* Handle any optional arguments */
+	if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+		return FALSE;
+
+	if ( opt_code != SIEVE_MATCH_OPT_END )
+		return FALSE;
+		
+	return
+		sieve_opr_string_dump(denv, address, "name") &&
+		sieve_opr_stringlist_dump(denv, address, "key list");
+}
+
+/* 
+ * Code execution 
+ */
+
+static int tst_environment_operation_execute
+(const struct sieve_operation *op ATTR_UNUSED, 
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{
+	int ret, mret;
+	bool result = TRUE;
+	int opt_code = 0;
+	const struct sieve_comparator *cmp = &i_ascii_casemap_comparator;
+	const struct sieve_match_type *mtch = &is_match_type;
+	struct sieve_match_context *mctx;
+	string_t *name;
+	struct sieve_coded_stringlist *key_list;
+	const char *env_item;
+	bool matched = FALSE;
+
+	/*
+	 * Read operands 
+	 */
+	
+	/* Handle match-type and comparator operands */
+	if ( (ret=sieve_match_read_optional_operands
+		(renv, address, &opt_code, &cmp, &mtch)) <= 0 )
+		return ret;
+	
+	/* Check whether we neatly finished the list of optional operands*/
+	if ( opt_code != SIEVE_MATCH_OPT_END) {
+		sieve_runtime_trace_error(renv, "invalid optional operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	/* Read source */
+	if ( !sieve_opr_string_read(renv, address, &name) ) {
+		sieve_runtime_trace_error(renv, "invalid name operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+	
+	/* Read key-list */
+	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
+		sieve_runtime_trace_error(renv, "invalid key-list operand");
+		return SIEVE_EXEC_BIN_CORRUPT;
+	}
+
+	/*
+	 * Perform operation
+	 */
+
+	sieve_runtime_trace(renv, "ENVIRONMENT test");
+
+	env_item = ext_environment_item_get_value(str_c(name), renv->scriptenv);
+
+	mctx = sieve_match_begin(renv->interp, mtch, cmp, NULL, key_list); 	
+
+	if ( (mret=sieve_match_value(mctx, env_item, strlen(env_item))) < 0 ) {
+		result = FALSE;
+	} else {
+		matched = ( mret > 0 );				
+	}
+
+	if ( (mret=sieve_match_end(mctx)) < 0 )
+		result = FALSE;
+	else
+		matched = ( mret > 0 || matched ); 	
+	
+	if ( result ) {
+		sieve_interpreter_set_test_result(renv->interp, matched);
+		return SIEVE_EXEC_OK;
+	}
+	
+	sieve_runtime_trace_error(renv, "invalid key list item");
+	return SIEVE_EXEC_BIN_CORRUPT;
+}
diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index 25f65e942..a4efa9bb9 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -79,6 +79,7 @@ extern const struct sieve_extension include_extension;
 extern const struct sieve_extension body_extension;
 extern const struct sieve_extension variables_extension;
 extern const struct sieve_extension enotify_extension;
+extern const struct sieve_extension environment_extension;
 
 /*
  * Extensions under development
@@ -108,7 +109,7 @@ const struct sieve_extension *sieve_core_extensions[] = {
 	&comparator_i_ascii_numeric_extension, 
 	&relational_extension, &regex_extension, &imap4flags_extension,
 	&copy_extension, &include_extension, &body_extension,
-	&variables_extension, &enotify_extension
+	&variables_extension, &enotify_extension, &environment_extension
 };
 
 const unsigned int sieve_core_extensions_count =
@@ -197,20 +198,21 @@ static bool _sieve_extension_load
 static struct sieve_extension_registration *_sieve_extension_register
 (const struct sieve_extension *extension, bool load)
 {
-	struct sieve_extension_registration *ereg = (struct sieve_extension_registration *)
-        hash_table_lookup(extension_index, extension->name);
+	struct sieve_extension_registration *ereg = 
+		(struct sieve_extension_registration *)	
+		hash_table_lookup(extension_index, extension->name);
 
 	/* Register extension if it is not registered already */
-    if ( ereg == NULL ) {
+	if ( ereg == NULL ) {
 		int ext_id = array_count(&extensions);
 
-        /* Add extension to the registry */
+		/* Add extension to the registry */
 
-        ereg = array_append_space(&extensions);
-        ereg->id = ext_id;
+		ereg = array_append_space(&extensions);
+		ereg->id = ext_id;
 
-        hash_table_insert(extension_index, (void *) extension->name, (void *) ereg);
-    }
+		hash_table_insert(extension_index, (void *) extension->name, (void *) ereg);
+	}
 
 	/* Enable extension */
 	if ( extension->_id != NULL && load ) {
diff --git a/tests/extensions/environment/basic.svtest b/tests/extensions/environment/basic.svtest
new file mode 100644
index 000000000..ed884d511
--- /dev/null
+++ b/tests/extensions/environment/basic.svtest
@@ -0,0 +1,21 @@
+require "vnd.dovecot.testsuite";
+require "environment";
+
+test "Name" {
+	if not environment :contains "name" "dovecot" {
+		test_fail "name environment returned invalid value";
+	}
+
+	if not environment :contains "name" "sieve" {
+		test_fail "name environment returned invalid value";
+	}
+
+	if environment :contains "name" "cyrus" {
+		test_fail "something is definitely wrong here";
+	}
+
+	if not environment :is :comparator "i;octet" "name" "Dovecot Sieve" {
+		test_fail "name environment does not match exactly with what is expected";
+	}
+}
+
-- 
GitLab