diff --git a/INSTALL b/INSTALL index ec6f2dd57ff6db47731001584fee31fdc76b3047..caebf2a456f585fe1db9038e26f8d3a9c4e572a1 100644 --- a/INSTALL +++ b/INSTALL @@ -4,12 +4,25 @@ Compiling First of all you'll need to have pre-built Dovecot 1.2 sources available. It's also not a good idea to build the plugin against self-compiled Dovecot sources, but then actually use a prebuilt binary package of Dovecot. That might work if -the Dovecot versions are the same, but it's not guaranteed. +the Dovecot versions are the same, but it's not guaranteed. You can also use +installed Dovecot headers to compile this package, but then command line tools +like sievec and sieved will not be compiled. This is also true for the test +suite. + +This package is compiled and configured as follows: ./configure --with-dovecot=../dovecot-1.2 make sudo make install +The --with-dovecot parameter points to your Dovecot sources or, in case you are +compiling against the headers, to the directory where the dovecot-config file is +installed. + +If you downloaded this package through Mercurial, you need to execute +./autogen.sh first to build the automake/autoconf structure. This requires +autotools and libtool to be installed. + Configuring ----------- @@ -18,3 +31,15 @@ however, and not 'cmusieve'. For a detailed description on installing the cmusieve plugin for deliver refer to the dovecot wiki: http://wiki.dovecot.org/LDA/Sieve + +Test Suite +---------- + +This package includes a test suite to verify the basic processing of the Sieve +interpreter on your particular platform. Note that the test suite is not +available when this package is compiled against the Dovecot headers. The test +suite executes a list of test cases and halts when one of them fails. If it +executes all test cases successfully, the test suite finishes. You can execute +the test suite using `make test`. + + diff --git a/Makefile.am b/Makefile.am index 0a4f57f1f3b813a24369e7061f6369d90a917001..e5b50b4cbdc632b3c7ba50852cfdc0f457820869 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,6 +28,12 @@ endif TESTSUITE_BIN = $(top_srcdir)/src/testsuite/testsuite +if TESTSUITE_VALGRIND +TEST_BIN = valgrind -q --error-exitcode=1 --leak-check=full $(TESTSUITE_BIN) +else +TEST_BIN = $(TESTSUITE_BIN) +endif + test_cases = \ tests/testsuite.svtest\ tests/control-structures.svtest \ @@ -53,8 +59,8 @@ test_cases = \ tests/extensions/variables/string.svtest \ tests/extensions/variables/errors.svtest \ tests/extensions/variables/regex.svtest \ - tests/extensions/include/variables.svtest \ tests/extensions/include/errors.svtest \ + tests/extensions/include/variables.svtest \ tests/extensions/imapflags/basic.svtest \ tests/extensions/imapflags/hasflag.svtest \ tests/extensions/imapflags/execute.svtest \ @@ -75,7 +81,7 @@ test_cases = \ if HAVE_DOVECOT_LIBS $(test_cases): - @$(TESTSUITE_BIN) $@ + @$(TEST_BIN) $@ .PHONY: $(test_cases) diff --git a/NEWS b/NEWS index 213abad0f175964c35633947250c6c1689c733f5..7794c7684e2a795cf10f922609016ed56976275a 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,26 @@ +v0.1.1 14-11-2008 Stephan Bosch <stephan@rename-it.nl> + + * Re-enabled support for compiling against dovecot headers. Much like + cmusieve, command line tools like sievec and sieved are not compiled in this + case. + + + Improved logging of errors at specific occasions and added debug messages to + find script execution problems quicker. + + Removed code duplication between command line tools and the test suite. + Also restructured the sources of the tools. + + Added UTF-8 to UTF-7 folder name conversion for compatibility with IMAP. + + Created man pages for the command line tools. These are automatically + installed upon 'make install' + - Fixed compile error surfacing for gcc3.4. Forgot mask argument for the + open() system call when the O_CREAT flag is specified. Bug found by + Sergey Ivanov. + - Fixed bug in the sievec tool. -d output was always written to stdout. + - Fixed important bug in the imap4flags extension. When no :flags argument is + specified, the previous version would always use the final value of the + internal variable to set the flags. This means that modifications to the + internal variable also affected the bare fileinto/keep actions executed + earlier. This does not comply to the RFC. + v0.1.0 23-10-2008 Stephan Bosch <stephan@rename-it.nl> * Initial release diff --git a/README b/README index 6cfe7fd31c09f57e7ccb055a4cebf0235c6b7a17..56a87917a4517305f3a1d82ee98d37b27d32d503 100644 --- a/README +++ b/README @@ -54,12 +54,12 @@ Features Dovecot v1.2. Therefore, ManageSieve support can be added to Dovecot for the new Sieve plugin just as for the cmusieve plugin. -* Testsuite included: +* Test suite included: - This package includes a testsuite to automatically asses whether the compiled - sieve engine works correctly. The testsuite is an extension to the Sieve + This package includes a test suite to automatically asses whether the compiled + sieve engine works correctly. The test suite is an extension to the Sieve language and is therefore easily extended with new tests. Currently, the - testsuite is limited to testing script processing. The performed actions are + test suite is limited to testing script processing. The performed actions are not tested automatically yet. Implementation Status @@ -133,9 +133,9 @@ that no other files are written and no permission to do so is necessary for the global script directories. To test the sieve engine outside deliver it is useful to execute the commands -that exist in the src/sieve-tools/ directory of this package. After installation, -these are available at your $prefix/bin directory. The following commands are -installed: +that exist in the src/sieve-tools/ directory of this package. After +installation, these are available at your $prefix/bin directory. The following +commands are installed: -- diff --git a/configure.in b/configure.in index 5e39a346ff7432ecf28144b8e55337ea939d0aee..d84b6750796ee3372b0c559fd866099ab555bc43 100644 --- a/configure.in +++ b/configure.in @@ -69,6 +69,16 @@ fi # # +AC_ARG_ENABLE(valgrind, +[AC_HELP_STRING([--enable-valgrind], [Enable Valgrind memory leak checks in testsuite [default=no]])], + if test x$enableval = xno || test x$enableval = xauto; then + want_valgrind=$enableval + else + want_valgrind=yes + fi, + want_valgrind=no) +AM_CONDITIONAL(TESTSUITE_VALGRIND, test "$want_valgrind" = "yes") + dnl replace relative ../ paths in the file with full paths eval `cat $dovecotdir/dovecot-config|sed 's,\$(top_builddir)/,$dovecotdir/,g'` diff --git a/src/lib-sieve-tool/mail-raw.c b/src/lib-sieve-tool/mail-raw.c index 2899de706b867d9bd9591992baabb41fd6435039..f5117c97deb94cefe532d4d1c4a2cb082a2e4fee 100644 --- a/src/lib-sieve-tool/mail-raw.c +++ b/src/lib-sieve-tool/mail-raw.c @@ -127,7 +127,6 @@ void mail_raw_init(const char *user) void mail_raw_deinit(void) { mail_user_deinit(&raw_mail_user); - mail_namespaces_deinit(&raw_ns); } /* diff --git a/src/lib-sieve-tool/sieve-tool.c b/src/lib-sieve-tool/sieve-tool.c index 6c0274a785fb4012286712b30cc196d8f2476cbe..b5f30766a8b985ce6389cb02911dcd7a1dc576ba 100644 --- a/src/lib-sieve-tool/sieve-tool.c +++ b/src/lib-sieve-tool/sieve-tool.c @@ -75,14 +75,25 @@ void sieve_tool_deinit(void) const char *sieve_tool_get_user(void) { - uid_t process_euid = geteuid(); - struct passwd *pw = getpwuid(process_euid); - if (pw != NULL) { - return t_strdup(pw->pw_name); - } - - i_fatal("couldn't lookup our username (uid=%s)", dec2str(process_euid)); - return NULL; + const char *user; + uid_t process_euid; + struct passwd *pw; + + user = getenv("USER"); + + if ( user == NULL || *user == '\0' ) { + process_euid = geteuid(); + + if ((pw = getpwuid(process_euid)) != NULL) { + user = t_strdup(pw->pw_name); + } + } + + if ( user == NULL || *user == '\0' ) { + i_fatal("couldn't lookup our username (uid=%s)", dec2str(process_euid)); + } + + return user; } void sieve_tool_get_envelope_data diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c index fc5525ef44d36ab882fc373cd3e73d7107ed587d..a722e20e12ed83b5e98e69949a4857e510454719 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.c +++ b/src/lib-sieve/plugins/include/ext-include-binary.c @@ -68,7 +68,9 @@ static struct ext_include_binary_context *ext_include_binary_create_context (hash_callback_t *) sieve_script_hash, (hash_cmp_callback_t *) sieve_script_cmp); p_array_init(&ctx->include_index, pool, 128); - + + sieve_binary_extension_set(sbin, &include_binary_ext, ctx); + return ctx; } @@ -78,10 +80,8 @@ struct ext_include_binary_context *ext_include_binary_get_context struct ext_include_binary_context *ctx = (struct ext_include_binary_context *) sieve_binary_extension_get_context(sbin, &include_extension); - if ( ctx == NULL ) { + if ( ctx == NULL ) ctx = ext_include_binary_create_context(sbin); - sieve_binary_extension_set_context(sbin, &include_extension, ctx); - }; return ctx; } @@ -344,7 +344,6 @@ static void ext_include_binary_free(struct sieve_binary *sbin) if ( binctx->global_vars != NULL ) sieve_variable_scope_unref(&binctx->global_vars); - } /* diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index 621e3691873017d18935102dca6333d61198d36e..63cee40fa39bbd223b1cdd864ddfecdd211c41c1 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -586,9 +586,10 @@ bool ext_include_execute_include /* Free any sub-interpreters that might still be active */ while ( curctx != NULL && curctx->parent != NULL ) { struct ext_include_interpreter_context *nextctx = curctx->parent; + struct sieve_interpreter *killed_interp = curctx->interp; /* This kills curctx too */ - sieve_interpreter_free(&curctx->interp); + sieve_interpreter_free(&killed_interp); /* Luckily we recorded the parent earlier */ curctx = nextctx; diff --git a/src/lib-sieve/plugins/include/ext-include-variables.c b/src/lib-sieve/plugins/include/ext-include-variables.c index bae0048063e9b43fd6a058fc467d8d312850fe8b..2441801ce8cab928879a105a4f651a9302302aa0 100644 --- a/src/lib-sieve/plugins/include/ext-include-variables.c +++ b/src/lib-sieve/plugins/include/ext-include-variables.c @@ -47,8 +47,7 @@ struct sieve_variable *ext_include_variable_import_global struct sieve_variable *var = NULL, *impvar = NULL; /* Check if the requested variable was imported already */ - if ( export && - (impvar=sieve_variable_scope_get_variable(ctx->import_vars, variable, FALSE)) + if ( (impvar=sieve_variable_scope_get_variable(ctx->import_vars, variable, FALSE)) != NULL ) { if ( export ) { /* Yes, and now export is attempted. ERROR */ @@ -61,7 +60,7 @@ struct sieve_variable *ext_include_variable_import_global struct ext_include_variable *varctx = (struct ext_include_variable *) impvar->context; sieve_command_validate_warning(valdtr, cmd, - "variable '%s' already imported earlier at line %d", variable, + "variable '%s' already imported earlier at line %d", variable, varctx->source_line); } } @@ -94,22 +93,25 @@ struct sieve_variable *ext_include_variable_import_global var->context = varctx; } - /* Import the global variable into the local script scope */ - main_scope = sieve_ext_variables_get_main_scope(valdtr); - (void)sieve_variable_scope_import(main_scope, var); + /* Import the global variable into the local script scope */ + if ( impvar == NULL ) { + main_scope = sieve_ext_variables_get_main_scope(valdtr); + (void)sieve_variable_scope_import(main_scope, var); - /* If this is an import it needs to be registered to detect duplicates */ - if ( !export && impvar == NULL ) { - pool_t pool = sieve_variable_scope_pool(ctx->import_vars); - struct ext_include_variable *varctx; + /* If this is an import it needs to be registered to detect duplicates */ + if ( !export ) { + pool_t pool = sieve_variable_scope_pool(ctx->import_vars); + struct ext_include_variable *varctx; - impvar = sieve_variable_scope_declare(ctx->import_vars, variable); + impvar = sieve_variable_scope_declare(ctx->import_vars, variable); - i_assert( impvar != NULL ); + i_assert( impvar != NULL ); - varctx = p_new(pool, struct ext_include_variable, 1); - varctx->type = EXT_INCLUDE_VAR_IMPORTED; - impvar->context = varctx; + varctx = p_new(pool, struct ext_include_variable, 1); + varctx->type = EXT_INCLUDE_VAR_IMPORTED; + varctx->source_line = cmd->ast_node->source_line; + impvar->context = varctx; + } } return var; diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c index 58f3f63ad71f0fe413bdc341f981d28bfe4e456b..20c4b7213ee8db0b276544e8c49564cf020d2296 100644 --- a/src/lib-sieve/plugins/include/ext-include.c +++ b/src/lib-sieve/plugins/include/ext-include.c @@ -50,7 +50,6 @@ static const struct sieve_operation *ext_include_operations[] = { static bool ext_include_load(int ext_id); static bool ext_include_validator_load(struct sieve_validator *validator); static bool ext_include_generator_load(const struct sieve_codegen_env *cgenv); -static bool ext_include_binary_load(struct sieve_binary *sbin); static bool ext_include_interpreter_load (const struct sieve_runtime_env *renv, sieve_size_t *address); @@ -65,7 +64,7 @@ const struct sieve_extension include_extension = { ext_include_validator_load, ext_include_generator_load, ext_include_interpreter_load, - ext_include_binary_load, + NULL, ext_include_binary_dump, ext_include_code_dump, SIEVE_EXT_DEFINE_OPERATIONS(ext_include_operations), @@ -107,13 +106,3 @@ static bool ext_include_interpreter_load return TRUE; } -static bool ext_include_binary_load(struct sieve_binary *sbin) -{ - /* Register extension to the binary object to get notified of events like - * opening or saving the binary. The implemententation of these hooks is found - * in ext-include-binary.c - */ - sieve_binary_extension_set(sbin, &include_extension, &include_binary_ext); - - return TRUE; -} diff --git a/src/lib-sieve/sieve-actions.c b/src/lib-sieve/sieve-actions.c index 2462395079d47bcc948bf3076f235616328c2509..abcc9803c6aa01fe9fb4ae1d0f191774b4ed7f4b 100644 --- a/src/lib-sieve/sieve-actions.c +++ b/src/lib-sieve/sieve-actions.c @@ -168,10 +168,18 @@ static void act_store_get_storage_error static struct mailbox *act_store_mailbox_open (const struct sieve_action_exec_env *aenv, struct mail_namespace *ns, const char *folder) { + enum mailbox_open_flags open_flags = + MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT | + MAILBOX_OPEN_SAVEONLY | MAILBOX_OPEN_POST_SESSION; struct mailbox *box; + if (strcasecmp(folder, "INBOX") == 0) { + /* Deliveries to INBOX must always succeed, regardless of ACLs */ + open_flags |= MAILBOX_OPEN_IGNORE_ACLS; + } + box = mailbox_open - (ns->storage, folder, NULL, MAILBOX_OPEN_FAST |MAILBOX_OPEN_KEEP_RECENT); + (ns->storage, folder, NULL, open_flags); if ( box == NULL && aenv->scriptenv->mailbox_autocreate ) { enum mail_error error; @@ -191,7 +199,7 @@ static struct mailbox *act_store_mailbox_open /* Try opening again */ box = mailbox_open - (ns->storage, folder, NULL, MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT); + (ns->storage, folder, NULL, open_flags); if (box == NULL) return NULL; diff --git a/src/lib-sieve/sieve-binary.c b/src/lib-sieve/sieve-binary.c index 3c79a089cb11262310aa0c577dc34678cdc61b95..133f4a89968505e30e998f553b5a66b8f4336765 100644 --- a/src/lib-sieve/sieve-binary.c +++ b/src/lib-sieve/sieve-binary.c @@ -1289,13 +1289,16 @@ const void *sieve_binary_extension_get_context } void sieve_binary_extension_set -(struct sieve_binary *sbin, const struct sieve_extension *ext, - const struct sieve_binary_extension *bext) +(struct sieve_binary *sbin, const struct sieve_binary_extension *bext, + void *context) { struct sieve_binary_extension_reg *ereg = - sieve_binary_extension_get_reg(sbin, ext, TRUE); + sieve_binary_extension_get_reg(sbin, bext->extension, TRUE); ereg->binext = bext; + + if ( context != NULL ) + ereg->context = context; } unsigned int sieve_binary_extension_create_block diff --git a/src/lib-sieve/sieve-binary.h b/src/lib-sieve/sieve-binary.h index d934c86cabd40dd4bf59cf9e8e4ed862fd265b66..edb0e0f17da68f11f02db3d15888f722e7cef450 100644 --- a/src/lib-sieve/sieve-binary.h +++ b/src/lib-sieve/sieve-binary.h @@ -83,8 +83,8 @@ const void *sieve_binary_extension_get_context (struct sieve_binary *sbin, const struct sieve_extension *ext); void sieve_binary_extension_set - (struct sieve_binary *sbin, const struct sieve_extension *ext, - const struct sieve_binary_extension *bext); + (struct sieve_binary *sbin, const struct sieve_binary_extension *bext, + void *context); unsigned int sieve_binary_extension_create_block (struct sieve_binary *sbin, const struct sieve_extension *ext); diff --git a/src/lib-sieve/sieve-script.c b/src/lib-sieve/sieve-script.c index 8186f4eb521bd8a6d671df6d29607d28824cdf78..01ed8739936427856829304b8b70d97c48fb35f1 100644 --- a/src/lib-sieve/sieve-script.c +++ b/src/lib-sieve/sieve-script.c @@ -38,14 +38,14 @@ static inline const char *_sieve_scriptfile_get_basename(const char *filename) static inline const char *_sieve_scriptfile_from_name(const char *name) { - const char *ext; + const char *ext; - /* See if it ends in .sieve already */ - ext = strrchr(name, '.'); - if ( ext == NULL || ext == name || strncmp(ext,".sieve",6) != 0 ) - return t_strconcat(name, ".sieve", NULL); + /* See if it ends in .sieve already */ + ext = strrchr(name, '.'); + if ( ext == NULL || ext == name || strncmp(ext,".sieve",6) != 0 ) + return t_strconcat(name, ".sieve", NULL); - return name; + return name; } /* @@ -86,10 +86,11 @@ struct sieve_script *sieve_script_init /* First obtain stat data from the system */ - if ( (ret=lstat(path, &st)) < 0 && (errno != ENOENT || exists_r == NULL) ) { + if ( (ret=lstat(path, &st)) < 0 ) { if ( errno == ENOENT ) { - sieve_error(ehandler, basename, "sieve script does not exist"); - if ( exists_r != NULL ) + if ( exists_r == NULL ) + sieve_error(ehandler, basename, "sieve script does not exist"); + else *exists_r = FALSE; } else sieve_critical(ehandler, basename, @@ -104,12 +105,11 @@ struct sieve_script *sieve_script_init /* Only create/init the object if it stat()s without problems */ if (S_ISLNK(st.st_mode)) { - if ( (ret=stat(path, &st)) < 0 && - (errno != ENOENT || exists_r == NULL) ) { - + if ( (ret=stat(path, &st)) < 0 ) { if ( errno == ENOENT ) { - sieve_error(ehandler, basename, "sieve script does not exist"); - if ( exists_r != NULL ) + if ( exists_r == NULL ) + sieve_error(ehandler, basename, "sieve script does not exist"); + else *exists_r = FALSE; } else sieve_critical(ehandler, basename, diff --git a/tests/header.svtest b/tests/header.svtest index 3db64b4872591171f83b91253af4cda99d6a7f91..2fc5e8051c3077aeeefeedd95e3fe2f8a2bdce57 100644 --- a/tests/header.svtest +++ b/tests/header.svtest @@ -6,31 +6,34 @@ To: nico@vestingbar.nl Subject: Help X-A: Text X-B: Text +X-Multiline: This is a multi-line + header body, which should be + unfolded correctly. Text . ; -test "STRIP-center" { +test "Strip center" { if not header :is "subject" "Help" { test_fail "header test does not strip leading or trailing whitespace"; } } -test "STRIP-lead" { +test "Strip lead" { if not header :is "x-a" "Text" { test_fail "header test does not strip leading whitespace"; } } -test "STRIP-trail" { +test "Strip trail" { if not header :is "x-b" "Text" { test_fail "header test does not strip trailing whitespace"; } } -test "CONTAINS-EMPTY-exist" { +test "Contains empty - exist" { if not header :contains "subject" "" { test_fail "header test :contains match type fails to match \"\" on existing header"; } @@ -40,10 +43,15 @@ test "CONTAINS-EMPTY-exist" { } } -test "CONTAINS-EMPTY-not-exist" { +test "Contains empty - not exist" { if header :contains "x-nonsense" "" { test_fail "header test :contains match type matches \"\" on non-existant header"; } } - +test "Folded - equals" { + if not header :is "x-multiline" + "This is a multi-line header body, which should be unfolded correctly." { + test_fail "failed to properly unfold folded header."; + } +}