summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/test-mailbox-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/test-mailbox-list.c')
-rw-r--r--src/lib-storage/test-mailbox-list.c512
1 files changed, 512 insertions, 0 deletions
diff --git a/src/lib-storage/test-mailbox-list.c b/src/lib-storage/test-mailbox-list.c
new file mode 100644
index 0000000..aa8f62a
--- /dev/null
+++ b/src/lib-storage/test-mailbox-list.c
@@ -0,0 +1,512 @@
+/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "test-common.h"
+#include "mailbox-list-private.h"
+
+enum test_flags {
+ TEST_FLAG_NO_VNAME = BIT(0),
+ TEST_FLAG_NO_STORAGE_NAME = BIT(1),
+ TEST_FLAG_NO_MUTF7 = BIT(2),
+ TEST_FLAG_NO_UTF8 = BIT(3),
+};
+
+struct test_mailbox_list_name {
+ const char *vname;
+ const char *storage_name;
+ enum test_flags flags;
+ char *ns_prefix;
+ enum namespace_flags ns_flags;
+ char ns_sep;
+ char list_sep;
+ char vname_escape_char;
+ char storage_name_escape_char;
+ const char *maildir_name;
+};
+
+static char list_hierarchy_sep;
+static char ns_sep[2] = { '\0', '\0' };
+
+static void test_init_list(struct mailbox_list *list_r)
+{
+ i_zero(list_r);
+ list_r->ns = t_new(struct mail_namespace, 1);
+ list_r->ns->user = t_new(struct mail_user, 1);
+ list_r->ns->user->event = event_create(NULL);
+}
+
+static void test_deinit_list(struct mailbox_list *list)
+{
+ mailbox_list_clear_error(list);
+ if (array_is_created(&list->error_stack)) {
+ mailbox_list_clear_error(list);
+ i_assert(array_count(&list->error_stack) == 0);
+ array_free(&list->error_stack);
+ }
+ event_unref(&list->ns->user->event);
+}
+
+static void test_mailbox_list_errors(void)
+{
+ /* NOTE: keep in sync with test-mail-storage.c */
+ struct mailbox_list list;
+ enum mail_error mail_error;
+ const char *errstr;
+
+ test_begin("mail list errors");
+ test_init_list(&list);
+
+ /* try a regular error */
+ mailbox_list_set_error(&list, MAIL_ERROR_PERM, "error1");
+ test_assert(strcmp(mailbox_list_get_last_error(&list, &mail_error),
+ "error1") == 0);
+ test_assert(mail_error == MAIL_ERROR_PERM);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "error1") == 0);
+ test_assert(mail_error == MAIL_ERROR_PERM);
+ test_assert(!list.last_error_is_internal);
+
+ /* set the error to itself */
+ mailbox_list_set_error(&list, MAIL_ERROR_PARAMS,
+ mailbox_list_get_last_error(&list, &mail_error));
+ test_assert(strcmp(mailbox_list_get_last_error(&list, &mail_error),
+ "error1") == 0);
+ test_assert(mail_error == MAIL_ERROR_PARAMS);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "error1") == 0);
+ test_assert(mail_error == MAIL_ERROR_PARAMS);
+ test_assert(!list.last_error_is_internal);
+
+ /* clear the error - asking for it afterwards is a bug */
+ mailbox_list_clear_error(&list);
+ test_assert(strcmp(mailbox_list_get_last_error(&list, &mail_error),
+ "BUG: Unknown internal list error") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "BUG: Unknown internal list error") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(!list.last_error_is_internal);
+
+ /* set internal error in preparation for the next test */
+ test_expect_error_string("critical0");
+ mailbox_list_set_critical(&list, "critical0");
+ test_expect_no_more_errors();
+ test_assert(strstr(mailbox_list_get_last_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "critical0") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(list.last_error_is_internal);
+
+ /* internal error without specifying what it is. this needs to clear
+ the previous internal error. */
+ mailbox_list_set_internal_error(&list);
+ test_assert(strstr(mailbox_list_get_last_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strstr(mailbox_list_get_last_internal_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(!list.last_error_is_internal);
+
+ /* proper internal error */
+ test_expect_error_string("critical1");
+ mailbox_list_set_critical(&list, "critical1");
+ test_expect_no_more_errors();
+ test_assert(strstr(mailbox_list_get_last_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "critical1") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(list.last_error_is_internal);
+
+ /* use it in the following internal error */
+ test_expect_error_string("critical2: critical1");
+ mailbox_list_set_critical(&list, "critical2: %s",
+ mailbox_list_get_last_internal_error(&list, &mail_error));
+ test_expect_no_more_errors();
+ test_assert(strstr(mailbox_list_get_last_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "critical2: critical1") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(list.last_error_is_internal);
+
+ /* use the previous non-internal error as part of the internal error */
+ test_expect_error_string("critical3: "MAIL_ERRSTR_CRITICAL_MSG);
+ mailbox_list_set_critical(&list, "critical3: %s",
+ mailbox_list_get_last_error(&list, &mail_error));
+ test_expect_no_more_errors();
+ test_assert(strstr(mailbox_list_get_last_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ errstr = mailbox_list_get_last_internal_error(&list, &mail_error);
+ test_assert(str_begins(errstr, "critical3: "));
+ test_assert(strstr(errstr+11, MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(list.last_error_is_internal);
+
+ /* clear the error again and check that all is as expected */
+ mailbox_list_clear_error(&list);
+ test_assert(strcmp(mailbox_list_get_last_error(&list, &mail_error),
+ "BUG: Unknown internal list error") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "BUG: Unknown internal list error") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(!list.last_error_is_internal);
+
+ /* use internal error as a regular error (although that really
+ shouldn't be done) */
+ test_expect_error_string("critical4");
+ mailbox_list_set_critical(&list, "critical4");
+ mailbox_list_set_error(&list, MAIL_ERROR_PARAMS,
+ mailbox_list_get_last_internal_error(&list, &mail_error));
+ test_expect_no_more_errors();
+ test_assert(strcmp(mailbox_list_get_last_error(&list, &mail_error),
+ "critical4") == 0);
+ test_assert(mail_error == MAIL_ERROR_PARAMS);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "critical4") == 0);
+ test_assert(mail_error == MAIL_ERROR_PARAMS);
+ test_assert(!list.last_error_is_internal);
+
+ test_deinit_list(&list);
+ test_end();
+}
+
+static void test_mailbox_list_last_error_push_pop(void)
+{
+ /* NOTE: keep in sync with test-mail-storage.c */
+ struct mailbox_list list;
+ enum mail_error mail_error;
+
+ test_begin("mailbox_list_last_error_push/pop()");
+ test_init_list(&list);
+
+ /* regular error 1 */
+ mailbox_list_set_error(&list, MAIL_ERROR_PERM, "regular error 1");
+ mailbox_list_last_error_push(&list);
+
+ /* critical error 1 */
+ test_expect_error_string("critical error 1");
+ mailbox_list_set_critical(&list, "critical error 1");
+ test_expect_no_more_errors();
+ mailbox_list_last_error_push(&list);
+
+ /* regular error 2 */
+ mailbox_list_set_error(&list, MAIL_ERROR_PARAMS, "regular error 2");
+ mailbox_list_last_error_push(&list);
+
+ /* critical error 2 */
+ test_expect_error_string("critical error 2");
+ mailbox_list_set_critical(&list, "critical error 2");
+ test_expect_no_more_errors();
+ mailbox_list_last_error_push(&list);
+
+ /* -- clear all errors -- */
+ mailbox_list_clear_error(&list);
+
+ /* critical error 2 pop */
+ mailbox_list_last_error_pop(&list);
+ test_assert(strstr(mailbox_list_get_last_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "critical error 2") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(list.last_error_is_internal);
+
+ /* regular error 2 pop */
+ mailbox_list_last_error_pop(&list);
+ test_assert(strcmp(mailbox_list_get_last_error(&list, &mail_error),
+ "regular error 2") == 0);
+ test_assert(mail_error == MAIL_ERROR_PARAMS);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "regular error 2") == 0);
+ test_assert(mail_error == MAIL_ERROR_PARAMS);
+ test_assert(!list.last_error_is_internal);
+
+ /* critical error 1 pop */
+ mailbox_list_last_error_pop(&list);
+ test_assert(strstr(mailbox_list_get_last_error(&list, &mail_error),
+ MAIL_ERRSTR_CRITICAL_MSG) != NULL);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "critical error 1") == 0);
+ test_assert(mail_error == MAIL_ERROR_TEMP);
+ test_assert(list.last_error_is_internal);
+
+ /* regular error 1 pop */
+ mailbox_list_last_error_pop(&list);
+ test_assert(strcmp(mailbox_list_get_last_error(&list, &mail_error),
+ "regular error 1") == 0);
+ test_assert(mail_error == MAIL_ERROR_PERM);
+ test_assert(strcmp(mailbox_list_get_last_internal_error(&list, &mail_error),
+ "regular error 1") == 0);
+ test_assert(mail_error == MAIL_ERROR_PERM);
+ test_assert(!list.last_error_is_internal);
+
+ test_deinit_list(&list);
+ test_end();
+}
+
+static char
+test_mailbox_list_get_hierarchy_sep(struct mailbox_list *list ATTR_UNUSED)
+{
+ return list_hierarchy_sep;
+}
+
+static void
+test_maibox_list_name_init(struct mailbox_list *list,
+ const struct test_mailbox_list_name *test,
+ bool mutf7)
+{
+ list->ns->prefix = test->ns_prefix == NULL ? "" :
+ test->ns_prefix;
+ list->ns->prefix_len = strlen(list->ns->prefix);
+ list->ns->flags = test->ns_flags;
+ ns_sep[0] = test->ns_sep;
+ list_hierarchy_sep = test->list_sep;
+ list->set.utf8 = !mutf7;
+ list->set.vname_escape_char = test->vname_escape_char;
+ list->set.storage_name_escape_char =
+ test->storage_name_escape_char;
+ list->set.maildir_name = test->maildir_name == NULL ? "" :
+ test->maildir_name;
+}
+
+static void test_mailbox_list_get_names(void)
+{
+ const struct test_mailbox_list_name tests[] = {
+ { .vname = "parent/child",
+ .storage_name = "parent/child",
+ .ns_sep = '/', .list_sep = '/' },
+ { .vname = "parent/child",
+ .storage_name = "parent.child",
+ .ns_sep = '/', .list_sep = '.' },
+ { .vname = "ns_prefix/parent/child",
+ .storage_name = "parent.child",
+ .ns_prefix = "ns_prefix/", .ns_sep = '/', .list_sep = '.' },
+ { .vname = "ns/prefix/parent/child",
+ .storage_name = "parent.child",
+ .ns_prefix = "ns/prefix/", .ns_sep = '/', .list_sep = '.' },
+ { .vname = "ns/prefix",
+ .storage_name = "",
+ .ns_prefix = "ns/prefix/", .ns_sep = '/', .list_sep = '.' },
+ { .vname = "\xC3\xA4/\xC3\xB6",
+ .storage_name = "&APY-",
+ .flags = TEST_FLAG_NO_UTF8,
+ .ns_prefix = "\xC3\xA4/", .ns_sep = '/', .list_sep = '.' },
+ { .vname = "\xC3\xA4/\xC3\xB6&test",
+ .storage_name = "\xC3\xB6&test",
+ .flags = TEST_FLAG_NO_MUTF7,
+ .ns_prefix = "\xC3\xA4/", .ns_sep = '/', .list_sep = '.' },
+
+ /* storage_name escaping: */
+ { .vname = "~home",
+ .storage_name = "%7ehome",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '%' },
+ { .vname = "es%cape%",
+ .storage_name = "es%25cape%25",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '%' },
+ { .vname = "slash/",
+ .storage_name = "slash%2f",
+ .ns_sep = '^', .list_sep = '.',
+ .storage_name_escape_char = '%' },
+ { .vname = "list.separator",
+ .storage_name = "list%2eseparator",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '%' },
+ { .vname = "Maildir",
+ .storage_name = "%4daildir",
+ .ns_sep = '^', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .maildir_name = "Maildir" },
+ { .vname = "~Maildir",
+ .storage_name = "%7eMaildir",
+ .ns_sep = '^', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .maildir_name = "Maildir" },
+ { .vname = "Maildir/suffix",
+ .storage_name = "%4daildir%2fsuffix",
+ .ns_sep = '^', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .maildir_name = "Maildir" },
+ { .vname = "prefix/Maildir",
+ .storage_name = "prefix%2f%4daildir",
+ .ns_sep = '^', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .maildir_name = "Maildir" },
+ { .vname = "sep/Maildir/sep",
+ .storage_name = "sep.%4daildir.sep",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .maildir_name = "Maildir" },
+ { .vname = "~/.%--Maildir",
+ .storage_name = "%4daildir",
+ .ns_prefix = "~/.%--",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .maildir_name = "Maildir" },
+ { .vname = "%foo",
+ .storage_name = "%foo",
+ .flags = TEST_FLAG_NO_STORAGE_NAME,
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '%' },
+ { .vname = "A/B.C%D",
+ .storage_name = "A.B%2eC%25D",
+ .storage_name_escape_char='%',
+ .ns_sep = '/', .list_sep = '.' },
+
+ /* vname escaping: */
+ { .vname = "%7c|child",
+ .storage_name = "|.child",
+ .ns_sep = '|', .list_sep = '.',
+ .vname_escape_char = '%' },
+ { .vname = "%7c|child.",
+ .storage_name = "|.child+2e",
+ .ns_sep = '|', .list_sep = '.',
+ .storage_name_escape_char = '+',
+ .vname_escape_char = '%' },
+ { .vname = "%7c|child.",
+ .storage_name = "|.child%2e",
+ .ns_sep = '|', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .vname_escape_char = '%' },
+ { .vname = "%2f/child",
+ .storage_name = "/.child",
+ .ns_sep = '/', .list_sep = '.',
+ .vname_escape_char = '%' },
+ { .vname = "%2f/child.",
+ .storage_name = "+2f.child+2e",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '+',
+ .vname_escape_char = '%' },
+ { .vname = "%2f/child.",
+ .storage_name = "%2f.child%2e",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '%',
+ .vname_escape_char = '%' },
+ { .vname = "%25escaped+",
+ .storage_name = "%escaped+2b",
+ .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '+',
+ .vname_escape_char = '%' },
+ { .vname = "x%2666-y",
+ .storage_name = "x&66-y",
+ .flags = TEST_FLAG_NO_UTF8,
+ .ns_sep = '/', .list_sep = '.',
+ .vname_escape_char = '%' },
+ { .vname = "p\xC3\xA4iv\xC3\xA4 & y%26APY %ff m\xC3\xB6h",
+ .storage_name = "p&AOQ-iv&AOQ- &- y&APY \xff m&APY-h",
+ .flags = TEST_FLAG_NO_UTF8,
+ .ns_sep = '/', .list_sep = '.',
+ .vname_escape_char = '%' },
+ { .vname = "%foo",
+ .storage_name = "%foo",
+ .flags = TEST_FLAG_NO_VNAME,
+ .ns_sep = '/', .list_sep = '.',
+ .vname_escape_char = '%' },
+ { .vname = "A%2fB/C%25D",
+ .storage_name = "A/B.C%D",
+ .ns_sep = '/', .list_sep = '.',
+ .vname_escape_char = '%' },
+
+ /* INBOX: */
+ { .vname = "inBox",
+ .storage_name = "inBox",
+ .ns_sep = '/', .list_sep = '.' },
+ { .vname = "inBox",
+ .storage_name = "INBOX",
+ .flags = TEST_FLAG_NO_VNAME,
+ .ns_flags = NAMESPACE_FLAG_INBOX_USER,
+ .ns_sep = '/', .list_sep = '.' },
+ { .vname = "inBox",
+ .storage_name = "INBOX",
+ .flags = TEST_FLAG_NO_VNAME,
+ .ns_flags = NAMESPACE_FLAG_INBOX_USER,
+ .ns_prefix = "prefix/", .ns_sep = '/', .list_sep = '.' },
+ { .vname = "prefix/inBox",
+ .storage_name = "inBox",
+ .ns_flags = NAMESPACE_FLAG_INBOX_USER,
+ .ns_prefix = "prefix/", .ns_sep = '/', .list_sep = '.' },
+ { .vname = "prefix/INBOX",
+ .storage_name = "+49NBOX",
+ .ns_flags = NAMESPACE_FLAG_INBOX_USER,
+ .ns_prefix = "prefix/", .ns_sep = '/', .list_sep = '.',
+ .storage_name_escape_char = '+' },
+
+ /* Problematic cases - not reversible: */
+ { .vname = "parent.child",
+ .storage_name = "parent.child",
+ .flags = TEST_FLAG_NO_VNAME,
+ .ns_sep = '/', .list_sep = '.' },
+ { .vname = "prefix/INBOX",
+ .storage_name = "INBOX",
+ .flags = TEST_FLAG_NO_VNAME,
+ .ns_flags = NAMESPACE_FLAG_INBOX_USER,
+ .ns_prefix = "prefix/", .ns_sep = '/', .list_sep = '.' },
+ { .vname = "invalid&mutf7",
+ .storage_name = "invalid&mutf7",
+ .flags = TEST_FLAG_NO_STORAGE_NAME,
+ .ns_sep = '/', .list_sep = '.' },
+ };
+ struct mail_namespace_settings ns_set = {
+ .separator = ns_sep,
+ };
+ struct mail_namespace ns = {
+ .set = &ns_set,
+ };
+ struct mailbox_list list = {
+ .ns = &ns,
+ .v = {
+ .get_hierarchy_sep = test_mailbox_list_get_hierarchy_sep,
+ },
+ };
+
+ test_begin("mailbox list get names");
+ for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+ if ((tests[i].flags & TEST_FLAG_NO_MUTF7) == 0) {
+ test_maibox_list_name_init(&list, &tests[i], TRUE);
+ if ((tests[i].flags & TEST_FLAG_NO_STORAGE_NAME) == 0) {
+ test_assert_strcmp_idx(mailbox_list_default_get_storage_name(&list, tests[i].vname),
+ tests[i].storage_name, i);
+ }
+ if ((tests[i].flags & TEST_FLAG_NO_VNAME) == 0) {
+ test_assert_strcmp_idx(mailbox_list_default_get_vname(&list, tests[i].storage_name),
+ tests[i].vname, i);
+ }
+ }
+ if ((tests[i].flags & TEST_FLAG_NO_UTF8) == 0) {
+ test_maibox_list_name_init(&list, &tests[i], FALSE);
+ if ((tests[i].flags & TEST_FLAG_NO_STORAGE_NAME) == 0) {
+ test_assert_strcmp_idx(mailbox_list_default_get_storage_name(&list, tests[i].vname),
+ tests[i].storage_name, i);
+ }
+ if ((tests[i].flags & TEST_FLAG_NO_VNAME) == 0) {
+ test_assert_strcmp_idx(mailbox_list_default_get_vname(&list, tests[i].storage_name),
+ tests[i].vname, i);
+ }
+ }
+ }
+ test_end();
+}
+
+int main(void)
+{
+ void (*const tests[])(void) = {
+ test_mailbox_list_errors,
+ test_mailbox_list_last_error_push_pop,
+ test_mailbox_list_get_names,
+ NULL
+ };
+ return test_run(tests);
+}