From f0166f99c13661d325bbdd8336e34ef38c8c6eb2 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Wed, 5 Aug 2009 23:38:20 +0200
Subject: [PATCH] Built skeleton implementation for the date extension (RFC
 5260).

---
 configure.in                                 |   1 +
 src/lib-sieve/Makefile.am                    |   9 +-
 src/lib-sieve/plugins/Makefile.am            |   7 +-
 src/lib-sieve/plugins/date/Makefile.am       |  18 +
 src/lib-sieve/plugins/date/ext-date-common.h |  32 ++
 src/lib-sieve/plugins/date/ext-date.c        |  65 +++
 src/lib-sieve/plugins/date/tst-date.c        | 438 +++++++++++++++++++
 src/lib-sieve/sieve-extensions.c             |   2 +
 8 files changed, 569 insertions(+), 3 deletions(-)
 create mode 100644 src/lib-sieve/plugins/date/Makefile.am
 create mode 100644 src/lib-sieve/plugins/date/ext-date-common.h
 create mode 100644 src/lib-sieve/plugins/date/ext-date.c
 create mode 100644 src/lib-sieve/plugins/date/tst-date.c

diff --git a/configure.in b/configure.in
index 639600760..37db2abde 100644
--- a/configure.in
+++ b/configure.in
@@ -116,6 +116,7 @@ src/lib-sieve/plugins/enotify/Makefile
 src/lib-sieve/plugins/notify/Makefile
 src/lib-sieve/plugins/environment/Makefile
 src/lib-sieve/plugins/mailbox/Makefile
+src/lib-sieve/plugins/date/Makefile
 src/lib-sieve-tool/Makefile
 src/plugins/Makefile
 src/plugins/lda-sieve/Makefile
diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am
index ed89d64c1..48550d985 100644
--- a/src/lib-sieve/Makefile.am
+++ b/src/lib-sieve/Makefile.am
@@ -42,6 +42,11 @@ comparators = \
 	cmp-i-octet.c \
 	cmp-i-ascii-casemap.c
 
+if BUILD_UNFINISHED
+unfinished_plugins = \
+	./plugins/date/libsieve_ext_date.la
+endif
+
 # These are not actual plugins just yet...
 plugins = \
 	./plugins/vacation/libsieve_ext_vacation.la \
@@ -57,8 +62,8 @@ plugins = \
 	./plugins/enotify/libsieve_ext_enotify.la \
 	./plugins/notify/libsieve_ext_notify.la \
 	./plugins/environment/libsieve_ext_environment.la \
-	./plugins/mailbox/libsieve_ext_mailbox.la
-
+	./plugins/mailbox/libsieve_ext_mailbox.la \
+	$(unfinished_plugins)
 
 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 c098e06c7..8b29be1fd 100644
--- a/src/lib-sieve/plugins/Makefile.am
+++ b/src/lib-sieve/plugins/Makefile.am
@@ -1,3 +1,7 @@
+if BUILD_UNFINISHED
+UNFINISHED = date
+endif
+
 SUBDIRS = \
 	vacation \
 	subaddress \
@@ -12,5 +16,6 @@ SUBDIRS = \
 	enotify \
 	notify \
 	environment \
-	mailbox
+	mailbox \
+	$(UNFINISHED)
 
diff --git a/src/lib-sieve/plugins/date/Makefile.am b/src/lib-sieve/plugins/date/Makefile.am
new file mode 100644
index 000000000..8d1f73122
--- /dev/null
+++ b/src/lib-sieve/plugins/date/Makefile.am
@@ -0,0 +1,18 @@
+noinst_LTLIBRARIES = libsieve_ext_date.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-date.c
+
+libsieve_ext_date_la_SOURCES = \
+	$(tests) \
+	ext-date.c
+
+noinst_HEADERS = \
+	ext-date-common.h
diff --git a/src/lib-sieve/plugins/date/ext-date-common.h b/src/lib-sieve/plugins/date/ext-date-common.h
new file mode 100644
index 000000000..81001c44a
--- /dev/null
+++ b/src/lib-sieve/plugins/date/ext-date-common.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+ 
+#ifndef __EXT_DATE_COMMON_H
+#define __EXT_DATE_COMMON_H
+
+/*
+ * Extension
+ */
+ 
+extern const struct sieve_extension date_extension;
+
+/* 
+ * Tests
+ */
+
+extern const struct sieve_command date_test;
+extern const struct sieve_command currentdate_test;
+ 
+/*
+ * Operations
+ */
+
+enum ext_date_opcode {
+	EXT_DATE_OPERATION_DATE,
+	EXT_DATE_OPERATION_CURRENTDATE
+};
+
+extern const struct sieve_operation date_operation;
+extern const struct sieve_operation currentdate_operation;
+
+#endif /* __EXT_DATE_COMMON_H */
diff --git a/src/lib-sieve/plugins/date/ext-date.c b/src/lib-sieve/plugins/date/ext-date.c
new file mode 100644
index 000000000..a899fb0cd
--- /dev/null
+++ b/src/lib-sieve/plugins/date/ext-date.c
@@ -0,0 +1,65 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+/* Extension date
+ * ------------------
+ *
+ * Authors: Stephan Bosch
+ * Specification: RFC 5260
+ * Implementation: skeleton
+ * Status: under development
+ *
+ */
+ 
+#include "lib.h"
+#include "array.h"
+
+#include "sieve-common.h"
+
+#include "sieve-extensions.h"
+#include "sieve-commands.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-binary.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+
+#include "ext-date-common.h"
+
+/* 
+ * Extension 
+ */
+
+static bool ext_date_validator_load(struct sieve_validator *validator);
+
+int ext_date_my_id = -1;
+
+const struct sieve_operation *ext_date_operations[] = {
+	&date_operation,
+	&currentdate_operation
+};
+
+const struct sieve_extension date_extension = { 
+	"date", 
+	&ext_date_my_id,
+	NULL, NULL,
+	ext_date_validator_load, 
+	NULL, NULL, NULL, NULL, NULL,
+	SIEVE_EXT_DEFINE_OPERATIONS(ext_date_operations), 
+	SIEVE_EXT_DEFINE_NO_OPERANDS
+};
+
+static bool ext_date_validator_load(struct sieve_validator *valdtr)
+{
+	/* Register new test */
+	sieve_validator_register_command(valdtr, &date_test);
+	sieve_validator_register_command(valdtr, &currentdate_test);
+
+	return TRUE;
+}
+
+
diff --git a/src/lib-sieve/plugins/date/tst-date.c b/src/lib-sieve/plugins/date/tst-date.c
new file mode 100644
index 000000000..ecc78d18d
--- /dev/null
+++ b/src/lib-sieve/plugins/date/tst-date.c
@@ -0,0 +1,438 @@
+/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str-sanitize.h"
+
+#include "sieve-common.h"
+#include "sieve-commands.h"
+#include "sieve-code.h"
+#include "sieve-comparators.h"
+#include "sieve-match-types.h"
+#include "sieve-address-parts.h"
+#include "sieve-validator.h"
+#include "sieve-generator.h"
+#include "sieve-interpreter.h"
+#include "sieve-dump.h"
+#include "sieve-match.h"
+
+#include "ext-date-common.h"
+
+/*
+ * Tests
+ */
+
+static bool tst_date_validate
+	(struct sieve_validator *valdtr, struct sieve_command_context *tst);
+static bool tst_date_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);
+ 
+/* Address test
+ *
+ * Syntax:
+ *    date [<":zone" <time-zone: string>> / ":originalzone"]
+ *         [COMPARATOR] [MATCH-TYPE] <header-name: string>
+ *         <date-part: string> <key-list: string-list>
+ */
+
+static bool tst_date_registered
+	(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg);
+
+const struct sieve_command date_test = { 
+	"date", 
+	SCT_TEST, 
+	3, 0, FALSE, FALSE,
+	tst_date_registered,
+	NULL, 
+	tst_date_validate, 
+	tst_date_generate, 
+	NULL 
+};
+
+/* Currentdate test
+ * 
+ * Syntax:
+ *    currentdate [":zone" <time-zone: string>]
+ *                [COMPARATOR] [MATCH-TYPE]
+ *                <date-part: string> <key-list: string-list>
+ */
+
+static bool tst_currentdate_registered
+	(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg);
+
+const struct sieve_command currentdate_test = { 
+	"currentdate", 
+	SCT_TEST, 
+	2, 0, FALSE, FALSE,
+	tst_currentdate_registered,
+	NULL, 
+	tst_date_validate, 
+	tst_date_generate, 
+	NULL 
+};
+
+/* 
+ * Tagged arguments 
+ */
+
+/* Forward declarations */
+
+static bool tag_zone_validate
+	(struct sieve_validator *validator, struct sieve_ast_argument **arg,
+		struct sieve_command_context *cmd);
+static bool tag_zone_generate
+	(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+		struct sieve_command_context *cmd);
+
+/* Argument objects */
+
+static const struct sieve_argument date_zone_tag = {
+ 	"zone",
+	NULL, NULL,
+	tag_zone_validate,
+	NULL,
+	tag_zone_generate
+};
+
+static const struct sieve_argument date_originalzone_tag = {
+	"originalzone",
+	NULL, NULL,
+	tag_zone_validate,
+	NULL,
+	tag_zone_generate
+};
+
+/* 
+ * Address operation 
+ */
+
+static bool tst_date_operation_dump
+	(const struct sieve_operation *op, 
+		const struct sieve_dumptime_env *denv, sieve_size_t *address);
+static int tst_date_operation_execute
+	(const struct sieve_operation *op, 
+		const struct sieve_runtime_env *renv, sieve_size_t *address);
+
+const struct sieve_operation date_operation = { 
+	"DATE",
+	&date_extension,
+	EXT_DATE_OPERATION_DATE,
+	tst_date_operation_dump, 
+	tst_date_operation_execute 
+};
+
+const struct sieve_operation currentdate_operation = { 
+	"CURRENTDATE",
+	&date_extension,
+	EXT_DATE_OPERATION_CURRENTDATE,
+	tst_date_operation_dump, 
+	tst_date_operation_execute 
+};
+
+/*
+ * Optional operands
+ */
+
+enum tst_date_optional {
+	OPT_DATE_ZONE = SIEVE_MATCH_OPT_LAST,
+	OPT_DATE_LAST
+};
+
+/*
+ * Tag implementation
+ */
+
+static bool tag_zone_validate
+(struct sieve_validator *validator, struct sieve_ast_argument **arg,
+    struct sieve_command_context *cmd)
+{
+    struct sieve_ast_argument *tag = *arg;
+
+    if ( (bool) cmd->data ) {
+		if ( cmd->command == &date_test ) {
+			sieve_argument_validate_error(validator, *arg,
+				"multiple :zone or :originalzone arguments specified for "
+				"the currentdate test");
+		} else {
+	        sieve_argument_validate_error(validator, *arg,
+    	        "multiple :zone arguments specified for the currentdate test");
+		}
+		return FALSE;
+    }
+
+	/* Skip tag */
+ 	*arg = sieve_ast_argument_next(*arg);
+
+	/* :content tag has a string-list argument */
+	if ( tag->argument == &date_zone_tag ) {
+		/* Check syntax:
+		 *   :zone <time-zone: string>
+		 */
+		if ( !sieve_validate_tag_parameter
+			(validator, cmd, tag, *arg, SAAT_STRING) ) {
+			return FALSE;
+		}
+
+        /* Assign tag parameters */
+		tag->parameters = *arg;
+		*arg = sieve_ast_arguments_detach(*arg,1);
+	} 
+
+	cmd->data = (void *) TRUE;
+
+	return TRUE;
+}
+
+/* 
+ * Test registration 
+ */
+
+static bool tst_date_registered
+(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg) 
+{
+	sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+	sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, &date_zone_tag, OPT_DATE_ZONE);
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, &date_originalzone_tag, OPT_DATE_ZONE);
+
+	return TRUE;
+}
+
+static bool tst_currentdate_registered
+(struct sieve_validator *valdtr, struct sieve_command_registration *cmd_reg) 
+{
+	sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR);
+	sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE);
+
+	sieve_validator_register_tag
+		(valdtr, cmd_reg, &date_zone_tag, OPT_DATE_ZONE);
+
+	return TRUE;
+}
+
+/* 
+ * Validation 
+ */
+ 
+static bool tst_date_validate
+	(struct sieve_validator *valdtr, struct sieve_command_context *tst) 
+{
+	struct sieve_ast_argument *arg = tst->first_positional;
+	unsigned int arg_offset = 0 ;
+		
+	/* Check header name */
+
+	if ( tst->command == &date_test ) {
+		arg_offset = 1;
+
+		if ( !sieve_validate_positional_argument
+			(valdtr, tst, arg, "header name", 1, SAAT_STRING) ) {
+			return FALSE;
+		}
+	
+		if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+			return FALSE;
+
+		if ( !sieve_command_verify_headers_argument(valdtr, arg) )
+    	    return FALSE;
+
+		arg = sieve_ast_argument_next(arg);
+	}
+
+	/* Check date part */
+
+	if ( !sieve_validate_positional_argument
+		(valdtr, tst, arg, "date part", arg_offset + 1, SAAT_STRING) ) {
+		return FALSE;
+	}
+	
+	if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+		return FALSE;
+
+	arg = sieve_ast_argument_next(arg);
+
+	/* Check key list */
+		
+	if ( !sieve_validate_positional_argument
+		(valdtr, tst, arg, "key list", arg_offset + 2, SAAT_STRING_LIST) ) {
+		return FALSE;
+	}
+
+	if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) )
+		return FALSE;
+	
+	/* Validate the key argument to a specified match type */
+	return sieve_match_type_validate
+		(valdtr, tst, arg, &is_match_type, &i_ascii_casemap_comparator); 
+}
+
+/* 
+ * Code generation 
+ */
+
+static bool tst_date_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_command_context *tst) 
+{
+	if ( tst->command == &date_test )
+		sieve_operation_emit_code(cgenv->sbin, &date_operation);
+	else if ( tst->command == &currentdate_test )
+		sieve_operation_emit_code(cgenv->sbin, &currentdate_operation);
+	else
+		i_unreached();
+
+	/* Generate arguments */  	
+	return sieve_generate_arguments(cgenv, tst, NULL);
+}
+
+static bool tag_zone_generate
+(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
+    struct sieve_command_context *cmd)
+{
+	struct sieve_ast_argument *param = arg->parameters;
+
+	if ( param == NULL ) {
+		sieve_opr_omitted_emit(cgenv->sbin);
+		return TRUE;
+	}
+
+	if ( param->argument != NULL && param->argument->generate != NULL )
+		return param->argument->generate(cgenv, param, cmd);
+
+	return FALSE;	
+}
+
+/* 
+ * Code dump 
+ */
+
+static bool tst_date_operation_dump
+(const struct sieve_operation *op,	
+	const struct sieve_dumptime_env *denv, sieve_size_t *address)
+{
+	int opt_code = 0;
+	const struct sieve_operand *operand;
+
+	sieve_code_dumpf(denv, "%s", op->mnemonic);
+	sieve_code_descend(denv);
+	
+	/* Handle any optional arguments */
+    do {
+
+        if ( !sieve_match_dump_optional_operands(denv, address, &opt_code) )
+            return FALSE;
+
+        switch ( opt_code ) {
+        case SIEVE_MATCH_OPT_END:
+            break;
+        case OPT_DATE_ZONE:
+			operand = sieve_operand_read(denv->sbin, address);
+			if ( operand == NULL ) {
+				sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
+				return FALSE;
+			}				
+
+			if ( sieve_operand_is_omitted(operand) ) {
+				sieve_code_dumpf(denv, "zone: ORIGINAL");
+			} else {
+   				if ( !sieve_opr_string_dump_data
+					(denv, operand, address, "zone") )
+					return FALSE;
+			}
+			break;
+
+        default:
+            return FALSE;
+        }
+    } while ( opt_code != SIEVE_MATCH_OPT_END );
+
+	if ( op == &date_operation &&
+		!sieve_opr_string_dump(denv, address, "header name") )
+		return FALSE;
+
+	return
+		sieve_opr_string_dump(denv, address, "date part") && 
+		sieve_opr_stringlist_dump(denv, address, "key list");
+}
+
+/* 
+ * Code execution 
+ */
+
+static int tst_date_operation_execute
+(const struct sieve_operation *op, 
+	const struct sieve_runtime_env *renv, sieve_size_t *address)
+{	
+	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;
+	const struct sieve_operand *operand;
+	struct sieve_match_context *mctx;
+	string_t *header_name = NULL, *date_part = NULL, *zone = NULL;
+	struct sieve_coded_stringlist *key_list;
+	bool matched = FALSE;
+	int ret;
+	
+	/* Read optional operands */
+	do {
+		if ( (ret=sieve_match_read_optional_operands
+			(renv, address, &opt_code, &cmp, &mtch)) <= 0 )
+			return ret;
+
+		switch ( opt_code ) {
+		case SIEVE_MATCH_OPT_END:
+			break;
+		case OPT_DATE_ZONE:
+			operand = sieve_operand_read(renv->sbin, address);
+			if ( operand == NULL ) {
+				sieve_runtime_trace_error(renv, "invalid operand");
+				return SIEVE_EXEC_BIN_CORRUPT;
+			}
+
+			if ( !sieve_operand_is_omitted(operand) ) {
+				if ( !sieve_opr_string_read_data
+					(renv, operand, address, &zone) ) {
+					sieve_runtime_trace_error(renv, "invalid zone operand");
+					return SIEVE_EXEC_BIN_CORRUPT;
+				} 
+    		}
+			break;
+		default:
+			sieve_runtime_trace_error(renv, "unknown optional operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
+		}
+	} while ( opt_code != SIEVE_MATCH_OPT_END );
+
+
+	if ( op == &date_operation ) {
+		/* Read header name */
+		if ( !sieve_opr_string_read(renv, address, &header_name) ) {
+			sieve_runtime_trace_error(renv, "invalid header-name operand");
+			return SIEVE_EXEC_BIN_CORRUPT;
+		}
+	}
+
+	/* Read date part */
+	if ( !sieve_opr_string_read(renv, address, &date_part) ) {
+		sieve_runtime_trace_error(renv, "invalid date-part 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 test */
+
+	sieve_runtime_trace(renv, "%s test", op->mnemonic);
+
+	/* FIXME: implement */
+
+	sieve_interpreter_set_test_result(renv->interp, matched);
+	return SIEVE_EXEC_OK;
+}
diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index 897f7071f..040d86b9e 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -89,6 +89,7 @@ extern const struct sieve_extension mailbox_extension;
 #ifdef HAVE_SIEVE_UNFINISHED
 
 extern const struct sieve_extension ereject_extension;
+extern const struct sieve_extension date_extension;
 
 #endif
 
@@ -111,6 +112,7 @@ const struct sieve_extension *sieve_core_extensions[] = {
 
 #ifdef HAVE_SIEVE_UNFINISHED
 	&ereject_extension,
+	&date_extension,
 #endif
 	
 	/* 'Plugins' */
-- 
GitLab