diff --git a/Makefile.am b/Makefile.am index 07f4f2d704898677182b6d0ea717371f23641f9a..6693f4649525cde8cc8f0dc75d4a56f3c6fba521 100644 --- a/Makefile.am +++ b/Makefile.am @@ -158,6 +158,7 @@ test_cases = \ tests/extensions/duplicate/errors.svtest \ tests/extensions/duplicate/execute.svtest \ tests/extensions/duplicate/execute-vnd.svtest \ + tests/extensions/metadata/execute.svtest \ tests/extensions/vnd.dovecot/debug/execute.svtest \ tests/deprecated/notify/basic.svtest \ tests/deprecated/notify/mailto.svtest \ diff --git a/src/lib-sieve/plugins/metadata/tst-metadata.c b/src/lib-sieve/plugins/metadata/tst-metadata.c index 7be3c70f4c3bf05a1ecfabc1951eaa541d7cece0..fe45fa9148762bbdec5ff48ea4d8b703149e9423 100644 --- a/src/lib-sieve/plugins/metadata/tst-metadata.c +++ b/src/lib-sieve/plugins/metadata/tst-metadata.c @@ -299,6 +299,8 @@ static int tst_metadata_get_annotation *annotation_r = avalue.value; } (void)imap_metadata_transaction_commit(&imtrans, NULL, NULL); + if ( box != NULL ) + mailbox_free(&box); return status; } diff --git a/src/testsuite/Makefile.am b/src/testsuite/Makefile.am index 440979270ff2ff0dfc8d371d181b3e77f5159cb1..8bca4136a70bc55f296fb24c18067379533df929 100644 --- a/src/testsuite/Makefile.am +++ b/src/testsuite/Makefile.am @@ -24,7 +24,8 @@ commands = \ cmd-test-result.c \ cmd-test-message.c \ cmd-test-mailbox.c \ - cmd-test-binary.c + cmd-test-binary.c \ + cmd-test-imap-metadata.c tests = \ tst-test-script-compile.c \ diff --git a/src/testsuite/cmd-test-imap-metadata.c b/src/testsuite/cmd-test-imap-metadata.c new file mode 100644 index 0000000000000000000000000000000000000000..0868c1dfff6885851591eb1761b10e19e73a37fd --- /dev/null +++ b/src/testsuite/cmd-test-imap-metadata.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2002-2014 Pigeonhole authors, see the included COPYING file + */ + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-dump.h" + +#include "testsuite-common.h" +#include "testsuite-mailstore.h" + +/* + * Commands + */ + +static bool cmd_test_imap_metadata_validate + (struct sieve_validator *valdtr, struct sieve_command *cmd); +static bool cmd_test_imap_metadata_generate + (const struct sieve_codegen_env *cgenv, struct sieve_command *ctx); + +/* Test_mailbox_create command + * + * Syntax: + * test_imap_metadata_set + * <mailbox: string> <annotation: string> <value:string> + */ + +const struct sieve_command_def cmd_test_imap_metadata_set = { + "test_imap_metadata_set", + SCT_COMMAND, + 3, 0, FALSE, FALSE, + NULL, NULL, + cmd_test_imap_metadata_validate, + NULL, + cmd_test_imap_metadata_generate, + NULL +}; + +/* + * Operations + */ + +static bool cmd_test_imap_metadata_operation_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); +static int cmd_test_imap_metadata_operation_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Test_mailbox_create operation */ + +const struct sieve_operation_def test_imap_metadata_set_operation = { + "TEST_IMAP_METADATA_SET", + &testsuite_extension, + TESTSUITE_OPERATION_TEST_IMAP_METADATA_SET, + cmd_test_imap_metadata_operation_dump, + cmd_test_imap_metadata_operation_execute +}; + +/* + * Validation + */ + +static bool cmd_test_imap_metadata_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd) +{ + struct sieve_ast_argument *arg = cmd->first_positional; + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "mailbox", 1, SAAT_STRING) ) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "annotation", 2, SAAT_STRING) ) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + + arg = sieve_ast_argument_next(arg); + + if ( !sieve_validate_positional_argument + (valdtr, cmd, arg, "value", 3, SAAT_STRING) ) + return FALSE; + + if (!sieve_validator_argument_activate(valdtr, cmd, arg, FALSE)) + return FALSE; + return TRUE; +} + +/* + * Code generation + */ + +static bool cmd_test_imap_metadata_generate +(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd) +{ + /* Emit operation */ + if ( sieve_command_is(cmd, cmd_test_imap_metadata_set) ) + sieve_operation_emit + (cgenv->sblock, cmd->ext, &test_imap_metadata_set_operation); + else + i_unreached(); + + /* Generate arguments */ + if ( !sieve_generate_arguments(cgenv, cmd, NULL) ) + return FALSE; + return TRUE; +} + +/* + * Code dump + */ + +static bool cmd_test_imap_metadata_operation_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + sieve_code_dumpf(denv, "%s:", sieve_operation_mnemonic(denv->oprtn)); + + sieve_code_descend(denv); + + return (sieve_opr_string_dump(denv, address, "mailbox") && + sieve_opr_string_dump(denv, address, "annotation") && + sieve_opr_string_dump(denv, address, "value")); +} + +/* + * Intepretation + */ + +static int cmd_test_imap_metadata_operation_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address) +{ + const struct sieve_operation *oprtn = renv->oprtn; + string_t *mailbox = NULL, *annotation = NULL, *value = NULL; + int ret; + + /* + * Read operands + */ + + if ( (ret=sieve_opr_string_read + (renv, address, "mailbox", &mailbox)) <= 0 ) + return ret; + if ( (ret=sieve_opr_string_read + (renv, address, "annotation", &annotation)) <= 0 ) + return ret; + if ( (ret=sieve_opr_string_read + (renv, address, "value", &value)) <= 0 ) + return ret; + + /* + * Perform operation + */ + + if ( sieve_operation_is(oprtn, test_imap_metadata_set_operation) ) { + if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) { + sieve_runtime_trace(renv, 0, "testsuite/test_imap_metadata_set command"); + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, "set annotation `%s'", str_c(mailbox)); + } + + if (testsuite_mailstore_set_imap_metadata + (str_c(mailbox), str_c(annotation), str_c(value)) < 0) + return SIEVE_EXEC_FAILURE; + } + + return SIEVE_EXEC_OK; +} diff --git a/src/testsuite/ext-testsuite.c b/src/testsuite/ext-testsuite.c index b8fd4171e971e45c9dea8c2128bff9fe54161c3a..431ffcb16b3ee77793234d8af9cbc390ecaaf5dc 100644 --- a/src/testsuite/ext-testsuite.c +++ b/src/testsuite/ext-testsuite.c @@ -74,7 +74,8 @@ const struct sieve_operation_def *testsuite_operations[] = { &test_mailbox_create_operation, &test_mailbox_delete_operation, &test_binary_load_operation, - &test_binary_save_operation + &test_binary_save_operation, + &test_imap_metadata_set_operation }; /* @@ -134,6 +135,7 @@ static bool ext_testsuite_validator_load sieve_validator_register_command(valdtr, ext, &cmd_test_mailbox_delete); sieve_validator_register_command(valdtr, ext, &cmd_test_binary_load); sieve_validator_register_command(valdtr, ext, &cmd_test_binary_save); + sieve_validator_register_command(valdtr, ext, &cmd_test_imap_metadata_set); sieve_validator_register_command(valdtr, ext, &tst_test_script_compile); sieve_validator_register_command(valdtr, ext, &tst_test_script_run); diff --git a/src/testsuite/testsuite-common.h b/src/testsuite/testsuite-common.h index 48c3ceee39efcb7a029cbc1f8ee8bca38f89c7c9..8850cdae11531dd149684a0604c71d55f5c62111 100644 --- a/src/testsuite/testsuite-common.h +++ b/src/testsuite/testsuite-common.h @@ -78,6 +78,7 @@ extern const struct sieve_command_def cmd_test_mailbox_create; extern const struct sieve_command_def cmd_test_mailbox_delete; extern const struct sieve_command_def cmd_test_binary_load; extern const struct sieve_command_def cmd_test_binary_save; +extern const struct sieve_command_def cmd_test_imap_metadata_set; /* * Tests @@ -117,6 +118,7 @@ enum testsuite_operation_code { TESTSUITE_OPERATION_TEST_MAILBOX_DELETE, TESTSUITE_OPERATION_TEST_BINARY_LOAD, TESTSUITE_OPERATION_TEST_BINARY_SAVE, + TESTSUITE_OPERATION_TEST_IMAP_METADATA_SET }; extern const struct sieve_operation_def test_operation; @@ -141,6 +143,7 @@ extern const struct sieve_operation_def test_mailbox_create_operation; extern const struct sieve_operation_def test_mailbox_delete_operation; extern const struct sieve_operation_def test_binary_load_operation; extern const struct sieve_operation_def test_binary_save_operation; +extern const struct sieve_operation_def test_imap_metadata_set_operation; /* * Operands diff --git a/src/testsuite/testsuite-mailstore.c b/src/testsuite/testsuite-mailstore.c index 7b6dd33a06810c957fdee91c5ad107eaf1f95580..a3dc2796124f8c4078d3c3a654870209cb7124f2 100644 --- a/src/testsuite/testsuite-mailstore.c +++ b/src/testsuite/testsuite-mailstore.c @@ -6,10 +6,13 @@ #include "imem.h" #include "array.h" #include "strfuncs.h" +#include "str-sanitize.h" +#include "abspath.h" #include "unlink-directory.h" #include "env-util.h" #include "mail-namespace.h" #include "mail-storage.h" +#include "imap-metadata.h" #include "sieve-common.h" #include "sieve-error.h" @@ -34,7 +37,10 @@ static void testsuite_mailstore_close(void); * State */ -static char *testsuite_mailstore_tmp = NULL; +static struct mail_user *testsuite_mailstore_user = NULL; + +static char *testsuite_mailstore_location = NULL; +static char *testsuite_mailstore_attrs = NULL; static char *testsuite_mailstore_folder = NULL; static struct mailbox *testsuite_mailstore_box = NULL; @@ -47,34 +53,81 @@ static struct mail *testsuite_mailstore_mail = NULL; void testsuite_mailstore_init(void) { - testsuite_mailstore_tmp = i_strconcat - (testsuite_tmp_dir_get(), "/mailstore", NULL); - - if ( mkdir(testsuite_mailstore_tmp, 0700) < 0 ) { + struct mail_user *mail_user_dovecot, *mail_user; + struct mail_namespace *ns; + struct mail_namespace_settings *ns_set; + struct mail_storage_settings *mail_set; + const char *tmpdir, *error; + + tmpdir = testsuite_tmp_dir_get(); + testsuite_mailstore_location = + i_strconcat(tmpdir, "/mailstore", NULL); + testsuite_mailstore_attrs = + i_strconcat(tmpdir, "/mail-attrs.dict", NULL); + + if ( mkdir(testsuite_mailstore_location, 0700) < 0 ) { i_fatal("failed to create temporary directory '%s': %m.", - testsuite_mailstore_tmp); + testsuite_mailstore_location); } - - sieve_tool_init_mail_user - (sieve_tool, t_strconcat("maildir:", testsuite_mailstore_tmp, NULL)); + + mail_user_dovecot = sieve_tool_get_mail_user(sieve_tool); + mail_user = mail_user_alloc("testsuite mail user", + mail_user_dovecot->set_info, mail_user_dovecot->unexpanded_set); + mail_user->autocreated = TRUE; + mail_user_set_home(mail_user, t_abspath("")); + if (mail_user_init(mail_user, &error) < 0) + i_fatal("Testsuite user initialization failed: %s", error); + + ns_set = p_new(mail_user->pool, struct mail_namespace_settings, 1); + ns_set->location = testsuite_mailstore_location; + ns_set->separator = "."; + + ns = mail_namespaces_init_empty(mail_user); + ns->flags |= NAMESPACE_FLAG_INBOX_USER; + ns->set = ns_set; + /* absolute paths are ok with raw storage */ + mail_set = p_new(mail_user->pool, struct mail_storage_settings, 1); + *mail_set = *ns->mail_set; + mail_set->mail_location = p_strconcat + (mail_user->pool, "maildir:", testsuite_mailstore_location, NULL); + mail_set->mail_attribute_dict = p_strconcat + (mail_user->pool, "file:", testsuite_mailstore_attrs, NULL); + mail_set->mail_full_filesystem_access = TRUE; + ns->mail_set = mail_set; + + if (mail_storage_create(ns, "maildir", 0, &error) < 0) + i_fatal("Couldn't create testsuite storage: %s", error); + + testsuite_mailstore_user = mail_user; } void testsuite_mailstore_deinit(void) { testsuite_mailstore_close(); - if ( unlink_directory(testsuite_mailstore_tmp, TRUE) < 0 ) { + if ( unlink_directory(testsuite_mailstore_location, TRUE) < 0 ) { i_warning("failed to remove temporary directory '%s': %m.", - testsuite_mailstore_tmp); + testsuite_mailstore_location); } - i_free(testsuite_mailstore_tmp); + i_free(testsuite_mailstore_location); + i_free(testsuite_mailstore_attrs); + mail_user_unref(&testsuite_mailstore_user); } void testsuite_mailstore_reset(void) { } +/* + * Mail user + */ + +struct mail_user *testsuite_mailstore_get_user(void) +{ + return testsuite_mailstore_user; +} + /* * Mailbox Access */ @@ -82,7 +135,7 @@ void testsuite_mailstore_reset(void) bool testsuite_mailstore_mailbox_create (const struct sieve_runtime_env *renv ATTR_UNUSED, const char *folder) { - struct mail_user *mail_user = sieve_tool_get_mail_user(sieve_tool); + struct mail_user *mail_user = testsuite_mailstore_user; struct mail_namespace *ns = mail_user->namespaces; struct mailbox *box; @@ -117,7 +170,7 @@ static struct mail *testsuite_mailstore_open(const char *folder) { enum mailbox_flags flags = MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_POST_SESSION; - struct mail_user *mail_user = sieve_tool_get_mail_user(sieve_tool); + struct mail_user *mail_user = testsuite_mailstore_user; struct mail_namespace *ns = mail_user->namespaces; struct mailbox *box; struct mailbox_transaction_context *t; @@ -173,3 +226,59 @@ bool testsuite_mailstore_mail_index return TRUE; } + +/* + * IMAP metadata + */ + +int testsuite_mailstore_set_imap_metadata +(const char *mailbox, const char *annotation, const char *value) +{ + struct imap_metadata_transaction *imtrans; + struct mail_attribute_value avalue; + struct mailbox *box; + enum mail_error error_code; + const char *error; + int ret; + + if ( !imap_metadata_verify_entry_name(annotation, &error) ) { + sieve_sys_error(testsuite_sieve_instance, + "testsuite: imap metadata: " + "specified annotation name `%s' is invalid: %s", + str_sanitize(annotation, 256), error); + return -1; + } + + if ( mailbox != NULL ) { + struct mail_namespace *ns; + ns = mail_namespace_find + (testsuite_mailstore_user->namespaces, mailbox); + box = mailbox_alloc(ns->list, mailbox, 0); + imtrans = imap_metadata_transaction_begin(box); + } else { + imtrans = imap_metadata_transaction_begin_server + (testsuite_mailstore_user); + } + + memset(&avalue, 0, sizeof(avalue)); + avalue.value = value; + if ((ret=imap_metadata_set(imtrans, annotation, &avalue)) < 0) { + error = imap_metadata_transaction_get_last_error + (imtrans, &error_code); + imap_metadata_transaction_rollback(&imtrans); + } else { + ret = imap_metadata_transaction_commit + (&imtrans, &error_code, &error); + } + if ( box != NULL ) + mailbox_free(&box); + + if ( ret < 0 ) { + sieve_sys_error(testsuite_sieve_instance, + "testsuite: imap metadata: " + "failed to assign annotation `%s': %s", + str_sanitize(annotation, 256), error); + return -1; + } + return 0; +} diff --git a/src/testsuite/testsuite-mailstore.h b/src/testsuite/testsuite-mailstore.h index 1f2fa4051fe4dae09d3d1846564a3132a357db49..5bb5e2dd7af7cd113e2664095f3cf97d1669eb95 100644 --- a/src/testsuite/testsuite-mailstore.h +++ b/src/testsuite/testsuite-mailstore.h @@ -16,6 +16,11 @@ void testsuite_mailstore_init(void); void testsuite_mailstore_deinit(void); void testsuite_mailstore_reset(void); +/* + * Mail user + */ + +struct mail_user *testsuite_mailstore_get_user(void); /* * Mailbox Access @@ -28,4 +33,11 @@ bool testsuite_mailstore_mail_index (const struct sieve_runtime_env *renv, const char *folder, unsigned int index); +/* + * IMAP metadata + */ + +int testsuite_mailstore_set_imap_metadata + (const char *mailbox, const char *annotation, const char *value); + #endif /* __TESTSUITE_MAILSTORE */ diff --git a/src/testsuite/testsuite.c b/src/testsuite/testsuite.c index d53eafbc80786476a2a27876545aec1cc6d9dddc..4ca2da16833e1c278aa976c0d2476121bbe63f05 100644 --- a/src/testsuite/testsuite.c +++ b/src/testsuite/testsuite.c @@ -176,7 +176,7 @@ int main(int argc, char **argv) testsuite_message_init(); memset(&scriptenv, 0, sizeof(scriptenv)); - scriptenv.user = sieve_tool_get_mail_user(sieve_tool); + scriptenv.user = testsuite_mailstore_get_user(); scriptenv.default_mailbox = "INBOX"; scriptenv.postmaster_address = "postmaster@example.com"; scriptenv.smtp_start = testsuite_smtp_start; diff --git a/tests/extensions/metadata/execute.svtest b/tests/extensions/metadata/execute.svtest new file mode 100644 index 0000000000000000000000000000000000000000..799a730c4f18d7aff52293a249e7d3006f63b3ea --- /dev/null +++ b/tests/extensions/metadata/execute.svtest @@ -0,0 +1,58 @@ +require "vnd.dovecot.testsuite"; +require "mboxmetadata"; +require "fileinto"; + +test "MetadataExists - None exist" { + if metadataexists "INBOX" "/private/frop" { + test_fail "metadataexists confirms existence of unknown annotation"; + } +} + +test_imap_metadata_set "INBOX" "/private/frop" "FROP!"; +test_imap_metadata_set "INBOX" "/private/friep" "FRIEP!"; + +test "MetadataExists - Not all exist" { + if metadataexists "INBOX" + ["/private/frop", "/private/friep", "/private/frml"] { + test_fail "metadataexists confirms existence of unknown annotation"; + } +} + +test_imap_metadata_set "INBOX" "/private/friep" "FRIEP!"; +test_imap_metadata_set "INBOX" "/private/frml" "FRML!"; + +test "MetadataExists - One exists" { + if not metadataexists "INBOX" ["/private/frop"] { + test_fail "metadataexists fails to recognize annotation"; + } +} + +test "MetadataExists - All exist" { + if not metadataexists "INBOX" + ["/private/frop", "/private/friep", "/private/frml"] { + test_fail "metadataexists fails to recognize annotations"; + } +} + +test "Metadata" { + if not metadata :is "INBOX" "/private/frop" "FROP!" { + test_fail "invalid metadata value for /private/frop"; + } + if metadata :is "INBOX" "/private/frop" "Hutsefluts" { + test_fail "unexpected match for /private/frop"; + } + + if not metadata :is "INBOX" "/private/friep" "FRIEP!" { + test_fail "invalid metadata value for /private/friep"; + } + if metadata :is "INBOX" "/private/friep" "Hutsefluts" { + test_fail "unexpected match for /private/friep"; + } + + if not metadata :is "INBOX" "/private/frml" "FRML!" { + test_fail "invalid metadata value for /private/frml"; + } + if metadata :is "INBOX" "/private/frml" "Hutsefluts" { + test_fail "unexpected match for /private/frml"; + } +}