mailbox-list-maildir.c revision 2fb976923a6d6e45a7c2da90484944b2fac50c06
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2006-2009 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)
b5a084602a3dc2c118345f6d2990f47c1ff66264Timo Sirainen pool = pool_alloconly_create("maildir++ list", 1024);
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 */
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (_list->mail_set->mail_full_filesystem_access &&
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return maildir_list_get_absolute_path(_list, name);
1d266a7419fafa25f3505a12217452a8c647074fTimo Sirainen if (strcmp(name, "INBOX") == 0 && _list->set.inbox_path != NULL)
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainen return maildir_list_get_dirname_path(_list, _list->set.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,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainenstatic int rename_dir(struct mailbox_list *oldlist, const char *oldname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen struct mailbox_list *newlist, const char *newname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen oldpath = mailbox_list_get_path(oldlist, oldname, type);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen newpath = mailbox_list_get_path(newlist, newname, type);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen mailbox_list_set_critical(oldlist, "rename(%s, %s) failed: %m",
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainenmaildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen struct mailbox_list *newlist, const char *newname)
533bfba437e4120aa29dd45bca2aa87e30ee28a2Timo Sirainen const char *pattern, *oldpath, *newpath, *old_listname, *new_listname;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen const char *const *names;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen unsigned int i, count;
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen /* first get the list of the children and save them to memory, because
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen we can't rely on readdir() not skipping files while the directory
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen is being modified. this doesn't protect against modifications by
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen other processes though. */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen pool = pool_alloconly_create("Maildir++ children list", 1024);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen old_sep = mailbox_list_get_hierarchy_sep(oldlist);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen pattern = t_strdup_printf("%s%c*", oldname, old_sep);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen iter = mailbox_list_iter_init(oldlist, pattern,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen while ((info = mailbox_list_iter_next(iter)) != NULL) {
33f002135617b5b34577433b30c32560bd4b5e3cTimo Sirainen /* verify that the prefix matches, otherwise we could have
33f002135617b5b34577433b30c32560bd4b5e3cTimo Sirainen problems with mailbox names containing '%' and '*' chars */
33f002135617b5b34577433b30c32560bd4b5e3cTimo Sirainen if (strncmp(info->name, oldname, oldnamelen) == 0 &&
33f002135617b5b34577433b30c32560bd4b5e3cTimo Sirainen name = p_strdup(pool, info->name + oldnamelen);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen for (i = 0; i < count; i++) {
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen old_listname = t_strconcat(oldname, names[i], NULL);
738cb8e908b144d6dd6f6d7484a40ad67054aac5Timo Sirainen /* When doing RENAME "a" "a.b" we see "a.b" here.
738cb8e908b144d6dd6f6d7484a40ad67054aac5Timo Sirainen We don't want to rename it anymore to "a.b.b". */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen new_listname = t_strconcat(newname, names[i], NULL);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen oldpath = mailbox_list_get_path(oldlist, old_listname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen newpath = mailbox_list_get_path(newlist, new_listname,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen /* FIXME: it's possible to merge two mailboxes if either one of
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen them doesn't have existing root mailbox. We could check this
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen but I'm not sure if it's worth it. It could be even
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen considered as a feature.
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen Anyway, the bug with merging is that if both mailboxes have
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen identically named child mailbox they conflict. Just ignore
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen those and leave them under the old mailbox. */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen "rename(%s, %s) failed: %m", oldpath, newpath);
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, old_listname, newlist, new_listname,
91b203fd2132510a47a4b34252c0ae0efd688a19Timo Sirainen (void)rename_dir(oldlist, old_listname, newlist, new_listname,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenmaildir_list_delete_mailbox(struct mailbox_list *list, const char *name)
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen /* let the backend handle the rest */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen return mailbox_list_delete_index_control(list, name);
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,
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",
63ca9cacc5d2d1b1cebfc430bb89637f8c138e4cTimo Sirainen MEMBER(name) MAILBOX_LIST_NAME_MAILDIRPLUSPLUS,
bbd0a870f8639767e4e4011d2aedadac08d5c66fTimo Sirainen MEMBER(props) MAILBOX_LIST_PROP_NO_MAILDIR_NAME,