bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
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,
e12d2886114e48ccd871ae73fd0a962375096a8aTimo Sirainen else if (list->name == imapdir_mailbox_list.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),
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic char maildir_list_get_hierarchy_sep(struct mailbox_list *_list)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_get_path(struct mailbox_list *_list, const char *name,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen enum mailbox_list_path_type type, const char **path_r)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen /* return root directories */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen return mailbox_list_set_get_root_path(&_list->set, type,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (_list->mail_set->mail_full_filesystem_access &&
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = maildir_list_get_absolute_path(_list, name);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = maildir_list_get_dirname_path(_list,
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen *path_r = maildir_list_get_dirname_path(_list,
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen /* fall through */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = maildir_list_get_dirname_path(_list,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = maildir_list_get_dirname_path(_list,
3c493c276f599d9b9cd10764876d648003046954Timo Sirainen /* don't use inbox_path */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen } else if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL) {
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen *path_r = maildir_list_get_dirname_path(_list, root_dir, name);
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,
3568ea090b5a072e498438e74db23b98103ff2deTimo Sirainen mailbox_list_set_error(_list, MAIL_ERROR_NOTPOSSIBLE,
3568ea090b5a072e498438e74db23b98103ff2deTimo Sirainen "Subscriptions not supported");
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen path = t_strconcat(_list->set.control_dir != NULL ?
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return subsfile_set_subscribed(_list, path, list->temp_prefix,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstatic const char *
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenmailbox_list_maildir_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/%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) {
e0aff4c7e3336ec4b5edbcfc3a72e1e118603ee2Timo Sirainen ret = mailbox_list_delete_mailbox_file(list, name, path);
fd057522c580091ec9803c0dceb4747d8bcaece5Timo Sirainen ret = maildir_list_delete_maildir(list, name);
3fb442057c352645e918314815f8fe2a12b6ee2bTimo Sirainen return mailbox_list_delete_finish_ret(list, name, ret == 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. */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (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");
1e4623e33bc4e37e61fcdc1e24e22327e49e303aTimo Sirainen } else if (errno == ENOENT || errno == ENOTDIR) {
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,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(oldlist, oldname, type, &oldpath) <= 0 ||
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen mailbox_list_get_path(newlist, newname, type, &newpath) <= 0)
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);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen old_vname = mailbox_list_get_vname(oldlist, oldname);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen new_vname = mailbox_list_get_vname(newlist, newname);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen old_ns_sep = mail_namespace_get_sep(oldlist->ns);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen pattern = t_strdup_printf("%s%c*", old_vname, old_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 */
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen if (strncmp(info->vname, old_vname, old_vnamelen) == 0 &&
402e999a878e0cc41a0afb830fea0a93afc75f0dTimo Sirainen name = p_strdup(pool, info->vname + old_vnamelen);
2649b237dd4690575e75a30b2bf3b39ebd37b835Timo Sirainen for (i = 0; i < count; i++) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen old_childname = mailbox_list_get_storage_name(oldlist,
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". */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen new_childname = mailbox_list_get_storage_name(newlist,
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_list_get_path(oldlist, old_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,
638600575ee95f2513c683ef09cb188f76eacd22Timo Sirainen (void)rename_dir(oldlist, old_childname, newlist, new_childname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainenmaildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname,
71e88fae3be360e9a93b3398e743f99a6f05d2edTimo Sirainen struct mailbox_list *newlist, const char *newname)
7fc0f80480063a9d4cb9e8c07b50db2a5627799eTimo Sirainen /* NOTE: it's possible to rename a nonexistent mailbox which has
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen children. In that case we should ignore the rename() error. */
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX, &oldpath) <= 0 ||
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen MAILBOX_LIST_PATH_TYPE_MAILBOX, &newpath) <= 0)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen root_path = mailbox_list_get_root_forced(oldlist,
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.",
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 struct mailbox_permissions old_perm, new_perm;
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen mailbox_list_get_permissions(oldlist, oldname, &old_perm);
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen mailbox_list_get_permissions(newlist, newname, &new_perm);
47d5cc09738defd0020c797866e2a25a344aa65aTimo Sirainen if ((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");
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, oldname, newlist, newname,
638600575ee95f2513c683ef09cb188f76eacd22Timo 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,
bdb026e2dc8a7c77585ed5ba489f0056df8074d4Timo Sirainen T_MAILBOX_LIST_ERR_NOT_FOUND(oldlist, oldname));
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,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_hierarchy_sep = maildir_list_get_hierarchy_sep,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_storage_name = mailbox_list_default_get_storage_name,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_temp_prefix = maildir_list_get_temp_prefix,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_mailbox_flags = maildir_list_get_mailbox_flags,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .subscriptions_refresh = mailbox_list_subscriptions_refresh,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .set_subscribed = maildir_list_set_subscribed,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .delete_mailbox = maildir_list_delete_mailbox,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .delete_symlink = mailbox_list_delete_symlink_default,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .rename_mailbox = maildir_list_rename_mailbox,
48ca4c43ebca6fa3eee217bd4439cba5b5376dd4Timo Sirainen .mailbox_name_max_length = MAILBOX_LIST_NAME_MAX_LENGTH,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_hierarchy_sep = maildir_list_get_hierarchy_sep,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_storage_name = mailbox_list_default_get_storage_name,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_temp_prefix = maildir_list_get_temp_prefix,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .get_mailbox_flags = maildir_list_get_mailbox_flags,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .subscriptions_refresh = mailbox_list_subscriptions_refresh,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .set_subscribed = maildir_list_set_subscribed,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .delete_mailbox = maildir_list_delete_mailbox,
a384b1fccb51884e8afe0f571420961851a2149dTimo Sirainen .delete_symlink = mailbox_list_delete_symlink_default,