static int mbox_mailbox_open(struct mailbox *box) { struct mbox_mailbox *mbox = (struct mbox_mailbox *)box; struct stat st; int ret; if (box->input != NULL) { i_stream_ref(box->input); mbox->mbox_file_stream = box->input; mbox->backend_readonly = TRUE; mbox->backend_readonly_set = TRUE; mbox->no_mbox_file = TRUE; return mbox_mailbox_open_finish(mbox, FALSE); } ret = stat(mailbox_get_path(box), &st); if (ret == 0) { if (!S_ISDIR(st.st_mode)) return mbox_mailbox_open_existing(mbox); mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, "Mailbox isn't selectable"); return -1; } else if (ENOTFOUND(errno)) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } else if (mail_storage_set_error_from_errno(box->storage)) { return -1; } else { mail_storage_set_critical(box->storage, "stat(%s) failed: %m", mailbox_get_path(box)); return -1; } }
static int acl_have_attribute_rights(struct mailbox *box) { int ret; /* RFC 5464: When the ACL extension [RFC4314] is present, users can only set and retrieve private or shared mailbox annotations on a mailbox on which they have the "l" right and any one of the "r", "s", "w", "i", or "p" rights. */ ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP); if (ret <= 0) { if (ret < 0) return -1; mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); return -1; } if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_READ) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE_SEEN) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_WRITE) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_INSERT) > 0) return 0; if (acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_POST) > 0) return 0; return -1; }
static void acl_mailbox_fail_not_found(struct mailbox *box) { int ret; ret = acl_mailbox_right_lookup(box, ACL_STORAGE_RIGHT_LOOKUP); if (ret > 0) { mail_storage_set_error(box->storage, MAIL_ERROR_PERM, MAIL_ERRSTR_NO_PERMISSION); } else if (ret == 0) { mail_storage_set_error(box->storage, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(box->vname)); } }
static int maildir_list_delete_dir(struct mailbox_list *list, const char *name) { const char *path; struct stat st; /* with maildir++ there aren't any non-selectable mailboxes. we'll always fail. */ if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR, &path) <= 0) i_unreached(); if (stat(path, &st) == 0) { mailbox_list_set_error(list, MAIL_ERROR_EXISTS, "Mailbox exists"); } else if (errno == ENOENT || errno == ENOTDIR) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); } else { mailbox_list_set_critical(list, "stat(%s) failed: %m", path); } return -1; }
int mailbox_list_delete_mailbox_file(struct mailbox_list *list, const char *name) { const char *path; path = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); /* we can simply unlink() the file */ if (unlink(path) == 0) return 0; else if (ENOTFOUND(errno)) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); return -1; } else { if (!mailbox_list_set_error_from_errno(list)) { mailbox_list_set_critical(list, "unlink(%s) failed: %m", path); } return -1; } }
int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list, const char *name, const char *trash_dir) { const char *src, *trash_dest; unsigned int count; src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (mailbox_list_check_root_delete(list, name, src) < 0) return -1; /* rename the mailbox dir to trash dir, which atomically marks it as being deleted. */ count = 0; trash_dest = trash_dir; for (; rename(src, trash_dest) < 0; count++) { if (ENOTFOUND(errno)) { if (trash_dest != trash_dir && count < 5) { /* either the source was just deleted or the trash dir was deleted. */ trash_dest = trash_dir; continue; } mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); return -1; } if (errno == EXDEV) { /* can't do this the fast way */ return 0; } if (!EDESTDIREXISTS(errno)) { if (mailbox_list_set_error_from_errno(list)) return -1; mailbox_list_set_critical(list, "rename(%s, %s) failed: %m", src, trash_dest); return -1; } /* trash dir already exists. the reasons for this are: a) another process is in the middle of deleting it b) previous process crashed and didn't delete it c) NFS: mailbox was recently deleted, but some connection still has that mailbox open. the directory contains .nfs* files that can't be deleted until the mailbox is closed. Because of c) we'll first try to rename the mailbox under the trash directory and only later try to delete the entire trash directory. */ if (trash_dir == trash_dest) { trash_dest = t_strconcat(trash_dir, "/", unique_fname(), NULL); } else if (mailbox_list_delete_trash(trash_dest) < 0 && (errno != ENOTEMPTY || count >= 5)) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", trash_dest); return -1; } } if (mailbox_list_delete_trash(trash_dir) < 0 && errno != ENOTEMPTY && errno != EBUSY) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", trash_dir); /* it's already renamed to trash dir, which means it's deleted as far as the client is concerned. Report success. */ } return 1; }
int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list, const char *name, const char *path, bool rmdir_path) { DIR *dir; struct dirent *d; string_t *full_path; unsigned int dir_len; bool mailbox_dir, unlinked_something = FALSE; if (mailbox_list_check_root_delete(list, name, path) < 0) return -1; dir = opendir(path); if (dir == NULL) { if (errno == ENOENT) { mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); } else { if (!mailbox_list_set_error_from_errno(list)) { mailbox_list_set_critical(list, "opendir(%s) failed: %m", path); } } return -1; } full_path = t_str_new(256); str_append(full_path, path); str_append_c(full_path, '/'); dir_len = str_len(full_path); for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) { if (d->d_name[0] == '.') { /* skip . and .. */ if (d->d_name[1] == '\0') continue; if (d->d_name[1] == '.' && d->d_name[2] == '\0') continue; } mailbox_dir = list->v.is_internal_name != NULL && list->v.is_internal_name(list, d->d_name); str_truncate(full_path, dir_len); str_append(full_path, d->d_name); if (mailbox_dir) { if (mailbox_list_delete_trash(str_c(full_path)) < 0) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", str_c(full_path)); } else { unlinked_something = TRUE; } continue; } /* trying to unlink() a directory gives either EPERM or EISDIR (non-POSIX). it doesn't really work anywhere in practise, so don't bother stat()ing the file first */ if (unlink(str_c(full_path)) == 0) unlinked_something = TRUE; else if (errno != ENOENT && errno != EISDIR && errno != EPERM) { mailbox_list_set_critical(list, "unlink_directory(%s) failed: %m", str_c(full_path)); } } if (errno != 0) mailbox_list_set_critical(list, "readdir(%s) failed: %m", path); if (closedir(dir) < 0) { mailbox_list_set_critical(list, "closedir(%s) failed: %m", path); } if (rmdir_path) { if (rmdir(path) == 0) unlinked_something = TRUE; else if (errno != ENOENT && errno != ENOTEMPTY && errno != EEXIST) { mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path); return -1; } } if (!unlinked_something) { mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, "Mailbox has children, can't delete it"); return -1; } return 0; }
static int maildir_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname) { const char *oldpath, *newpath, *root_path; int ret; bool found; /* NOTE: it's possible to rename a nonexistent mailbox which has children. In that case we should ignore the rename() error. */ if (mailbox_list_get_path(oldlist, oldname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &oldpath) <= 0 || mailbox_list_get_path(newlist, newname, MAILBOX_LIST_PATH_TYPE_MAILBOX, &newpath) <= 0) i_unreached(); root_path = mailbox_list_get_root_forced(oldlist, MAILBOX_LIST_PATH_TYPE_MAILBOX); if (strcmp(oldpath, root_path) == 0) { /* most likely INBOX */ mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, t_strdup_printf("Renaming %s isn't supported.", oldname)); return -1; } /* if we're renaming under another mailbox, require its permissions to be same as ours. */ if (strchr(newname, mailbox_list_get_hierarchy_sep(newlist)) != NULL) { struct mailbox_permissions old_perm, new_perm; mailbox_list_get_permissions(oldlist, oldname, &old_perm); mailbox_list_get_permissions(newlist, newname, &new_perm); if ((new_perm.file_create_mode != old_perm.file_create_mode || new_perm.dir_create_mode != old_perm.dir_create_mode || new_perm.file_create_gid != old_perm.file_create_gid)) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTPOSSIBLE, "Renaming not supported across conflicting " "directory permissions"); return -1; } } ret = rename(oldpath, newpath); if (ret == 0 || errno == ENOENT) { (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_CONTROL); (void)rename_dir(oldlist, oldname, newlist, newname, MAILBOX_LIST_PATH_TYPE_INDEX); found = ret == 0; T_BEGIN { ret = maildir_rename_children(oldlist, oldname, newlist, newname); } T_END; if (ret < 0) return -1; if (!found && ret == 0) { mailbox_list_set_error(oldlist, MAIL_ERROR_NOTFOUND, T_MAIL_ERR_MAILBOX_NOT_FOUND(oldname)); return -1; } return 0; }