Пример #1
0
int mailbox_list_delete_symlink_default(struct mailbox_list *list,
					const char *name)
{
	const char *path;
	int ret;

	ret = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_DIR,
				    &path);
	if (ret < 0)
		return -1;
	i_assert(ret > 0);

	if (unlink(path) == 0)
		return 0;

	if (errno == ENOENT) {
		mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
			T_MAILBOX_LIST_ERR_NOT_FOUND(list, name));
	} else if (errno == EISDIR ||
		   errno == EPERM) { /* Solaris */
		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
				       "Mailbox isn't a symlink");
	} else {
		mailbox_list_set_critical(list, "unlink(%s) failed: %m", path);
	}
	return -1;
}
Пример #2
0
static void
imapc_list_copy_error_from_reply(struct imapc_mailbox_list *list,
				 enum mail_error default_error,
				 const struct imapc_command_reply *reply)
{
	enum mail_error error;

	if (imap_resp_text_code_parse(reply->resp_text_key, &error)) {
		mailbox_list_set_error(&list->list, error,
				       reply->text_without_resp);
	} else {
		mailbox_list_set_error(&list->list, default_error,
				       reply->text_without_resp);
	}
}
Пример #3
0
static void shared_list_copy_error(struct mailbox_list *shared_list,
				   struct mail_namespace *backend_ns)
{
	const char *str;
	enum mail_error error;

	str = mailbox_list_get_last_error(backend_ns->list, &error);
	mailbox_list_set_error(shared_list, error, str);
}
Пример #4
0
static int
mailbox_list_check_root_delete(struct mailbox_list *list, const char *name,
			       const char *path)
{
	const char *root_dir;

	root_dir = mailbox_list_get_root_forced(list, MAILBOX_LIST_PATH_TYPE_DIR);
	if (strcmp(root_dir, path) != 0)
		return 0;

	if (strcmp(name, "INBOX") == 0 &&
	    (list->ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0) {
		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
				       "INBOX can't be deleted.");
		return -1;
	}
	mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
			       "Mail storage root can't be deleted.");
	return -1;
}
Пример #5
0
static void subswrite_set_syscall_error(struct mailbox_list *list,
					const char *function, const char *path)
{
	if (errno == EACCES && !list->mail_set->mail_debug) {
		mailbox_list_set_error(list, MAIL_ERROR_PERM,
				       "No permission to modify subscriptions");
	} else {
		mailbox_list_set_critical(list,
			"%s failed with subscription file %s: %m",
			function, path);
	}
}
Пример #6
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;
}
Пример #7
0
int mailbox_list_delete_mailbox_file(struct mailbox_list *list,
				     const char *name, const char *path)
{
	/* 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_MAILBOX_LIST_ERR_NOT_FOUND(list, name));
		return -1;
	} else {
		if (!mailbox_list_set_error_from_errno(list)) {
			mailbox_list_set_critical(list,
				"unlink(%s) failed: %m", path);
		}
		return -1;
	}
}
Пример #8
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;
}
Пример #9
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;
}
Пример #10
0
int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
			    const char *temp_prefix, const char *name,
			    bool set)
{
	const struct mail_storage_settings *mail_set = list->mail_set;
	struct dotlock_settings dotlock_set;
	struct dotlock *dotlock;
	struct mailbox_permissions perm;
	const char *line, *dir, *fname, *escaped_name;
	struct istream *input = NULL;
	struct ostream *output;
	int fd_in, fd_out;
	enum mailbox_list_path_type type;
	bool found, changed = FALSE, failed = FALSE;
	unsigned int version = 2;

	if (strcasecmp(name, "INBOX") == 0)
		name = "INBOX";

	memset(&dotlock_set, 0, sizeof(dotlock_set));
	dotlock_set.use_excl_lock = mail_set->dotlock_use_excl;
	dotlock_set.nfs_flush = mail_set->mail_nfs_storage;
	dotlock_set.temp_prefix = temp_prefix;
	dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
	dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;

	mailbox_list_get_root_permissions(list, &perm);
	fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
					 perm.file_create_mode,
					 perm.file_create_gid,
					 perm.file_create_gid_origin, &dotlock);
	if (fd_out == -1 && errno == ENOENT) {
		/* directory hasn't been created yet. */
		type = list->set.control_dir != NULL ?
			MAILBOX_LIST_PATH_TYPE_CONTROL :
			MAILBOX_LIST_PATH_TYPE_DIR;
		fname = strrchr(path, '/');
		if (fname != NULL) {
			dir = t_strdup_until(path, fname);
			if (mailbox_list_mkdir_root(list, dir, type) < 0)
				return -1;
		}
		fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
						 perm.file_create_mode,
						 perm.file_create_gid,
						 perm.file_create_gid_origin,
						 &dotlock);
	}
	if (fd_out == -1) {
		if (errno == EAGAIN) {
			mailbox_list_set_error(list, MAIL_ERROR_TEMP,
				"Timeout waiting for subscription file lock");
		} else {
			subswrite_set_syscall_error(list, "file_dotlock_open()",
						    path);
		}
		return -1;
	}

	fd_in = nfs_safe_open(path, O_RDONLY);
	if (fd_in == -1 && errno != ENOENT) {
		subswrite_set_syscall_error(list, "open()", path);
		file_dotlock_delete(&dotlock);
		return -1;
	}
	if (fd_in != -1) {
		input = i_stream_create_fd_autoclose(&fd_in, list->mailbox_name_max_length+1);
		i_stream_set_return_partial_line(input, TRUE);
		subsfile_list_read_header(list, input, &version);
	}

	found = FALSE;
	output = o_stream_create_fd_file(fd_out, 0, FALSE);
	o_stream_cork(output);
	if (version >= 2)
		o_stream_send_str(output, version2_header);
	if (version < 2 || name[0] == '\0')
		escaped_name = name;
	else {
		const char *const *tmp;
		char separators[2];
		string_t *str = t_str_new(64);

		separators[0] = mailbox_list_get_hierarchy_sep(list);
		separators[1] = '\0';
		tmp = t_strsplit(name, separators);
		str_append_tabescaped(str, *tmp);
		for (tmp++; *tmp != NULL; tmp++) {
			str_append_c(str, '\t');
			str_append_tabescaped(str, *tmp);
		}
		escaped_name = str_c(str);
	}
	if (input != NULL) {
		while ((line = next_line(list, path, input,
					 &failed, FALSE)) != NULL) {
			if (strcmp(line, escaped_name) == 0) {
				found = TRUE;
				if (!set) {
					changed = TRUE;
					continue;
				}
			}

			o_stream_nsend_str(output, line);
			o_stream_nsend(output, "\n", 1);
		}
		i_stream_destroy(&input);
	}

	if (!failed && set && !found) {
		/* append subscription */
		line = t_strconcat(escaped_name, "\n", NULL);
		o_stream_nsend_str(output, line);
		changed = TRUE;
	}

	if (changed && !failed) {
		if (o_stream_nfinish(output) < 0) {
			subswrite_set_syscall_error(list, "write()", path);
			failed = TRUE;
		} else if (mail_set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
			if (fsync(fd_out) < 0) {
				subswrite_set_syscall_error(list, "fsync()",
							    path);
				failed = TRUE;
			}
		}
	} else {
		o_stream_ignore_last_errors(output);
	}
	o_stream_destroy(&output);

	if (failed || !changed) {
		if (file_dotlock_delete(&dotlock) < 0) {
			subswrite_set_syscall_error(list,
				"file_dotlock_delete()", path);
			failed = TRUE;
		}
	} else {
		enum dotlock_replace_flags flags =
			DOTLOCK_REPLACE_FLAG_VERIFY_OWNER;
		if (file_dotlock_replace(&dotlock, flags) < 0) {
			subswrite_set_syscall_error(list,
				"file_dotlock_replace()", path);
			failed = TRUE;
		}
	}
	return failed ? -1 : (changed ? 1 : 0);
}
Пример #11
0
int subsfile_set_subscribed(struct mailbox_list *list, const char *path,
			    const char *temp_prefix, const char *name, bool set)
{
	const struct mail_storage_settings *mail_set = list->mail_set;
	struct dotlock_settings dotlock_set;
	struct dotlock *dotlock;
	const char *line, *origin;
	struct istream *input;
	struct ostream *output;
	int fd_in, fd_out;
	mode_t mode;
	gid_t gid;
	bool found, changed = FALSE, failed = FALSE;

	if (strcasecmp(name, "INBOX") == 0)
		name = "INBOX";

	memset(&dotlock_set, 0, sizeof(dotlock_set));
	dotlock_set.use_excl_lock = mail_set->dotlock_use_excl;
	dotlock_set.nfs_flush = mail_set->mail_nfs_storage;
	dotlock_set.temp_prefix = temp_prefix;
	dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
	dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;

	mailbox_list_get_permissions(list, NULL, &mode, &gid, &origin);
	fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
					 mode, gid, origin, &dotlock);
	if (fd_out == -1 && errno == ENOENT) {
		/* directory hasn't been created yet. */
		if (mailbox_list_create_parent_dir(list, NULL, path) < 0)
			return -1;
		fd_out = file_dotlock_open_group(&dotlock_set, path, 0,
						 mode, gid, origin, &dotlock);
	}
	if (fd_out == -1) {
		if (errno == EAGAIN) {
			mailbox_list_set_error(list, MAIL_ERROR_TEMP,
				"Timeout waiting for subscription file lock");
		} else {
			subswrite_set_syscall_error(list, "file_dotlock_open()",
						    path);
		}
		return -1;
	}

	fd_in = nfs_safe_open(path, O_RDONLY);
	if (fd_in == -1 && errno != ENOENT) {
		subswrite_set_syscall_error(list, "open()", path);
		(void)file_dotlock_delete(&dotlock);
		return -1;
	}

	input = fd_in == -1 ? NULL :
		i_stream_create_fd(fd_in, list->mailbox_name_max_length+1,
				   TRUE);
	output = o_stream_create_fd_file(fd_out, 0, FALSE);
	o_stream_cork(output);
	found = FALSE;
	while ((line = next_line(list, path, input,
				 &failed, FALSE)) != NULL) {
		if (strcmp(line, name) == 0) {
			found = TRUE;
			if (!set) {
				changed = TRUE;
				continue;
			}
		}

		(void)o_stream_send_str(output, line);
		(void)o_stream_send(output, "\n", 1);
	}

	if (!failed && set && !found) {
		/* append subscription */
		line = t_strconcat(name, "\n", NULL);
		(void)o_stream_send_str(output, line);
		changed = TRUE;
	}

	if (changed && !failed) {
		if (o_stream_flush(output) < 0) {
			subswrite_set_syscall_error(list, "write()", path);
			failed = TRUE;
		} else if (mail_set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
			if (fsync(fd_out) < 0) {
				subswrite_set_syscall_error(list, "fsync()",
							    path);
				failed = TRUE;
			}
		}
	}

	if (input != NULL)
		i_stream_destroy(&input);
	o_stream_destroy(&output);

	if (failed || !changed) {
		if (file_dotlock_delete(&dotlock) < 0) {
			subswrite_set_syscall_error(list,
				"file_dotlock_delete()", path);
			failed = TRUE;
		}
	} else {
		enum dotlock_replace_flags flags =
			DOTLOCK_REPLACE_FLAG_VERIFY_OWNER;
		if (file_dotlock_replace(&dotlock, flags) < 0) {
			subswrite_set_syscall_error(list,
				"file_dotlock_replace()", path);
			failed = TRUE;
		}
	}
	return failed ? -1 : (changed ? 1 : 0);
}
Пример #12
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;
	}