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, ®ex_extension, &imap4flags_extension, ©_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