mailbox-list-maildir.c revision 18ffea71d9beeec3cc1d400f751926ee72807f62
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen/* Copyright (C) 2006-2007 Timo Sirainen */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenextern struct mailbox_list maildir_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);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen p_strconcat(pool, "temp.", my_hostname, ".", my_pid, ".", NULL);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic void maildir_list_deinit(struct mailbox_list *_list)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenstatic const char *
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_get_absolute_path(struct mailbox_list *list, const char *name)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen const char *p;
83c21c990eb2a370f0da56240e73dac846f4acc3Timo Sirainen /* fallback to using as ~name */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return t_strdup_printf("%s/%c%s", 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. */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_is_valid_mask(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))
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
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)
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
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 */
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen i_assert(mailbox_list_is_valid_existing_name(_list, name));
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return maildir_list_get_absolute_path(_list, name);
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return t_strdup_printf("%s/%c%s", _list->set.index_dir,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen return t_strdup_printf("%s/%c%s", _list->set.root_dir,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_get_mailbox_name_status(struct mailbox_list *_list,
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainen if (strcmp(name, "INBOX") == 0 || stat(path, &st) == 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 *
a27e065f1a1f91c7fbdf7c2ea1c387441af0cbb3Timo Sirainenmaildir_list_get_temp_prefix(struct mailbox_list *_list)
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,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstatic int rename_dir(struct mailbox_list *list,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen oldpath = mailbox_list_get_path(list, oldname, type);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen newpath = mailbox_list_get_path(list, newname, type);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen if (rename(oldpath, newpath) < 0 && errno != ENOENT) {
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstatic int rename_children(struct mailbox_list *list,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen const char *mask, *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);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen while ((info = mailbox_list_iter_next(iter)) != NULL) {
b039dabf4c53f72454e795930e7643b6e0e625f9Timo 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);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen new_listname = t_strconcat(newname, names[i], NULL);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen oldpath = mailbox_list_get_path(list, old_listname,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen newpath = mailbox_list_get_path(list, 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);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
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);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainenstatic int maildir_list_rename_mailbox(struct mailbox_list *list,
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. */
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen oldpath = mailbox_list_get_path(list, oldname,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen newpath = mailbox_list_get_path(list, newname,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_CONTROL,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen (void)rename_dir(list, MAILBOX_LIST_PATH_TYPE_INDEX,
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen ret = rename_children(list, oldname, newname);
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
ff7056842f14fd3b30a2d327dfab165b9d15dd30Timo Sirainen "Target mailbox already exists");
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen mailbox_list_set_critical(list, "rename(%s, %s) failed: %m",