diff --git a/configure.ac b/configure.ac index 30e0c534777f444d0c555fe88c5a3c7519f94edd..2e925f35c15ad55e7c6bbc47786d6499da337fdc 100644 --- a/configure.ac +++ b/configure.ac @@ -120,6 +120,7 @@ src/lib-sieve/plugins/date/Makefile src/lib-sieve/plugins/spamvirustest/Makefile src/lib-sieve/plugins/ihave/Makefile src/lib-sieve/plugins/editheader/Makefile +src/lib-sieve/plugins/metadata/Makefile src/lib-sieve/plugins/vnd.dovecot/Makefile src/lib-sieve/plugins/vnd.dovecot/debug/Makefile src/lib-sieve/plugins/vnd.dovecot/duplicate/Makefile diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 7cd4acfaf2bc32ad6e7334761583869b9b5b7d9e..eea61baacea632194f0ab1742bb52e7f6d9c769e 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -42,7 +42,8 @@ comparators = \ cmp-i-ascii-casemap.c if BUILD_UNFINISHED -unfinished_plugins = +unfinished_plugins = \ + $(extdir)/metadata/libsieve_ext_metadata.la endif # These are not actual plugins just yet... diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am index 21fedfcee80a44d5cb27e2a28924d2f1702491ac..a26c4a820d4a9fb857f0419bbf44e1cc65e5ef63 100644 --- a/src/lib-sieve/plugins/Makefile.am +++ b/src/lib-sieve/plugins/Makefile.am @@ -1,5 +1,5 @@ if BUILD_UNFINISHED -UNFINISHED = +UNFINISHED = metadata endif SUBDIRS = \ diff --git a/src/lib-sieve/plugins/metadata/Makefile.am b/src/lib-sieve/plugins/metadata/Makefile.am new file mode 100644 index 0000000000000000000000000000000000000000..bac6f5d75fa6862946c9a76d301c49563a92b3f2 --- /dev/null +++ b/src/lib-sieve/plugins/metadata/Makefile.am @@ -0,0 +1,24 @@ + +noinst_LTLIBRARIES = libsieve_ext_metadata.la + +libsieve_ext_metadata_la_LDFLAGS = -module -avoid-version + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib-sieve \ + -I$(top_srcdir)/src/lib-sieve/plugins/variables \ + $(LIBDOVECOT_INCLUDE) + +tests = \ + tst-metadata.c \ + tst-metadataexists.c + +extensions = \ + ext-metadata.c + +libsieve_ext_metadata_la_SOURCES = \ + $(tests) \ + $(extensions) + +noinst_HEADERS = \ + ext-metadata-common.h + diff --git a/src/lib-sieve/plugins/metadata/ext-metadata-common.h b/src/lib-sieve/plugins/metadata/ext-metadata-common.h new file mode 100644 index 0000000000000000000000000000000000000000..a974436559a5a7b4cca2129c56a4df2e3e2800f5 --- /dev/null +++ b/src/lib-sieve/plugins/metadata/ext-metadata-common.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#ifndef __EXT_METADATA_COMMON_H +#define __EXT_METADATA_COMMON_H + +#include "sieve-common.h" + +/* + * Extension + */ + +extern const struct sieve_extension_def mboxmetadata_extension; +extern const struct sieve_extension_def servermetadata_extension; + +/* + * Commands + */ + +extern const struct sieve_command_def metadata_test; +extern const struct sieve_command_def servermetadata_test; +extern const struct sieve_command_def metadataexists_test; +extern const struct sieve_command_def servermetadataexists_test; + +/* + * Operations + */ + +enum ext_metadata_opcode { + EXT_METADATA_OPERATION_METADATA, + EXT_METADATA_OPERATION_METADATAEXISTS +}; + +extern const struct sieve_operation_def metadata_operation; +extern const struct sieve_operation_def servermetadata_operation; +extern const struct sieve_operation_def metadataexists_operation; +extern const struct sieve_operation_def servermetadataexists_operation; + +#endif /* __EXT_METADATA_COMMON_H */ diff --git a/src/lib-sieve/plugins/metadata/ext-metadata.c b/src/lib-sieve/plugins/metadata/ext-metadata.c new file mode 100644 index 0000000000000000000000000000000000000000..0beb6307997b65cdadd55b96921eeeed6219706d --- /dev/null +++ b/src/lib-sieve/plugins/metadata/ext-metadata.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-binary.h" + +#include "sieve-validator.h" +#include "sieve-interpreter.h" + +#include "ext-metadata-common.h" + +/* + * Extension mboxmetadata + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5490; Section 3 + * Implementation: skeleton + * Status: development + * + */ + +const struct sieve_operation_def *mboxmetadata_operations[] = { + &metadata_operation, + &metadataexists_operation, +}; + +static bool ext_mboxmetadata_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def mboxmetadata_extension = { + .name = "mboxmetadata", + .validator_load = ext_mboxmetadata_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(mboxmetadata_operations) +}; + +static bool ext_mboxmetadata_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_validator_register_command(valdtr, ext, &metadata_test); + sieve_validator_register_command(valdtr, ext, &metadataexists_test); + + return TRUE; +} + +/* + * Extension servermetadata + * ----------------------------- + * + * Authors: Stephan Bosch + * Specification: RFC 5490; Section 4 + * Implementation: skeleton + * Status: development + * + */ + +const struct sieve_operation_def *servermetadata_operations[] = { + &servermetadata_operation, + &servermetadataexists_operation, +}; + +static bool ext_servermetadata_validator_load + (const struct sieve_extension *ext, struct sieve_validator *valdtr); + +const struct sieve_extension_def servermetadata_extension = { + .name = "servermetadata", + .validator_load = ext_servermetadata_validator_load, + SIEVE_EXT_DEFINE_OPERATIONS(servermetadata_operations) +}; + +static bool ext_servermetadata_validator_load +(const struct sieve_extension *ext, struct sieve_validator *valdtr) +{ + sieve_validator_register_command(valdtr, ext, &servermetadata_test); + sieve_validator_register_command(valdtr, ext, &servermetadataexists_test); + + return TRUE; +} + + diff --git a/src/lib-sieve/plugins/metadata/tst-metadata.c b/src/lib-sieve/plugins/metadata/tst-metadata.c new file mode 100644 index 0000000000000000000000000000000000000000..fb85212cf88a352203565f63d981ac5ac50b3204 --- /dev/null +++ b/src/lib-sieve/plugins/metadata/tst-metadata.c @@ -0,0 +1,286 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.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-metadata-common.h" + +/* + * Test definitions + */ + +/* Forward declarations */ + +static bool tst_metadata_registered + (struct sieve_validator *valdtr, const struct sieve_extension *ext, + struct sieve_command_registration *cmd_reg); +static bool tst_metadata_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_metadata_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *cmd); + +/* Metadata test + * + * Syntax: + * metadata [MATCH-TYPE] [COMPARATOR] + * <mailbox: string> + * <annotation-name: string> <key-list: string-list> + */ + +const struct sieve_command_def metadata_test = { + "metadata", + SCT_TEST, + 3, 0, FALSE, FALSE, + tst_metadata_registered, + NULL, + tst_metadata_validate, + NULL, + tst_metadata_generate, + NULL +}; + +/* Servermetadata test + * + * Syntax: + * servermetadata [MATCH-TYPE] [COMPARATOR] + * <annotation-name: string> <key-list: string-list> + */ + +const struct sieve_command_def servermetadata_test = { + "servermetadata", + SCT_TEST, + 2, 0, FALSE, FALSE, + tst_metadata_registered, + NULL, + tst_metadata_validate, + NULL, + tst_metadata_generate, + NULL +}; + +/* + * Opcode definitions + */ + +static bool tst_metadata_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_metadata_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Metadata operation */ + +const struct sieve_operation_def metadata_operation = { + "METADATA", + &mboxmetadata_extension, + EXT_METADATA_OPERATION_METADATA, + tst_metadata_operation_dump, + tst_metadata_operation_execute +}; + +/* Servermetadata operation */ + +const struct sieve_operation_def servermetadata_operation = { + "SERVERMETADATA", + &servermetadata_extension, + EXT_METADATA_OPERATION_METADATA, + tst_metadata_operation_dump, + tst_metadata_operation_execute +}; + +/* + * Test registration + */ + +static bool tst_metadata_registered +(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, + struct sieve_command_registration *cmd_reg) +{ + /* The order of these is not significant */ + sieve_comparators_link_tag(valdtr, cmd_reg, SIEVE_MATCH_OPT_COMPARATOR); + sieve_match_types_link_tags(valdtr, cmd_reg, SIEVE_MATCH_OPT_MATCH_TYPE); + + return TRUE; +} + +/* + * Test validation + */ + +static bool tst_metadata_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + const struct sieve_match_type mcht_default = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + const struct sieve_comparator cmp_default = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + unsigned int arg_index = 1; + + if ( sieve_command_is(tst, metadata_test) ) { + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "mailbox", arg_index++, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + } + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "annotation-name", arg_index++, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "key list", arg_index++, 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, &mcht_default, &cmp_default); +} + +/* + * Test generation + */ + +static bool tst_metadata_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + if ( sieve_command_is(tst, metadata_test) ) { + sieve_operation_emit + (cgenv->sblock, tst->ext, &metadata_operation); + } else if ( sieve_command_is(tst, servermetadata_test) ) { + sieve_operation_emit + (cgenv->sblock, tst->ext, &servermetadata_operation); + } else { + i_unreached(); + } + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, tst, NULL) ) + return FALSE; + + return TRUE; +} + +/* + * Code dump + */ + +static bool tst_metadata_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + bool metadata = sieve_operation_is(denv->oprtn, metadata_operation); + + if ( metadata ) + sieve_code_dumpf(denv, "METADATA"); + else + sieve_code_dumpf(denv, "SERVERMETADATA"); + + sieve_code_descend(denv); + + /* Handle any optional arguments */ + if ( sieve_match_opr_optional_dump(denv, address, NULL) != 0 ) + return FALSE; + + if ( metadata && !sieve_opr_string_dump(denv, address, "mailbox") ) + return FALSE; + + return + sieve_opr_string_dump(denv, address, "annotation-name") && + sieve_opr_stringlist_dump(denv, address, "key list"); +} + +/* + * Code execution + */ + +static int tst_metadata_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + bool metadata = sieve_operation_is(renv->oprtn, metadata_operation); + struct sieve_match_type mcht = + SIEVE_MATCH_TYPE_DEFAULT(is_match_type); + struct sieve_comparator cmp = + SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); + string_t *mailbox, *annotation_name; + struct sieve_stringlist *value_list, *key_list; + const char *annotation = NULL; + int match, ret; + + /* + * Read operands + */ + + /* Handle match-type and comparator operands */ + if ( sieve_match_opr_optional_read + (renv, address, NULL, &ret, &cmp, &mcht) < 0 ) + return ret; + + /* Read mailbox */ + if ( metadata ) { + if ( (ret=sieve_opr_string_read(renv, address, "mailbox", &mailbox)) <= 0 ) + return ret; + } + + /* Read annotation-name */ + if ( (ret=sieve_opr_string_read + (renv, address, "annotation-name", &annotation_name)) <= 0 ) + return ret; + + /* Read key-list */ + if ( (ret=sieve_opr_stringlist_read + (renv, address, "key-list", &key_list)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + if ( metadata ) + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "metadata test"); + else + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "servermetadata test"); + + /* Get annotation */ + annotation = "FIXME"; + + /* Perform match */ + if ( annotation != NULL ) { + /* Create value stringlist */ + value_list = sieve_single_stringlist_create_cstr(renv, annotation, FALSE); + + /* Perform match */ + if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) + < 0 ) + return ret; + } else { + match = 0; + } + + /* Set test result for subsequent conditional jump */ + sieve_interpreter_set_test_result(renv->interp, match > 0); + return SIEVE_EXEC_OK; +} diff --git a/src/lib-sieve/plugins/metadata/tst-metadataexists.c b/src/lib-sieve/plugins/metadata/tst-metadataexists.c new file mode 100644 index 0000000000000000000000000000000000000000..b234201d6631bcc1ed9e8a87ea6a97edd1143d9a --- /dev/null +++ b/src/lib-sieve/plugins/metadata/tst-metadataexists.c @@ -0,0 +1,246 @@ +/* Copyright (c) 2002-2013 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str-sanitize.h" +#include "mail-storage.h" +#include "mail-namespace.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "ext-metadata-common.h" + +/* + * Command definitions + */ + +/* Forward declarations */ + +static bool tst_metadataexists_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_metadataexists_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +/* Metadataexists command + * + * Syntax: + * metadataexists <mailbox: string> <annotation-names: string-list> + */ + +static bool tst_metadataexists_validate + (struct sieve_validator *valdtr, struct sieve_command *tst); +static bool tst_metadataexists_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +const struct sieve_command_def metadataexists_test = { + "metadataexists", + SCT_TEST, + 2, 0, FALSE, FALSE, + NULL, NULL, + tst_metadataexists_validate, + NULL, + tst_metadataexists_generate, + NULL +}; + +/* Servermetadataexists command + * + * Syntax: + * servermetadataexists <annotation-names: string-list> + */ + +const struct sieve_command_def servermetadataexists_test = { + "servermetadataexists", + SCT_TEST, + 1, 0, FALSE, FALSE, + NULL, NULL, + tst_metadataexists_validate, + NULL, + tst_metadataexists_generate, + NULL +}; + + +/* + * Opcode definitions + */ + +static bool tst_metadataexists_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int tst_metadataexists_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Metadata operation */ + +const struct sieve_operation_def metadataexists_operation = { + "METADATAEXISTS", + &mboxmetadata_extension, + EXT_METADATA_OPERATION_METADATAEXISTS, + tst_metadataexists_operation_dump, + tst_metadataexists_operation_execute +}; + +/* Mailboxexists operation */ + +const struct sieve_operation_def servermetadataexists_operation = { + "SERVERMETADATAEXISTS", + &servermetadata_extension, + EXT_METADATA_OPERATION_METADATAEXISTS, + tst_metadataexists_operation_dump, + tst_metadataexists_operation_execute +}; + +/* + * Test validation + */ + +static bool tst_metadataexists_validate +(struct sieve_validator *valdtr, struct sieve_command *tst) +{ + struct sieve_ast_argument *arg = tst->first_positional; + unsigned int arg_index = 1; + + if ( sieve_command_is(tst, metadataexists_test) ) { + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "mailbox", arg_index++, SAAT_STRING) ) { + return FALSE; + } + + if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) + return FALSE; + + arg = sieve_ast_argument_next(arg); + } + + if ( !sieve_validate_positional_argument + (valdtr, tst, arg, "annotation-names", arg_index++, SAAT_STRING_LIST) ) { + return FALSE; + } + + return sieve_validator_argument_activate(valdtr, tst, arg, FALSE); +} + +/* + * Test generation + */ + +static bool tst_metadataexists_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *tst) +{ + if ( sieve_command_is(tst, metadataexists_test) ) { + sieve_operation_emit + (cgenv->sblock, tst->ext, &metadataexists_operation); + } else if ( sieve_command_is(tst, servermetadataexists_test) ) { + sieve_operation_emit + (cgenv->sblock, tst->ext, &servermetadataexists_operation); + } else { + i_unreached(); + } + + /* Generate arguments */ + return sieve_generate_arguments(cgenv, tst, NULL); +} + +/* + * Code dump + */ + +static bool tst_metadataexists_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + bool metadata = sieve_operation_is(denv->oprtn, metadataexists_operation); + + if ( metadata ) + sieve_code_dumpf(denv, "METADATAEXISTS"); + else + sieve_code_dumpf(denv, "SERVERMETADATAEXISTS"); + + sieve_code_descend(denv); + + if ( metadata && !sieve_opr_string_dump(denv, address, "mailbox") ) + return FALSE; + + return + sieve_opr_stringlist_dump(denv, address, "annotation-names"); +} + +/* + * Code execution + */ + +static int tst_metadataexists_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + bool metadata = sieve_operation_is(renv->oprtn, metadataexists_operation); + struct sieve_stringlist *annotation_names; + string_t *mailbox, *annotation_item; + bool trace = FALSE; + bool all_exist = TRUE; + int ret; + + /* + * Read operands + */ + + /* Read mailbox */ + if ( metadata ) { + if ( (ret=sieve_opr_string_read(renv, address, "mailbox", &mailbox)) <= 0 ) + return ret; + } + + /* Read annotation names */ + if ( (ret=sieve_opr_stringlist_read + (renv, address, "annotation-names", &annotation_names)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS) ) { + if ( metadata ) + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "metadataexists test"); + else + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "servermetadataexists test"); + + sieve_runtime_trace_descend(renv); + + trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + } + + if ( renv->scriptenv->user != NULL ) { + int ret; + + annotation_item = NULL; + while ( (ret=sieve_stringlist_next_item(annotation_names, &annotation_item)) + > 0 ) { + //const char *annotation = str_c(annotation_item); + + /* IMPLEMENT ... */ + all_exist = FALSE; + } + + if ( ret < 0 ) { + sieve_runtime_trace_error + (renv, "invalid annotation name stringlist item"); + return SIEVE_EXEC_BIN_CORRUPT; + } + } + + if ( trace ) { + if ( all_exist ) + sieve_runtime_trace(renv, 0, "all annotations exist"); + else + sieve_runtime_trace(renv, 0, "some mailboxes no not exist"); + } + + sieve_interpreter_set_test_result(renv->interp, all_exist); + return SIEVE_EXEC_OK; +} diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index bc3c4a2dd31954d15974bd006b52b42fc490af5a..c1497fdff2882d1d1a021c9c3b733729dfb7c974 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -99,6 +99,8 @@ extern const struct sieve_extension_def spamtestplus_extension; extern const struct sieve_extension_def virustest_extension; extern const struct sieve_extension_def ihave_extension; extern const struct sieve_extension_def editheader_extension; +extern const struct sieve_extension_def mboxmetadata_extension; +extern const struct sieve_extension_def servermetadata_extension; /* vnd.dovecot. */ extern const struct sieve_extension_def debug_extension; @@ -175,7 +177,7 @@ const unsigned int sieve_deprecated_extensions_count = extern const struct sieve_extension_def ereject_extension; const struct sieve_extension_def *sieve_unfinished_extensions[] = { - &ereject_extension + &ereject_extension, &mboxmetadata_extension, &servermetadata_extension }; const unsigned int sieve_unfinished_extensions_count =