From: Timo Sirainen Date: Mon, 15 Feb 2010 00:03:42 +0000 (+0200) Subject: lib-storage: Changed mailbox_list.iter_is_mailbox() API. X-Git-Tag: 2.0.beta3~83 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dca6d617a23e3f93af3b8df59acb46478179fe55;p=thirdparty%2Fdovecot%2Fcore.git lib-storage: Changed mailbox_list.iter_is_mailbox() API. --HG-- branch : HEAD --- diff --git a/src/lib-storage/index/cydir/cydir-storage.c b/src/lib-storage/index/cydir/cydir-storage.c index 7492b86326..367f41684e 100644 --- a/src/lib-storage/index/cydir/cydir-storage.c +++ b/src/lib-storage/index/cydir/cydir-storage.c @@ -116,66 +116,6 @@ static void cydir_notify_changes(struct mailbox *box) index_mailbox_check_add(&mbox->box, mbox->box.path); } -static int cydir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx - ATTR_UNUSED, - const char *dir, const char *fname, - const char *mailbox_name ATTR_UNUSED, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags) -{ - const char *mail_path; - struct stat st; - int ret = 1; - - /* try to avoid stat() with these checks */ - if (type != MAILBOX_LIST_FILE_TYPE_DIR && - type != MAILBOX_LIST_FILE_TYPE_SYMLINK && - type != MAILBOX_LIST_FILE_TYPE_UNKNOWN) { - /* it's a file */ - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - return 0; - } - - /* need to stat() then */ - mail_path = t_strconcat(dir, "/", fname, NULL); - if (stat(mail_path, &st) == 0) { - if (!S_ISDIR(st.st_mode)) { - /* non-directory */ - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - ret = 0; - } else if (st.st_nlink == 2) { - /* no subdirectories */ - *flags |= MAILBOX_NOCHILDREN; - } else if (*ctx->list->set.maildir_name != '\0') { - /* non-default configuration: we have one directory - containing the mailboxes. if there are 3 links, - either this is a selectable mailbox without children - or non-selectable mailbox with children */ - if (st.st_nlink > 3) - *flags |= MAILBOX_CHILDREN; - } else { - /* default configuration: all subdirectories are - child mailboxes. */ - if (st.st_nlink > 2) - *flags |= MAILBOX_CHILDREN; - } - } else if (errno == ENOENT) { - /* doesn't exist - probably a non-existing subscribed mailbox */ - *flags |= MAILBOX_NONEXISTENT; - } else { - /* non-selectable. probably either access denied, or symlink - destination not found. don't bother logging errors. */ - *flags |= MAILBOX_NOSELECT; - } - return ret; -} - -static void cydir_storage_add_list(struct mail_storage *storage ATTR_UNUSED, - struct mailbox_list *list) -{ - list->v.iter_is_mailbox = cydir_list_iter_is_mailbox; -} - struct mail_storage cydir_storage = { .name = CYDIR_STORAGE_NAME, .class_flags = 0, @@ -185,7 +125,7 @@ struct mail_storage cydir_storage = { cydir_storage_alloc, NULL, NULL, - cydir_storage_add_list, + NULL, cydir_storage_get_list_settings, NULL, cydir_mailbox_alloc, diff --git a/src/lib-storage/index/dbox-common/dbox-storage.c b/src/lib-storage/index/dbox-common/dbox-storage.c index 569853a32d..1b10d48c98 100644 --- a/src/lib-storage/index/dbox-common/dbox-storage.c +++ b/src/lib-storage/index/dbox-common/dbox-storage.c @@ -139,70 +139,3 @@ int dbox_mailbox_create(struct mailbox *box, return -1; return 0; } - -int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx ATTR_UNUSED, - const char *dir, const char *fname, - const char *mailbox_name ATTR_UNUSED, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags) -{ - const char *path, *maildir_path; - struct stat st, st2; - int ret = 1; - - /* try to avoid stat() with these checks */ - if (type != MAILBOX_LIST_FILE_TYPE_DIR && - type != MAILBOX_LIST_FILE_TYPE_SYMLINK && - type != MAILBOX_LIST_FILE_TYPE_UNKNOWN) { - /* it's a file */ - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - return 0; - } - - /* need to stat() then */ - path = t_strconcat(dir, "/", fname, NULL); - if (stat(path, &st) == 0) { - if (!S_ISDIR(st.st_mode)) { - /* non-directory */ - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - ret = 0; - } else if (st.st_nlink == 2) { - /* no subdirectories */ - *flags |= MAILBOX_NOCHILDREN; - } else if (*ctx->list->set.maildir_name != '\0') { - /* default configuration: we have one directory - containing the mailboxes. if there are 3 links, - either this is a selectable mailbox without children - or non-selectable mailbox with children */ - if (st.st_nlink > 3) - *flags |= MAILBOX_CHILDREN; - } else { - /* non-default configuration: all subdirectories are - child mailboxes. */ - if (st.st_nlink > 2) - *flags |= MAILBOX_CHILDREN; - } - } else if (errno == ENOENT) { - /* doesn't exist - probably a non-existing subscribed mailbox */ - *flags |= MAILBOX_NONEXISTENT; - } else { - /* non-selectable. probably either access denied, or symlink - destination not found. don't bother logging errors. */ - *flags |= MAILBOX_NOSELECT; - } - if ((*flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) { - /* make sure it's a selectable mailbox */ - maildir_path = t_strconcat(path, "/", - ctx->list->set.maildir_name, NULL); - if (stat(maildir_path, &st2) < 0 || !S_ISDIR(st2.st_mode)) - *flags |= MAILBOX_NOSELECT; - if (st.st_nlink == 3 && *ctx->list->set.maildir_name != '\0') { - /* now we know what link count 3 means. */ - if ((*flags & MAILBOX_NOSELECT) != 0) - *flags |= MAILBOX_CHILDREN; - else - *flags |= MAILBOX_NOCHILDREN; - } - } - return ret; -} diff --git a/src/lib-storage/index/dbox-common/dbox-storage.h b/src/lib-storage/index/dbox-common/dbox-storage.h index 145c13964b..c599b274b6 100644 --- a/src/lib-storage/index/dbox-common/dbox-storage.h +++ b/src/lib-storage/index/dbox-common/dbox-storage.h @@ -49,10 +49,5 @@ void dbox_notify_changes(struct mailbox *box); int dbox_mailbox_open(struct mailbox *box); int dbox_mailbox_create(struct mailbox *box, const struct mailbox_update *update, bool directory); -int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, - const char *dir, const char *fname, - const char *mailbox_name, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags); #endif diff --git a/src/lib-storage/index/dbox-multi/mdbox-storage.c b/src/lib-storage/index/dbox-multi/mdbox-storage.c index 3db6943ec8..8a13635635 100644 --- a/src/lib-storage/index/dbox-multi/mdbox-storage.c +++ b/src/lib-storage/index/dbox-multi/mdbox-storage.c @@ -14,20 +14,10 @@ #include "mdbox-storage-rebuild.h" #include "mdbox-storage.h" -#define MDBOX_LIST_CONTEXT(obj) \ - MODULE_CONTEXT(obj, mdbox_mailbox_list_module) - -struct mdbox_mailbox_list { - union mailbox_list_module_context module_ctx; -}; - extern struct mail_storage mdbox_storage; extern struct mailbox mdbox_mailbox; extern struct dbox_storage_vfuncs mdbox_dbox_storage_vfuncs; -static MODULE_CONTEXT_DEFINE_INIT(mdbox_mailbox_list_module, - &mailbox_list_module_register); - static struct mail_storage *mdbox_storage_alloc(void) { struct mdbox_storage *storage; @@ -313,18 +303,6 @@ static int mdbox_mailbox_delete(struct mailbox *box) return index_storage_mailbox_delete(box); } -static void dbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED, - struct mailbox_list *list) -{ - struct mdbox_mailbox_list *mlist; - - mlist = p_new(list->pool, struct mdbox_mailbox_list, 1); - mlist->module_ctx.super = list->v; - list->v.iter_is_mailbox = dbox_list_iter_is_mailbox; - - MODULE_CONTEXT_SET(list, mdbox_mailbox_list_module, mlist); -} - struct mail_storage mdbox_storage = { .name = MDBOX_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_UNIQUE_ROOT, @@ -334,7 +312,7 @@ struct mail_storage mdbox_storage = { mdbox_storage_alloc, mdbox_storage_create, mdbox_storage_destroy, - dbox_storage_add_list, + NULL, dbox_storage_get_list_settings, NULL, mdbox_mailbox_alloc, diff --git a/src/lib-storage/index/dbox-single/sdbox-storage.c b/src/lib-storage/index/dbox-single/sdbox-storage.c index ffd319c562..4ea1a896b7 100644 --- a/src/lib-storage/index/dbox-single/sdbox-storage.c +++ b/src/lib-storage/index/dbox-single/sdbox-storage.c @@ -8,20 +8,10 @@ #include "sdbox-sync.h" #include "sdbox-storage.h" -#define SDBOX_LIST_CONTEXT(obj) \ - MODULE_CONTEXT(obj, sdbox_mailbox_list_module) - -struct sdbox_mailbox_list { - union mailbox_list_module_context module_ctx; -}; - extern struct mail_storage dbox_storage; extern struct mailbox sdbox_mailbox; extern struct dbox_storage_vfuncs sdbox_dbox_storage_vfuncs; -static MODULE_CONTEXT_DEFINE_INIT(sdbox_mailbox_list_module, - &mailbox_list_module_register); - static struct mail_storage *sdbox_storage_alloc(void) { struct sdbox_storage *storage; @@ -203,18 +193,6 @@ dbox_mailbox_update(struct mailbox *box, const struct mailbox_update *update) return sdbox_write_index_header(box, update); } -static void sdbox_storage_add_list(struct mail_storage *storage ATTR_UNUSED, - struct mailbox_list *list) -{ - struct sdbox_mailbox_list *mlist; - - mlist = p_new(list->pool, struct sdbox_mailbox_list, 1); - mlist->module_ctx.super = list->v; - list->v.iter_is_mailbox = dbox_list_iter_is_mailbox; - - MODULE_CONTEXT_SET(list, sdbox_mailbox_list_module, mlist); -} - struct mail_storage dbox_storage = { .name = SDBOX_STORAGE_NAME, .class_flags = 0, @@ -224,7 +202,7 @@ struct mail_storage dbox_storage = { sdbox_storage_alloc, NULL, NULL, - sdbox_storage_add_list, + NULL, dbox_storage_get_list_settings, NULL, sdbox_mailbox_alloc, diff --git a/src/lib-storage/index/maildir/maildir-settings.c b/src/lib-storage/index/maildir/maildir-settings.c index ee66618000..2064681cb0 100644 --- a/src/lib-storage/index/maildir/maildir-settings.c +++ b/src/lib-storage/index/maildir/maildir-settings.c @@ -12,7 +12,6 @@ { type, #name, offsetof(struct maildir_settings, name), NULL } static const struct setting_define maildir_setting_defines[] = { - DEF(SET_BOOL, maildir_stat_dirs), DEF(SET_BOOL, maildir_copy_with_hardlinks), DEF(SET_BOOL, maildir_copy_preserve_filename), DEF(SET_BOOL, maildir_very_dirty_syncs), @@ -21,7 +20,6 @@ static const struct setting_define maildir_setting_defines[] = { }; static const struct maildir_settings maildir_default_settings = { - .maildir_stat_dirs = FALSE, .maildir_copy_with_hardlinks = TRUE, .maildir_copy_preserve_filename = FALSE, .maildir_very_dirty_syncs = FALSE diff --git a/src/lib-storage/index/maildir/maildir-settings.h b/src/lib-storage/index/maildir/maildir-settings.h index e74f0615f5..47bf33b962 100644 --- a/src/lib-storage/index/maildir/maildir-settings.h +++ b/src/lib-storage/index/maildir/maildir-settings.h @@ -2,7 +2,6 @@ #define MAILDIR_SETTINGS_H struct maildir_settings { - bool maildir_stat_dirs; bool maildir_copy_with_hardlinks; bool maildir_copy_preserve_filename; bool maildir_very_dirty_syncs; diff --git a/src/lib-storage/index/maildir/maildir-storage.c b/src/lib-storage/index/maildir/maildir-storage.c index 3b02bb61e1..cfa9d2261e 100644 --- a/src/lib-storage/index/maildir/maildir-storage.c +++ b/src/lib-storage/index/maildir/maildir-storage.c @@ -4,10 +4,8 @@ #include "ioloop.h" #include "mkdir-parents.h" #include "eacces-error.h" -#include "unlink-directory.h" #include "unlink-old-files.h" #include "mailbox-uidvalidity.h" -#include "list/mailbox-list-maildir.h" #include "maildir-storage.h" #include "maildir-uidlist.h" #include "maildir-keywords.h" @@ -31,52 +29,6 @@ static MODULE_CONTEXT_DEFINE_INIT(maildir_mailbox_list_module, &mailbox_list_module_register); static const char *maildir_subdirs[] = { "cur", "new", "tmp" }; -static bool maildir_is_internal_name(const char *name) -{ - return strcmp(name, "cur") == 0 || - strcmp(name, "new") == 0 || - strcmp(name, "tmp") == 0; -} - -static bool maildir_storage_is_valid_existing_name(struct mailbox_list *list, - const char *name) -{ - struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list); - const char *p; - - if (!mlist->module_ctx.super.is_valid_existing_name(list, name)) - return FALSE; - - /* Don't allow the mailbox name to end in cur/new/tmp */ - p = strrchr(name, '/'); - if (p != NULL) - name = p + 1; - return !maildir_is_internal_name(name); -} - -static bool maildir_storage_is_valid_create_name(struct mailbox_list *list, - const char *name) -{ - struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list); - bool ret = TRUE; - - if (!mlist->module_ctx.super.is_valid_create_name(list, name)) - return FALSE; - - /* Don't allow creating mailboxes under cur/new/tmp */ - T_BEGIN { - const char *const *tmp; - - for (tmp = t_strsplit(name, "/"); *tmp != NULL; tmp++) { - if (maildir_is_internal_name(*tmp)) { - ret = FALSE; - break; - } - } - } T_END; - return ret; -} - static struct mail_storage *maildir_storage_alloc(void) { struct maildir_storage *storage; @@ -128,7 +80,7 @@ static void maildir_storage_get_list_settings(const struct mail_namespace *ns, if (set->subscription_fname == NULL) set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME; - if (set->inbox_path == NULL && + if (set->inbox_path == NULL && set->maildir_name == NULL && (strcmp(set->layout, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0 || strcmp(set->layout, MAILBOX_LIST_NAME_FS) == 0) && (ns->flags & NAMESPACE_FLAG_INBOX) != 0) { @@ -533,181 +485,63 @@ static void maildir_notify_changes(struct mailbox *box) } static bool -maildir_is_mailbox_dir(struct mailbox_list *list ATTR_UNUSED, - const char *dir ATTR_UNUSED, const char *name) +maildir_is_internal_name(struct mailbox_list *list ATTR_UNUSED, + const char *name) { - return maildir_is_internal_name(name); + return strcmp(name, "cur") == 0 || + strcmp(name, "new") == 0 || + strcmp(name, "tmp") == 0; } static int -maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx - ATTR_UNUSED, - const char *dir, const char *fname, - const char *mailbox_name ATTR_UNUSED, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags) +maildir_list_get_mailbox_flags(struct mailbox_list *list, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + struct stat *st_r, + enum mailbox_info_flags *flags) { - struct stat st, st2; - const char *path, *cur_path; + struct maildir_mailbox_list_context *mlist = MAILDIR_LIST_CONTEXT(list); + struct stat st2; + const char *cur_path; int ret; - if (maildir_is_internal_name(fname)) { - *flags |= MAILBOX_NONEXISTENT; - return 0; - } - - switch (type) { - case MAILBOX_LIST_FILE_TYPE_FILE: - case MAILBOX_LIST_FILE_TYPE_OTHER: - /* non-directories are not */ + ret = mlist->module_ctx.super. + get_mailbox_flags(list, dir, fname, type, st_r, flags); + if (ret <= 0 || MAILBOX_INFO_FLAGS_FINISHED(*flags)) + return ret; + + /* see if it's a selectable mailbox. after that we can figure out based + on the link count if we have child mailboxes or not. for a + selectable mailbox we have 3 more links (cur/, new/ and tmp/) + than non-selectable. */ + cur_path = t_strconcat(dir, "/", fname, "/cur", NULL); + if (stat(cur_path, &st2) < 0 || !S_ISDIR(st2.st_mode)) { *flags |= MAILBOX_NOSELECT; - return 0; - - case MAILBOX_LIST_FILE_TYPE_DIR: - case MAILBOX_LIST_FILE_TYPE_UNKNOWN: - case MAILBOX_LIST_FILE_TYPE_SYMLINK: - break; - } - - path = t_strdup_printf("%s/%s", dir, fname); - if (stat(path, &st) == 0) { - if (!S_ISDIR(st.st_mode)) { - if (strncmp(fname, ".nfs", 4) == 0) { - /* temporary NFS file */ - *flags |= MAILBOX_NONEXISTENT; - } else { - *flags |= MAILBOX_NOSELECT | - MAILBOX_NOINFERIORS; - } - return 0; - } - ret = 1; - } else if (errno == ENOENT) { - /* doesn't exist - probably a non-existing subscribed mailbox */ - *flags |= MAILBOX_NONEXISTENT; - ret = 0; + if (st_r->st_nlink > 2) + *flags |= MAILBOX_CHILDREN; + else + *flags |= MAILBOX_NOCHILDREN; } else { - /* non-selectable. probably either access denied, or symlink - destination not found. don't bother logging errors. */ - *flags |= MAILBOX_NOSELECT; - ret = 1; + if (st_r->st_nlink > 5) + *flags |= MAILBOX_CHILDREN; + else + *flags |= MAILBOX_NOCHILDREN; } - if ((*flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) { - /* make sure it's a selectable mailbox */ - cur_path = t_strconcat(path, "/cur", NULL); - if (stat(cur_path, &st2) < 0 || !S_ISDIR(st2.st_mode)) - *flags |= MAILBOX_NOSELECT; - - if (*ctx->list->set.maildir_name == '\0') { - /* now we can figure out based on the link count if we - have child mailboxes or not. for a selectable - mailbox we have 3 more links (cur/, new/ and tmp/) - than non-selectable. */ - if ((*flags & MAILBOX_NOSELECT) == 0) { - if (st.st_nlink > 5) - *flags |= MAILBOX_CHILDREN; - else - *flags |= MAILBOX_NOCHILDREN; - } else { - if (st.st_nlink > 2) - *flags |= MAILBOX_CHILDREN; - else - *flags |= MAILBOX_NOCHILDREN; - } - } else { - /* link count 3 may mean either a selectable mailbox - or a non-selectable mailbox with 1 child. */ - if (st.st_nlink > 3) - *flags |= MAILBOX_CHILDREN; - else if (st.st_nlink == 3) { - if ((*flags & MAILBOX_NOSELECT) != 0) - *flags |= MAILBOX_CHILDREN; - else - *flags |= MAILBOX_NOCHILDREN; - } - } - } - return ret; + return 1; } -static int -maildirplusplus_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, - const char *dir, const char *fname, - const char *mailbox_name ATTR_UNUSED, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags) +static void maildir_storage_add_list(struct mail_storage *storage, + struct mailbox_list *list) { - struct maildir_mailbox_list_context *mlist = - MAILDIR_LIST_CONTEXT(ctx->list); - int ret; - - if (fname[1] == mailbox_list_get_hierarchy_sep(ctx->list) && - strcmp(fname+2, MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME) == 0) { - const char *path; - struct stat st; - - /* this directory is in the middle of being deleted, - or the process trying to delete it had died. - delete it ourself if it's been there longer than - one hour. */ - path = t_strdup_printf("%s/%s", dir, fname); - if (stat(path, &st) == 0 && - st.st_mtime < ioloop_time - 3600) - (void)unlink_directory(path, TRUE); - - *flags |= MAILBOX_NONEXISTENT; - return 0; - } - - switch (type) { - case MAILBOX_LIST_FILE_TYPE_DIR: - /* all directories are valid maildirs */ - return 1; - - case MAILBOX_LIST_FILE_TYPE_FILE: - case MAILBOX_LIST_FILE_TYPE_OTHER: - /* non-directories are not */ - *flags |= MAILBOX_NOSELECT; - return 0; + struct maildir_mailbox_list_context *mlist; - case MAILBOX_LIST_FILE_TYPE_UNKNOWN: - case MAILBOX_LIST_FILE_TYPE_SYMLINK: - /* need to check with stat() to be sure */ - break; - } + mlist = p_new(list->pool, struct maildir_mailbox_list_context, 1); + mlist->module_ctx.super = list->v; + mlist->set = mail_storage_get_driver_settings(storage); - /* Check files beginning with .nfs always because they may be - temporary files created by the kernel */ - if (mlist->set->maildir_stat_dirs || *fname == '\0' || - strncmp(fname, ".nfs", 4) == 0) { - const char *path; - struct stat st; - - /* if fname="", we're checking if a base maildir has INBOX */ - path = *fname == '\0' ? t_strdup_printf("%s/cur", dir) : - t_strdup_printf("%s/%s", dir, fname); - if (stat(path, &st) == 0) { - if (S_ISDIR(st.st_mode)) - ret = 1; - else { - if (strncmp(fname, ".nfs", 4) == 0) - *flags |= MAILBOX_NONEXISTENT; - else - *flags |= MAILBOX_NOSELECT; - ret = 0; - } - } else if (errno == ENOENT) { - /* just deleted? */ - *flags |= MAILBOX_NONEXISTENT; - ret = 0; - } else { - *flags |= MAILBOX_NOSELECT; - ret = 0; - } - } else { - ret = 1; - } - return ret; + list->v.is_internal_name = maildir_is_internal_name; + list->v.get_mailbox_flags = maildir_list_get_mailbox_flags; + MODULE_CONTEXT_SET(list, maildir_mailbox_list_module, mlist); } uint32_t maildir_get_uidvalidity_next(struct mailbox_list *list) @@ -720,28 +554,6 @@ uint32_t maildir_get_uidvalidity_next(struct mailbox_list *list) return mailbox_uidvalidity_next(list, path); } -static void maildir_storage_add_list(struct mail_storage *storage, - struct mailbox_list *list) -{ - struct maildir_mailbox_list_context *mlist; - - mlist = p_new(list->pool, struct maildir_mailbox_list_context, 1); - mlist->module_ctx.super = list->v; - mlist->set = mail_storage_get_driver_settings(storage); - - list->v.is_mailbox_dir = maildir_is_mailbox_dir; - if (strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0) { - list->v.iter_is_mailbox = maildirplusplus_iter_is_mailbox; - } else { - list->v.is_valid_existing_name = - maildir_storage_is_valid_existing_name; - list->v.is_valid_create_name = - maildir_storage_is_valid_create_name; - list->v.iter_is_mailbox = maildir_list_iter_is_mailbox; - } - MODULE_CONTEXT_SET(list, maildir_mailbox_list_module, mlist); -} - struct mail_storage maildir_storage = { .name = MAILDIR_STORAGE_NAME, .class_flags = 0, diff --git a/src/lib-storage/index/mbox/mbox-storage.c b/src/lib-storage/index/mbox/mbox-storage.c index f022715a9c..6e9adf6c03 100644 --- a/src/lib-storage/index/mbox/mbox-storage.c +++ b/src/lib-storage/index/mbox/mbox-storage.c @@ -583,111 +583,17 @@ static void mbox_notify_changes(struct mailbox *box) } static bool -is_inbox_file(struct mailbox_list *list, const char *path, const char *fname) +mbox_is_internal_name(struct mailbox_list *list ATTR_UNUSED, + const char *name) { - const char *inbox_path; + unsigned int len; - if (strcasecmp(fname, "INBOX") != 0) - return FALSE; - - inbox_path = mailbox_list_get_path(list, "INBOX", - MAILBOX_LIST_PATH_TYPE_MAILBOX); - return strcmp(inbox_path, path) == 0; -} - -static bool mbox_name_is_dotlock(const char *name) -{ - unsigned int len = strlen(name); - - return len >= 5 && strcmp(name + len - 5, ".lock") == 0; -} - -static bool -mbox_is_valid_existing_name(struct mailbox_list *list, const char *name) -{ - struct mbox_mailbox_list *mlist = MBOX_LIST_CONTEXT(list); - - return mlist->module_ctx.super.is_valid_existing_name(list, name) && - !mbox_name_is_dotlock(name); -} - -static bool -mbox_is_valid_create_name(struct mailbox_list *list, const char *name) -{ - struct mbox_mailbox_list *mlist = MBOX_LIST_CONTEXT(list); - - return mlist->module_ctx.super.is_valid_create_name(list, name) && - !mbox_name_is_dotlock(name); -} - -static int mbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, - const char *dir, const char *fname, - const char *mailbox_name ATTR_UNUSED, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags) -{ - const char *path, *root_dir; - size_t len; - struct stat st; - - if (strcmp(fname, MBOX_INDEX_DIR_NAME) == 0) { - *flags |= MAILBOX_NOSELECT; - return 0; - } - if (strcmp(fname, ctx->list->set.subscription_fname) == 0) { - root_dir = mailbox_list_get_path(ctx->list, NULL, - MAILBOX_LIST_PATH_TYPE_DIR); - if (strcmp(root_dir, dir) == 0) { - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - return 0; - } - } - - /* skip all .lock files */ - len = strlen(fname); - if (len > 5 && strcmp(fname+len-5, ".lock") == 0) { - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - return 0; - } - - /* try to avoid stat() with these checks */ - if (type == MAILBOX_LIST_FILE_TYPE_DIR) { - *flags |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; - return 1; - } - if (type != MAILBOX_LIST_FILE_TYPE_SYMLINK && - type != MAILBOX_LIST_FILE_TYPE_UNKNOWN && - (ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0) { - *flags |= MAILBOX_NOINFERIORS; - return 1; - } + /* don't allow *.lock files/dirs */ + len = strlen(name); + if (len > 5 && strcmp(name+len-5, ".lock") == 0) + return TRUE; - /* need to stat() then */ - path = t_strconcat(dir, "/", fname, NULL); - if (stat(path, &st) == 0) { - if (S_ISDIR(st.st_mode)) - *flags |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; - else { - *flags |= MAILBOX_NOINFERIORS | STAT_GET_MARKED(st); - if (is_inbox_file(ctx->list, path, fname) && - strcmp(fname, "INBOX") != 0) { - /* it's possible for INBOX to have child - mailboxes as long as the inbox file itself - isn't in /INBOX */ - *flags &= ~MAILBOX_NOINFERIORS; - } - } - return 1; - } else if (errno == ENOENT) { - /* doesn't exist - probably a non-existing subscribed mailbox */ - *flags |= MAILBOX_NONEXISTENT; - return 1; - } else { - /* non-selectable. probably either access denied, or symlink - destination not found. don't bother logging errors. */ - *flags |= MAILBOX_NOSELECT; - return 0; - } + return strcmp(name, MBOX_INDEX_DIR_NAME) == 0; } static void mbox_storage_add_list(struct mail_storage *storage, @@ -704,9 +610,7 @@ static void mbox_storage_add_list(struct mail_storage *storage, /* have to use .imap/ directories */ list->v.get_path = mbox_list_get_path; } - list->v.iter_is_mailbox = mbox_list_iter_is_mailbox; - list->v.is_valid_existing_name = mbox_is_valid_existing_name; - list->v.is_valid_create_name = mbox_is_valid_create_name; + list->v.is_internal_name = mbox_is_internal_name; MODULE_CONTEXT_SET(list, mbox_mailbox_list_module, mlist); } diff --git a/src/lib-storage/index/raw/raw-storage.c b/src/lib-storage/index/raw/raw-storage.c index 6bba989680..101ca45cbb 100644 --- a/src/lib-storage/index/raw/raw-storage.c +++ b/src/lib-storage/index/raw/raw-storage.c @@ -109,54 +109,6 @@ static void raw_notify_changes(struct mailbox *box ATTR_UNUSED) { } -static int raw_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, - const char *dir, const char *fname, - const char *mailbox_name ATTR_UNUSED, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags_r) -{ - const char *path; - struct stat st; - - /* try to avoid stat() with these checks */ - if (type == MAILBOX_LIST_FILE_TYPE_DIR) { - *flags_r = MAILBOX_NOSELECT | MAILBOX_CHILDREN; - return 1; - } - if (type != MAILBOX_LIST_FILE_TYPE_SYMLINK && - type != MAILBOX_LIST_FILE_TYPE_UNKNOWN && - (ctx->flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0) { - *flags_r = MAILBOX_NOINFERIORS; - return 1; - } - - /* need to stat() then */ - path = t_strconcat(dir, "/", fname, NULL); - if (stat(path, &st) == 0) { - if (S_ISDIR(st.st_mode)) - *flags_r = MAILBOX_NOSELECT | MAILBOX_CHILDREN; - else - *flags_r = MAILBOX_NOINFERIORS; - return 1; - } else if (errno == EACCES || errno == ELOOP) { - *flags_r = MAILBOX_NOSELECT; - return 1; - } else if (ENOTFOUND(errno)) { - *flags_r = MAILBOX_NONEXISTENT; - return 0; - } else { - mailbox_list_set_critical(ctx->list, "stat(%s) failed: %m", - path); - return -1; - } -} - -static void raw_storage_add_list(struct mail_storage *storage ATTR_UNUSED, - struct mailbox_list *list) -{ - list->v.iter_is_mailbox = raw_list_iter_is_mailbox; -} - struct mail_storage raw_storage = { .name = RAW_STORAGE_NAME, .class_flags = MAIL_STORAGE_CLASS_FLAG_MAILBOX_IS_FILE | @@ -167,7 +119,7 @@ struct mail_storage raw_storage = { raw_storage_alloc, NULL, NULL, - raw_storage_add_list, + NULL, raw_storage_get_list_settings, NULL, raw_mailbox_alloc, diff --git a/src/lib-storage/list/Makefile.am b/src/lib-storage/list/Makefile.am index a646539a15..2cbbcd2ef1 100644 --- a/src/lib-storage/list/Makefile.am +++ b/src/lib-storage/list/Makefile.am @@ -13,6 +13,7 @@ libstorage_list_la_SOURCES = \ index-mailbox-list-sync.c \ mailbox-list-delete.c \ mailbox-list-fs.c \ + mailbox-list-fs-flags.c \ mailbox-list-fs-iter.c \ mailbox-list-maildir.c \ mailbox-list-maildir-iter.c \ diff --git a/src/lib-storage/list/mailbox-list-delete.c b/src/lib-storage/list/mailbox-list-delete.c index c9fcb40858..07fb5b94b0 100644 --- a/src/lib-storage/list/mailbox-list-delete.c +++ b/src/lib-storage/list/mailbox-list-delete.c @@ -150,10 +150,10 @@ int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list, continue; } + mailbox_dir = list->v.is_internal_name != NULL && + list->v.is_internal_name(list, d->d_name); + str_truncate(full_path, dir_len); - mailbox_dir = list->v.is_mailbox_dir != NULL && - list->v.is_mailbox_dir(list, str_c(full_path), - d->d_name); str_append(full_path, d->d_name); if (mailbox_dir) { diff --git a/src/lib-storage/list/mailbox-list-fs-flags.c b/src/lib-storage/list/mailbox-list-fs-flags.c new file mode 100644 index 0000000000..67df324895 --- /dev/null +++ b/src/lib-storage/list/mailbox-list-fs-flags.c @@ -0,0 +1,208 @@ +/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mailbox-list-fs.h" + +#include + +/* Assume that if atime < mtime, there are new mails. If it's good enough for + UW-IMAP, it's good enough for us. */ +#define STAT_GET_MARKED_FILE(st) \ + ((st).st_size == 0 ? MAILBOX_UNMARKED : \ + (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED) + +static int +list_is_maildir_mailbox(struct mailbox_list *list, const char *dir, + const char *fname, enum mailbox_list_file_type type, + enum mailbox_info_flags *flags_r) +{ + const char *path, *maildir_path; + struct stat st, st2; + bool mailbox_files; + + switch (type) { + case MAILBOX_LIST_FILE_TYPE_FILE: + case MAILBOX_LIST_FILE_TYPE_OTHER: + /* non-directories aren't valid */ + *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + return 0; + + case MAILBOX_LIST_FILE_TYPE_DIR: + case MAILBOX_LIST_FILE_TYPE_UNKNOWN: + case MAILBOX_LIST_FILE_TYPE_SYMLINK: + break; + } + + path = t_strdup_printf("%s/%s", dir, fname); + if (stat(path, &st) < 0) { + if (errno == ENOENT) { + *flags_r |= MAILBOX_NONEXISTENT; + return 0; + } else { + /* non-selectable. probably either access denied, or + symlink destination not found. don't bother logging + errors. */ + *flags_r |= MAILBOX_NOSELECT; + return 1; + } + } + if (!S_ISDIR(st.st_mode)) { + if (strncmp(fname, ".nfs", 4) == 0) { + /* temporary NFS file */ + *flags_r |= MAILBOX_NONEXISTENT; + } else { + *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + } + return 0; + } + + /* ok, we've got a directory. see what we can do about it. */ + + /* 1st link is "." + 2nd link is ".." + 3rd link is either child mailbox or mailbox dir + rest of the links are child mailboxes + + if mailboxes are files, then 3+ links are all child mailboxes. + */ + mailbox_files = (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0; + if (st.st_nlink == 2 && !mailbox_files) { + *flags_r |= MAILBOX_NOSELECT; + return 1; + } + + /* we have at least one directory. see if this mailbox is selectable */ + maildir_path = t_strconcat(path, "/", list->set.maildir_name, NULL); + if (stat(maildir_path, &st2) < 0) + *flags_r |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; + else if (!S_ISDIR(st2.st_mode)) { + if (mailbox_files) { + *flags_r |= st.st_nlink == 2 ? + MAILBOX_NOCHILDREN : MAILBOX_CHILDREN; + } else { + *flags_r |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; + } + } else { + /* now we know what link count 3 means. */ + if (st.st_nlink == 3) + *flags_r |= MAILBOX_NOCHILDREN; + else + *flags_r |= MAILBOX_CHILDREN; + } + *flags_r |= MAILBOX_SELECT; + return 1; +} + +static bool +is_inbox_file(struct mailbox_list *list, const char *path, const char *fname) +{ + const char *inbox_path; + + if (strcasecmp(fname, "INBOX") != 0) + return FALSE; + + inbox_path = mailbox_list_get_path(list, "INBOX", + MAILBOX_LIST_PATH_TYPE_MAILBOX); + return strcmp(inbox_path, path) == 0; +} + +int fs_list_get_mailbox_flags(struct mailbox_list *list, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + struct stat *st_r, + enum mailbox_info_flags *flags_r) +{ + struct stat st; + const char *path; + + memset(st_r, 0, sizeof(*st_r)); + *flags_r = 0; + + if (*list->set.maildir_name != '\0') { + /* maildir_name is set: we the code is common for all + storage types */ + return list_is_maildir_mailbox(list, dir, fname, type, flags_r); + } + if (list->v.is_internal_name != NULL && + list->v.is_internal_name(list, fname)) { + /* skip internal dirs */ + *flags_r |= MAILBOX_NOSELECT; + return 0; + } + + switch (type) { + case MAILBOX_LIST_FILE_TYPE_DIR: + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { + *flags_r |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; + return 1; + } + break; + case MAILBOX_LIST_FILE_TYPE_FILE: + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { + *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + return 0; + } + break; + default: + break; + } + + /* we've done all filtering we can before stat()ing */ + path = t_strconcat(dir, "/", fname, NULL); + if (stat(path, &st) < 0) { + if (ENOTFOUND(errno)) { + *flags_r |= MAILBOX_NONEXISTENT; + return 0; + } else if (ENOACCESS(errno)) { + *flags_r |= MAILBOX_NOSELECT; + return 1; + } else { + /* non-selectable. probably either access denied, or + symlink destination not found. don't bother logging + errors. */ + mailbox_list_set_critical(list, "stat(%s) failed: %m", + path); + return -1; + } + } + *st_r = st; + + if (!S_ISDIR(st.st_mode)) { + if (strncmp(fname, ".nfs", 4) == 0) { + /* temporary NFS file */ + *flags_r |= MAILBOX_NONEXISTENT; + return 0; + } + + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { + *flags_r |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + return 0; + } + /* looks like a valid mailbox file */ + if (is_inbox_file(list, path, fname) && + strcmp(fname, "INBOX") != 0) { + /* it's possible for INBOX to have child + mailboxes as long as the inbox file itself + isn't in /INBOX */ + } else { + *flags_r |= MAILBOX_NOINFERIORS; + } + } else { + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { + *flags_r |= MAILBOX_NOSELECT | MAILBOX_CHILDREN; + return 1; + } + } + + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { + *flags_r |= STAT_GET_MARKED_FILE(st); + } else if (list->v.is_internal_name == NULL) { + /* link count < 2 can happen with filesystems that don't + support link counts. we'll just ignore them for now.. */ + if (st.st_nlink == 2) + *flags_r |= MAILBOX_NOCHILDREN; + else if (st.st_nlink > 2) + *flags_r |= MAILBOX_CHILDREN; + } + return 1; +} diff --git a/src/lib-storage/list/mailbox-list-fs-iter.c b/src/lib-storage/list/mailbox-list-fs-iter.c index 32a327afaf..8f509b7059 100644 --- a/src/lib-storage/list/mailbox-list-fs-iter.c +++ b/src/lib-storage/list/mailbox-list-fs-iter.c @@ -2,7 +2,6 @@ #include "lib.h" #include "array.h" -#include "unlink-directory.h" #include "imap-match.h" #include "mailbox-tree.h" #include "mailbox-list-subscriptions.h" @@ -408,17 +407,10 @@ static void inbox_flags_set(struct fs_list_iterate_context *ctx) static struct mailbox_info *fs_list_inbox(struct fs_list_iterate_context *ctx) { - const char *inbox_path, *dir, *fname; - ctx->info.flags = 0; ctx->info.name = "INBOX"; - inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX", - MAILBOX_LIST_PATH_TYPE_DIR); - path_split(inbox_path, &dir, &fname); - if (ctx->ctx.list->v.iter_is_mailbox(&ctx->ctx, dir, fname, "INBOX", - MAILBOX_LIST_FILE_TYPE_UNKNOWN, - &ctx->info.flags) < 0) + if (mailbox_list_mailbox(ctx->ctx.list, "INBOX", &ctx->info.flags) < 0) ctx->ctx.failed = TRUE; ctx->info.flags |= fs_list_get_subscription_flags(ctx, "INBOX"); @@ -544,8 +536,9 @@ list_file(struct fs_list_iterate_context *ctx, { struct mail_namespace *ns = ctx->ctx.list->ns; const char *fname = entry->fname; - const char *list_path; + const char *list_path, *root_dir; enum imap_match_result match; + struct stat st; int ret; /* skip . and .. */ @@ -566,11 +559,19 @@ list_file(struct fs_list_iterate_context *ctx, return 0; } + if (strcmp(fname, ctx->ctx.list->set.subscription_fname) == 0) { + /* skip subscriptions file */ + root_dir = mailbox_list_get_path(ctx->ctx.list, NULL, + MAILBOX_LIST_PATH_TYPE_DIR); + if (strcmp(root_dir, ctx->dir->real_path) == 0) + return 0; + } + + /* get the info.flags using callback */ - ctx->info.flags = 0; ret = ctx->ctx.list->v. - iter_is_mailbox(&ctx->ctx, ctx->dir->real_path, fname, - list_path, entry->type, &ctx->info.flags); + get_mailbox_flags(ctx->ctx.list, ctx->dir->real_path, fname, + entry->type, &st, &ctx->info.flags); if (ret <= 0) return ret; @@ -618,6 +619,7 @@ fs_list_subs(struct fs_list_iterate_context *ctx) struct mailbox_node *node; enum mailbox_info_flags flags; const char *path, *dir, *fname; + struct stat st; node = mailbox_tree_iterate_next(ctx->tree_iter, &ctx->info.name); if (node == NULL) @@ -635,11 +637,9 @@ fs_list_subs(struct fs_list_iterate_context *ctx) path = mailbox_list_get_path(ctx->ctx.list, ctx->info.name, MAILBOX_LIST_PATH_TYPE_DIR); path_split(path, &dir, &fname); - ctx->info.flags = 0; - if (ctx->ctx.list->v.iter_is_mailbox(&ctx->ctx, dir, fname, - ctx->info.name, - MAILBOX_LIST_FILE_TYPE_UNKNOWN, - &ctx->info.flags) < 0) + if (ctx->ctx.list->v.get_mailbox_flags(ctx->ctx.list, dir, fname, + MAILBOX_LIST_FILE_TYPE_UNKNOWN, + &st, &ctx->info.flags) < 0) ctx->ctx.failed = TRUE; ctx->info.flags |= flags; diff --git a/src/lib-storage/list/mailbox-list-fs.c b/src/lib-storage/list/mailbox-list-fs.c index 922c9afa5a..5c328577ef 100644 --- a/src/lib-storage/list/mailbox-list-fs.c +++ b/src/lib-storage/list/mailbox-list-fs.c @@ -52,9 +52,7 @@ static bool fs_list_is_valid_common(const char *name, size_t *len_r) static bool fs_list_is_valid_common_nonfs(struct mailbox_list *list, const char *name) { - const char *p; - bool newdir; - size_t maildir_len; + bool ret, allow_internal_dirs; /* make sure it's not absolute path */ if (*name == '/' || *name == '~') @@ -63,38 +61,37 @@ fs_list_is_valid_common_nonfs(struct mailbox_list *list, const char *name) /* make sure the mailbox name doesn't contain any foolishness: "../" could give access outside the mailbox directory. "./" and "//" could fool ACL checks. */ - newdir = TRUE; - maildir_len = strlen(list->set.maildir_name); - for (p = name; *p != '\0'; p++) { - if (newdir) { - if (p[0] == '/') - return FALSE; /* // */ - if (p[0] == '.') { - if (p[1] == '/' || p[1] == '\0') - return FALSE; /* ./ */ - if (p[1] == '.' && - (p[2] == '/' || p[2] == '\0')) - return FALSE; /* ../ */ + allow_internal_dirs = list->v.is_internal_name == NULL || + *list->set.maildir_name != '\0'; + T_BEGIN { + const char *const *names; + + names = t_strsplit(name, "/"); + for (; *names != NULL; names++) { + const char *n = *names; + + if (*n == '\0') + break; /* // */ + if (*n == '.') { + if (n[1] == '\0') + break; /* ./ */ + if (n[1] == '.' && n[2] == '\0') + break; /* ../ */ } - if (maildir_len > 0 && - strncmp(p, list->set.maildir_name, - maildir_len) == 0 && - (p[maildir_len] == '\0' || - p[maildir_len] == '/')) { + if (*list->set.maildir_name != '\0' && + strcmp(list->set.maildir_name, n) == 0) { /* don't allow maildir_name to be used as part of the mailbox name */ - return FALSE; + break; } - } - newdir = p[0] == '/'; - } - if (name[0] == '.' && (name[1] == '\0' || - (name[1] == '.' && name[2] == '\0'))) { - /* "." and ".." aren't allowed. */ - return FALSE; - } + if (!allow_internal_dirs && + list->v.is_internal_name(list, n)) + break; + } + ret = *names == NULL; + } T_END; - return TRUE; + return ret; } static bool @@ -237,12 +234,25 @@ fs_list_get_mailbox_name_status(struct mailbox_list *_list, const char *name, { struct stat st; const char *path, *dir_path; + enum mailbox_info_flags flags; path = mailbox_list_get_path(_list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 0) { - *status = MAILBOX_NAME_EXISTS_MAILBOX; + if (*_list->set.maildir_name != '\0' || + _list->v.is_internal_name == NULL || !S_ISDIR(st.st_mode)) { + *status = MAILBOX_NAME_EXISTS_MAILBOX; + return 0; + } + + /* check if mailbox is selectable */ + if (mailbox_list_mailbox(_list, name, &flags) < 0) + return -1; + if ((flags & (MAILBOX_NOSELECT | MAILBOX_NONEXISTENT)) == 0) + *status = MAILBOX_NAME_EXISTS_MAILBOX; + else + *status = MAILBOX_NAME_EXISTS_DIR; return 0; } if (errno == ENOENT) { @@ -625,7 +635,7 @@ struct mailbox_list fs_mailbox_list = { fs_list_iter_init, fs_list_iter_next, fs_list_iter_deinit, - NULL, + fs_list_get_mailbox_flags, NULL, fs_list_set_subscribed, fs_list_create_mailbox_dir, diff --git a/src/lib-storage/list/mailbox-list-fs.h b/src/lib-storage/list/mailbox-list-fs.h index e089b2fbf8..5724747245 100644 --- a/src/lib-storage/list/mailbox-list-fs.h +++ b/src/lib-storage/list/mailbox-list-fs.h @@ -24,4 +24,10 @@ int fs_list_iter_deinit(struct mailbox_list_iterate_context *ctx); const struct mailbox_info * fs_list_iter_next(struct mailbox_list_iterate_context *ctx); +int fs_list_get_mailbox_flags(struct mailbox_list *list, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + struct stat *st_r, + enum mailbox_info_flags *flags); + #endif diff --git a/src/lib-storage/list/mailbox-list-maildir-iter.c b/src/lib-storage/list/mailbox-list-maildir-iter.c index dd4c157470..076b773858 100644 --- a/src/lib-storage/list/mailbox-list-maildir-iter.c +++ b/src/lib-storage/list/mailbox-list-maildir-iter.c @@ -2,18 +2,22 @@ #include "lib.h" #include "str.h" +#include "ioloop.h" +#include "unlink-directory.h" #include "imap-match.h" #include "mailbox-tree.h" #include "mailbox-list-subscriptions.h" #include "mailbox-list-maildir.h" #include +#include struct maildir_list_iterate_context { struct mailbox_list_iterate_context ctx; pool_t pool; const char *dir; + char prefix_char; struct mailbox_tree_context *tree_ctx; struct mailbox_tree_iterate_context *tree_iter; @@ -117,7 +121,7 @@ static void maildir_set_children(struct maildir_list_iterate_context *ctx, static int maildir_fill_inbox(struct maildir_list_iterate_context *ctx, - const char *mailbox_name, struct imap_match_glob *glob, + struct imap_match_glob *glob, bool update_only) { const struct mailbox_list_settings *set = &ctx->ctx.list->set; @@ -136,9 +140,7 @@ maildir_fill_inbox(struct maildir_list_iterate_context *ctx, } else { /* INBOX is in Maildir root. show it only if it has already been created */ - ret = ctx->ctx.list->v. - iter_is_mailbox(&ctx->ctx, ctx->dir, "", mailbox_name, - MAILBOX_LIST_FILE_TYPE_UNKNOWN, &flags); + ret = mailbox_list_mailbox(ctx->ctx.list, "INBOX", &flags); if (ret < 0) return -1; if (ret == 0) @@ -147,14 +149,14 @@ maildir_fill_inbox(struct maildir_list_iterate_context *ctx, created = FALSE; node = update_only ? - mailbox_tree_lookup(ctx->tree_ctx, mailbox_name) : - mailbox_tree_get(ctx->tree_ctx, mailbox_name, &created); + mailbox_tree_lookup(ctx->tree_ctx, "INBOX") : + mailbox_tree_get(ctx->tree_ctx, "INBO", &created); if (created) node->flags = MAILBOX_NOCHILDREN; else if (node != NULL) node->flags &= ~MAILBOX_NONEXISTENT; - match = imap_match(glob, mailbox_name); + match = imap_match(glob, "INBOX"); if ((match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) != 0) { if (!update_only) node->flags |= MAILBOX_MATCHED; @@ -162,11 +164,120 @@ maildir_fill_inbox(struct maildir_list_iterate_context *ctx, return 0; } +static bool +maildir_get_type(const char *dir, const char *fname, + enum mailbox_list_file_type *type_r, + struct stat *st_r, + enum mailbox_info_flags *flags) +{ + const char *path; + struct stat st; + + path = t_strdup_printf("%s/%s", dir, fname); + if (stat(path, &st) < 0) { + if (errno == ENOENT) { + /* just deleted? */ + *flags |= MAILBOX_NONEXISTENT; + } else { + *flags |= MAILBOX_NOSELECT; + } + return FALSE; + } + + *st_r = st; + if (S_ISDIR(st.st_mode)) { + *type_r = MAILBOX_LIST_FILE_TYPE_DIR; + return TRUE; + } else { + if (strncmp(fname, ".nfs", 4) == 0) + *flags |= MAILBOX_NONEXISTENT; + else + *flags |= MAILBOX_NOSELECT; + return FALSE; + } +} + +int maildir_list_get_mailbox_flags(struct mailbox_list *list, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + struct stat *st_r, + enum mailbox_info_flags *flags_r) +{ + memset(st_r, 0, sizeof(*st_r)); + *flags_r = 0; + + switch (type) { + case MAILBOX_LIST_FILE_TYPE_DIR: + case MAILBOX_LIST_FILE_TYPE_FILE: + case MAILBOX_LIST_FILE_TYPE_OTHER: + break; + case MAILBOX_LIST_FILE_TYPE_UNKNOWN: + case MAILBOX_LIST_FILE_TYPE_SYMLINK: + /* need to check with stat() to be sure */ + if (!list->mail_set->maildir_stat_dirs && + strcmp(list->name, MAILBOX_LIST_NAME_MAILDIRPLUSPLUS) == 0 && + strncmp(fname, ".nfs", 4) != 0) { + /* just assume it's a valid mailbox */ + return 1; + } + + if (!maildir_get_type(dir, fname, &type, st_r, flags_r)) + return 0; + break; + } + + switch (type) { + case MAILBOX_LIST_FILE_TYPE_DIR: + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) { + *flags_r |= MAILBOX_NOSELECT; + return 0; + } + break; + case MAILBOX_LIST_FILE_TYPE_FILE: + if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0) { + *flags_r |= MAILBOX_NOSELECT; + return 0; + } + break; + case MAILBOX_LIST_FILE_TYPE_OTHER: + *flags_r |= MAILBOX_NOSELECT; + return 0; + + case MAILBOX_LIST_FILE_TYPE_UNKNOWN: + case MAILBOX_LIST_FILE_TYPE_SYMLINK: + i_unreached(); + } + *flags_r |= MAILBOX_SELECT; + return 1; +} + +static bool maildir_delete_trash_dir(struct maildir_list_iterate_context *ctx, + const char *fname) +{ + const char *path; + struct stat st; + + if (fname[1] != ctx->prefix_char || ctx->prefix_char == '\0' || + strcmp(fname+2, MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME) != 0) + return FALSE; + + /* this directory is in the middle of being deleted, or the process + trying to delete it had died. delete it ourself if it's been there + longer than one hour. */ + path = t_strdup_printf("%s/%s", ctx->dir, fname); + if (stat(path, &st) == 0 && + st.st_mtime < ioloop_time - 3600) + (void)unlink_directory(path, TRUE); + + return TRUE; +} + static int maildir_fill_readdir(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, bool update_only) { - struct mail_namespace *ns = ctx->ctx.list->ns; + struct mailbox_list *list = ctx->ctx.list; + struct mail_namespace *ns = list->ns; DIR *dirp; struct dirent *d; const char *mailbox_name; @@ -175,16 +286,16 @@ maildir_fill_readdir(struct maildir_list_iterate_context *ctx, enum imap_match_result match; struct mailbox_node *node; bool created, virtual_names; - char prefix_char; + struct stat st; int ret; dirp = opendir(ctx->dir); if (dirp == NULL) { if (errno == EACCES) { - mailbox_list_set_critical(ctx->ctx.list, "%s", + mailbox_list_set_critical(list, "%s", mail_error_eacces_msg("opendir", ctx->dir)); } else if (errno != ENOENT) { - mailbox_list_set_critical(ctx->ctx.list, + mailbox_list_set_critical(list, "opendir(%s) failed: %m", ctx->dir); return -1; } @@ -192,18 +303,15 @@ maildir_fill_readdir(struct maildir_list_iterate_context *ctx, } virtual_names = (ctx->ctx.flags & MAILBOX_LIST_ITER_VIRTUAL_NAMES) != 0; - prefix_char = - strcmp(ctx->ctx.list->name, MAILBOX_LIST_NAME_IMAPDIR) != 0 ? - ctx->ctx.list->hierarchy_sep : '\0'; mailbox = t_str_new(MAILBOX_LIST_NAME_MAX_LENGTH); while ((d = readdir(dirp)) != NULL) { const char *fname = d->d_name; - if (fname[0] == prefix_char) + if (fname[0] == ctx->prefix_char) mailbox_name = fname + 1; else { - if (prefix_char != '\0' || fname[0] == '.') + if (ctx->prefix_char != '\0' || fname[0] == '.') continue; mailbox_name = fname; } @@ -228,12 +336,12 @@ maildir_fill_readdir(struct maildir_list_iterate_context *ctx, continue; /* check if this is an actual mailbox */ - flags = 0; + if (maildir_delete_trash_dir(ctx, fname)) + continue; + T_BEGIN { - ret = ctx->ctx.list->v. - iter_is_mailbox(&ctx->ctx, ctx->dir, fname, - mailbox_name, - mailbox_list_get_file_type(d), &flags); + ret = list->v.get_mailbox_flags(list, ctx->dir, fname, + mailbox_list_get_file_type(d), &st, &flags); } T_END; if (ret <= 0) { if (ret < 0) @@ -276,8 +384,8 @@ maildir_fill_readdir(struct maildir_list_iterate_context *ctx, } if (closedir(dirp) < 0) { - mailbox_list_set_critical(ctx->ctx.list, - "readdir(%s) failed: %m", ctx->dir); + mailbox_list_set_critical(list, "readdir(%s) failed: %m", + ctx->dir); return -1; } @@ -285,9 +393,7 @@ maildir_fill_readdir(struct maildir_list_iterate_context *ctx, return 0; else { /* make sure INBOX is listed */ - mailbox_name = !virtual_names ? "INBOX" : - mail_namespace_get_vname(ns, mailbox, "INBOX"); - return maildir_fill_inbox(ctx, mailbox_name, glob, update_only); + return maildir_fill_inbox(ctx, glob, update_only); } } @@ -311,6 +417,8 @@ maildir_list_iter_init(struct mailbox_list *_list, const char *const *patterns, ctx->pool = pool; ctx->tree_ctx = mailbox_tree_init(sep); ctx->info.ns = _list->ns; + ctx->prefix_char = strcmp(_list->name, MAILBOX_LIST_NAME_IMAPDIR) == 0 ? + '\0' : _list->hierarchy_sep; glob = imap_match_init_multiple(pool, patterns, TRUE, sep); diff --git a/src/lib-storage/list/mailbox-list-maildir.c b/src/lib-storage/list/mailbox-list-maildir.c index 8ae113502c..3744cd6147 100644 --- a/src/lib-storage/list/mailbox-list-maildir.c +++ b/src/lib-storage/list/mailbox-list-maildir.c @@ -630,7 +630,7 @@ struct mailbox_list maildir_mailbox_list = { maildir_list_iter_init, maildir_list_iter_next, maildir_list_iter_deinit, - NULL, + maildir_list_get_mailbox_flags, NULL, maildir_list_set_subscribed, maildir_list_create_mailbox_dir, @@ -662,7 +662,7 @@ struct mailbox_list imapdir_mailbox_list = { maildir_list_iter_init, maildir_list_iter_next, maildir_list_iter_deinit, - NULL, + maildir_list_get_mailbox_flags, NULL, maildir_list_set_subscribed, maildir_list_create_mailbox_dir, diff --git a/src/lib-storage/list/mailbox-list-maildir.h b/src/lib-storage/list/mailbox-list-maildir.h index f6bb39a2cb..cefb48f140 100644 --- a/src/lib-storage/list/mailbox-list-maildir.h +++ b/src/lib-storage/list/mailbox-list-maildir.h @@ -24,4 +24,10 @@ int maildir_list_iter_deinit(struct mailbox_list_iterate_context *ctx); const struct mailbox_info * maildir_list_iter_next(struct mailbox_list_iterate_context *ctx); +int maildir_list_get_mailbox_flags(struct mailbox_list *list, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + struct stat *st_r, + enum mailbox_info_flags *flags); + #endif diff --git a/src/lib-storage/mail-storage-settings.c b/src/lib-storage/mail-storage-settings.c index af1331c0e3..a9772c8725 100644 --- a/src/lib-storage/mail-storage-settings.c +++ b/src/lib-storage/mail-storage-settings.c @@ -37,6 +37,7 @@ static const struct setting_define mail_storage_setting_defines[] = { DEF(SET_BOOL, mailbox_list_index_disable), DEF(SET_BOOL, mail_debug), DEF(SET_BOOL, mail_full_filesystem_access), + DEF(SET_BOOL, maildir_stat_dirs), DEF(SET_ENUM, lock_method), DEF(SET_STR, pop3_uidl_format), @@ -59,6 +60,7 @@ const struct mail_storage_settings mail_storage_default_settings = { .mailbox_list_index_disable = FALSE, .mail_debug = FALSE, .mail_full_filesystem_access = FALSE, + .maildir_stat_dirs = FALSE, .lock_method = "fcntl:flock:dotlock", .pop3_uidl_format = "%08Xu%08Xv" }; diff --git a/src/lib-storage/mail-storage-settings.h b/src/lib-storage/mail-storage-settings.h index e4df6c3893..cd207eb7c0 100644 --- a/src/lib-storage/mail-storage-settings.h +++ b/src/lib-storage/mail-storage-settings.h @@ -24,6 +24,7 @@ struct mail_storage_settings { bool mailbox_list_index_disable; bool mail_debug; bool mail_full_filesystem_access; + bool maildir_stat_dirs; const char *lock_method; const char *pop3_uidl_format; diff --git a/src/lib-storage/mailbox-list-private.h b/src/lib-storage/mailbox-list-private.h index b4d5575acc..165bcf0512 100644 --- a/src/lib-storage/mailbox-list-private.h +++ b/src/lib-storage/mailbox-list-private.h @@ -12,10 +12,16 @@ #define MAILBOX_LOG_FILE_NAME "dovecot.mailbox.log" enum mailbox_log_record_type; +struct stat; struct dirent; struct imap_match_glob; struct mailbox_tree_context; +#define MAILBOX_INFO_FLAGS_FINISHED(flags) \ + (((flags) & (MAILBOX_SELECT | MAILBOX_NOSELECT | \ + MAILBOX_NONEXISTENT)) != 0) + + struct mailbox_list_vfuncs { struct mailbox_list *(*alloc)(void); void (*deinit)(struct mailbox_list *list); @@ -47,17 +53,14 @@ struct mailbox_list_vfuncs { (*iter_next)(struct mailbox_list_iterate_context *ctx); int (*iter_deinit)(struct mailbox_list_iterate_context *ctx); - /* Returns -1 if error, 0 if it's not a valid mailbox, 1 if it is. - flags may be updated (especially the children flags). */ - int (*iter_is_mailbox)(struct mailbox_list_iterate_context *ctx, - const char *dir, const char *fname, - const char *mailbox_name, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags_r); - /* Returns TRUE if dir/name points to mailbox's internal directory. + int (*get_mailbox_flags)(struct mailbox_list *list, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + struct stat *st_r, + enum mailbox_info_flags *flags_r); + /* Returns TRUE if name is mailbox's internal file/directory. If it does, mailbox deletion assumes it can safely delete it. */ - bool (*is_mailbox_dir)(struct mailbox_list *list, const char *dir, - const char *name); + bool (*is_internal_name)(struct mailbox_list *list, const char *name); int (*set_subscribed)(struct mailbox_list *list, const char *name, bool set); diff --git a/src/lib-storage/mailbox-list.c b/src/lib-storage/mailbox-list.c index 3465b53ce3..a54f78b644 100644 --- a/src/lib-storage/mailbox-list.c +++ b/src/lib-storage/mailbox-list.c @@ -663,18 +663,21 @@ int mailbox_list_iter_deinit(struct mailbox_list_iterate_context **_ctx) int mailbox_list_mailbox(struct mailbox_list *list, const char *name, enum mailbox_info_flags *flags_r) { - struct mailbox_list_iterate_context ctx; - const char *path; - - memset(&ctx, 0, sizeof(ctx)); - ctx.list = list; + const char *path, *fname; + struct stat st; - *flags_r = 0; path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR); - return list->v.iter_is_mailbox == NULL ? 0 : - list->v.iter_is_mailbox(&ctx, path, "", "", - MAILBOX_LIST_FILE_TYPE_UNKNOWN, - flags_r); + fname = strrchr(path, '/'); + if (fname == NULL) { + fname = path; + path = "/"; + } else { + path = t_strdup_until(path, fname); + fname++; + } + return list->v.get_mailbox_flags(list, path, fname, + MAILBOX_LIST_FILE_TYPE_UNKNOWN, + &st, flags_r); } static bool mailbox_list_init_changelog(struct mailbox_list *list) diff --git a/src/lib-storage/mailbox-list.h b/src/lib-storage/mailbox-list.h index ba98442ef8..41f3197fb8 100644 --- a/src/lib-storage/mailbox-list.h +++ b/src/lib-storage/mailbox-list.h @@ -41,6 +41,7 @@ enum mailbox_info_flags { MAILBOX_CHILD_SUBSCRIBED = 0x100, /* Internally used by lib-storage */ + MAILBOX_SELECT = 0x20000000, MAILBOX_MATCHED = 0x40000000 }; diff --git a/src/plugins/virtual/virtual-storage.c b/src/plugins/virtual/virtual-storage.c index 00066cde4e..60a92b5c49 100644 --- a/src/plugins/virtual/virtual-storage.c +++ b/src/plugins/virtual/virtual-storage.c @@ -304,63 +304,44 @@ static void virtual_notify_changes(struct mailbox *box ATTR_UNUSED) } static int -virtual_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx - ATTR_UNUSED, - const char *dir, const char *fname, - const char *mailbox_name ATTR_UNUSED, - enum mailbox_list_file_type type, - enum mailbox_info_flags *flags) +virtual_list_get_mailbox_flags(struct mailbox_list *list, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + struct stat *st_r, + enum mailbox_info_flags *flags) { - const char *path, *maildir_path; - struct stat st; - int ret = 1; - - /* try to avoid stat() with these checks */ - if (type != MAILBOX_LIST_FILE_TYPE_DIR && - type != MAILBOX_LIST_FILE_TYPE_SYMLINK && - type != MAILBOX_LIST_FILE_TYPE_UNKNOWN) { - /* it's a file */ - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - return 0; - } + struct virtual_mailbox_list *mlist = VIRTUAL_LIST_CONTEXT(list); + struct stat st2; + const char *virtual_path; + int ret; - /* need to stat() then */ - path = t_strconcat(dir, "/", fname, NULL); - if (stat(path, &st) == 0) { - if (!S_ISDIR(st.st_mode)) { - /* non-directory */ - *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; - ret = 0; - } else if (st.st_nlink == 2) { - /* no subdirectories */ - *flags |= MAILBOX_NOCHILDREN; - } else if (*ctx->list->set.maildir_name != '\0') { - /* non-default configuration: we have one directory - containing the mailboxes. if there are 3 links, - either this is a selectable mailbox without children - or non-selectable mailbox with children */ - if (st.st_nlink > 3) - *flags |= MAILBOX_CHILDREN; - } else { - /* default configuration: all subdirectories are - child mailboxes. */ - if (st.st_nlink > 2) - *flags |= MAILBOX_CHILDREN; - } - } else { - /* non-selectable. probably either access denied, or symlink - destination not found. don't bother logging errors. */ + ret = mlist->module_ctx.super. + get_mailbox_flags(list, dir, fname, type, st_r, flags); + if (ret <= 0 || MAILBOX_INFO_FLAGS_FINISHED(*flags)) + return ret; + + /* see if it's a selectable mailbox */ + virtual_path = t_strconcat(dir, "/", fname, "/"VIRTUAL_CONFIG_FNAME, + NULL); + if (stat(virtual_path, &st2) < 0) *flags |= MAILBOX_NOSELECT; - } - if ((*flags & MAILBOX_NOSELECT) == 0) { - /* make sure it's a selectable mailbox */ - maildir_path = t_strconcat(path, "/"VIRTUAL_CONFIG_FNAME, NULL); - if (stat(maildir_path, &st) < 0) - *flags |= MAILBOX_NOSELECT; - } return ret; } +static void virtual_storage_add_list(struct mail_storage *storage ATTR_UNUSED, + struct mailbox_list *list) +{ + struct virtual_mailbox_list *mlist; + + mlist = p_new(list->pool, struct virtual_mailbox_list, 1); + mlist->module_ctx.super = list->v; + + list->ns->flags |= NAMESPACE_FLAG_NOQUOTA; + list->v.get_mailbox_flags = virtual_list_get_mailbox_flags; + + MODULE_CONTEXT_SET(list, virtual_mailbox_list_module, mlist); +} + static int virtual_backend_uidmap_cmp(const uint32_t *uid, const struct virtual_backend_uidmap *map) { @@ -440,20 +421,6 @@ static bool virtual_is_inconsistent(struct mailbox *box) return index_storage_is_inconsistent(box); } -static void virtual_storage_add_list(struct mail_storage *storage ATTR_UNUSED, - struct mailbox_list *list) -{ - struct virtual_mailbox_list *mlist; - - mlist = p_new(list->pool, struct virtual_mailbox_list, 1); - mlist->module_ctx.super = list->v; - - list->ns->flags |= NAMESPACE_FLAG_NOQUOTA; - list->v.iter_is_mailbox = virtual_list_iter_is_mailbox; - - MODULE_CONTEXT_SET(list, virtual_mailbox_list_module, mlist); -} - struct mail_storage virtual_storage = { .name = VIRTUAL_STORAGE_NAME, .class_flags = 0,