bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic struct mailbox_list *fs_list_alloc(void)
4c1deab456fe8877bf025d11843167ac1f36327aTimo Sirainen pool = pool_alloconly_create("fs list", 2048);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen list = p_new(pool, struct fs_mailbox_list, 1);
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen list->temp_prefix = p_strconcat(pool, GLOBAL_TEMP_PREFIX,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic void fs_list_deinit(struct mailbox_list *_list)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic char fs_list_get_hierarchy_sep(struct mailbox_list *list ATTR_UNUSED)
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainenstatic const char *
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainenfs_list_get_path_to(const struct mailbox_list_settings *set,
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainen if (*set->maildir_name != '\0' && set->index_control_use_maildir_name) {
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainen return t_strdup_printf("%s/%s%s/%s", root_dir,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenfs_list_get_path(struct mailbox_list *_list, const char *name,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen enum mailbox_list_path_type type, const char **path_r)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const struct mailbox_list_settings *set = &_list->set;
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* return root directories */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen return mailbox_list_set_get_root_path(set, type, path_r) ? 1 : 0;
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen i_assert(mailbox_list_is_valid_name(_list, name, &error));
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_try_get_absolute_path(_list, &name)) {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = t_strdup_printf("%s/%s%s", set->root_dir,
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainen /* maildir_name is for the mailbox, caller is asking
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainen for the directory name */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = t_strdup_printf("%s/%s%s", set->alt_dir,
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainen *path_r = fs_list_get_path_to(set, set->control_dir, name);
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen *path_r = fs_list_get_path_to(set, set->index_cache_dir, name);
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen /* fall through */
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainen *path_r = fs_list_get_path_to(set, set->index_dir, name);
12b4dbf933ee54f7b96968ba150095baa985fdafTimo Sirainen *path_r = fs_list_get_path_to(set, set->index_pvt_dir, name);
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen /* don't use inbox_path */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen } else if (strcmp(name, "INBOX") == 0 && set->inbox_path != NULL) {
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen /* If INBOX is a file, index and control directories are
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen located in root directory. */
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen if ((_list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) == 0 ||
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = t_strdup_printf("%s/%s%s", root_dir,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = t_strdup_printf("%s/%s%s/%s", root_dir,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic const char *
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainenfs_list_get_temp_prefix(struct mailbox_list *_list, bool global)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
c040ee67d0ac0fb7375bb543965bf67dcae6affaTimo Sirainen return global ? GLOBAL_TEMP_PREFIX : list->temp_prefix;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainenstatic const char *
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainenfs_list_join_refpattern(struct mailbox_list *_list ATTR_UNUSED,
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen /* pattern overrides reference */
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen /* merge reference and pattern */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic int fs_list_set_subscribed(struct mailbox_list *_list,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen struct fs_mailbox_list *list = (struct fs_mailbox_list *)_list;
3568ea090b5a072e498438e74db23b98103ff2deTimo Sirainen mailbox_list_set_error(_list, MAIL_ERROR_NOTPOSSIBLE,
3568ea090b5a072e498438e74db23b98103ff2deTimo Sirainen "Subscriptions not supported");
177295e3c4000a9ab0ff27ce4e6eba6b96aeac40Timo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR;
177295e3c4000a9ab0ff27ce4e6eba6b96aeac40Timo Sirainen path = t_strconcat(mailbox_list_get_root_forced(_list, type),
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return subsfile_set_subscribed(_list, path, list->temp_prefix,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainenstatic const char *mailbox_list_fs_get_trash_dir(struct mailbox_list *list)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen return t_strdup_printf("%s/"MAILBOX_LIST_FS_TRASH_DIR_NAME, root_dir);
fd057522c580091ec9803c0dceb4747d8bcaece5Timo Sirainenfs_list_delete_maildir(struct mailbox_list *list, const char *name)
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen trash_dir = mailbox_list_fs_get_trash_dir(list);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen ret = mailbox_list_delete_maildir_via_trash(list, name,
fd057522c580091ec9803c0dceb4747d8bcaece5Timo Sirainen /* try to delete the parent directory */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX,
fd057522c580091ec9803c0dceb4747d8bcaece5Timo Sirainen return mailbox_list_delete_mailbox_nonrecursive(list, name, path,
fd057522c580091ec9803c0dceb4747d8bcaece5Timo Sirainenstatic int fs_list_delete_mailbox(struct mailbox_list *list, const char *name)
fd057522c580091ec9803c0dceb4747d8bcaece5Timo Sirainen if ((list->flags & MAILBOX_LIST_FLAG_MAILBOX_FILES) != 0) {
e0aff4c7e3336ec4b5edbcfc3a72e1e118603ee2Timo Sirainen ret = mailbox_list_delete_mailbox_file(list, name, path);
eaa2d473ed2ecdb9856cd98a33f4d3063cfaf2a1Timo Sirainen mailbox_list_delete_until_root(list, path, MAILBOX_LIST_PATH_TYPE_MAILBOX);
3fb442057c352645e918314815f8fe2a12b6ee2bTimo Sirainen return mailbox_list_delete_finish_ret(list, name, ret == 0);
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainenstatic int fs_list_rmdir(struct mailbox_list *list, const char *name,
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainen mailbox_list_add_change(list, MAILBOX_LOG_RECORD_DELETE_DIR,
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainenstatic int fs_list_delete_dir(struct mailbox_list *list, const char *name)
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainen const char *path, *child_name, *child_path, *p;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen /* it should exist only in the mail directory */
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen /* the primary list location is the index directory, but it
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen exists in both index and mail directories. */
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_INDEX,
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen /* partial existence: exists in _DIR, but not in
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen _INDEX. return success anyway. */
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen /* a) both directories didn't exist
1728ff34ee03de825ad3aeed67d19f8ae140ee2eTimo Sirainen b) index directory couldn't be rmdir()ed for some reason */
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
10a2e8716e9040908fb60fcda56b5315ea4c1312Timo Sirainen } else if (errno == ENOTEMPTY || errno == EEXIST) {
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainen /* mbox workaround: if only .imap/ directory is preventing the
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainen deletion, remove it */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen child_name = t_strdup_printf("%s%cchild", name, sep);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen strncmp(path, child_path, strlen(path)) == 0) {
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainen /* drop the "/child" part out. */
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainen if (rmdir(t_strdup_until(child_path, p)) == 0) {
095c07765b69592c1dd644361f05ee37b1d39bd4Timo Sirainen /* try again */
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen mailbox_list_set_error(list, MAIL_ERROR_EXISTS,
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen "Mailbox has children, delete them first");
2f30b72d49fbff0c4096125c139e4bdfef45669cTimo Sirainen mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainenstatic int rename_dir(struct mailbox_list *oldlist, const char *oldname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen struct mailbox_list *newlist, const char *newname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen enum mailbox_list_path_type type, bool rmdir_parent)
253201e2b423d3eceb6a8b41cb3493edeab4d224Timo Sirainen const char *oldpath, *newpath, *p, *oldparent, *newparent;
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(oldlist, oldname, type, &oldpath) <= 0 ||
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen mailbox_list_get_path(newlist, newname, type, &newpath) <= 0)
253201e2b423d3eceb6a8b41cb3493edeab4d224Timo Sirainen oldparent = p == NULL ? "/" : t_strdup_until(oldpath, p);
253201e2b423d3eceb6a8b41cb3493edeab4d224Timo Sirainen newparent = p == NULL ? "/" : t_strdup_until(newpath, p);
253201e2b423d3eceb6a8b41cb3493edeab4d224Timo Sirainen if (strcmp(oldparent, newparent) != 0 && stat(oldpath, &st) == 0) {
253201e2b423d3eceb6a8b41cb3493edeab4d224Timo Sirainen /* make sure the newparent exists */
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen mailbox_list_get_root_permissions(newlist, &perm);
76b91bac787101e6b0075122ab6478dd98c8a884Timo Sirainen if (mkdir_parents_chgrp(newparent, perm.dir_create_mode,
253201e2b423d3eceb6a8b41cb3493edeab4d224Timo Sirainen if (mailbox_list_set_error_from_errno(oldlist))
f35141938f1ce4fd822a589045c7a01e866922a2Timo Sirainen if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen if (rmdir_parent && (p = strrchr(oldpath, '/')) != NULL) {
ae1a57954535642c09c3b8aee184736ddbb06cdfTimo Sirainen /* avoid leaving empty directories lying around */
ae1a57954535642c09c3b8aee184736ddbb06cdfTimo Sirainen mailbox_list_delete_until_root(oldlist, oldpath, type);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainenstatic int fs_list_rename_mailbox(struct mailbox_list *oldlist,
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen const char *oldvname, *oldpath, *newpath, *alt_newpath, *root_path, *p;
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen struct mailbox_permissions old_perm, new_perm;
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen oldvname = mailbox_list_get_vname(oldlist, oldname);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (mailbox_list_get_storage(&oldlist, oldvname, &oldstorage) < 0)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(newlist, newname, MAILBOX_LIST_PATH_TYPE_ALT_DIR,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen root_path = mailbox_list_get_root_forced(oldlist, MAILBOX_LIST_PATH_TYPE_MAILBOX);
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.",
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen mailbox_list_get_permissions(oldlist, oldname, &old_perm);
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen mailbox_list_get_permissions(newlist, newname, &new_perm);
4b2e3b39bc79281211ba9887ab1ebc6fa21da79aTimo Sirainen /* if we're renaming under another mailbox, require its permissions
4b2e3b39bc79281211ba9887ab1ebc6fa21da79aTimo Sirainen to be same as ours. */
4b2e3b39bc79281211ba9887ab1ebc6fa21da79aTimo Sirainen if (strchr(newname, mailbox_list_get_hierarchy_sep(newlist)) != NULL &&
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen (new_perm.file_create_mode != old_perm.file_create_mode ||
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen new_perm.dir_create_mode != old_perm.dir_create_mode ||
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen new_perm.file_create_gid != old_perm.file_create_gid)) {
4b2e3b39bc79281211ba9887ab1ebc6fa21da79aTimo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
4b2e3b39bc79281211ba9887ab1ebc6fa21da79aTimo Sirainen "Renaming not supported across conflicting "
4b2e3b39bc79281211ba9887ab1ebc6fa21da79aTimo Sirainen "directory permissions");
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen /* create the hierarchy */
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen if (mkdir_parents_chgrp(p, new_perm.dir_create_mode,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen if (mailbox_list_set_error_from_errno(oldlist))
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen "mkdir_parents(%s) failed: %m", p);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen /* first check that the destination mailbox doesn't exist.
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen this is racy, but we need to be atomic and there's hardly any
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen possibility that someone actually tries to rename two mailboxes
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen to same new one */
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen "Target mailbox already exists");
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen "Target mailbox doesn't allow inferior mailboxes");
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen } else if (errno != ENOENT && errno != EACCES) {
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_critical(oldlist, "lstat(%s) failed: %m",
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen /* race condition or a directory left there lying around?
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen safest to just report error. */
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_EXISTS,
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen "Target mailbox already exists");
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen mailbox_list_set_critical(oldlist, "stat(%s) failed: %m",
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND,
bdb026e2dc8a7c77585ed5ba489f0056df8074d4Timo Sirainen T_MAILBOX_LIST_ERR_NOT_FOUND(oldlist, oldname));
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen } else if (!mailbox_list_set_error_from_errno(oldlist)) {
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen "rename(%s, %s) failed: %m", oldpath, newpath);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
71e88fae3be360e9a93b3398e743f99a6f05d2edTimo Sirainen MAILBOX_LIST_PATH_TYPE_ALT_DIR, rmdir_parent);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL, rmdir_parent);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen MAILBOX_LIST_PATH_TYPE_INDEX_CACHE, rmdir_parent);
48ca4c43ebca6fa3eee217bd4439cba5b5376dd4Timo Sirainen .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_hierarchy_sep = fs_list_get_hierarchy_sep,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_storage_name = mailbox_list_default_get_storage_name,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_mailbox_flags = fs_list_get_mailbox_flags,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .subscriptions_refresh = mailbox_list_subscriptions_refresh,