mailbox-list-maildir.c revision c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abe
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2006-2010 Dovecot authors, see the included COPYING file */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen#define MAILDIR_SUBFOLDER_FILENAME "maildirfolder"
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenextern struct mailbox_list maildir_mailbox_list;
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainenextern struct mailbox_list imapdir_mailbox_list;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic struct mailbox_list *maildir_list_alloc(void)
14e581093719c4353205bf5ba4743d8575c49d1bTimo Sirainen pool = pool_alloconly_create("maildir++ list", 2048);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen list = p_new(pool, struct maildir_mailbox_list, 1);
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen list->global_temp_prefix = MAILDIR_GLOBAL_TEMP_PREFIX;
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen list->temp_prefix = p_strconcat(pool, list->global_temp_prefix,
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainenstatic struct mailbox_list *imapdir_list_alloc(void)
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainen pool = pool_alloconly_create("imapdir list", 1024);
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainen list = p_new(pool, struct maildir_mailbox_list, 1);
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen list->global_temp_prefix = IMAPDIR_GLOBAL_TEMP_PREFIX;
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen list->temp_prefix = p_strconcat(pool, list->global_temp_prefix,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic void maildir_list_deinit(struct mailbox_list *_list)
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainenstatic const char *
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainenmaildir_list_get_dirname_path(struct mailbox_list *list, const char *dir,
1c885b304f060e3ac4fe04195a2f53457d0ac99eTimo Sirainen if (strcmp(list->name, MAILBOX_LIST_NAME_IMAPDIR) == 0 || *name == '\0')
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainen return t_strdup_printf("%s/%c%s", dir, list->hierarchy_sep, name);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic const char *
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_get_absolute_path(struct mailbox_list *list, const char *name)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *p;
96f2533c48ce5def0004931606a2fdf275578880Timo Sirainen if (!mailbox_list_try_get_absolute_path(list, &name)) {
83c21c990eb2a370f0da56240e73dac846f4acc3Timo Sirainen /* fallback to using as ~name */
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainen return maildir_list_get_dirname_path(list, t_strdup_until(name, p),
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_is_valid_common(struct mailbox_list *list, const char *name,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* check that there are no adjacent hierarchy separators */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic bool maildir_list_is_valid_common_nonfs(const char *name)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (*name == '~' || strchr(name, '/') != NULL)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* "." and ".." aren't allowed. */
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainenmaildir_is_valid_pattern(struct mailbox_list *list ATTR_UNUSED,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_is_valid_existing_name(struct mailbox_list *list, const char *name)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (!maildir_list_is_valid_common(list, name, &len))
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (list->mail_set->mail_full_filesystem_access)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return maildir_list_is_valid_common_nonfs(name);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_is_valid_create_name(struct mailbox_list *list, const char *name)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (!maildir_list_is_valid_common(list, name, &len))
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (len > MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (list->mail_set->mail_full_filesystem_access)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (!maildir_list_is_valid_common_nonfs(name))
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (mailbox_list_name_is_too_large(name, list->hierarchy_sep))
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic const char *
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_get_path(struct mailbox_list *_list, const char *name,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* return root directories */
e0ca8f2484847b57e20798a9f9c7040708696a90Timo Sirainen return mailbox_list_get_root_path(&_list->set, type);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (_list->mail_set->mail_full_filesystem_access &&
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return maildir_list_get_absolute_path(_list, name);
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen /* don't use inbox_path */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen } else if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL)
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen return maildir_list_get_dirname_path(_list, root_dir, name);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_get_mailbox_name_status(struct mailbox_list *_list,
1d266a7419fafa25f3505a12217452a8c647074fTimo Sirainen (_list->ns->flags & NAMESPACE_FLAG_INBOX) != 0) ||
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (!mailbox_list_is_valid_create_name(_list, name)) {
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen mailbox_list_set_critical(_list, "stat(%s) failed: %m", path);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic const char *
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainenmaildir_list_get_temp_prefix(struct mailbox_list *_list, bool global)
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen return global ? list->global_temp_prefix : list->temp_prefix;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic int maildir_list_set_subscribed(struct mailbox_list *_list,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen path = t_strconcat(_list->set.control_dir != NULL ?
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return subsfile_set_subscribed(_list, path, list->temp_prefix,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainenmaildir_list_create_maildirfolder_file(struct mailbox_list *list,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen const char *dir)
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen /* Maildir++ spec wants that maildirfolder named file is created for
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen all subfolders. */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen mailbox_list_get_permissions(list, NULL, &mode, &gid, &gid_origin);
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen path = t_strconcat(dir, "/" MAILDIR_SUBFOLDER_FILENAME, NULL);
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen "Mailbox was deleted while it was being created");
3c493c276f599d9b9cd10764876d648003046954Timo Sirainenmaildir_list_create_mailbox_dir(struct mailbox_list *list, const char *name,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen (list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0;
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen /* we only need to make sure that the parent directory exists */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen mailbox_list_get_dir_permissions(list, NULL, &mode,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen if (mkdir_parents_chgrp(path, mode, gid, gid_origin) == 0) {
029c8542982001bf1386aee0ea9cb9843e5ab62dTimo Sirainen /* even though the root directory exists,
029c8542982001bf1386aee0ea9cb9843e5ab62dTimo Sirainen the mailbox might not */
029c8542982001bf1386aee0ea9cb9843e5ab62dTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
029c8542982001bf1386aee0ea9cb9843e5ab62dTimo Sirainen "Mailbox already exists");
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen } else if (mailbox_list_set_error_from_errno(list)) {
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen mailbox_list_set_critical(list, "mkdir(%s) failed: %m", path);
029c8542982001bf1386aee0ea9cb9843e5ab62dTimo Sirainen return create_parent_dir || strcmp(path, root_dir) == 0 ? 0 :
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen maildir_list_create_maildirfolder_file(list, path);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic const char *mailbox_list_maildir_get_trash_dir(struct mailbox_list *list)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen return t_strdup_printf("%s/%c%c"MAILBOX_LIST_MAILDIR_TRASH_DIR_NAME,
c18c8b130311374a5145226be03cda903047b1f8Timo Sirainenmaildir_list_delete_maildir(struct mailbox_list *list, const char *name)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen trash_dir = mailbox_list_maildir_get_trash_dir(list);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen ret = mailbox_list_delete_maildir_via_trash(list, name, trash_dir);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* we could actually use just unlink_directory()
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen but error handling is easier this way :) */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen if (mailbox_list_delete_mailbox_nonrecursive(list, name,
c18c8b130311374a5145226be03cda903047b1f8Timo Sirainenmaildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
c18c8b130311374a5145226be03cda903047b1f8Timo Sirainen if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) {
c18c8b130311374a5145226be03cda903047b1f8Timo Sirainen if (mailbox_list_delete_mailbox_file(list, name) < 0)
c18c8b130311374a5145226be03cda903047b1f8Timo Sirainen if (maildir_list_delete_maildir(list, name) < 0)
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainenstatic int maildir_list_delete_dir(struct mailbox_list *list, const char *name)
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen /* with maildir++ there aren't any non-selectable mailboxes.
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen we'll always fail. */
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR);
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen "Mailbox exists");
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen mailbox_list_set_critical(list, "stat(%s) failed: %m", path);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainenstatic int rename_dir(struct mailbox_list *oldlist, const char *oldname,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen struct mailbox_list *newlist, const char *newname,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen oldpath = mailbox_list_get_path(oldlist, oldname, type);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen newpath = mailbox_list_get_path(newlist, newname, type);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainenmaildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen struct mailbox_list *newlist, const char *newname)
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen const char *pattern, *oldpath, *newpath, *old_childname, *new_childname;
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen const char *const *names, *old_vname, *new_vname;
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen /* first get the list of the children and save them to memory, because
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen we can't rely on readdir() not skipping files while the directory
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen is being modified. this doesn't protect against modifications by
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen other processes though. */
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen pool = pool_alloconly_create("Maildir++ children list", 1024);
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen old_vname = t_strdup(mail_namespace_get_vname(oldlist->ns, str, oldname));
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen new_vname = t_strdup(mail_namespace_get_vname(newlist->ns, str, newname));
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen pattern = t_strdup_printf("%s%c*", old_vname, oldlist->ns->sep);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen iter = mailbox_list_iter_init(oldlist, pattern,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen while ((info = mailbox_list_iter_next(iter)) != NULL) {
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen /* verify that the prefix matches, otherwise we could have
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen problems with mailbox names containing '%' and '*' chars */
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen if (strncmp(info->name, old_vname, old_vnamelen) == 0 &&
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen info->name[old_vnamelen] == oldlist->ns->sep) {
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen name = p_strdup(pool, info->name + old_vnamelen);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen for (i = 0; i < count; i++) {
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen old_childname = mail_namespace_get_storage_name(oldlist->ns,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen /* When doing RENAME "a" "a.b" we see "a.b" here.
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen We don't want to rename it anymore to "a.b.b". */
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen new_childname = mail_namespace_get_storage_name(newlist->ns,
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen oldpath = mailbox_list_get_path(oldlist, old_childname,
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen newpath = mailbox_list_get_path(newlist, new_childname,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen /* FIXME: it's possible to merge two mailboxes if either one of
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen them doesn't have existing root mailbox. We could check this
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen but I'm not sure if it's worth it. It could be even
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen considered as a feature.
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen Anyway, the bug with merging is that if both mailboxes have
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen identically named child mailbox they conflict. Just ignore
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen those and leave them under the old mailbox. */
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen "rename(%s, %s) failed: %m", oldpath, newpath);
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen (void)rename_dir(oldlist, old_childname, newlist, new_childname,
c19f0dd4d274c15ef7b592dcc9d8c2c8c1e22abeTimo Sirainen (void)rename_dir(oldlist, old_childname, newlist, new_childname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainenmaildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen struct mailbox_list *newlist, const char *newname,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen /* NOTE: it's possible to rename a nonexisting mailbox which has
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen children. In that case we should ignore the rename() error. */
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen oldpath = mailbox_list_get_path(oldlist, oldname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen newpath = mailbox_list_get_path(newlist, newname,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen root_path = mailbox_list_get_path(oldlist, NULL,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen /* most likely INBOX */
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen t_strdup_printf("Renaming %s isn't supported.",
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen ret = maildir_rename_children(oldlist, oldname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen "Target mailbox already exists");
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
48ca4c43ebca6fa3eee217bd4439cba5b5376dd4Timo Sirainen .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,