From f63b20cdab5a9ba4d7db1bd96e9236c2d39338d8 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan@rename-it.nl> Date: Sun, 16 Nov 2014 23:13:58 +0100 Subject: [PATCH] lib-sieve: Finished support for mboxmetadata and servermetadata extensions. --- src/lib-sieve/Makefile.am | 4 +- src/lib-sieve/plugins/Makefile.am | 3 +- src/lib-sieve/plugins/metadata/Makefile.am | 3 +- .../plugins/metadata/ext-metadata-common.h | 4 + src/lib-sieve/plugins/metadata/tst-metadata.c | 147 +++++++++++++++--- .../plugins/metadata/tst-metadataexists.c | 137 +++++++++++++--- src/lib-sieve/sieve-extensions.c | 7 +- src/sieve-tools/sieve-test.c | 2 +- 8 files changed, 256 insertions(+), 51 deletions(-) diff --git a/src/lib-sieve/Makefile.am b/src/lib-sieve/Makefile.am index 143287bbc..bdc35c80c 100644 --- a/src/lib-sieve/Makefile.am +++ b/src/lib-sieve/Makefile.am @@ -44,8 +44,7 @@ comparators = \ if BUILD_UNFINISHED unfinished_storages = -unfinished_plugins = \ - $(extdir)/metadata/libsieve_ext_metadata.la +unfinished_plugins = endif strgdir = $(top_builddir)/src/lib-sieve/storage @@ -77,6 +76,7 @@ plugins = \ $(extdir)/editheader/libsieve_ext_editheader.la \ $(extdir)/duplicate/libsieve_ext_duplicate.la \ $(extdir)/index/libsieve_ext_index.la \ + $(extdir)/metadata/libsieve_ext_metadata.la \ $(extdir)/vnd.dovecot/debug/libsieve_ext_debug.la \ $(unfinished_plugins) diff --git a/src/lib-sieve/plugins/Makefile.am b/src/lib-sieve/plugins/Makefile.am index ee69303f4..4e626582d 100644 --- a/src/lib-sieve/plugins/Makefile.am +++ b/src/lib-sieve/plugins/Makefile.am @@ -1,5 +1,5 @@ if BUILD_UNFINISHED -UNFINISHED = metadata +UNFINISHED = endif SUBDIRS = \ @@ -23,6 +23,7 @@ SUBDIRS = \ editheader \ duplicate \ index \ + metadata \ vnd.dovecot \ $(UNFINISHED) diff --git a/src/lib-sieve/plugins/metadata/Makefile.am b/src/lib-sieve/plugins/metadata/Makefile.am index 7aeaf2e18..6d69ba822 100644 --- a/src/lib-sieve/plugins/metadata/Makefile.am +++ b/src/lib-sieve/plugins/metadata/Makefile.am @@ -5,7 +5,8 @@ libsieve_ext_metadata_la_LDFLAGS = -module -avoid-version AM_CPPFLAGS = \ -I$(srcdir)/../.. \ -I$(srcdir)/../variables \ - $(LIBDOVECOT_INCLUDE) + $(LIBDOVECOT_INCLUDE) \ + $(LIBDOVECOT_STORAGE_INCLUDE) tests = \ tst-metadata.c \ diff --git a/src/lib-sieve/plugins/metadata/ext-metadata-common.h b/src/lib-sieve/plugins/metadata/ext-metadata-common.h index c86da3dcf..d63c9f026 100644 --- a/src/lib-sieve/plugins/metadata/ext-metadata-common.h +++ b/src/lib-sieve/plugins/metadata/ext-metadata-common.h @@ -4,6 +4,10 @@ #ifndef __EXT_METADATA_COMMON_H #define __EXT_METADATA_COMMON_H +#include "lib.h" +#include "mail-storage.h" +#include "imap-metadata.h" + #include "sieve-common.h" /* diff --git a/src/lib-sieve/plugins/metadata/tst-metadata.c b/src/lib-sieve/plugins/metadata/tst-metadata.c index 01b1296c1..7be3c70f4 100644 --- a/src/lib-sieve/plugins/metadata/tst-metadata.c +++ b/src/lib-sieve/plugins/metadata/tst-metadata.c @@ -1,7 +1,12 @@ /* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file */ +#include "lib.h" +#include "str-sanitize.h" +#include "istream.h" + #include "sieve-common.h" +#include "sieve-limits.h" #include "sieve-commands.h" #include "sieve-stringlist.h" #include "sieve-code.h" @@ -15,6 +20,10 @@ #include "ext-metadata-common.h" +#include <ctype.h> + +#define TST_METADATA_MAX_MATCH_SIZE SIEVE_MAX_STRING_LEN + /* * Test definitions */ @@ -102,7 +111,8 @@ const struct sieve_operation_def servermetadata_operation = { */ static bool tst_metadata_registered -(struct sieve_validator *valdtr, const struct sieve_extension *ext ATTR_UNUSED, +(struct sieve_validator *valdtr, + const struct sieve_extension *ext ATTR_UNUSED, struct sieve_command_registration *cmd_reg) { /* The order of these is not significant */ @@ -125,7 +135,9 @@ static bool tst_metadata_validate const struct sieve_comparator cmp_default = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); unsigned int arg_index = 1; + const char *error; + /* mailbox */ if ( sieve_command_is(tst, metadata_test) ) { if ( !sieve_validate_positional_argument (valdtr, tst, arg, "mailbox", arg_index++, SAAT_STRING) ) { @@ -138,6 +150,7 @@ static bool tst_metadata_validate arg = sieve_ast_argument_next(arg); } + /* annotation-name */ if ( !sieve_validate_positional_argument (valdtr, tst, arg, "annotation-name", arg_index++, SAAT_STRING) ) { return FALSE; @@ -146,10 +159,25 @@ static bool tst_metadata_validate if ( !sieve_validator_argument_activate(valdtr, tst, arg, FALSE) ) return FALSE; + if ( sieve_argument_is_string_literal(arg) ) { + string_t *aname = sieve_ast_argument_str(arg); + + if ( !imap_metadata_verify_entry_name(str_c(aname), &error) ) { + char *lcerror = t_strdup_noconst(error); + lcerror[0] = i_tolower(lcerror[0]); + sieve_argument_validate_warning + (valdtr, arg, "%s test: " + "specified annotation name `%s' is invalid: %s", + sieve_command_identifier(tst), + str_sanitize(str_c(aname), 256), lcerror); + } + } + arg = sieve_ast_argument_next(arg); + /* key-list */ if ( !sieve_validate_positional_argument - (valdtr, tst, arg, "key list", arg_index++, SAAT_STRING_LIST) ) { + (valdtr, tst, arg, "key-list", arg_index++, SAAT_STRING_LIST) ) { return FALSE; } @@ -217,6 +245,63 @@ static bool tst_metadata_operation_dump * Code execution */ +static inline const char *_lc_error(const char *error) +{ + char *lcerror = t_strdup_noconst(error); + lcerror[0] = i_tolower(lcerror[0]); + + return lcerror; +} + +static int tst_metadata_get_annotation +(const struct sieve_runtime_env *renv, const char *mailbox, + const char *aname, const char **annotation_r) +{ + struct mail_user *user = renv->scriptenv->user; + struct mailbox *box; + struct imap_metadata_transaction *imtrans; + struct mail_attribute_value avalue; + int status, ret; + + *annotation_r = NULL; + + if ( user == NULL ) + return SIEVE_EXEC_OK; + + if ( mailbox != NULL ) { + struct mail_namespace *ns; + ns = mail_namespace_find(user->namespaces, mailbox); + box = mailbox_alloc(ns->list, mailbox, 0); + imtrans = imap_metadata_transaction_begin(box); + } else { + imtrans = imap_metadata_transaction_begin_server(user); + } + + status = SIEVE_EXEC_OK; + ret = imap_metadata_get(imtrans, aname, &avalue); + if (ret < 0) { + enum mail_error error_code; + const char *error; + + error = imap_metadata_transaction_get_last_error + (imtrans, &error_code); + + sieve_runtime_error(renv, NULL, "%s test: " + "failed to retrieve annotation `%s': %s%s", + (mailbox != NULL ? "metadata" : "servermetadata"), + str_sanitize(aname, 256), _lc_error(error), + (error_code == MAIL_ERROR_TEMP ? " (temporary failure)" : "")); + + status = ( error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE ); + + } else if (avalue.value != NULL) { + *annotation_r = avalue.value; + } + (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL); + return status; +} + static int tst_metadata_operation_execute (const struct sieve_runtime_env *renv, sieve_size_t *address) { @@ -225,9 +310,9 @@ static int tst_metadata_operation_execute SIEVE_MATCH_TYPE_DEFAULT(is_match_type); struct sieve_comparator cmp = SIEVE_COMPARATOR_DEFAULT(i_ascii_casemap_comparator); - string_t *mailbox, *annotation_name; + string_t *mailbox, *aname; struct sieve_stringlist *value_list, *key_list; - const char *annotation = NULL; + const char *annotation = NULL, *error; int match, ret; /* @@ -247,7 +332,7 @@ static int tst_metadata_operation_execute /* Read annotation-name */ if ( (ret=sieve_opr_string_read - (renv, address, "annotation-name", &annotation_name)) <= 0 ) + (renv, address, "annotation-name", &aname)) <= 0 ) return ret; /* Read key-list */ @@ -263,24 +348,48 @@ static int tst_metadata_operation_execute sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "metadata test"); else sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, "servermetadata test"); + sieve_runtime_trace_descend(renv); + + if ( !imap_metadata_verify_entry_name(str_c(aname), &error) ) { + sieve_runtime_warning(renv, NULL, "%s test: " + "specified annotation name `%s' is invalid: %s", + (metadata ? "metadata" : "servermetadata"), + str_sanitize(str_c(aname), 256), _lc_error(error)); + sieve_interpreter_set_test_result(renv->interp, FALSE); + return SIEVE_EXEC_OK; + } - /* Get annotation */ - annotation = "FIXME"; - - /* Perform match */ - if ( annotation != NULL ) { - /* Create value stringlist */ - value_list = sieve_single_stringlist_create_cstr(renv, annotation, FALSE); + if ( metadata ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "retrieving annotation `%s' from mailbox `%s'", + str_sanitize(str_c(aname), 256), + str_sanitize(str_c(mailbox), 80)); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "retrieving server annotation `%s'", + str_sanitize(str_c(aname), 256)); + } + /* Get annotation */ + if ( (ret=tst_metadata_get_annotation + (renv, (metadata ? str_c(mailbox) : NULL), str_c(aname), &annotation)) + == SIEVE_EXEC_OK ) { /* Perform match */ - if ( (match=sieve_match(renv, &mcht, &cmp, value_list, key_list, &ret)) - < 0 ) - return ret; - } else { - match = 0; + 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; + if (ret == SIEVE_EXEC_OK) + sieve_interpreter_set_test_result(renv->interp, match > 0); + return ret; } diff --git a/src/lib-sieve/plugins/metadata/tst-metadataexists.c b/src/lib-sieve/plugins/metadata/tst-metadataexists.c index ba1a67d7b..58808354a 100644 --- a/src/lib-sieve/plugins/metadata/tst-metadataexists.c +++ b/src/lib-sieve/plugins/metadata/tst-metadataexists.c @@ -17,6 +17,9 @@ #include "ext-metadata-common.h" +#include <ctype.h> + + /* * Command definitions */ @@ -175,14 +178,114 @@ static bool tst_metadataexists_operation_dump * Code execution */ +static inline const char *_lc_error(const char *error) +{ + char *lcerror = t_strdup_noconst(error); + lcerror[0] = i_tolower(lcerror[0]); + + return lcerror; +} + +static int tst_metadataexists_check_annotations +(const struct sieve_runtime_env *renv, const char *mailbox, + struct sieve_stringlist *anames, bool *all_exist_r) +{ + struct mail_user *user = renv->scriptenv->user; + struct mailbox *box = NULL; + struct imap_metadata_transaction *imtrans; + string_t *aname; + bool all_exist = TRUE; + int ret, sret, status; + + *all_exist_r = FALSE; + + if ( user == NULL ) + return SIEVE_EXEC_OK; + + if ( mailbox != NULL ) { + struct mail_namespace *ns; + ns = mail_namespace_find(user->namespaces, mailbox); + box = mailbox_alloc(ns->list, mailbox, 0); + imtrans = imap_metadata_transaction_begin(box); + } else { + imtrans = imap_metadata_transaction_begin_server(user); + } + + if ( mailbox != NULL ) { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "checking annotations of mailbox `%s':", + str_sanitize(mailbox, 80)); + } else { + sieve_runtime_trace(renv, SIEVE_TRLVL_TESTS, + "checking server annotations"); + } + + aname = NULL; + status = SIEVE_EXEC_OK; + while ( all_exist && + (sret=sieve_stringlist_next_item(anames, &aname)) > 0 ) { + struct mail_attribute_value avalue; + const char *error; + + if ( !imap_metadata_verify_entry_name(str_c(aname), &error) ) { + sieve_runtime_warning(renv, NULL, "%s test: " + "specified annotation name `%s' is invalid: %s", + (mailbox != NULL ? "metadataexists" : "servermetadataexists"), + str_sanitize(str_c(aname), 256), _lc_error(error)); + continue; + } + + ret = imap_metadata_get(imtrans, str_c(aname), &avalue); + if (ret < 0) { + enum mail_error error_code; + const char *error; + + error = imap_metadata_transaction_get_last_error + (imtrans, &error_code); + sieve_runtime_error(renv, NULL, "%s test: " + "failed to retrieve annotation `%s': %s%s", + (mailbox != NULL ? "metadataexists" : "servermetadataexists"), + str_sanitize(str_c(aname), 256), _lc_error(error), + (error_code == MAIL_ERROR_TEMP ? " (temporary failure)" : "")); + + all_exist = FALSE; + status = ( error_code == MAIL_ERROR_TEMP ? + SIEVE_EXEC_TEMP_FAILURE : SIEVE_EXEC_FAILURE ); + break; + + } else if (avalue.value == NULL && avalue.value_stream == NULL) { + all_exist = FALSE; + sieve_runtime_trace(renv, 0, + "annotation `%s': not found", str_c(aname)); + break; + + } else { + sieve_runtime_trace(renv, 0, + "annotation `%s': found", str_c(aname)); + } + } + + if ( sret < 0 ) { + sieve_runtime_trace_error + (renv, "invalid annotation name stringlist item"); + status = SIEVE_EXEC_BIN_CORRUPT; + } + + (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL); + if ( box != NULL ) + mailbox_free(&box); + + *all_exist_r = all_exist; + return status; +} + 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; + struct sieve_stringlist *anames; + string_t *mailbox; + bool trace = FALSE, all_exist = TRUE; int ret; /* @@ -197,7 +300,7 @@ static int tst_metadataexists_operation_execute /* Read annotation names */ if ( (ret=sieve_opr_stringlist_read - (renv, address, "annotation-names", &annotation_names)) <= 0 ) + (renv, address, "annotation-names", &anames)) <= 0 ) return ret; /* @@ -215,30 +318,16 @@ static int tst_metadataexists_operation_execute 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 ( (ret=tst_metadataexists_check_annotations + (renv, (metadata ? str_c(mailbox) : NULL), anames, + &all_exist)) <= 0 ) + return ret; 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_runtime_trace(renv, 0, "some annotations do not exist"); } sieve_interpreter_set_test_result(renv->interp, all_exist); diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c index ff887afa4..32a13b5a4 100644 --- a/src/lib-sieve/sieve-extensions.c +++ b/src/lib-sieve/sieve-extensions.c @@ -106,6 +106,8 @@ extern const struct sieve_extension_def date_extension; extern const struct sieve_extension_def index_extension; extern const struct sieve_extension_def ihave_extension; extern const struct sieve_extension_def duplicate_extension; +extern const struct sieve_extension_def mboxmetadata_extension; +extern const struct sieve_extension_def servermetadata_extension; const struct sieve_extension_def *sieve_core_extensions[] = { /* Core extensions */ @@ -141,6 +143,7 @@ extern const struct sieve_extension_def vnd_debug_extension; const struct sieve_extension_def *sieve_extra_extensions[] = { &vacation_seconds_extension, &spamtest_extension, &spamtestplus_extension, &virustest_extension, &editheader_extension, + &mboxmetadata_extension, &servermetadata_extension, /* vnd.dovecot. */ &vnd_debug_extension @@ -173,11 +176,9 @@ const unsigned int sieve_deprecated_extensions_count = #ifdef HAVE_SIEVE_UNFINISHED extern const struct sieve_extension_def ereject_extension; -extern const struct sieve_extension_def mboxmetadata_extension; -extern const struct sieve_extension_def servermetadata_extension; const struct sieve_extension_def *sieve_unfinished_extensions[] = { - &ereject_extension, &mboxmetadata_extension, &servermetadata_extension + &ereject_extension }; const unsigned int sieve_unfinished_extensions_count = diff --git a/src/sieve-tools/sieve-test.c b/src/sieve-tools/sieve-test.c index 3de246aeb..8b4c833e1 100644 --- a/src/sieve-tools/sieve-test.c +++ b/src/sieve-tools/sieve-test.c @@ -223,7 +223,7 @@ int main(int argc, char **argv) } /* Finish tool initialization */ - svinst = sieve_tool_init_finish(sieve_tool, execute && mailloc == NULL, FALSE); + svinst = sieve_tool_init_finish(sieve_tool, mailloc == NULL, FALSE); /* Enable debug extension */ sieve_enable_debug_extension(svinst); -- GitLab