Esempio n. 1
0
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));
	}
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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;
	}
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
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;
}
Esempio n. 8
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;
	}