# HG changeset patch # User Timo Sirainen # Date 1175155151 -10800 # Node ID beabd433cdaefb766ae064126bf778c8b90b5688 # Parent 04404fd54cf6757d6eb57f912fd5a041d45ee00d Moved delete/rename operations to mailbox_list API. Fixed mbox/maildir to work with either fs/maildir++ directory layout. They can be changed by appending :LAYOUT=fs|maildir++ to mail_location. diff -r 04404fd54cf6 -r beabd433cdae src/imap/cmd-delete.c --- a/src/imap/cmd-delete.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/imap/cmd-delete.c Thu Mar 29 10:59:11 2007 +0300 @@ -7,6 +7,7 @@ { struct client *client = cmd->client; struct mail_storage *storage; + struct mailbox_list *list; struct mailbox *mailbox; const char *name; @@ -41,9 +42,12 @@ name = t_strndup(name, strlen(name)-1); } - if (mail_storage_mailbox_delete(storage, name) < 0) + list = mail_storage_get_list(storage); + if (mailbox_list_delete_mailbox(list, name) < 0) { + mail_storage_set_list_error(storage); client_send_storage_error(cmd, storage); - else + } else { client_send_tagline(cmd, "OK Delete completed."); + } return TRUE; } diff -r 04404fd54cf6 -r beabd433cdae src/imap/cmd-rename.c --- a/src/imap/cmd-rename.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/imap/cmd-rename.c Thu Mar 29 10:59:11 2007 +0300 @@ -6,6 +6,7 @@ bool cmd_rename(struct client_command_context *cmd) { struct mail_storage *old_storage, *new_storage; + struct mailbox_list *list; const char *oldname, *newname; /* */ @@ -29,10 +30,12 @@ return TRUE; } - if (mail_storage_mailbox_rename(old_storage, oldname, newname) < 0) + list = mail_storage_get_list(old_storage); + if (mailbox_list_rename_mailbox(list, oldname, newname) < 0) { + mail_storage_set_list_error(old_storage); client_send_storage_error(cmd, old_storage); - else + } else { client_send_tagline(cmd, "OK Rename completed."); - + } return TRUE; } diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/dbox/dbox-file.c --- a/src/lib-storage/index/dbox/dbox-file.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/dbox/dbox-file.c Thu Mar 29 10:59:11 2007 +0300 @@ -117,8 +117,7 @@ mbox->file->fd = -1; mbox->file->path = - i_strdup_printf("%s/"DBOX_MAILDIR_NAME"/" - DBOX_MAIL_FILE_FORMAT, + i_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT, mbox->path, file_seq); } diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/dbox/dbox-storage.c --- a/src/lib-storage/index/dbox/dbox-storage.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/dbox/dbox-storage.c Thu Mar 29 10:59:11 2007 +0300 @@ -1,6 +1,7 @@ /* Copyright (C) 2005-2007 Timo Sirainen */ #include "lib.h" +#include "array.h" #include "ioloop.h" #include "mkdir-parents.h" #include "unlink-directory.h" @@ -21,6 +22,10 @@ /* How often to touch the uidlist lock file when using KEEP_LOCKED flag */ #define DBOX_LOCK_TOUCH_MSECS (10*1000) +#define DBOX_LIST_CONTEXT(obj) \ + *((void **)array_idx_modifiable(&(obj)->module_contexts, \ + dbox_mailbox_list_module_id)) + const struct dotlock_settings default_uidlist_dotlock_set = { MEMBER(temp_prefix) NULL, MEMBER(lock_suffix) NULL, @@ -63,17 +68,51 @@ extern struct mail_storage dbox_storage; extern struct mailbox dbox_mailbox; -static bool dbox_handle_errors(struct mail_storage *storage) +static unsigned int dbox_mailbox_list_module_id = 0; + +static int +dbox_list_delete_mailbox(struct mailbox_list *list, const char *name); +static int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags); + +static bool +dbox_storage_is_valid_existing_name(struct mailbox_list *list, const char *name) { - if (ENOACCESS(errno)) - mail_storage_set_error(storage, MAIL_STORAGE_ERR_NO_PERMISSION); - else if (ENOSPACE(errno)) - mail_storage_set_error(storage, "Not enough disk space"); - else if (ENOTFOUND(errno)) - mail_storage_set_error(storage, "Directory structure is broken"); - else + struct dbox_storage *storage = DBOX_LIST_CONTEXT(list); + const char *p; + + if (!storage->list_super.is_valid_existing_name(list, name)) return FALSE; - return TRUE; + + /* Don't allow the mailbox name to end in dbox-Mails */ + p = strrchr(name, '/'); + if (p != NULL) + name = p + 1; + return strcmp(name, DBOX_MAILDIR_NAME) != 0; +} + +static bool +dbox_storage_is_valid_create_name(struct mailbox_list *list, const char *name) +{ + struct dbox_storage *storage = DBOX_LIST_CONTEXT(list); + const char *const *tmp; + bool ret = TRUE; + + if (!storage->list_super.is_valid_create_name(list, name)) + return FALSE; + + /* Don't allow creating mailboxes under dbox-Mails */ + t_push(); + for (tmp = t_strsplit(name, "/"); *tmp != NULL; tmp++) { + if (strcmp(*tmp, DBOX_MAILDIR_NAME) == 0) { + ret = FALSE; + break; + } + } + t_pop(); + return ret; } static int @@ -86,7 +125,7 @@ memset(list_set, 0, sizeof(*list_set)); list_set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME; - list_set->maildir_name = ""; + list_set->maildir_name = DBOX_MAILDIR_NAME; if (data == NULL || *data == '\0') { /* we won't do any guessing for this format. */ @@ -162,12 +201,19 @@ if (mailbox_list_init("fs", &list_set, mail_storage_get_list_flags(flags), - mailbox_storage_list_is_mailbox, storage, &list, &error) < 0) { i_error("dbox fs: %s", error); pool_unref(pool); return NULL; } + storage->list_super = list->v; + list->v.is_valid_existing_name = dbox_storage_is_valid_existing_name; + list->v.is_valid_create_name = dbox_storage_is_valid_create_name; + list->v.iter_is_mailbox = dbox_list_iter_is_mailbox; + list->v.delete_mailbox = dbox_list_delete_mailbox; + + array_idx_set(&list->module_contexts, + dbox_mailbox_list_module_id, &storage); storage->uidlist_dotlock_set = default_uidlist_dotlock_set; storage->file_dotlock_set = default_file_dotlock_set; @@ -204,7 +250,7 @@ data = t_strcut(data, ':'); - path = t_strconcat(data, "/inbox/"DBOX_MAILDIR_NAME, NULL); + path = t_strconcat(data, "/INBOX/"DBOX_MAILDIR_NAME, NULL); if (stat(path, &st) < 0) { if (debug) i_info("dbox autodetect: stat(%s) failed: %m", path); @@ -221,9 +267,13 @@ static int create_dbox(struct mail_storage *storage, const char *path) { + const char *error; + if (mkdir_parents(path, CREATE_MODE) < 0 && errno != EEXIST) { - if (dbox_handle_errors(storage)) + if (mail_storage_errno2str(&error)) { + mail_storage_set_error(storage, "%s", error); return -1; + } mail_storage_set_critical(storage, "mkdir(%s) failed: %m", path); @@ -367,7 +417,7 @@ return dbox_open(storage, name, flags); } else if (errno == ENOENT) { mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name); return NULL; } else { mail_storage_set_critical(_storage, "stat(%s) failed: %m", @@ -400,34 +450,30 @@ return create_dbox(_storage, path); } -static int dbox_mailbox_delete(struct mail_storage *_storage, - const char *name) +static int +dbox_list_delete_mailbox(struct mailbox_list *list, const char *name) { - const char *path, *mail_path; + struct dbox_storage *storage = DBOX_LIST_CONTEXT(list); struct stat st; - - mail_storage_clear_error(_storage); + const char *path, *mail_path, *error; - if (strcmp(name, "INBOX") == 0) { - mail_storage_set_error(_storage, "INBOX can't be deleted."); + /* make sure the indexes are closed before trying to delete the + directory that contains them */ + index_storage_destroy_unrefed(); + + /* delete the index and control directories */ + if (storage->list_super.delete_mailbox(list, name) < 0) return -1; - } - if (!mailbox_list_is_valid_existing_name(_storage->list, name)) { - mail_storage_set_error(_storage, "Invalid mailbox name"); - return -1; - } - - path = mailbox_list_get_path(_storage->list, name, - MAILBOX_LIST_PATH_TYPE_DIR); - mail_path = mailbox_list_get_path(_storage->list, name, + path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR); + mail_path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (stat(mail_path, &st) < 0 && ENOTFOUND(errno)) { if (stat(path, &st) < 0) { /* doesn't exist at all */ - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); return -1; } @@ -436,23 +482,23 @@ return 0; if (errno == ENOTEMPTY) { - mail_storage_set_error(_storage, - "Mailbox has only submailboxes: %s", name); + mailbox_list_set_error(list, t_strdup_printf( + "Directory %s isn't empty, can't delete it.", + name)); } else { - mail_storage_set_critical(_storage, + mailbox_list_set_critical(list, "rmdir() failed for %s: %m", path); } return -1; } - /* make sure the indexes are closed before trying to delete the - directory that contains them */ - index_storage_destroy_unrefed(); if (unlink_directory(mail_path, TRUE) < 0) { - if (!dbox_handle_errors(_storage)) { - mail_storage_set_critical(_storage, + if (mail_storage_errno2str(&error)) + mailbox_list_set_error(list, error); + else { + mailbox_list_set_critical(list, "unlink_directory() failed for %s: %m", mail_path); } @@ -467,79 +513,12 @@ break; name = t_strdup_until(name, p); - path = mailbox_list_get_path(_storage->list, name, + path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR); } return 0; } -static int dbox_mailbox_rename(struct mail_storage *_storage, - const char *oldname, const char *newname) -{ - const char *oldpath, *newpath, *p; - struct stat st; - - mail_storage_clear_error(_storage); - - if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) || - !mailbox_list_is_valid_create_name(_storage->list, newname)) { - mail_storage_set_error(_storage, "Invalid mailbox name"); - return -1; - } - - oldpath = mailbox_list_get_path(_storage->list, oldname, - MAILBOX_LIST_PATH_TYPE_DIR); - newpath = mailbox_list_get_path(_storage->list, newname, - MAILBOX_LIST_PATH_TYPE_DIR); - - /* create the hierarchy */ - p = strrchr(newpath, '/'); - if (p != NULL) { - p = t_strdup_until(newpath, p); - if (mkdir_parents(p, CREATE_MODE) < 0) { - if (dbox_handle_errors(_storage)) - return -1; - - mail_storage_set_critical(_storage, - "mkdir_parents(%s) failed: %m", p); - return -1; - } - } - - /* first check that the destination mailbox doesn't exist. - this is racy, but we need to be atomic and there's hardly any - possibility that someone actually tries to rename two mailboxes - to same new one */ - if (lstat(newpath, &st) == 0) { - mail_storage_set_error(_storage, - "Target mailbox already exists"); - return -1; - } else if (errno == ENOTDIR) { - mail_storage_set_error(_storage, - "Target mailbox doesn't allow inferior mailboxes"); - return -1; - } else if (errno != ENOENT && errno != EACCES) { - mail_storage_set_critical(_storage, "lstat(%s) failed: %m", - newpath); - return -1; - } - - /* NOTE: renaming INBOX works just fine with us, it's simply recreated - the next time it's needed. */ - if (rename(oldpath, newpath) < 0) { - if (ENOTFOUND(errno)) { - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname); - } else if (!dbox_handle_errors(_storage)) { - mail_storage_set_critical(_storage, - "rename(%s, %s) failed: %m", oldpath, newpath); - } - return -1; - } - - return 0; -} - static int dbox_storage_close(struct mailbox *box) { struct dbox_mailbox *mbox = (struct dbox_mailbox *)box; @@ -571,18 +550,15 @@ return; } - index_mailbox_check_add(&mbox->ibox, - t_strconcat(mbox->path, "/"DBOX_MAILDIR_NAME, NULL)); + index_mailbox_check_add(&mbox->ibox, mbox->path); } -static int dbox_is_mailbox(struct mail_storage *storage, - const char *dir, const char *fname, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags, - enum mailbox_list_file_type type) +static int dbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags) { - const char *path, *mail_path; - size_t len; + const char *mail_path; struct stat st; int ret = 1; @@ -591,18 +567,11 @@ 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 && type != MAILBOX_LIST_FILE_TYPE_SYMLINK && type != MAILBOX_LIST_FILE_TYPE_UNKNOWN && - (iter_flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) { + (ctx->flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) { /* it's a file */ *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; return 0; @@ -610,8 +579,7 @@ /* need to stat() then */ t_push(); - path = t_strconcat(dir, "/", fname, NULL); - mail_path = t_strconcat(path, "/"DBOX_MAILDIR_NAME, NULL); + mail_path = t_strconcat(dir, "/", fname, "/"DBOX_MAILDIR_NAME, NULL); if (stat(mail_path, &st) == 0) { if (!S_ISDIR(st.st_mode)) { @@ -621,17 +589,9 @@ } } else { /* non-selectable, but may contain subdirs */ + if (errno != ENOTDIR) + *flags |= MAILBOX_CHILDREN; *flags |= MAILBOX_NOSELECT; - if (stat(path, &st) < 0) { - if (ENOTFOUND(errno)) { - /* just lost it */ - ret = 0; - } else if (errno != EACCES && errno != ELOOP) { - mail_storage_set_critical(storage, - "stat(%s) failed: %m", path); - ret = -1; - } - } } t_pop(); @@ -640,6 +600,7 @@ static void dbox_class_init(void) { + dbox_mailbox_list_module_id = mailbox_list_module_id++; dbox_transaction_class_init(); } @@ -661,9 +622,6 @@ index_storage_set_callbacks, dbox_mailbox_open, dbox_mailbox_create, - dbox_mailbox_delete, - dbox_mailbox_rename, - dbox_is_mailbox, index_storage_get_last_error } }; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/dbox/dbox-storage.h --- a/src/lib-storage/index/dbox/dbox-storage.h Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/dbox/dbox-storage.h Thu Mar 29 10:59:11 2007 +0300 @@ -2,6 +2,7 @@ #define __DBOX_STORAGE_H #include "index-storage.h" +#include "mailbox-list-private.h" #include "dbox-format.h" #define DBOX_STORAGE_NAME "dbox" @@ -14,6 +15,7 @@ struct dbox_storage { struct index_storage storage; + struct mailbox_list_vfuncs list_super; struct dotlock_settings uidlist_dotlock_set; struct dotlock_settings file_dotlock_set; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/dbox/dbox-sync-expunge.c --- a/src/lib-storage/index/dbox/dbox-sync-expunge.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/dbox/dbox-sync-expunge.c Thu Mar 29 10:59:11 2007 +0300 @@ -116,8 +116,7 @@ file_seq = dbox_uidlist_get_new_file_seq(mbox->uidlist); for (;; file_seq++) { - path = t_strdup_printf("%s/"DBOX_MAILDIR_NAME"/" - DBOX_MAIL_FILE_FORMAT, + path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT, mbox->path, file_seq); fd = file_dotlock_open(&mbox->storage->new_file_dotlock_set, path, DOTLOCK_CREATE_FLAG_NONBLOCK, @@ -352,8 +351,7 @@ if (!skipped_expunges) { /* all mails expunged from file, unlink it. */ - path = t_strdup_printf("%s/"DBOX_MAILDIR_NAME"/" - DBOX_MAIL_FILE_FORMAT, + path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT, mbox->path, entry->file_seq); if (unlink(path) < 0) { mail_storage_set_critical(STORAGE(mbox->storage), @@ -469,8 +467,7 @@ if (ctx->dotlock_failed_file_seq != sync_entry->file_seq) { /* we need to have the file locked in case another process is appending there already. */ - path = t_strdup_printf("%s/"DBOX_MAILDIR_NAME"/" - DBOX_MAIL_FILE_FORMAT, + path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT, mbox->path, sync_entry->file_seq); ret = file_dotlock_create(&mbox->storage->new_file_dotlock_set, path, DOTLOCK_CREATE_FLAG_NONBLOCK, diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/dbox/dbox-sync-full.c --- a/src/lib-storage/index/dbox/dbox-sync-full.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/dbox/dbox-sync-full.c Thu Mar 29 10:59:11 2007 +0300 @@ -193,7 +193,6 @@ struct dbox_mailbox *mbox = ctx->mbox; const struct mail_index_header *hdr; unsigned int file_prefix_len = strlen(DBOX_MAIL_FILE_PREFIX); - const char *path; uint32_t file_seq; DIR *dirp; struct dirent *dp; @@ -201,11 +200,10 @@ /* go through msg.* files, sync them to index and based on it write dbox's index file */ - path = t_strconcat(mbox->path, "/"DBOX_MAILDIR_NAME, NULL); - dirp = opendir(path); + dirp = opendir(mbox->path); if (dirp == NULL) { mail_storage_set_critical(STORAGE(mbox->storage), - "opendir(%s) failed: %m", path); + "opendir(%s) failed: %m", mbox->path); return -1; } @@ -231,7 +229,7 @@ } if (closedir(dirp) < 0) { mail_storage_set_critical(STORAGE(mbox->storage), - "closedir(%s) failed: %m", path); + "closedir(%s) failed: %m", mbox->path); ret = -1; } diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/dbox/dbox-uidlist.c --- a/src/lib-storage/index/dbox/dbox-uidlist.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/dbox/dbox-uidlist.c Thu Mar 29 10:59:11 2007 +0300 @@ -93,9 +93,7 @@ uidlist->lock_fd = -1; uidlist->entry_pool = pool_alloconly_create("uidlist entry pool", 1024*32); - uidlist->path = - i_strconcat(mbox->path, "/"DBOX_MAILDIR_NAME"/" - DBOX_UIDLIST_FILENAME, NULL); + uidlist->path = i_strconcat(mbox->path, "/"DBOX_UIDLIST_FILENAME, NULL); i_array_init(&uidlist->entries, 64); return uidlist; } @@ -1054,8 +1052,8 @@ /* try locking the file. */ str_truncate(path, 0); - str_printfa(path, "%s/"DBOX_MAILDIR_NAME"/" - DBOX_MAIL_FILE_FORMAT, mbox->path, file_seq); + str_printfa(path, "%s/"DBOX_MAIL_FILE_FORMAT, + mbox->path, file_seq); ret = file_dotlock_create(&mbox->storage->file_dotlock_set, str_c(path), DOTLOCK_CREATE_FLAG_NONBLOCK, diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/maildir/maildir-storage.c --- a/src/lib-storage/index/maildir/maildir-storage.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/maildir/maildir-storage.c Thu Mar 29 10:59:11 2007 +0300 @@ -4,6 +4,7 @@ #include "ioloop.h" #include "array.h" #include "hostpid.h" +#include "str.h" #include "home-expand.h" #include "mkdir-parents.h" #include "unlink-directory.h" @@ -15,6 +16,7 @@ #include #include +#include #include #include @@ -23,6 +25,10 @@ #define MAILDIR_PLUSPLUS_DRIVER_NAME "maildir++" #define MAILDIR_SUBFOLDER_FILENAME "maildirfolder" +#define MAILDIR_LIST_CONTEXT(obj) \ + *((void **)array_idx_modifiable(&(obj)->module_contexts, \ + maildir_mailbox_list_module_id)) + struct rename_context { bool found; size_t oldnamelen; @@ -32,8 +38,26 @@ extern struct mail_storage maildir_storage; extern struct mailbox maildir_mailbox; +static unsigned int maildir_mailbox_list_module_id = 0; + static int verify_inbox(struct mail_storage *storage, enum mailbox_open_flags *flags); +static int +maildir_list_delete_mailbox(struct mailbox_list *list, const char *name); +static int +maildir_list_rename_mailbox(struct mailbox_list *list, + const char *oldname, const char *newname); +static int +maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx + __attr_unused__, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags_r); +static int +maildirplusplus_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags_r); static const char *strip_tail_slash(const char *path) { @@ -52,11 +76,14 @@ static int maildir_get_list_settings(struct mailbox_list_settings *list_set, - const char *data, enum mail_storage_flags flags) + const char *data, enum mail_storage_flags flags, + const char **layout_r) { bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0; const char *home, *path, *p; + *layout_r = MAILDIR_PLUSPLUS_DRIVER_NAME; + memset(list_set, 0, sizeof(*list_set)); list_set->subscription_fname = MAILDIR_SUBSCRIPTION_FILE_NAME; list_set->maildir_name = ""; @@ -114,6 +141,9 @@ } else if (strncmp(p, "CONTROL=", 8) == 0) { list_set->control_dir = strip_tail_slash_and_cut(p+8); + } else if (strncmp(p, "LAYOUT=", 7) == 0) { + *layout_r = + strip_tail_slash_and_cut(p+7); } p = strchr(p, ':'); } while (p != NULL); @@ -126,6 +156,8 @@ return -1; } list_set->root_dir = strip_tail_slash(list_set->root_dir); + if (list_set->inbox_path == NULL) + list_set->inbox_path = list_set->root_dir; if (list_set->index_dir != NULL && strcmp(list_set->index_dir, "MEMORY") == 0) @@ -133,6 +165,51 @@ return 0; } +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_storage *storage = MAILDIR_LIST_CONTEXT(list); + const char *p; + + if (!storage->list_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_storage *storage = MAILDIR_LIST_CONTEXT(list); + const char *const *tmp; + bool ret = TRUE; + + if (!storage->list_super.is_valid_create_name(list, name)) + return FALSE; + + /* Don't allow creating mailboxes under cur/new/tmp */ + t_push(); + for (tmp = t_strsplit(name, "/"); *tmp != NULL; tmp++) { + if (maildir_is_internal_name(*tmp)) { + ret = FALSE; + break; + } + } + t_pop(); + return ret; +} + static struct mail_storage * maildir_create(const char *data, const char *user, enum mail_storage_flags flags, @@ -143,11 +220,11 @@ struct mailbox_list_settings list_set; struct mailbox_list *list; enum mailbox_open_flags open_flags; - const char *error; + const char *layout, *error; struct stat st; pool_t pool; - if (maildir_get_list_settings(&list_set, data, flags) < 0) + if (maildir_get_list_settings(&list_set, data, flags, &layout) < 0) return NULL; list_set.mail_storage_flags = &flags; list_set.lock_method = &lock_method; @@ -166,14 +243,28 @@ pool = pool_alloconly_create("storage", 512+256); storage = p_new(pool, struct maildir_storage, 1); - if (mailbox_list_init("maildir++", &list_set, + if (mailbox_list_init(layout, &list_set, mail_storage_get_list_flags(flags), - mailbox_storage_list_is_mailbox, storage, &list, &error) < 0) { - i_error("maildir++: %s", error); + i_error("maildir %s: %s", layout, error); pool_unref(pool); return NULL; } + storage->list_super = list->v; + if (strcmp(layout, MAILDIR_PLUSPLUS_DRIVER_NAME) == 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; + } + list->v.delete_mailbox = maildir_list_delete_mailbox; + list->v.rename_mailbox = maildir_list_rename_mailbox; + + array_idx_set(&list->module_contexts, + maildir_mailbox_list_module_id, &storage); storage->copy_with_hardlinks = getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL; @@ -231,27 +322,6 @@ return TRUE; } -static const char * -maildir_get_unlink_dest(struct mail_storage *storage, const char *name) -{ - const char *root_dir; - - if ((storage->flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0 && - (*name == '/' || *name == '~')) - return NULL; - - if (strcmp(mailbox_list_get_driver_name(storage->list), - MAILDIR_PLUSPLUS_DRIVER_NAME) != 0) { - /* Not maildir++ driver. Don't use this trick. */ - return NULL; - } - - root_dir = mailbox_list_get_path(storage->list, NULL, - MAILBOX_LIST_PATH_TYPE_DIR); - return t_strdup_printf("%s/%c"MAILDIR_UNLINK_DIRNAME, root_dir, - mailbox_list_get_hierarchy_sep(storage->list)); -} - static int mkdir_verify(struct mail_storage *storage, const char *dir, bool verify) { @@ -510,7 +580,7 @@ return maildir_open(storage, name, flags); } else if (errno == ENOENT) { mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name); return NULL; } else { mail_storage_set_critical(_storage, "stat(%s) failed: %m", @@ -608,33 +678,117 @@ return 0; } -static int maildir_mailbox_delete(struct mail_storage *_storage, - const char *name) +static const char * +maildir_get_unlink_dest(struct mailbox_list *list, const char *name) { - struct stat st; - const char *src, *dest, *index_dir, *control_dir; - int count; + const char *root_dir; + + if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 && + (*name == '/' || *name == '~')) + return NULL; + + if (strcmp(mailbox_list_get_driver_name(list), + MAILDIR_PLUSPLUS_DRIVER_NAME) != 0) { + /* Not maildir++ driver. Don't use this trick. */ + return NULL; + } + + root_dir = mailbox_list_get_path(list, NULL, + MAILBOX_LIST_PATH_TYPE_DIR); + return t_strdup_printf("%s/%c"MAILDIR_UNLINK_DIRNAME, root_dir, + mailbox_list_get_hierarchy_sep(list)); +} - mail_storage_clear_error(_storage); +static int +maildir_delete_nonrecursive(struct mailbox_list *list, const char *path, + const char *name) +{ + DIR *dir; + struct dirent *d; + string_t *full_path; + unsigned int dir_len; + bool unlinked_something = FALSE; - if (strcmp(name, "INBOX") == 0) { - mail_storage_set_error(_storage, "INBOX can't be deleted."); + dir = opendir(path); + if (dir == NULL) { + if (errno == ENOENT) { + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); + } else { + mailbox_list_set_critical(list, + "opendir(%s) failed: %m", path); + } return -1; } - if (!mailbox_list_is_valid_existing_name(_storage->list, name)) { - mail_storage_set_error(_storage, "Invalid mailbox name"); + full_path = t_str_new(256); + str_append(full_path, path); + str_append_c(full_path, '/'); + dir_len = str_len(full_path); + + errno = 0; + while ((d = readdir(dir)) != NULL) { + if (d->d_name[0] == '.') { + /* skip . and .. */ + if (d->d_name[1] == '\0') + continue; + if (d->d_name[1] == '.' && d->d_name[2] == '\0') + continue; + } + + str_truncate(full_path, dir_len); + str_append(full_path, d->d_name); + + if (maildir_is_internal_name(d->d_name)) { + if (unlink_directory(str_c(full_path), TRUE) < 0) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", + str_c(full_path)); + } else { + unlinked_something = TRUE; + } + continue; + } + + /* trying to unlink() a directory gives either EPERM or EISDIR + (non-POSIX). it doesn't really work anywhere in practise, + so don't bother stat()ing the file first */ + if (unlink(str_c(full_path)) == 0) + unlinked_something = TRUE; + else if (errno != ENOENT && errno != EISDIR && errno != EPERM) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", + str_c(full_path)); + } + } + + if (closedir(dir) < 0) { + mailbox_list_set_critical(list, "closedir(%s) failed: %m", + path); + } + + if (rmdir(path) == 0) + unlinked_something = TRUE; + else if (errno != ENOENT && errno != ENOTEMPTY) { + mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); return -1; } - /* check if the mailbox actually exists */ - src = mailbox_list_get_path(_storage->list, name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (stat(src, &st) != 0 && errno == ENOENT) { - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); + if (!unlinked_something) { + mailbox_list_set_error(list, t_strdup_printf( + "Directory %s isn't empty, can't delete it.", name)); return -1; } + return 0; +} + +static int +maildir_list_delete_mailbox(struct mailbox_list *list, const char *name) +{ + struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(list); + struct stat st; + const char *src, *dest; + int count; /* Make sure the indexes are closed before trying to delete the directory that contains them. It can still fail with some NFS @@ -642,241 +796,84 @@ that can't really be helped. */ index_storage_destroy_unrefed(); - /* if there's a separate index directory, delete it before the actual - maildir. this way we never see partially deleted mailboxes. */ - index_dir = mailbox_list_get_path(_storage->list, name, - MAILBOX_LIST_PATH_TYPE_INDEX); - if (strcmp(index_dir, src) != 0) { - i_assert(*name != '/' && *name != '~'); + /* delete the index and control directories */ + if (storage->list_super.delete_mailbox(list, name) < 0) + return -1; + + /* check if the mailbox actually exists */ + src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (stat(src, &st) != 0 && errno == ENOENT) { + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); + return -1; + } - if (unlink_directory(index_dir, TRUE) < 0 && - errno != ENOTEMPTY) { - mail_storage_set_critical(_storage, - "unlink_directory(%s) failed: %m", index_dir); + dest = maildir_get_unlink_dest(list, name); + if (dest == NULL) { + /* delete the directory directly without any renaming */ + return maildir_delete_nonrecursive(list, src, name); + } + + /* rename the .maildir into ..DOVECOT-TRASH which atomically + marks it as being deleted. If we die before deleting the + ..DOVECOT-TRASH directory, it gets deleted the next time + mailbox listing sees it. */ + count = 0; + while (rename(src, dest) < 0 && count < 2) { + if (errno == ENOENT) { + /* it was just deleted under us by + another process */ + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); + return -1; + } + if (!EDESTDIREXISTS(errno)) { + mailbox_list_set_critical(list, + "rename(%s, %s) failed: %m", src, dest); return -1; } + + /* already existed, delete it and try again */ + if (unlink_directory(dest, TRUE) < 0) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", dest); + return -1; + } + count++; } - control_dir = mailbox_list_get_path(_storage->list, name, - MAILBOX_LIST_PATH_TYPE_CONTROL); - if (strcmp(control_dir, src) != 0 && - strcmp(control_dir, index_dir) != 0) { - i_assert(*name != '/' && *name != '~'); + + if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", dest); + + /* it's already renamed to ..dir, which means it's + deleted as far as the client is concerned. Report + success. */ + } + return 0; +} - if (unlink_directory(control_dir, TRUE) < 0 && - errno != ENOTEMPTY) { - mail_storage_set_critical(_storage, - "unlink_directory(%s) failed: %m", control_dir); +static int maildir_list_rename_mailbox(struct mailbox_list *list, + const char *oldname, const char *newname) +{ + struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(list); + const char *path1, *path2; + + if (strcmp(oldname, "INBOX") == 0) { + /* INBOX often exists as the root ~/Maildir. + We can't rename it then. */ + path1 = mailbox_list_get_path(list, oldname, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + path2 = mailbox_list_get_path(list, NULL, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (strcmp(path1, path2) == 0) { + mailbox_list_set_error(list, + "Renaming INBOX isn't supported."); return -1; } } - dest = maildir_get_unlink_dest(_storage, name); - if (dest == NULL) { - /* absolute maildir path, delete the directory directly - without any renaming */ - dest = src; - } else { - /* rename the .maildir into ..DOVECOT-TRASH which atomically - marks it as being deleted. If we die before deleting the - ..DOVECOT-TRASH directory, it gets deleted the next time - mailbox listing sees it. */ - count = 0; - while (rename(src, dest) < 0 && count < 2) { - if (errno == ENOENT) { - /* it was just deleted under us by - another process */ - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, - name); - return -1; - } - if (!EDESTDIREXISTS(errno)) { - mail_storage_set_critical(_storage, - "rename(%s, %s) failed: %m", src, dest); - return -1; - } - - /* already existed, delete it and try again */ - if (unlink_directory(dest, TRUE) < 0) { - mail_storage_set_critical(_storage, - "unlink_directory(%s) failed: %m", - dest); - return -1; - } - count++; - } - } - - if (unlink_directory(dest, TRUE) < 0 && errno != ENOTEMPTY) { - mail_storage_set_critical(_storage, - "unlink_directory(%s) failed: %m", dest); - - /* it's already renamed to ..dir, which means it's deleted - as far as the client is concerned. Report success. */ - } - - return 0; -} - -static int rename_dir(struct mail_storage *storage, - enum mailbox_list_path_type type, - const char *oldname, const char *newname) -{ - const char *oldpath, *newpath; - - oldpath = mailbox_list_get_path(storage->list, oldname, type); - newpath = mailbox_list_get_path(storage->list, newname, type); - if (strcmp(oldpath, newpath) == 0) - return 0; - - if (rename(oldpath, newpath) < 0 && errno != ENOENT) { - mail_storage_set_critical(storage, "rename(%s, %s) failed: %m", - oldpath, newpath); - return -1; - } - - return 0; -} - -static int rename_subfolders(struct mail_storage *storage, - const char *oldname, const char *newname) -{ - struct mailbox_list_iterate_context *iter; - struct mailbox_info *info; - ARRAY_DEFINE(names_arr, const char *); - const char *mask, *oldpath, *newpath, *old_listname, *new_listname; - const char *const *names; - unsigned int i, count; - size_t oldnamelen; - pool_t pool; - int ret; - - ret = 0; - oldnamelen = strlen(oldname); - - /* first get a list of the subfolders and save them to memory, because - we can't rely on readdir() not skipping files while the directory - is being modified. this doesn't protect against modifications by - other processes though. */ - pool = pool_alloconly_create("Maildir subfolders list", 1024); - i_array_init(&names_arr, 64); - - mask = t_strdup_printf("%s%c*", oldname, - mailbox_list_get_hierarchy_sep(storage->list)); - iter = mailbox_list_iter_init(storage->list, mask, - MAILBOX_LIST_ITER_FAST_FLAGS); - while ((info = mailbox_list_iter_next(iter)) != NULL) { - const char *name; - - i_assert(oldnamelen <= strlen(info->name)); - - name = p_strdup(pool, info->name + oldnamelen); - array_append(&names_arr, &name, 1); - } - if (mailbox_list_iter_deinit(&iter) < 0) { - ret = -1; - names = NULL; count = 0; - } else { - names = array_get(&names_arr, &count); - } - - for (i = 0; i < count; i++) { - t_push(); - - old_listname = t_strconcat(oldname, names[i], NULL); - new_listname = t_strconcat(newname, names[i], NULL); - oldpath = mailbox_list_get_path(storage->list, old_listname, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - newpath = mailbox_list_get_path(storage->list, new_listname, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - - /* FIXME: it's possible to merge two folders if either one of - them doesn't have existing root folder. We could check this - but I'm not sure if it's worth it. It could be even - considered as a feature. - - Anyway, the bug with merging is that if both folders have - identically named subfolder they conflict. Just ignore those - and leave them under the old folder. */ - if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno)) - ret = 1; - else { - mail_storage_set_critical(storage, - "rename(%s, %s) failed: %m", oldpath, newpath); - ret = -1; - t_pop(); - break; - } - - (void)rename_dir(storage, MAILBOX_LIST_PATH_TYPE_CONTROL, - old_listname, new_listname); - (void)rename_dir(storage, MAILBOX_LIST_PATH_TYPE_INDEX, - old_listname, new_listname); - t_pop(); - } - array_free(&names_arr); - pool_unref(pool); - - return ret; -} - -static int maildir_mailbox_rename(struct mail_storage *_storage, - const char *oldname, const char *newname) -{ - const char *oldpath, *newpath; - int ret; - bool found; - - mail_storage_clear_error(_storage); - - if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) || - !mailbox_list_is_valid_create_name(_storage->list, newname)) { - mail_storage_set_error(_storage, "Invalid mailbox name"); - return -1; - } - - if (strcmp(oldname, "INBOX") == 0) { - mail_storage_set_error(_storage, - "Renaming INBOX isn't supported."); - return -1; - } - - /* NOTE: it's possible to rename a nonexisting folder which has - subfolders. In that case we should ignore the rename() error. */ - oldpath = mailbox_list_get_path(_storage->list, oldname, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - newpath = mailbox_list_get_path(_storage->list, newname, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - - ret = rename(oldpath, newpath); - if (ret == 0 || errno == ENOENT) { - (void)rename_dir(_storage, MAILBOX_LIST_PATH_TYPE_CONTROL, - oldname, newname); - (void)rename_dir(_storage, MAILBOX_LIST_PATH_TYPE_INDEX, - oldname, newname); - - found = ret == 0; - ret = rename_subfolders(_storage, oldname, newname); - if (ret < 0) - return -1; - if (!found && ret == 0) { - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname); - return -1; - } - - return 0; - } - - if (EDESTDIREXISTS(errno)) { - mail_storage_set_error(_storage, - "Target mailbox already exists"); - return -1; - } else { - mail_storage_set_critical(_storage, "rename(%s, %s) failed: %m", - oldpath, newpath); - return -1; - } + return storage->list_super.rename_mailbox(list, oldname, newname); } static int maildir_storage_close(struct mailbox *box) @@ -922,20 +919,71 @@ } static int -maildir_storage_is_mailbox(struct mail_storage *_storage, - const char *dir, const char *fname, - enum mailbox_list_iter_flags iter_flags - __attr_unused__, - enum mailbox_info_flags *flags, - enum mailbox_list_file_type type) +maildir_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx + __attr_unused__, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags_r) { - struct maildir_storage *storage = (struct maildir_storage *)_storage; + struct stat st; + const char *path; + int ret; + + if (maildir_is_internal_name(fname)) { + *flags_r = MAILBOX_NONEXISTENT; + return 0; + } + + switch (type) { + case MAILBOX_LIST_FILE_TYPE_FILE: + case MAILBOX_LIST_FILE_TYPE_OTHER: + /* non-directories are not */ + *flags_r = MAILBOX_NOSELECT; + return 0; + + case MAILBOX_LIST_FILE_TYPE_DIR: + case MAILBOX_LIST_FILE_TYPE_UNKNOWN: + case MAILBOX_LIST_FILE_TYPE_SYMLINK: + break; + } + + t_push(); + path = 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) { + /* temporary NFS file */ + *flags_r = MAILBOX_NONEXISTENT; + ret = 0; + } else { + *flags_r = MAILBOX_NOSELECT; + ret = 0; + } + } else if (errno == ENOENT) { + /* this was a directory. maybe it has children. */ + *flags_r = MAILBOX_NOSELECT; + ret = 1; + } else { + *flags_r = MAILBOX_NOSELECT; + ret = 0; + } + t_pop(); + return ret; +} + +static int +maildirplusplus_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags_r) +{ + struct maildir_storage *storage = MAILDIR_LIST_CONTEXT(ctx->list); + struct mail_storage *_storage = &storage->storage.storage; int ret; if (fname[1] == mailbox_list_get_hierarchy_sep(_storage->list) && - strcmp(fname+2, MAILDIR_UNLINK_DIRNAME) == 0 && - strcmp(mailbox_list_get_driver_name(_storage->list), - MAILDIR_PLUSPLUS_DRIVER_NAME) == 0) { + strcmp(fname+2, MAILDIR_UNLINK_DIRNAME) == 0) { const char *path; struct stat st; @@ -950,7 +998,7 @@ (void)unlink_directory(path, TRUE); t_pop(); - *flags = MAILBOX_NONEXISTENT; + *flags_r = MAILBOX_NONEXISTENT; return 0; } @@ -962,7 +1010,7 @@ case MAILBOX_LIST_FILE_TYPE_FILE: case MAILBOX_LIST_FILE_TYPE_OTHER: /* non-directories are not */ - *flags = MAILBOX_NOSELECT; + *flags_r = MAILBOX_NOSELECT; return 0; case MAILBOX_LIST_FILE_TYPE_UNKNOWN: @@ -979,11 +1027,25 @@ t_push(); path = t_strdup_printf("%s/%s", dir, fname); - ret = (stat(path, &st) < 0 || !S_ISDIR(st.st_mode)) ? 0 : 1; + if (stat(path, &st) == 0) { + if (S_ISDIR(st.st_mode)) + ret = 1; + else { + if (strncmp(fname, ".nfs", 4) == 0) + *flags_r = MAILBOX_NONEXISTENT; + else + *flags_r = MAILBOX_NOSELECT; + ret = 0; + } + } else if (errno == ENOENT) { + /* just deleted? */ + *flags_r = MAILBOX_NONEXISTENT; + ret = 0; + } else { + *flags_r = MAILBOX_NOSELECT; + ret = 0; + } t_pop(); - - if (ret == 0) - *flags = MAILBOX_NONEXISTENT; } else { ret = 1; } @@ -992,6 +1054,7 @@ static void maildir_class_init(void) { + maildir_mailbox_list_module_id = mailbox_list_module_id++; maildir_transaction_class_init(); } @@ -1013,9 +1076,6 @@ index_storage_set_callbacks, maildir_mailbox_open, maildir_mailbox_create, - maildir_mailbox_delete, - maildir_mailbox_rename, - maildir_storage_is_mailbox, index_storage_get_last_error } }; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/maildir/maildir-storage.h --- a/src/lib-storage/index/maildir/maildir-storage.h Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/maildir/maildir-storage.h Thu Mar 29 10:59:11 2007 +0300 @@ -43,6 +43,7 @@ #define MAILDIR_SAVE_FLAG_DELETED 0x20000000 #include "index-storage.h" +#include "mailbox-list-private.h" #define STORAGE(maildir_storage) \ (&(maildir_storage)->storage.storage) @@ -56,6 +57,7 @@ struct maildir_storage { struct index_storage storage; + struct mailbox_list_vfuncs list_super; const char *temp_prefix; unsigned int copy_with_hardlinks:1; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/index/mbox/mbox-storage.c --- a/src/lib-storage/index/mbox/mbox-storage.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/index/mbox/mbox-storage.c Thu Mar 29 10:59:11 2007 +0300 @@ -31,6 +31,10 @@ ((st).st_size == 0 ? MAILBOX_UNMARKED : \ (st).st_atime < (st).st_mtime ? MAILBOX_MARKED : MAILBOX_UNMARKED) +#define MBOX_LIST_CONTEXT(obj) \ + *((void **)array_idx_modifiable(&(obj)->module_contexts, \ + mbox_mailbox_list_module_id)) + /* NOTE: must be sorted for istream-header-filter. Note that it's not such a good idea to change this list, as the messages will then change from client's point of view. So if you do it, change all mailboxes' UIDVALIDITY @@ -65,8 +69,14 @@ extern struct mail_storage mbox_storage; extern struct mailbox mbox_mailbox; -static bool mbox_mailbox_list_module_id_set = FALSE; -static unsigned int mbox_mailbox_list_module_id; +static unsigned int mbox_mailbox_list_module_id = 0; + +static int mbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags); +static int mbox_list_delete_mailbox(struct mailbox_list *list, + const char *name); int mbox_set_syscall_error(struct mbox_mailbox *mbox, const char *function) { @@ -83,21 +93,6 @@ return -1; } -static bool mbox_handle_errors(struct index_storage *istorage) -{ - struct mail_storage *storage = &istorage->storage; - - if (ENOACCESS(errno)) - mail_storage_set_error(storage, MAIL_STORAGE_ERR_NO_PERMISSION); - else if (ENOSPACE(errno)) - mail_storage_set_error(storage, "Not enough disk space"); - else if (ENOTFOUND(errno)) - mail_storage_set_error(storage, "Directory structure is broken"); - else - return FALSE; - return TRUE; -} - static bool mbox_is_file(const char *path, const char *name, bool debug) { struct stat st; @@ -282,13 +277,16 @@ static int mbox_get_list_settings(struct mailbox_list_settings *list_set, - const char *data, enum mail_storage_flags flags) + const char *data, enum mail_storage_flags flags, + const char **layout_r) { bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0; const char *p; struct stat st; bool autodetect; + *layout_r = "fs"; + memset(list_set, 0, sizeof(*list_set)); list_set->subscription_fname = MBOX_SUBSCRIPTION_FILE_NAME; list_set->maildir_name = ""; @@ -329,6 +327,8 @@ } else if (strncmp(p, "INDEX=", 6) == 0) { list_set->index_dir = t_strcut(p+6, ':'); + } else if (strncmp(p, "LAYOUT=", 7) == 0) { + *layout_r = t_strcut(p+7, ':'); } p = strchr(p, ':'); } while (p != NULL); @@ -384,9 +384,7 @@ mbox_list_get_path(struct mailbox_list *list, const char *name, enum mailbox_list_path_type type) { - struct mbox_storage *storage = - *((void **)array_idx_modifiable(&list->module_contexts, - mbox_mailbox_list_module_id)); + struct mbox_storage *storage = MBOX_LIST_CONTEXT(list); const char *path, *p; path = storage->list_super.get_path(list, name, type); @@ -410,10 +408,10 @@ struct index_storage *istorage; struct mailbox_list_settings list_set; struct mailbox_list *list; - const char *error; + const char *layout, *error; pool_t pool; - if (mbox_get_list_settings(&list_set, data, flags) < 0) + if (mbox_get_list_settings(&list_set, data, flags, &layout) < 0) return NULL; list_set.mail_storage_flags = &flags; list_set.lock_method = &lock_method; @@ -421,22 +419,21 @@ pool = pool_alloconly_create("storage", 512+256); storage = p_new(pool, struct mbox_storage, 1); - if (mailbox_list_init("fs", &list_set, + if (mailbox_list_init(layout, &list_set, mail_storage_get_list_flags(flags), - mailbox_storage_list_is_mailbox, storage, &list, &error) < 0) { - i_error("mbox fs: %s", error); + i_error("mbox %s: %s", layout, error); pool_unref(pool); return NULL; } - storage->list_super = list->v; - list->v.get_path = mbox_list_get_path; + if (strcmp(layout, "fs") == 0 && *list_set.maildir_name == '\0') { + /* 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.delete_mailbox = mbox_list_delete_mailbox; - if (!mbox_mailbox_list_module_id_set) { - mbox_mailbox_list_module_id_set = TRUE; - mbox_mailbox_list_module_id = mailbox_list_module_id++; - } array_idx_set(&list->module_contexts, mbox_mailbox_list_module_id, &storage); @@ -667,8 +664,7 @@ struct istream *input, enum mailbox_open_flags flags) { struct mbox_storage *storage = (struct mbox_storage *)_storage; - struct index_storage *istorage = INDEX_STORAGE(storage); - const char *path; + const char *path, *error; struct stat st; mail_storage_clear_error(_storage); @@ -702,8 +698,10 @@ if (ENOTFOUND(errno)) { mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); - } else if (!mbox_handle_errors(istorage)) { + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name); + } else if (mail_storage_errno2str(&error)) + mail_storage_set_error(_storage, "%s", error); + else { mail_storage_set_critical(_storage, "stat(%s) failed: %m", path); } @@ -714,8 +712,7 @@ static int mbox_mailbox_create(struct mail_storage *_storage, const char *name, bool directory) { - struct index_storage *storage = (struct index_storage *)_storage; - const char *path, *p; + const char *path, *p, *error; struct stat st; int fd; @@ -726,17 +723,6 @@ return -1; } - if (strncasecmp(name, "INBOX/", 6) == 0) { - /* We might actually be able to create mailboxes under INBOX - because the real INBOX file isn't usually named as INBOX - in the root mail directory. that would anyway be a special - case which would require special handling elsewhere, so just - don't allow it. */ - mail_storage_set_error(_storage, - "Mailbox doesn't allow inferior mailboxes"); - return -1; - } - /* make sure it doesn't exist already */ path = mailbox_list_get_path(_storage->list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); @@ -749,7 +735,9 @@ if (errno == ENOTDIR) { mail_storage_set_error(_storage, "Mailbox doesn't allow inferior mailboxes"); - } else if (!mbox_handle_errors(storage)) { + } else if (mail_storage_errno2str(&error)) + mail_storage_set_error(_storage, "%s", error); + else { mail_storage_set_critical(_storage, "stat() failed for mbox file %s: %m", path); } @@ -761,11 +749,12 @@ if (p != NULL) { p = t_strdup_until(path, p); if (mkdir_parents(p, CREATE_MODE) < 0) { - if (mbox_handle_errors(storage)) - return -1; - - mail_storage_set_critical(_storage, - "mkdir_parents(%s) failed: %m", p); + if (mail_storage_errno2str(&error)) + mail_storage_set_error(_storage, "%s", error); + else { + mail_storage_set_critical(_storage, + "mkdir_parents(%s) failed: %m", p); + } return -1; } @@ -785,194 +774,15 @@ if (errno == EEXIST) { /* mailbox was just created between stat() and open() call.. */ mail_storage_set_error(_storage, "Mailbox already exists"); - } else if (!mbox_handle_errors(storage)) { + } else if (mail_storage_errno2str(&error)) + mail_storage_set_error(_storage, "%s", error); + else { mail_storage_set_critical(_storage, "Can't create mailbox %s: %m", name); } return -1; } -static int mbox_mailbox_delete(struct mail_storage *_storage, const char *name) -{ - struct index_storage *storage = (struct index_storage *)_storage; - const char *index_dir, *path; - struct stat st; - - mail_storage_clear_error(_storage); - - if (strcmp(name, "INBOX") == 0) { - mail_storage_set_error(_storage, "INBOX can't be deleted."); - return -1; - } - - if (!mailbox_list_is_valid_existing_name(_storage->list, name)) { - mail_storage_set_error(_storage, "Invalid mailbox name"); - return -1; - } - - path = mailbox_list_get_path(_storage->list, name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (lstat(path, &st) < 0) { - if (ENOTFOUND(errno)) { - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); - } else if (!mbox_handle_errors(storage)) { - mail_storage_set_critical(_storage, - "lstat() failed for %s: %m", path); - } - return -1; - } - - if (S_ISDIR(st.st_mode)) { - /* deleting a directory. allow it only if it doesn't contain - anything. Delete the ".imap" directory first in case there - have been indexes. */ - index_dir = mailbox_list_get_path(_storage->list, name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - - if (*index_dir != '\0' && rmdir(index_dir) < 0 && - !ENOTFOUND(errno) && errno != ENOTEMPTY) { - if (!mbox_handle_errors(storage)) { - mail_storage_set_critical(_storage, - "rmdir() failed for %s: %m", index_dir); - return -1; - } - } - - if (rmdir(path) == 0) - return 0; - - if (ENOTFOUND(errno)) { - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); - } else if (errno == ENOTEMPTY) { - mail_storage_set_error(_storage, - "Folder %s isn't empty, can't delete it.", - name); - } else if (!mbox_handle_errors(storage)) { - mail_storage_set_critical(_storage, - "rmdir() failed for %s: %m", path); - } - return -1; - } - - /* delete the index directory first, so that if we crash we don't - leave indexes for deleted mailboxes lying around */ - index_dir = mailbox_list_get_path(_storage->list, name, - MAILBOX_LIST_PATH_TYPE_INDEX); - if (*index_dir != '\0') { - index_storage_destroy_unrefed(); - - if (unlink_directory(index_dir, TRUE) < 0 && errno != ENOENT) { - mail_storage_set_critical(_storage, - "unlink_directory(%s) failed: %m", index_dir); - return -1; - } - } - - if (unlink(path) < 0) { - if (ENOTFOUND(errno)) { - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); - } else if (!mbox_handle_errors(storage)) { - mail_storage_set_critical(_storage, - "unlink() failed for %s: %m", path); - } - return -1; - } - - return 0; -} - -static int mbox_mailbox_rename(struct mail_storage *_storage, - const char *oldname, const char *newname) -{ - struct index_storage *storage = (struct index_storage *)_storage; - const char *oldpath, *newpath, *old_indexdir, *new_indexdir, *p; - struct stat st; - - mail_storage_clear_error(_storage); - - if (!mailbox_list_is_valid_existing_name(_storage->list, oldname) || - !mailbox_list_is_valid_create_name(_storage->list, newname)) { - mail_storage_set_error(_storage, "Invalid mailbox name"); - return -1; - } - - if (strncasecmp(newname, "INBOX/", 6) == 0) { - /* Not allowed - see explanation in mbox_mailbox_create */ - mail_storage_set_error(_storage, - "Target mailbox doesn't allow inferior mailboxes"); - return -1; - } - - oldpath = mailbox_list_get_path(_storage->list, oldname, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - newpath = mailbox_list_get_path(_storage->list, newname, - MAILBOX_LIST_PATH_TYPE_MAILBOX); - - /* create the hierarchy */ - p = strrchr(newpath, '/'); - if (p != NULL) { - p = t_strdup_until(newpath, p); - if (mkdir_parents(p, CREATE_MODE) < 0) { - if (mbox_handle_errors(storage)) - return -1; - - mail_storage_set_critical(_storage, - "mkdir_parents(%s) failed: %m", p); - return -1; - } - } - - /* first check that the destination mailbox doesn't exist. - this is racy, but we need to be atomic and there's hardly any - possibility that someone actually tries to rename two mailboxes - to same new one */ - if (lstat(newpath, &st) == 0) { - mail_storage_set_error(_storage, - "Target mailbox already exists"); - return -1; - } else if (errno == ENOTDIR) { - mail_storage_set_error(_storage, - "Target mailbox doesn't allow inferior mailboxes"); - return -1; - } else if (errno != ENOENT && errno != EACCES) { - mail_storage_set_critical(_storage, "lstat(%s) failed: %m", - newpath); - return -1; - } - - /* NOTE: renaming INBOX works just fine with us, it's simply recreated - the next time it's needed. */ - if (rename(oldpath, newpath) < 0) { - if (ENOTFOUND(errno)) { - mail_storage_set_error(_storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname); - } else if (!mbox_handle_errors(storage)) { - mail_storage_set_critical(_storage, - "rename(%s, %s) failed: %m", oldpath, newpath); - } - return -1; - } - - /* we need to rename the index directory as well */ - old_indexdir = mailbox_list_get_path(_storage->list, oldname, - MAILBOX_LIST_PATH_TYPE_INDEX); - new_indexdir = mailbox_list_get_path(_storage->list, newname, - MAILBOX_LIST_PATH_TYPE_INDEX); - if (*old_indexdir != '\0') { - if (rename(old_indexdir, new_indexdir) < 0 && - errno != ENOENT) { - mail_storage_set_critical(_storage, - "rename(%s, %s) failed: %m", - old_indexdir, new_indexdir); - } - } - - return 0; -} - static int mbox_storage_close(struct mailbox *box) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; @@ -1019,26 +829,39 @@ index_mailbox_check_add(&mbox->ibox, mbox->path); } -static int mbox_is_mailbox(struct mail_storage *storage, - const char *dir, const char *fname, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags, - enum mailbox_list_file_type type) +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; +} + +static int mbox_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags_r) +{ + struct mail_storage *storage = MBOX_LIST_CONTEXT(ctx->list); const char *path, *root_dir; size_t len; struct stat st; int ret = 1; if (strcmp(fname, MBOX_INDEX_DIR_NAME) == 0) { - *flags = MAILBOX_NOSELECT; + *flags_r = MAILBOX_NOSELECT; return 0; } if (strcmp(fname, MBOX_SUBSCRIPTION_FILE_NAME) == 0) { root_dir = mailbox_list_get_path(storage->list, NULL, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(root_dir, dir) == 0) { - *flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + *flags_r = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; return 0; } } @@ -1046,19 +869,19 @@ /* skip all .lock files */ len = strlen(fname); if (len > 5 && strcmp(fname+len-5, ".lock") == 0) { - *flags = MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + *flags_r = 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; + *flags_r = MAILBOX_NOSELECT | MAILBOX_CHILDREN; return 1; } if (type != MAILBOX_LIST_FILE_TYPE_SYMLINK && type != MAILBOX_LIST_FILE_TYPE_UNKNOWN && - (iter_flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) { - *flags |= MAILBOX_NOINFERIORS; + (ctx->flags & MAILBOX_LIST_ITER_FAST_FLAGS) != 0) { + *flags_r = MAILBOX_NOINFERIORS; return 1; } @@ -1067,11 +890,19 @@ 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); + *flags_r = MAILBOX_NOSELECT | MAILBOX_CHILDREN; + else { + *flags_r = 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_r &= ~MAILBOX_NOINFERIORS; + } + } } else if (errno == EACCES || errno == ELOOP) - *flags |= MAILBOX_NOSELECT; + *flags_r = MAILBOX_NOSELECT; else if (ENOTFOUND(errno)) ret = 0; else { @@ -1083,8 +914,91 @@ return ret; } +static int mbox_list_delete_mailbox(struct mailbox_list *list, + const char *name) +{ + struct mbox_storage *storage = MBOX_LIST_CONTEXT(list); + struct stat st; + const char *path, *index_dir, *error; + + path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (lstat(path, &st) < 0) { + if (ENOTFOUND(errno)) { + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); + } else if (mail_storage_errno2str(&error)) + mailbox_list_set_error(list, error); + else { + mailbox_list_set_critical(list, + "lstat() failed for %s: %m", path); + } + return -1; + } + + if (S_ISDIR(st.st_mode)) { + /* deleting a directory. allow it only if it doesn't contain + anything. Delete the ".imap" directory first in case there + have been indexes. */ + index_dir = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + index_dir = *index_dir == '\0' ? "" : + t_strconcat(index_dir, "/"MBOX_INDEX_DIR_NAME, NULL); + + if (*index_dir != '\0' && rmdir(index_dir) < 0 && + !ENOTFOUND(errno) && errno != ENOTEMPTY) { + if (mail_storage_errno2str(&error)) + mailbox_list_set_error(list, error); + else { + mailbox_list_set_critical(list, + "rmdir() failed for %s: %m", index_dir); + } + return -1; + } + + if (rmdir(path) == 0) + return 0; + + if (ENOTFOUND(errno)) { + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); + } else if (errno == ENOTEMPTY) { + mailbox_list_set_error(list, t_strdup_printf( + "Directory %s isn't empty, can't delete it.", + name)); + } else if (mail_storage_errno2str(&error)) + mailbox_list_set_error(list, error); + else { + mailbox_list_set_critical(list, + "rmdir() failed for %s: %m", path); + } + return -1; + } + + /* delete index / control files first */ + index_storage_destroy_unrefed(); + if (storage->list_super.delete_mailbox(list, name) < 0) + return -1; + + if (unlink(path) < 0) { + if (ENOTFOUND(errno)) { + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); + } else if (mail_storage_errno2str(&error)) + mailbox_list_set_error(list, error); + else { + mailbox_list_set_critical(list, + "unlink() failed for %s: %m", path); + } + return -1; + } + + return 0; +} + static void mbox_class_init(void) { + mbox_mailbox_list_module_id = mailbox_list_module_id++; mbox_transaction_class_init(); } @@ -1106,9 +1020,6 @@ index_storage_set_callbacks, mbox_mailbox_open, mbox_mailbox_create, - mbox_mailbox_delete, - mbox_mailbox_rename, - mbox_is_mailbox, index_storage_get_last_error } }; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/list/index-mailbox-list-sync.c --- a/src/lib-storage/list/index-mailbox-list-sync.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/list/index-mailbox-list-sync.c Thu Mar 29 10:59:11 2007 +0300 @@ -470,7 +470,7 @@ index_list_next_hook_mailbox_created(box); /* FIXME: maildir-only for now */ - if (strcmp(box->storage->name, "maildir") != 0) + if (strcmp(box->storage->list->name, "maildir++") != 0) return; ibox = p_new(box->pool, struct index_list_mailbox, 1); diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/list/mailbox-list-fs-iter.c --- a/src/lib-storage/list/mailbox-list-fs-iter.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/list/mailbox-list-fs-iter.c Thu Mar 29 10:59:11 2007 +0300 @@ -22,7 +22,8 @@ struct imap_match_glob *glob; struct subsfile_list_context *subsfile_ctx; - bool inbox_found; + bool inbox_found, inbox_listed; + enum mailbox_info_flags inbox_flags; struct mailbox_info *(*next)(struct fs_list_iterate_context *ctx); @@ -121,7 +122,7 @@ virtual_path = mask_get_dir(mask); path = mailbox_list_get_path(_list, virtual_path, - MAILBOX_LIST_PATH_TYPE_MAILBOX); + MAILBOX_LIST_PATH_TYPE_DIR); if (list_opendir(_list, path, TRUE, &dirp) < 0) return &ctx->ctx; /* if user gave invalid directory, we just don't show any results. */ @@ -189,6 +190,41 @@ return ctx->next(ctx); } +static void +path_split(const char *path, const char **dir_r, const char **fname_r) +{ + const char *p; + + p = strrchr(path, '/'); + if (p == NULL) { + *dir_r = ""; + *fname_r = path; + } else { + *dir_r = t_strdup_until(path, p); + *fname_r = p + 1; + } +} + +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"; + + t_push(); + 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, + MAILBOX_LIST_FILE_TYPE_UNKNOWN, + &ctx->info.flags) < 0) + ctx->ctx.failed = TRUE; + t_pop(); + + return &ctx->info; +} + static int list_file(struct fs_list_iterate_context *ctx, const struct dirent *d) { @@ -218,23 +254,50 @@ /* get the info.flags using callback */ ctx->info.flags = 0; - ret = ctx->ctx.list->callback(ctx->dir->real_path, fname, - mailbox_list_get_file_type(d), - ctx->ctx.flags, &ctx->info.flags, - ctx->ctx.list->context); + ret = ctx->ctx.list->v. + iter_is_mailbox(&ctx->ctx, ctx->dir->real_path, fname, + mailbox_list_get_file_type(d), + &ctx->info.flags); if (ret <= 0) return ret; /* make sure we give only one correct INBOX */ real_path = t_strconcat(ctx->dir->real_path, "/", fname, NULL); - if (strcasecmp(list_path, "INBOX") == 0 && - (ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0) { + if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 && + strcasecmp(list_path, "INBOX") == 0) { + if (ctx->inbox_listed) { + /* already listed the INBOX */ + return 0; + } + inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX", - MAILBOX_LIST_PATH_TYPE_MAILBOX); - if (ctx->inbox_found || strcmp(real_path, inbox_path) != 0) + MAILBOX_LIST_PATH_TYPE_DIR); + if (strcmp(real_path, inbox_path) == 0) { + /* delay listing in case there's a INBOX/ directory */ + ctx->inbox_found = TRUE; + ctx->inbox_flags = ctx->info.flags; + return 0; + } + if (strcmp(fname, "INBOX") != 0 || + (ctx->info.flags & MAILBOX_NOINFERIORS) != 0) { + /* duplicate INBOX, can't show this */ return 0; + } - ctx->inbox_found = TRUE; + /* INBOX/ directory. show the INBOX list now */ + if (!ctx->inbox_found) { + enum mailbox_info_flags dir_flags = ctx->info.flags; + + (void)fs_list_inbox(ctx); + ctx->info.flags &= ~(MAILBOX_NOINFERIORS | + MAILBOX_NOCHILDREN); + ctx->info.flags |= dir_flags; + ctx->inbox_found = TRUE; + } else { + ctx->info.flags &= ~MAILBOX_NOSELECT; + ctx->info.flags |= ctx->inbox_flags; + } + ctx->inbox_listed = TRUE; } if ((ctx->info.flags & MAILBOX_NOINFERIORS) == 0) { @@ -270,21 +333,6 @@ return 0; } -static void -path_split(const char *path, const char **dir_r, const char **fname_r) -{ - const char *p; - - p = strrchr(path, '/'); - if (p == NULL) { - *dir_r = ""; - *fname_r = path; - } else { - *dir_r = t_strdup_until(path, p); - *fname_r = p + 1; - } -} - static struct mailbox_info *fs_list_subs(struct fs_list_iterate_context *ctx) { const char *name, *path, *p, *dir, *fname; @@ -321,12 +369,11 @@ t_push(); path = mailbox_list_get_path(ctx->ctx.list, ctx->info.name, - MAILBOX_LIST_PATH_TYPE_MAILBOX); + MAILBOX_LIST_PATH_TYPE_DIR); path_split(path, &dir, &fname); - if (ctx->ctx.list->callback(dir, fname, - MAILBOX_LIST_FILE_TYPE_UNKNOWN, - ctx->ctx.flags, &ctx->info.flags, - ctx->ctx.list->context) < 0) + if (ctx->ctx.list->v.iter_is_mailbox(&ctx->ctx, dir, fname, + MAILBOX_LIST_FILE_TYPE_UNKNOWN, + &ctx->info.flags) < 0) ctx->ctx.failed = TRUE; t_pop(); return &ctx->info; @@ -346,27 +393,6 @@ return ctx->next(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"; - - t_push(); - inbox_path = mailbox_list_get_path(ctx->ctx.list, "INBOX", - MAILBOX_LIST_PATH_TYPE_MAILBOX); - path_split(inbox_path, &dir, &fname); - if (ctx->ctx.list->callback(dir, fname, - MAILBOX_LIST_FILE_TYPE_UNKNOWN, - ctx->ctx.flags, &ctx->info.flags, - ctx->ctx.list->context) < 0) - ctx->ctx.failed = TRUE; - t_pop(); - - return &ctx->info; -} - static struct mailbox_info *fs_list_next(struct fs_list_iterate_context *ctx) { struct list_dir_context *dir; @@ -399,9 +425,16 @@ (ctx->ctx.list->flags & MAILBOX_LIST_FLAG_INBOX) != 0 && ctx->glob != NULL && imap_match(ctx->glob, "INBOX") > 0) { /* show inbox */ + ctx->inbox_listed = TRUE; ctx->inbox_found = TRUE; return fs_list_inbox(ctx); } + if (!ctx->inbox_listed && ctx->inbox_found) { + ctx->inbox_listed = TRUE; + ctx->info.flags = ctx->inbox_flags; + ctx->info.name = "INBOX"; + return &ctx->info; + } /* finished */ return NULL; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/list/mailbox-list-fs.c --- a/src/lib-storage/list/mailbox-list-fs.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/list/mailbox-list-fs.c Thu Mar 29 10:59:11 2007 +0300 @@ -1,13 +1,18 @@ -/* Copyright (C) 2006 Timo Sirainen */ +/* Copyright (C) 2006-2007 Timo Sirainen */ #include "lib.h" #include "hostpid.h" #include "home-expand.h" +#include "mkdir-parents.h" #include "subscription-file.h" #include "mailbox-list-fs.h" +#include +#include #include +#define CREATE_MODE 0770 /* umask() should limit it more */ + extern struct mailbox_list fs_mailbox_list; static struct mailbox_list *fs_list_alloc(void) @@ -28,8 +33,7 @@ static void fs_list_deinit(struct mailbox_list *_list) { - struct fs_mailbox_list *list = - (struct fs_mailbox_list *)_list; + struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; pool_unref(list->list.pool); } @@ -136,8 +140,7 @@ fs_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type) { - struct fs_mailbox_list *list = - (struct fs_mailbox_list *)_list; + struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; const struct mailbox_list_settings *set = &_list->set; mailbox_list_clear_error(&list->list); @@ -158,7 +161,7 @@ i_unreached(); } - i_assert(mailbox_list_is_valid_existing_name(_list, name)); + i_assert(mailbox_list_is_valid_mask(_list, name)); if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 && (*name == '/' || *name == '~')) { @@ -188,10 +191,8 @@ break; } - if (strcmp(name, "INBOX") == 0) { - return set->inbox_path != NULL ? - set->inbox_path : set->root_dir; - } + if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) + return set->inbox_path; if (*set->maildir_name == '\0') return t_strdup_printf("%s/%s", set->root_dir, name); @@ -205,8 +206,7 @@ fs_list_get_mailbox_name_status(struct mailbox_list *_list, const char *name, enum mailbox_name_status *status) { - struct fs_mailbox_list *list = - (struct fs_mailbox_list *)_list; + struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; struct stat st; const char *path; @@ -245,8 +245,7 @@ static const char * fs_list_get_temp_prefix(struct mailbox_list *_list) { - struct fs_mailbox_list *list = - (struct fs_mailbox_list *)_list; + struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; return list->temp_prefix; } @@ -267,8 +266,7 @@ static int fs_list_set_subscribed(struct mailbox_list *_list, const char *name, bool set) { - struct fs_mailbox_list *list = - (struct fs_mailbox_list *)_list; + struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list; const char *path; mailbox_list_clear_error(&list->list); @@ -280,6 +278,103 @@ name, set); } +static int fs_list_delete_mailbox(struct mailbox_list *list, const char *name) +{ + /* let the backend handle the rest */ + return mailbox_list_delete_index_control(list, name); +} + +static bool fs_handle_errors(struct mailbox_list *list) +{ + if (ENOACCESS(errno)) + mailbox_list_set_error(list, MAILBOX_LIST_ERR_NO_PERMISSION); + else if (ENOSPACE(errno)) + mailbox_list_set_error(list, "Not enough disk space"); + else if (ENOTFOUND(errno)) + mailbox_list_set_error(list, "Directory structure is broken"); + else + return FALSE; + return TRUE; +} + +static int fs_list_rename_mailbox(struct mailbox_list *list, + const char *oldname, const char *newname) +{ + const char *oldpath, *newpath, *old_indexdir, *new_indexdir, *p; + struct stat st; + + if (!mailbox_list_is_valid_existing_name(list, oldname) || + !mailbox_list_is_valid_create_name(list, newname)) { + mailbox_list_set_error(list, "Invalid mailbox name"); + return -1; + } + + oldpath = mailbox_list_get_path(list, oldname, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + newpath = mailbox_list_get_path(list, newname, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + + /* create the hierarchy */ + p = strrchr(newpath, '/'); + if (p != NULL) { + p = t_strdup_until(newpath, p); + if (mkdir_parents(p, CREATE_MODE) < 0) { + if (fs_handle_errors(list)) + return -1; + + mailbox_list_set_critical(list, + "mkdir_parents(%s) failed: %m", p); + return -1; + } + } + + /* first check that the destination mailbox doesn't exist. + this is racy, but we need to be atomic and there's hardly any + possibility that someone actually tries to rename two mailboxes + to same new one */ + if (lstat(newpath, &st) == 0) { + mailbox_list_set_error(list, "Target mailbox already exists"); + return -1; + } else if (errno == ENOTDIR) { + mailbox_list_set_error(list, + "Target mailbox doesn't allow inferior mailboxes"); + return -1; + } else if (errno != ENOENT && errno != EACCES) { + mailbox_list_set_critical(list, "lstat(%s) failed: %m", + newpath); + return -1; + } + + /* NOTE: renaming INBOX works just fine with us, it's simply recreated + the next time it's needed. */ + if (rename(oldpath, newpath) < 0) { + if (ENOTFOUND(errno)) { + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname)); + } else if (!fs_handle_errors(list)) { + mailbox_list_set_critical(list, + "rename(%s, %s) failed: %m", oldpath, newpath); + } + return -1; + } + + /* we need to rename the index directory as well */ + old_indexdir = mailbox_list_get_path(list, oldname, + MAILBOX_LIST_PATH_TYPE_INDEX); + new_indexdir = mailbox_list_get_path(list, newname, + MAILBOX_LIST_PATH_TYPE_INDEX); + if (*old_indexdir != '\0') { + if (rename(old_indexdir, new_indexdir) < 0 && + errno != ENOENT) { + mailbox_list_set_critical(list, + "rename(%s, %s) failed: %m", + old_indexdir, new_indexdir); + } + } + + return 0; +} + struct mailbox_list fs_mailbox_list = { MEMBER(name) "fs", MEMBER(hierarchy_sep) '/', @@ -298,6 +393,9 @@ fs_list_iter_init, fs_list_iter_next, fs_list_iter_deinit, - fs_list_set_subscribed + NULL, + fs_list_set_subscribed, + fs_list_delete_mailbox, + fs_list_rename_mailbox } }; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/list/mailbox-list-maildir-iter.c --- a/src/lib-storage/list/mailbox-list-maildir-iter.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/list/mailbox-list-maildir-iter.c Thu Mar 29 10:59:11 2007 +0300 @@ -98,10 +98,9 @@ /* check if this is an actual mailbox */ flags = 0; - ret = ctx->ctx.list->callback(ctx->dir, fname, - mailbox_list_get_file_type(d), - ctx->ctx.flags, &flags, - ctx->ctx.list->context); + ret = ctx->ctx.list->v. + iter_is_mailbox(&ctx->ctx, ctx->dir, fname, + mailbox_list_get_file_type(d), &flags); if (ret < 0) { t_pop(); return -1; @@ -149,6 +148,11 @@ node->flags |= MAILBOX_FLAG_MATCHED; } } + if (node != NULL) { + node->flags |= flags & ~(MAILBOX_NOINFERIORS | + MAILBOX_CHILDREN | + MAILBOX_NOCHILDREN); + } } t_pop(); diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/list/mailbox-list-maildir.c --- a/src/lib-storage/list/mailbox-list-maildir.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/list/mailbox-list-maildir.c Thu Mar 29 10:59:11 2007 +0300 @@ -1,11 +1,13 @@ -/* Copyright (C) 2006 Timo Sirainen */ +/* Copyright (C) 2006-2007 Timo Sirainen */ #include "lib.h" +#include "array.h" #include "hostpid.h" #include "home-expand.h" #include "subscription-file.h" #include "mailbox-list-maildir.h" +#include #include extern struct mailbox_list maildir_mailbox_list; @@ -270,6 +272,164 @@ name, set); } +static int rename_dir(struct mailbox_list *list, + enum mailbox_list_path_type type, + const char *oldname, const char *newname) +{ + const char *oldpath, *newpath; + + oldpath = mailbox_list_get_path(list, oldname, type); + newpath = mailbox_list_get_path(list, newname, type); + + if (strcmp(oldpath, newpath) == 0) + return 0; + + if (rename(oldpath, newpath) < 0 && errno != ENOENT) { + mailbox_list_set_critical(list, "rename(%s, %s) failed: %m", + oldpath, newpath); + return -1; + } + + return 0; +} + +static int rename_children(struct mailbox_list *list, + const char *oldname, const char *newname) +{ + struct mailbox_list_iterate_context *iter; + struct mailbox_info *info; + ARRAY_DEFINE(names_arr, const char *); + const char *mask, *oldpath, *newpath, *old_listname, *new_listname; + const char *const *names; + unsigned int i, count; + size_t oldnamelen; + pool_t pool; + int ret; + + ret = 0; + oldnamelen = strlen(oldname); + + /* first get the list of the children and save them to memory, because + we can't rely on readdir() not skipping files while the directory + is being modified. this doesn't protect against modifications by + other processes though. */ + pool = pool_alloconly_create("Maildir++ children list", 1024); + i_array_init(&names_arr, 64); + + mask = t_strdup_printf("%s%c*", oldname, + mailbox_list_get_hierarchy_sep(list)); + iter = mailbox_list_iter_init(list, mask, MAILBOX_LIST_ITER_FAST_FLAGS); + while ((info = mailbox_list_iter_next(iter)) != NULL) { + const char *name; + + i_assert(oldnamelen <= strlen(info->name)); + + name = p_strdup(pool, info->name + oldnamelen); + array_append(&names_arr, &name, 1); + } + if (mailbox_list_iter_deinit(&iter) < 0) { + ret = -1; + names = NULL; count = 0; + } else { + names = array_get(&names_arr, &count); + } + + for (i = 0; i < count; i++) { + t_push(); + + old_listname = t_strconcat(oldname, names[i], NULL); + new_listname = t_strconcat(newname, names[i], NULL); + oldpath = mailbox_list_get_path(list, old_listname, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + newpath = mailbox_list_get_path(list, new_listname, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + + /* FIXME: it's possible to merge two mailboxes if either one of + them doesn't have existing root mailbox. We could check this + but I'm not sure if it's worth it. It could be even + considered as a feature. + + Anyway, the bug with merging is that if both mailboxes have + identically named child mailbox they conflict. Just ignore + those and leave them under the old mailbox. */ + if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno)) + ret = 1; + else { + mailbox_list_set_critical(list, + "rename(%s, %s) failed: %m", oldpath, newpath); + ret = -1; + t_pop(); + break; + } + + (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL, + old_listname, new_listname); + (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX, + old_listname, new_listname); + t_pop(); + } + array_free(&names_arr); + pool_unref(pool); + + return ret; +} + +static int +maildir_list_delete_mailbox(struct mailbox_list *list, const char *name) +{ + /* let the backend handle the rest */ + return mailbox_list_delete_index_control(list, name); +} + +static int maildir_list_rename_mailbox(struct mailbox_list *list, + const char *oldname, const char *newname) +{ + const char *oldpath, *newpath; + int ret; + bool found; + + if (!mailbox_list_is_valid_existing_name(list, oldname) || + !mailbox_list_is_valid_create_name(list, newname)) { + mailbox_list_set_error(list, "Invalid mailbox name"); + return -1; + } + + /* NOTE: it's possible to rename a nonexisting mailbox which has + children. In that case we should ignore the rename() error. */ + oldpath = mailbox_list_get_path(list, oldname, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + newpath = mailbox_list_get_path(list, newname, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + + ret = rename(oldpath, newpath); + if (ret == 0 || errno == ENOENT) { + (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL, + oldname, newname); + (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX, + oldname, newname); + + found = ret == 0; + ret = rename_children(list, oldname, newname); + if (ret < 0) + return -1; + if (!found && ret == 0) { + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname)); + return -1; + } + + return 0; + } + + if (EDESTDIREXISTS(errno)) { + mailbox_list_set_error(list, "Target mailbox already exists"); + } else { + mailbox_list_set_critical(list, "rename(%s, %s) failed: %m", + oldpath, newpath); + } + return -1; +} + struct mailbox_list maildir_mailbox_list = { MEMBER(name) "maildir++", MEMBER(hierarchy_sep) '.', @@ -288,6 +448,9 @@ maildir_list_iter_init, maildir_list_iter_next, maildir_list_iter_deinit, - maildir_list_set_subscribed + NULL, + maildir_list_set_subscribed, + maildir_list_delete_mailbox, + maildir_list_rename_mailbox } }; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/mail-storage-private.h --- a/src/lib-storage/mail-storage-private.h Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/mail-storage-private.h Thu Mar 29 10:59:11 2007 +0300 @@ -4,11 +4,6 @@ #include "file-lock.h" #include "mail-storage.h" -/* Some error strings that should be used everywhere to avoid - permissions checks from revealing mailbox's existence */ -#define MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND "Mailbox doesn't exist: %s" -#define MAIL_STORAGE_ERR_NO_PERMISSION "Permission denied" - /* Called after mail storage has been created */ extern void (*hook_mail_storage_created)(struct mail_storage *storage); /* Called after mailbox has been opened */ @@ -37,15 +32,6 @@ int (*mailbox_create)(struct mail_storage *storage, const char *name, bool directory); - int (*mailbox_delete)(struct mail_storage *storage, const char *name); - int (*mailbox_rename)(struct mail_storage *storage, const char *oldname, - const char *newname); - - int (*is_mailbox)(struct mail_storage *storage, - const char *dir, const char *fname, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags, - enum mailbox_list_file_type type); const char *(*get_last_error)(struct mail_storage *storage, bool *syntax_error_r, @@ -289,10 +275,6 @@ enum mailbox_list_flags mail_storage_get_list_flags(enum mail_storage_flags storage_flags); -int mailbox_storage_list_is_mailbox(const char *dir, const char *fname, - enum mailbox_list_file_type type, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags, - void *context); +bool mail_storage_errno2str(const char **error_r); #endif diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/mail-storage.c --- a/src/lib-storage/mail-storage.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/mail-storage.c Thu Mar 29 10:59:11 2007 +0300 @@ -1,10 +1,11 @@ -/* Copyright (C) 2002-2006 Timo Sirainen */ +/* Copyright (C) 2002-2007 Timo Sirainen */ #include "lib.h" #include "ioloop.h" #include "array.h" #include "var-expand.h" #include "mail-index-private.h" +#include "mailbox-list-private.h" #include "mail-storage-private.h" #include @@ -318,17 +319,6 @@ return storage->v.mailbox_create(storage, name, directory); } -int mail_storage_mailbox_delete(struct mail_storage *storage, const char *name) -{ - return storage->v.mailbox_delete(storage, name); -} - -int mail_storage_mailbox_rename(struct mail_storage *storage, - const char *oldname, const char *newname) -{ - return storage->v.mailbox_rename(storage, oldname, newname); -} - const char *mail_storage_get_last_error(struct mail_storage *storage, bool *syntax_error_r, bool *temporary_error_r) @@ -385,27 +375,17 @@ return list_flags; } -int mailbox_storage_list_is_mailbox(const char *dir, const char *fname, - enum mailbox_list_file_type type, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags, - void *context) +bool mail_storage_errno2str(const char **error_r) { - struct mail_storage *storage = context; - - return mail_storage_is_mailbox(storage, dir, fname, iter_flags, - flags, type); -} - - -int mail_storage_is_mailbox(struct mail_storage *storage, - const char *dir, const char *fname, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags, - enum mailbox_list_file_type type) -{ - return storage->v.is_mailbox(storage, dir, fname, iter_flags, - flags, type); + if (ENOACCESS(errno)) + *error_r = MAILBOX_LIST_ERR_NO_PERMISSION; + else if (ENOSPACE(errno)) + *error_r = "Not enough disk space"; + else if (ENOTFOUND(errno)) + *error_r = "Directory structure is broken"; + else + return FALSE; + return TRUE; } struct mailbox *mailbox_open(struct mail_storage *storage, const char *name, diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/mail-storage.h --- a/src/lib-storage/mail-storage.h Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/mail-storage.h Thu Mar 29 10:59:11 2007 +0300 @@ -229,18 +229,6 @@ created as long as it shows in LIST. */ int mail_storage_mailbox_create(struct mail_storage *storage, const char *name, bool directory); -/* Only the specified mailbox is deleted, ie. folders under the - specified mailbox must not be deleted. */ -int mail_storage_mailbox_delete(struct mail_storage *storage, const char *name); -/* If the name has inferior hierarchical names, then the inferior - hierarchical names MUST also be renamed (ie. foo -> bar renames - also foo/bar -> bar/bar). newname may contain multiple new - hierarchies. - - If oldname is case-insensitively "INBOX", the mails are moved - into new folder but the INBOX folder must not be deleted. */ -int mail_storage_mailbox_rename(struct mail_storage *storage, - const char *oldname, const char *newname); /* Returns the error message of last occurred error. */ const char *mail_storage_get_last_error(struct mail_storage *storage, @@ -262,12 +250,6 @@ const char *mail_storage_get_mailbox_index_dir(struct mail_storage *storage, const char *name); -int mail_storage_is_mailbox(struct mail_storage *storage, - const char *dir, const char *fname, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags, - enum mailbox_list_file_type type); - /* Open a mailbox. If input stream is given, mailbox is opened read-only using it as a backend. If storage doesn't support stream backends and its tried to be used, NULL is returned. diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/mailbox-list-private.h --- a/src/lib-storage/mailbox-list-private.h Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/mailbox-list-private.h Thu Mar 29 10:59:11 2007 +0300 @@ -1,6 +1,11 @@ #ifndef __MAILBOX_LIST_PRIVATE_H #define __MAILBOX_LIST_PRIVATE_H +/* Some error strings that should be used everywhere to avoid + permissions checks from revealing mailbox's existence */ +#define MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND "Mailbox doesn't exist: %s" +#define MAILBOX_LIST_ERR_NO_PERMISSION "Permission denied" + #include "mailbox-list.h" struct dirent; @@ -32,8 +37,18 @@ (*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, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags_r); + int (*set_subscribed)(struct mailbox_list *list, const char *name, bool set); + int (*delete_mailbox)(struct mailbox_list *list, const char *name); + int (*rename_mailbox)(struct mailbox_list *list, const char *oldname, + const char *newname); }; struct mailbox_list { @@ -51,9 +66,6 @@ char *error; bool temporary_error; - mailbox_list_is_mailbox_t *callback; - void *context; - ARRAY_DEFINE(module_contexts, void); }; @@ -69,6 +81,9 @@ extern void (*hook_mailbox_list_created)(struct mailbox_list *list); +int mailbox_list_delete_index_control(struct mailbox_list *list, + const char *name); + bool mailbox_list_name_is_too_large(const char *name, char sep); enum mailbox_list_file_type mailbox_list_get_file_type(const struct dirent *d); diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/mailbox-list.c --- a/src/lib-storage/mailbox-list.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/mailbox-list.c Thu Mar 29 10:59:11 2007 +0300 @@ -1,9 +1,10 @@ -/* Copyright (C) 2006 Timo Sirainen */ +/* Copyright (C) 2006-2007 Timo Sirainen */ #include "lib.h" #include "array.h" #include "ioloop.h" #include "mkdir-parents.h" +#include "unlink-directory.h" #include "mailbox-list-private.h" #include @@ -76,7 +77,6 @@ int mailbox_list_init(const char *driver, const struct mailbox_list_settings *set, enum mailbox_list_flags flags, - mailbox_list_is_mailbox_t *callback, void *context, struct mailbox_list **list_r, const char **error_r) { const struct mailbox_list *const *class_p; @@ -95,8 +95,6 @@ list = (*class_p)->v.alloc(); list->flags = flags; - list->callback = callback; - list->context = context; /* copy settings */ list->set.root_dir = p_strdup(list->pool, set->root_dir); @@ -234,6 +232,60 @@ return list->v.set_subscribed(list, name, set); } +int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name) +{ + return list->v.delete_mailbox(list, name); +} + +int mailbox_list_rename_mailbox(struct mailbox_list *list, + const char *oldname, const char *newname) +{ + return list->v.rename_mailbox(list, oldname, newname); +} + +int mailbox_list_delete_index_control(struct mailbox_list *list, + const char *name) +{ + const char *path, *index_dir, *dir; + + if (strcmp(name, "INBOX") == 0) { + mailbox_list_set_error(list, "INBOX can't be deleted."); + return -1; + } + + if (!mailbox_list_is_valid_existing_name(list, name)) { + mailbox_list_set_error(list, "Invalid mailbox name"); + return -1; + } + + path = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + + /* delete the index directory first, so that if we crash we don't + leave indexes for deleted mailboxes lying around */ + index_dir = mailbox_list_get_path(list, name, + MAILBOX_LIST_PATH_TYPE_INDEX); + if (*index_dir != '\0' && strcmp(index_dir, path) != 0) { + if (unlink_directory(index_dir, TRUE) < 0 && errno != ENOENT) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", index_dir); + return -1; + } + } + + /* control directory next */ + dir = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_CONTROL); + if (*dir != '\0' && strcmp(dir, path) != 0 && + strcmp(dir, index_dir) != 0) { + if (unlink_directory(dir, TRUE) < 0 && errno != ENOENT) { + mailbox_list_set_critical(list, + "unlink_directory(%s) failed: %m", dir); + return -1; + } + } + return 0; +} + bool mailbox_list_name_is_too_large(const char *name, char sep) { unsigned int levels = 1, level_len = 0; diff -r 04404fd54cf6 -r beabd433cdae src/lib-storage/mailbox-list.h --- a/src/lib-storage/mailbox-list.h Wed Mar 28 23:07:38 2007 +0300 +++ b/src/lib-storage/mailbox-list.h Thu Mar 29 10:59:11 2007 +0300 @@ -42,9 +42,9 @@ }; enum mailbox_list_path_type { - /* Return directory's path (eg. /home/user/mail) */ + /* Return directory's path (eg. ~/dbox/INBOX) */ MAILBOX_LIST_PATH_TYPE_DIR, - /* Return mailbox path (dir + maildir_name) */ + /* Return mailbox path (eg. ~/dbox/INBOX/dbox-Mails) */ MAILBOX_LIST_PATH_TYPE_MAILBOX, /* Return control directory */ MAILBOX_LIST_PATH_TYPE_CONTROL, @@ -92,14 +92,6 @@ enum mailbox_info_flags flags; }; -/* Returns -1 if error, 0 if it's not a valid mailbox, 1 if it is. - flags may be updated (especially the children flags). */ -typedef int mailbox_list_is_mailbox_t(const char *dir, const char *fname, - enum mailbox_list_file_type type, - enum mailbox_list_iter_flags iter_flags, - enum mailbox_info_flags *flags_r, - void *context); - /* register all drivers */ void mailbox_list_register_all(void); @@ -110,7 +102,6 @@ int mailbox_list_init(const char *driver, const struct mailbox_list_settings *set, enum mailbox_list_flags flags, - mailbox_list_is_mailbox_t *callback, void *context, struct mailbox_list **list_r, const char **error_r); void mailbox_list_deinit(struct mailbox_list *list); @@ -161,6 +152,18 @@ int mailbox_list_set_subscribed(struct mailbox_list *list, const char *name, bool set); +/* Delete the given mailbox. If it has children, they aren't deleted. */ +int mailbox_list_delete_mailbox(struct mailbox_list *list, const char *name); +/* If the name has inferior hierarchical names, then the inferior + hierarchical names MUST also be renamed (ie. foo -> bar renames + also foo/bar -> bar/bar). newname may contain multiple new + hierarchies. + + If oldname is case-insensitively "INBOX", the mails are moved + into new mailbox but the INBOX mailbox must not be deleted. */ +int mailbox_list_rename_mailbox(struct mailbox_list *list, + const char *oldname, const char *newname); + /* Returns the error message of last occurred error. */ const char *mailbox_list_get_last_error(struct mailbox_list *list, bool *temporary_error_r); diff -r 04404fd54cf6 -r beabd433cdae src/plugins/acl/acl-mailbox-list.c --- a/src/plugins/acl/acl-mailbox-list.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/acl/acl-mailbox-list.c Thu Mar 29 10:59:11 2007 +0300 @@ -102,6 +102,76 @@ return 0; } +static int +acl_mailbox_list_delete(struct mailbox_list *list, const char *name) +{ + struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); + bool can_see; + int ret; + + ret = acl_storage_have_right(alist->storage, name, + ACL_STORAGE_RIGHT_DELETE, &can_see); + if (ret <= 0) { + if (ret < 0) + return -1; + if (can_see) { + mail_storage_set_error(alist->storage, + MAILBOX_LIST_ERR_NO_PERMISSION); + } else { + mail_storage_set_error(alist->storage, + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name); + } + return -1; + } + + return alist->super.delete_mailbox(list, name); +} + +static int +acl_mailbox_list_rename(struct mailbox_list *list, + const char *oldname, const char *newname) +{ + struct acl_mailbox_list *alist = ACL_LIST_CONTEXT(list); + bool can_see; + int ret; + + /* renaming requires rights to delete the old mailbox */ + ret = acl_storage_have_right(alist->storage, oldname, + ACL_STORAGE_RIGHT_DELETE, &can_see); + if (ret <= 0) { + if (ret < 0) + return -1; + if (can_see) { + mail_storage_set_error(alist->storage, + MAILBOX_LIST_ERR_NO_PERMISSION); + } else { + mail_storage_set_error(alist->storage, + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, oldname); + } + return 0; + } + + /* and create the new one under the parent mailbox */ + t_push(); + ret = acl_storage_have_right(alist->storage, + acl_storage_get_parent_mailbox_name(alist->storage, newname), + ACL_STORAGE_RIGHT_CREATE, NULL); + t_pop(); + + if (ret <= 0) { + if (ret == 0) { + /* Note that if the mailbox didn't have LOOKUP + permission, this not reveals to user the mailbox's + existence. Can't help it. */ + mail_storage_set_error(alist->storage, + MAILBOX_LIST_ERR_NO_PERMISSION); + } + return -1; + } + + return alist->super.rename_mailbox(list, oldname, newname); +} + void acl_mailbox_list_created(struct mailbox_list *list) { struct acl_mailbox_list *alist; @@ -113,6 +183,8 @@ alist->super = list->v; list->v.iter_next = acl_mailbox_list_iter_next; list->v.get_mailbox_name_status = acl_get_mailbox_name_status; + list->v.delete_mailbox = acl_mailbox_list_delete; + list->v.rename_mailbox = acl_mailbox_list_rename; if (!acl_mailbox_list_module_id_set) { acl_mailbox_list_module_id = mailbox_list_module_id++; diff -r 04404fd54cf6 -r beabd433cdae src/plugins/acl/acl-mailbox.c --- a/src/plugins/acl/acl-mailbox.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/acl/acl-mailbox.c Thu Mar 29 10:59:11 2007 +0300 @@ -7,7 +7,7 @@ #include "lib.h" #include "array.h" #include "istream.h" -#include "mail-storage-private.h" +#include "mailbox-list-private.h" #include "acl-api-private.h" #include "acl-plugin.h" @@ -49,7 +49,7 @@ if (ret < 0) return -1; - mail_storage_set_error(box->storage, MAIL_STORAGE_ERR_NO_PERMISSION); + mail_storage_set_error(box->storage, MAILBOX_LIST_ERR_NO_PERMISSION); return 0; } diff -r 04404fd54cf6 -r beabd433cdae src/plugins/acl/acl-storage.c --- a/src/plugins/acl/acl-storage.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/acl/acl-storage.c Thu Mar 29 10:59:11 2007 +0300 @@ -4,6 +4,7 @@ #include "array.h" #include "istream.h" #include "acl-api-private.h" +#include "mailbox-list-private.h" #include "acl-plugin.h" #include @@ -92,10 +93,10 @@ return NULL; if (can_see) { mail_storage_set_error(storage, - MAIL_STORAGE_ERR_NO_PERMISSION); + MAILBOX_LIST_ERR_NO_PERMISSION); } else { mail_storage_set_error(storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name); } return NULL; } @@ -125,7 +126,7 @@ permission, this not reveals to user the mailbox's existence. Can't help it. */ mail_storage_set_error(storage, - MAIL_STORAGE_ERR_NO_PERMISSION); + MAILBOX_LIST_ERR_NO_PERMISSION); } return -1; } @@ -133,74 +134,6 @@ return astorage->super.mailbox_create(storage, name, directory); } -static int acl_mailbox_delete(struct mail_storage *storage, const char *name) -{ - struct acl_mail_storage *astorage = ACL_CONTEXT(storage); - bool can_see; - int ret; - - ret = acl_storage_have_right(storage, name, ACL_STORAGE_RIGHT_DELETE, - &can_see); - if (ret <= 0) { - if (ret < 0) - return -1; - if (can_see) { - mail_storage_set_error(storage, - MAIL_STORAGE_ERR_NO_PERMISSION); - } else { - mail_storage_set_error(storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); - } - return -1; - } - - return astorage->super.mailbox_delete(storage, name); -} - -static int acl_mailbox_rename(struct mail_storage *storage, const char *oldname, - const char *newname) -{ - struct acl_mail_storage *astorage = ACL_CONTEXT(storage); - bool can_see; - int ret; - - /* renaming requires rights to delete the old mailbox */ - ret = acl_storage_have_right(storage, oldname, - ACL_STORAGE_RIGHT_DELETE, &can_see); - if (ret <= 0) { - if (ret < 0) - return -1; - if (can_see) { - mail_storage_set_error(storage, - MAIL_STORAGE_ERR_NO_PERMISSION); - } else { - mail_storage_set_error(storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, oldname); - } - return 0; - } - - /* and create the new one under the parent mailbox */ - t_push(); - ret = acl_storage_have_right(storage, - acl_storage_get_parent_mailbox_name(storage, newname), - ACL_STORAGE_RIGHT_CREATE, NULL); - t_pop(); - - if (ret <= 0) { - if (ret == 0) { - /* Note that if the mailbox didn't have LOOKUP - permission, this not reveals to user the mailbox's - existence. Can't help it. */ - mail_storage_set_error(storage, - MAIL_STORAGE_ERR_NO_PERMISSION); - } - return -1; - } - - return astorage->super.mailbox_rename(storage, oldname, newname); -} - void acl_mail_storage_created(struct mail_storage *storage) { struct acl_mail_storage *astorage; @@ -240,8 +173,6 @@ storage->v.destroy = acl_storage_destroy; storage->v.mailbox_open = acl_mailbox_open; storage->v.mailbox_create = acl_mailbox_create; - storage->v.mailbox_delete = acl_mailbox_delete; - storage->v.mailbox_rename = acl_mailbox_rename; acl_mailbox_list_set_storage(storage); diff -r 04404fd54cf6 -r beabd433cdae src/plugins/lazy-expunge/lazy-expunge-plugin.c --- a/src/plugins/lazy-expunge/lazy-expunge-plugin.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/lazy-expunge/lazy-expunge-plugin.c Thu Mar 29 10:59:11 2007 +0300 @@ -19,6 +19,9 @@ #define LAZY_EXPUNGE_CONTEXT(obj) \ *((void **)array_idx_modifiable(&(obj)->module_contexts, \ lazy_expunge_storage_module_id)) +#define LAZY_EXPUNGE_LIST_CONTEXT(obj) \ + *((void **)array_idx_modifiable(&(obj)->module_contexts, \ + lazy_expunge_mailbox_list_module_id)) enum lazy_namespace { LAZY_NAMESPACE_EXPUNGE, @@ -28,6 +31,12 @@ LAZY_NAMESPACE_COUNT }; +struct lazy_expunge_mailbox_list { + struct mailbox_list_vfuncs super; + + struct mail_storage *storage; +}; + struct lazy_expunge_mail_storage { struct mail_storage_vfuncs super; bool internal_namespace; @@ -48,13 +57,18 @@ const char *lazy_expunge_plugin_version = PACKAGE_VERSION; +static void (*lazy_expunge_next_hook_client_created)(struct client **client); static void (*lazy_expunge_next_hook_mail_storage_created) (struct mail_storage *storage); -static void (*lazy_expunge_next_hook_client_created)(struct client **client); +static void (*lazy_expunge_next_hook_mailbox_list_created) + (struct mailbox_list *list); static unsigned int lazy_expunge_storage_module_id = 0; static bool lazy_expunge_storage_module_id_set = FALSE; +static unsigned int lazy_expunge_mailbox_list_module_id = 0; +static bool lazy_expunge_mailbox_list_module_id_set = FALSE; + static struct namespace *lazy_namespaces[LAZY_NAMESPACE_COUNT]; static struct mailbox * @@ -275,7 +289,7 @@ return box; } -static int dir_move_or_merge(struct mail_storage *storage, +static int dir_move_or_merge(struct mailbox_list *list, const char *srcdir, const char *destdir) { DIR *dir; @@ -288,14 +302,14 @@ return 0; if (!EDESTDIREXISTS(errno)) { - mail_storage_set_critical(storage, + mailbox_list_set_critical(list, "rename(%s, %s) failed: %m", srcdir, destdir); } /* rename all the files separately */ dir = opendir(srcdir); if (dir == NULL) { - mail_storage_set_critical(storage, + mailbox_list_set_critical(list, "opendir(%s) failed: %m", srcdir); return -1; } @@ -323,20 +337,20 @@ if (rename(str_c(src_path), str_c(dest_path)) < 0 && errno != ENOENT) { - mail_storage_set_critical(storage, + mailbox_list_set_critical(list, "rename(%s, %s) failed: %m", str_c(src_path), str_c(dest_path)); ret = -1; } } if (closedir(dir) < 0) { - mail_storage_set_critical(storage, + mailbox_list_set_critical(list, "closedir(%s) failed: %m", srcdir); ret = -1; } if (ret == 0) { if (rmdir(srcdir) < 0) { - mail_storage_set_critical(storage, + mailbox_list_set_critical(list, "rmdir(%s) failed: %m", srcdir); ret = -1; } @@ -345,22 +359,22 @@ } static int -mailbox_move(struct mail_storage *src_storage, const char *src_name, - struct mail_storage *dest_storage, const char **_dest_name) +mailbox_move(struct mailbox_list *src_list, const char *src_name, + struct mailbox_list *dest_list, const char **_dest_name) { const char *dest_name = *_dest_name; const char *srcdir, *src2dir, *src3dir, *destdir; - bool is_file; - srcdir = mail_storage_get_mailbox_path(src_storage, src_name, &is_file); - destdir = mail_storage_get_mailbox_path(dest_storage, dest_name, - &is_file); + srcdir = mailbox_list_get_path(src_list, src_name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + destdir = mailbox_list_get_path(dest_list, dest_name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); while (rename(srcdir, destdir) < 0) { if (errno == ENOENT) return 0; if (!EDESTDIREXISTS(errno)) { - mail_storage_set_critical(src_storage, + mailbox_list_set_critical(src_list, "rename(%s, %s) failed: %m", srcdir, destdir); return -1; } @@ -369,22 +383,24 @@ update the filename. */ dest_name = t_strdup_printf("%s-%04u", *_dest_name, (uint32_t)random()); - destdir = mail_storage_get_mailbox_path(dest_storage, dest_name, - &is_file); + destdir = mailbox_list_get_path(dest_list, dest_name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); } t_push(); - src2dir = mail_storage_get_mailbox_control_dir(src_storage, src_name); + src2dir = mailbox_list_get_path(src_list, src_name, + MAILBOX_LIST_PATH_TYPE_CONTROL); if (strcmp(src2dir, srcdir) != 0) { - destdir = mail_storage_get_mailbox_control_dir(dest_storage, - dest_name); - (void)dir_move_or_merge(src_storage, src2dir, destdir); + destdir = mailbox_list_get_path(dest_list, dest_name, + MAILBOX_LIST_PATH_TYPE_CONTROL); + (void)dir_move_or_merge(src_list, src2dir, destdir); } - src3dir = mail_storage_get_mailbox_index_dir(src_storage, src_name); + src3dir = mailbox_list_get_path(src_list, src_name, + MAILBOX_LIST_PATH_TYPE_INDEX); if (strcmp(src3dir, srcdir) != 0 && strcmp(src3dir, src2dir) != 0) { - destdir = mail_storage_get_mailbox_index_dir(dest_storage, - dest_name); - (void)dir_move_or_merge(src_storage, src3dir, destdir); + destdir = mailbox_list_get_path(dest_list, dest_name, + MAILBOX_LIST_PATH_TYPE_INDEX); + (void)dir_move_or_merge(src_list, src3dir, destdir); } t_pop(); @@ -393,33 +409,37 @@ } static int -lazy_expunge_mailbox_delete(struct mail_storage *storage, const char *name) +lazy_expunge_mailbox_list_delete(struct mailbox_list *list, const char *name) { - struct lazy_expunge_mail_storage *lstorage = - LAZY_EXPUNGE_CONTEXT(storage); - struct mail_storage *dest_storage; + struct lazy_expunge_mailbox_list *llist = + LAZY_EXPUNGE_LIST_CONTEXT(list); + struct lazy_expunge_mail_storage *lstorage; + struct mailbox_list *dest_list; enum mailbox_name_status status; const char *destname; struct tm *tm; char timestamp[256]; int ret; + if (llist->storage == NULL) { + /* not a maildir storage */ + return llist->super.delete_mailbox(list, name); + } + + lstorage = LAZY_EXPUNGE_CONTEXT(llist->storage); if (lstorage->internal_namespace) - return lstorage->super.mailbox_delete(storage, name); - - mail_storage_clear_error(storage); + return llist->super.delete_mailbox(list, name); /* first do the normal sanity checks */ if (strcmp(name, "INBOX") == 0) { - mail_storage_set_error(storage, "INBOX can't be deleted."); + mailbox_list_set_error(list, "INBOX can't be deleted."); return -1; } - if (mailbox_list_get_mailbox_name_status(storage->list, name, - &status) < 0) + if (mailbox_list_get_mailbox_name_status(list, name, &status) < 0) return -1; if (status == MAILBOX_NAME_INVALID) { - mail_storage_set_error(storage, "Invalid mailbox name"); + mailbox_list_set_error(list, "Invalid mailbox name"); return -1; } @@ -430,24 +450,27 @@ destname = t_strconcat(name, "-", timestamp, NULL); /* first move the actual mailbox */ - dest_storage = lazy_namespaces[LAZY_NAMESPACE_DELETE]->storage; - if ((ret = mailbox_move(storage, name, dest_storage, &destname)) < 0) + dest_list = lazy_namespaces[LAZY_NAMESPACE_DELETE]->storage->list; + if ((ret = mailbox_move(list, name, dest_list, &destname)) < 0) return -1; if (ret == 0) { - mail_storage_set_error(storage, - MAIL_STORAGE_ERR_MAILBOX_NOT_FOUND, name); + mailbox_list_set_error(list, t_strdup_printf( + MAILBOX_LIST_ERR_MAILBOX_NOT_FOUND, name)); return -1; } /* next move the expunged messages mailbox, if it exists */ - storage = lazy_namespaces[LAZY_NAMESPACE_EXPUNGE]->storage; - dest_storage = lazy_namespaces[LAZY_NAMESPACE_DELETE_EXPUNGE]->storage; - (void)mailbox_move(storage, name, dest_storage, &destname); + list = lazy_namespaces[LAZY_NAMESPACE_EXPUNGE]->storage->list; + dest_list = + lazy_namespaces[LAZY_NAMESPACE_DELETE_EXPUNGE]->storage->list; + (void)mailbox_move(list, name, dest_list, &destname); return 0; } static void lazy_expunge_mail_storage_created(struct mail_storage *storage) { + struct lazy_expunge_mailbox_list *llist = + LAZY_EXPUNGE_LIST_CONTEXT(storage->list); struct lazy_expunge_mail_storage *lstorage; if (lazy_expunge_next_hook_mail_storage_created != NULL) @@ -457,10 +480,11 @@ if (strcmp(storage->name, "maildir") != 0) return; + llist->storage = storage; + lstorage = p_new(storage->pool, struct lazy_expunge_mail_storage, 1); lstorage->super = storage->v; storage->v.mailbox_open = lazy_expunge_mailbox_open; - storage->v.mailbox_delete = lazy_expunge_mailbox_delete; if (!lazy_expunge_storage_module_id_set) { lazy_expunge_storage_module_id = mail_storage_module_id++; @@ -471,6 +495,26 @@ lazy_expunge_storage_module_id, &lstorage); } +static void lazy_expunge_mailbox_list_created(struct mailbox_list *list) +{ + struct lazy_expunge_mailbox_list *llist; + + if (lazy_expunge_next_hook_mailbox_list_created != NULL) + lazy_expunge_next_hook_mailbox_list_created(list); + + llist = p_new(list->pool, struct lazy_expunge_mailbox_list, 1); + llist->super = list->v; + list->v.delete_mailbox = lazy_expunge_mailbox_list_delete; + + if (!lazy_expunge_mailbox_list_module_id_set) { + lazy_expunge_mailbox_list_module_id = mailbox_list_module_id++; + lazy_expunge_mailbox_list_module_id_set = TRUE; + } + + array_idx_set(&list->module_contexts, + lazy_expunge_mailbox_list_module_id, &llist); +} + static void lazy_expunge_hook_client_created(struct client **client) { struct lazy_expunge_mail_storage *lstorage; @@ -517,14 +561,17 @@ lazy_expunge_next_hook_mail_storage_created = hook_mail_storage_created; hook_mail_storage_created = lazy_expunge_mail_storage_created; + + lazy_expunge_next_hook_mailbox_list_created = hook_mailbox_list_created; + hook_mailbox_list_created = lazy_expunge_mailbox_list_created; } void lazy_expunge_plugin_deinit(void) { - if (lazy_expunge_storage_module_id_set) { - hook_client_created = lazy_expunge_hook_client_created; + if (getenv("LAZY_EXPUNGE") == NULL) + return; - hook_mail_storage_created = - lazy_expunge_next_hook_mail_storage_created; - } + hook_client_created = lazy_expunge_hook_client_created; + hook_mail_storage_created = lazy_expunge_next_hook_mail_storage_created; + hook_mailbox_list_created = lazy_expunge_next_hook_mailbox_list_created; } diff -r 04404fd54cf6 -r beabd433cdae src/plugins/mail-log/mail-log-plugin.c --- a/src/plugins/mail-log/mail-log-plugin.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/mail-log/mail-log-plugin.c Thu Mar 29 10:59:11 2007 +0300 @@ -4,6 +4,7 @@ #include "array.h" #include "str-sanitize.h" #include "mail-storage-private.h" +#include "mailbox-list-private.h" #include "mail-log-plugin.h" #define MAILBOX_NAME_LOG_LEN 64 @@ -12,6 +13,13 @@ #define MAIL_LOG_CONTEXT(obj) \ *((void **)array_idx_modifiable(&(obj)->module_contexts, \ mail_log_storage_module_id)) +#define MAIL_LOG_LIST_CONTEXT(obj) \ + *((void **)array_idx_modifiable(&(obj)->module_contexts, \ + mail_log_mailbox_list_module_id)) + +struct mail_log_mailbox_list { + struct mailbox_list_vfuncs super; +}; struct mail_log_mail_storage { struct mail_storage_vfuncs super; @@ -29,10 +37,15 @@ static void (*mail_log_next_hook_mail_storage_created) (struct mail_storage *storage); +static void (*mail_log_next_hook_mailbox_list_created) + (struct mailbox_list *list); static unsigned int mail_log_storage_module_id = 0; static bool mail_log_storage_module_id_set = FALSE; +static unsigned int mail_log_mailbox_list_module_id = 0; +static bool mail_log_mailbox_list_module_id_set = FALSE; + static void mail_log_action(struct mail *mail, const char *action) { const char *msgid, *mailbox_str; @@ -160,11 +173,11 @@ } static int -mail_log_mailbox_delete(struct mail_storage *storage, const char *name) +mail_log_mailbox_list_delete(struct mailbox_list *list, const char *name) { - struct mail_log_mail_storage *lstorage = MAIL_LOG_CONTEXT(storage); + struct mail_log_mailbox_list *llist = MAIL_LOG_LIST_CONTEXT(list); - if (lstorage->super.mailbox_delete(storage, name) < 0) + if (llist->super.delete_mailbox(list, name) < 0) return -1; i_info("Mailbox deleted: %s", str_sanitize(name, MAILBOX_NAME_LOG_LEN)); @@ -181,7 +194,6 @@ lstorage = p_new(storage->pool, struct mail_log_mail_storage, 1); lstorage->super = storage->v; storage->v.mailbox_open = mail_log_mailbox_open; - storage->v.mailbox_delete = mail_log_mailbox_delete; if (!mail_log_storage_module_id_set) { mail_log_storage_module_id = mail_storage_module_id++; @@ -192,17 +204,37 @@ mail_log_storage_module_id, &lstorage); } +static void mail_log_mailbox_list_created(struct mailbox_list *list) +{ + struct mail_log_mailbox_list *llist; + + if (mail_log_next_hook_mailbox_list_created != NULL) + mail_log_next_hook_mailbox_list_created(list); + + llist = p_new(list->pool, struct mail_log_mailbox_list, 1); + llist->super = list->v; + list->v.delete_mailbox = mail_log_mailbox_list_delete; + + if (!mail_log_mailbox_list_module_id_set) { + mail_log_mailbox_list_module_id = mailbox_list_module_id++; + mail_log_mailbox_list_module_id_set = TRUE; + } + + array_idx_set(&list->module_contexts, + mail_log_mailbox_list_module_id, &llist); +} + void mail_log_plugin_init(void) { - mail_log_next_hook_mail_storage_created = - hook_mail_storage_created; + mail_log_next_hook_mail_storage_created = hook_mail_storage_created; hook_mail_storage_created = mail_log_mail_storage_created; + + mail_log_next_hook_mailbox_list_created = hook_mailbox_list_created; + hook_mailbox_list_created = mail_log_mailbox_list_created; } void mail_log_plugin_deinit(void) { - if (mail_log_storage_module_id_set) { - hook_mail_storage_created = - mail_log_next_hook_mail_storage_created; - } + hook_mail_storage_created = mail_log_next_hook_mail_storage_created; + hook_mailbox_list_created = mail_log_next_hook_mailbox_list_created; } diff -r 04404fd54cf6 -r beabd433cdae src/plugins/quota/quota-plugin.c --- a/src/plugins/quota/quota-plugin.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/quota/quota-plugin.c Thu Mar 29 10:59:11 2007 +0300 @@ -2,6 +2,7 @@ #include "lib.h" #include "mail-storage.h" +#include "mailbox-list-private.h" #include "quota.h" #include "quota-plugin.h" @@ -11,6 +12,7 @@ extern void (*hook_mail_storage_created)(struct mail_storage *storage); void (*quota_next_hook_mail_storage_created)(struct mail_storage *storage); +void (*quota_next_hook_mailbox_list_created)(struct mailbox_list *list); const char *quota_plugin_version = PACKAGE_VERSION; struct quota *quota_set; @@ -74,9 +76,11 @@ } t_pop(); - quota_next_hook_mail_storage_created = - hook_mail_storage_created; + quota_next_hook_mail_storage_created = hook_mail_storage_created; hook_mail_storage_created = quota_mail_storage_created; + + quota_next_hook_mailbox_list_created = hook_mailbox_list_created; + hook_mailbox_list_created = quota_mailbox_list_created; } void quota_plugin_deinit(void) @@ -84,6 +88,8 @@ if (quota_set != NULL) { hook_mail_storage_created = quota_next_hook_mail_storage_created; + hook_mailbox_list_created = + quota_next_hook_mailbox_list_created; quota_deinit(quota_set); } } diff -r 04404fd54cf6 -r beabd433cdae src/plugins/quota/quota-plugin.h --- a/src/plugins/quota/quota-plugin.h Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/quota/quota-plugin.h Thu Mar 29 10:59:11 2007 +0300 @@ -5,11 +5,14 @@ extern void (*quota_next_hook_mail_storage_created) (struct mail_storage *storage); +extern void (*quota_next_hook_mailbox_list_created)(struct mailbox_list *list); + /* "quota" symbol already exists in OSX, so we'll use this slightly uglier name. */ extern struct quota *quota_set; void quota_mail_storage_created(struct mail_storage *storage); +void quota_mailbox_list_created(struct mailbox_list *list); void quota_plugin_init(void); void quota_plugin_deinit(void); diff -r 04404fd54cf6 -r beabd433cdae src/plugins/quota/quota-storage.c --- a/src/plugins/quota/quota-storage.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/quota/quota-storage.c Thu Mar 29 10:59:11 2007 +0300 @@ -5,6 +5,7 @@ #include "istream.h" #include "mail-search.h" #include "mail-storage-private.h" +#include "mailbox-list-private.h" #include "quota-private.h" #include "quota-plugin.h" @@ -13,6 +14,15 @@ #define QUOTA_CONTEXT(obj) \ *((void **)array_idx_modifiable(&(obj)->module_contexts, \ quota_storage_module_id)) +#define QUOTA_LIST_CONTEXT(obj) \ + *((void **)array_idx_modifiable(&(obj)->module_contexts, \ + quota_mailbox_list_module_id)) + +struct quota_mailbox_list { + struct mailbox_list_vfuncs super; + + struct mail_storage *storage; +}; struct quota_mail_storage { struct mail_storage_vfuncs super; @@ -31,6 +41,9 @@ static unsigned int quota_storage_module_id = 0; static bool quota_storage_module_id_set = FALSE; +static unsigned int quota_mailbox_list_module_id = 0; +static bool quota_mailbox_list_module_id_set = FALSE; + static int quota_mail_expunge(struct mail *_mail) { struct mail_private *mail = (struct mail_private *)_mail; @@ -247,9 +260,10 @@ return box; } -static int quota_mailbox_delete(struct mail_storage *storage, const char *name) +static int +quota_mailbox_list_delete(struct mailbox_list *list, const char *name) { - struct quota_mail_storage *qstorage = QUOTA_CONTEXT(storage); + struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(list); struct mailbox *box; struct mail_search_context *ctx; struct mailbox_transaction_context *t; @@ -262,7 +276,7 @@ and free the quota for all the messages existing in it. Open the mailbox locked so that other processes can't mess up the quota calculations by adding/removing mails while we're doing this. */ - box = mailbox_open(storage, name, NULL, MAILBOX_OPEN_FAST | + box = mailbox_open(qlist->storage, name, NULL, MAILBOX_OPEN_FAST | MAILBOX_OPEN_KEEP_RECENT | MAILBOX_OPEN_KEEP_LOCKED); if (box == NULL) return -1; @@ -287,7 +301,7 @@ mailbox_close(&box); /* FIXME: here's an unfortunate race condition */ return ret < 0 ? -1 : - qstorage->super.mailbox_delete(storage, name); + qlist->super.delete_mailbox(list, name); } static void quota_storage_destroy(struct mail_storage *storage) @@ -301,16 +315,18 @@ void quota_mail_storage_created(struct mail_storage *storage) { + struct quota_mailbox_list *qlist = QUOTA_LIST_CONTEXT(storage->list); struct quota_mail_storage *qstorage; if (quota_next_hook_mail_storage_created != NULL) quota_next_hook_mail_storage_created(storage); + qlist->storage = storage; + qstorage = p_new(storage->pool, struct quota_mail_storage, 1); qstorage->super = storage->v; storage->v.destroy = quota_storage_destroy; storage->v.mailbox_open = quota_mailbox_open; - storage->v.mailbox_delete = quota_mailbox_delete; if (!quota_storage_module_id_set) { quota_storage_module_id = mail_storage_module_id++; @@ -325,3 +341,23 @@ quota_add_user_storage(quota_set, storage); } } + +void quota_mailbox_list_created(struct mailbox_list *list) +{ + struct quota_mailbox_list *qlist; + + if (quota_next_hook_mailbox_list_created != NULL) + quota_next_hook_mailbox_list_created(list); + + qlist = p_new(list->pool, struct quota_mailbox_list, 1); + qlist->super = list->v; + list->v.delete_mailbox = quota_mailbox_list_delete; + + if (!quota_mailbox_list_module_id_set) { + quota_mailbox_list_module_id = mailbox_list_module_id++; + quota_mailbox_list_module_id_set = TRUE; + } + + array_idx_set(&list->module_contexts, + quota_mailbox_list_module_id, &qlist); +} diff -r 04404fd54cf6 -r beabd433cdae src/plugins/quota/quota.c --- a/src/plugins/quota/quota.c Wed Mar 28 23:07:38 2007 +0300 +++ b/src/plugins/quota/quota.c Thu Mar 29 10:59:11 2007 +0300 @@ -3,6 +3,7 @@ #include "lib.h" #include "array.h" #include "hash.h" +#include "mailbox-list-private.h" #include "quota-private.h" #include "quota-fs.h" #include @@ -410,7 +411,7 @@ /* the quota information comes from userdb (or even config file), so there's really no way to support this until some major changes are done */ - *error_r = MAIL_STORAGE_ERR_NO_PERMISSION; + *error_r = MAILBOX_LIST_ERR_NO_PERMISSION; return -1; }