static const char *
get_parent_mailbox(struct acl_backend *backend, const char *name)
{
	const char *p;

	p = strrchr(name, mailbox_list_get_hierarchy_sep(backend->list));
	return p == NULL ? NULL : t_strdup_until(name, p);
}
static void
mailbox_list_index_update_info(struct mailbox_list_index_iterate_context *ctx)
{
	struct mailbox_list_index_node *node = ctx->next_node;
	struct mailbox *box;

	p_clear(ctx->info_pool);

	str_truncate(ctx->path, ctx->parent_len);
	/* the root directory may have an empty name. in that case we'll still
	   want to insert the separator, so check for non-NULL parent rather
	   than non-empty path. */
	if (node->parent != NULL) {
		str_append_c(ctx->path,
			     mailbox_list_get_hierarchy_sep(ctx->ctx.list));
	}
	str_append(ctx->path, node->name);

	ctx->info.vname = mailbox_list_get_vname(ctx->ctx.list, str_c(ctx->path));
	ctx->info.flags = node->children != NULL ?
		MAILBOX_CHILDREN : MAILBOX_NOCHILDREN;
	if (strcmp(ctx->info.vname, "INBOX") != 0) {
		/* non-INBOX */
		ctx->info.vname = p_strdup(ctx->info_pool, ctx->info.vname);
	} else if (!ctx->prefix_inbox_list) {
		/* listing INBOX itself */
		ctx->info.vname = "INBOX";
		if (mail_namespace_is_inbox_noinferiors(ctx->info.ns)) {
			ctx->info.flags &= ~(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN);
			ctx->info.flags |= MAILBOX_NOINFERIORS;
		}
	} else {
		/* listing INBOX/INBOX */
		ctx->info.vname = p_strconcat(ctx->info_pool,
			ctx->ctx.list->ns->prefix, "INBOX", NULL);
		ctx->info.flags |= MAILBOX_NONEXISTENT;
	}
	if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0)
		ctx->info.flags |= MAILBOX_NONEXISTENT;
	else if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0)
		ctx->info.flags |= MAILBOX_NOSELECT;
	if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOINFERIORS) != 0)
		ctx->info.flags |= MAILBOX_NOINFERIORS;

	if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
			       MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) {
		mailbox_list_set_subscription_flags(ctx->ctx.list,
						    ctx->info.vname,
						    &ctx->info.flags);
	}

	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0) {
		box = mailbox_alloc(ctx->ctx.list, ctx->info.vname, 0);
		mailbox_list_index_status_set_info_flags(box, node->uid,
							 &ctx->info.flags);
		mailbox_free(&box);
	}
}
static void
dsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname,
		       char alt_char)
{
	const char *old_vname;
	char *p, list_sep = mailbox_list_get_hierarchy_sep(ns->list);
	guid_128_t guid;

	/* replace control chars */
	for (p = str_c_modifiable(vname); *p != '\0'; p++) {
		if ((unsigned char)*p < ' ')
			*p = alt_char;
	}
	/* make it valid UTF8 */
	if (!uni_utf8_str_is_valid(str_c(vname))) {
		old_vname = t_strdup(str_c(vname));
		str_truncate(vname, 0);
		if (uni_utf8_get_valid_data((const void *)old_vname,
					    strlen(old_vname), vname))
			i_unreached();
	}
	if (dsync_is_valid_name(ns, str_c(vname)))
		return;

	/* 1) change any real separators to alt separators (this wouldn't
	   be necessary with listescape, but don't bother detecting it) */
	if (list_sep != mail_namespace_get_sep(ns)) {
		for (p = str_c_modifiable(vname); *p != '\0'; p++) {
			if (*p == list_sep)
				*p = alt_char;
		}
		if (dsync_is_valid_name(ns, str_c(vname)))
			return;
	}
	/* 2) '/' characters aren't valid without listescape */
	if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') {
		for (p = str_c_modifiable(vname); *p != '\0'; p++) {
			if (*p == '/')
				*p = alt_char;
		}
		if (dsync_is_valid_name(ns, str_c(vname)))
			return;
	}
	/* 3) probably some reserved name (e.g. dbox-Mails) */
	str_insert(vname, ns->prefix_len, "_");
	if (dsync_is_valid_name(ns, str_c(vname)))
		return;

	/* 4) name is too long? just give up and generate a unique name */
	guid_128_generate(guid);
	str_truncate(vname, 0);
	str_append(vname, ns->prefix);
	str_append(vname, guid_128_to_string(guid));
	i_assert(dsync_is_valid_name(ns, str_c(vname)));
}
static const char *
maildir_list_get_dirname_path(struct mailbox_list *list, const char *dir,
			      const char *name)
{
	if (*name == '\0')
		return dir;
	else if (list->name == imapdir_mailbox_list.name)
		return t_strdup_printf("%s/%s", dir, name);

	return t_strdup_printf("%s/%c%s", dir,
			       mailbox_list_get_hierarchy_sep(list), name);
}
static const char *
subsfile_list_unescaped(struct subsfile_list_context *ctx, const char *line)
{
	const char *p;

	str_truncate(ctx->name, 0);
	while ((p = strchr(line, '\t')) != NULL) {
		str_append_tabunescaped(ctx->name, line, p-line);
		str_append_c(ctx->name, mailbox_list_get_hierarchy_sep(ctx->list));
		line = p+1;
	}
	str_append_tabunescaped(ctx->name, line, strlen(line));
	return str_c(ctx->name);
}
Exemple #6
0
static const char *
imapc_list_to_vname(struct imapc_mailbox_list *list, const char *imapc_name)
{
	const char *list_name;

	/* typically mailbox_list_escape_name() is used to escape vname into
	   a list name. but we want to convert remote IMAP name to a list name,
	   so we need to use the remote IMAP separator. */
	list_name = mailbox_list_escape_name_params(imapc_name, "", list->root_sep,
		mailbox_list_get_hierarchy_sep(&list->list),
		list->list.set.escape_char, "");
	/* list_name is now valid, so we can convert it to vname */
	return mailbox_list_get_vname(&list->list, list_name);
}
Exemple #7
0
static const char *
imapc_list_get_storage_name(struct mailbox_list *_list, const char *vname)
{
	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
	const char *prefix = list->set->imapc_list_prefix;
	const char *storage_name;

	storage_name = mailbox_list_default_get_storage_name(_list, vname);
	if (*prefix != '\0' && strcasecmp(storage_name, "INBOX") != 0) {
		storage_name = storage_name[0] == '\0' ? prefix :
			t_strdup_printf("%s%c%s", prefix,
			mailbox_list_get_hierarchy_sep(_list),
			storage_name);
	}
	return storage_name;
}
Exemple #8
0
static const char *
acl_mailbox_list_iter_get_name(struct mailbox_list_iterate_context *ctx,
			       const char *vname)
{
	struct mail_namespace *ns = ctx->list->ns;
	const char *name;
	unsigned int len;

	name = mailbox_list_get_storage_name(ns->list, vname);
	len = strlen(name);
	if (len > 0 && name[len-1] == mailbox_list_get_hierarchy_sep(ns->list)) {
		/* name ends with separator. this can happen if doing e.g.
		   LIST "" foo/% and it lists "foo/". */
		name = t_strndup(name, len-1);
	}
	return name;
}
static struct mailbox_list_index_node *
mailbox_list_index_lookup_real(struct mailbox_list *list, const char *name)
{
	struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
	struct mailbox_list_index_node *node = ilist->mailbox_tree;
	const char *const *path;
	unsigned int i;
	char sep[2];

	if (*name == '\0')
		return mailbox_list_index_node_find_sibling(node, "");

	sep[0] = mailbox_list_get_hierarchy_sep(list); sep[1] = '\0';
	path = t_strsplit(name, sep);
	for (i = 0;; i++) {
		node = mailbox_list_index_node_find_sibling(node, path[i]);
		if (node == NULL || path[i+1] == NULL)
			break;
		node = node->children;
	}
	return node;
}
static void
mailbox_list_index_update_info(struct mailbox_list_index_iterate_context *ctx)
{
	struct mailbox_list_index_node *node = ctx->next_node;
	struct mailbox *box;

	p_clear(ctx->info_pool);

	str_truncate(ctx->path, ctx->parent_len);
	if (str_len(ctx->path) > 0) {
		str_append_c(ctx->path,
			     mailbox_list_get_hierarchy_sep(ctx->ctx.list));
	}
	str_append(ctx->path, node->name);

	ctx->info.vname = mailbox_list_get_vname(ctx->ctx.list, str_c(ctx->path));
	ctx->info.vname = p_strdup(ctx->info_pool, ctx->info.vname);
	ctx->info.flags = 0;
	if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0)
		ctx->info.flags |= MAILBOX_NONEXISTENT;
	else if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0)
		ctx->info.flags |= MAILBOX_NOSELECT;
	if ((node->flags & MAILBOX_LIST_INDEX_FLAG_NOINFERIORS) != 0)
		ctx->info.flags |= MAILBOX_NOINFERIORS;
	ctx->info.flags |= node->children != NULL ?
		MAILBOX_CHILDREN : MAILBOX_NOCHILDREN;

	if ((ctx->ctx.flags & (MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
			       MAILBOX_LIST_ITER_RETURN_SUBSCRIBED)) != 0) {
		mailbox_list_set_subscription_flags(ctx->ctx.list,
						    ctx->info.vname,
						    &ctx->info.flags);
	}

	box = mailbox_alloc(ctx->ctx.list, ctx->info.vname, 0);
	mailbox_list_index_status_set_info_flags(box, node->uid,
						 &ctx->info.flags);
	mailbox_free(&box);
}
Exemple #11
0
static const char *
imapc_list_get_vname(struct mailbox_list *_list, const char *storage_name)
{
	struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list;
	const char *prefix = list->set->imapc_list_prefix;
	unsigned int prefix_len;

	if (*storage_name == '\0') {
		/* ACL plugin does these lookups */
	} else if (*prefix != '\0' && strcasecmp(storage_name, "INBOX") != 0) {
		prefix_len = strlen(prefix);
		i_assert(strncmp(prefix, storage_name, prefix_len) == 0);
		storage_name += prefix_len;
		if (storage_name[0] == '\0') {
			/* we're looking up the prefix itself */
		} else {
			i_assert(storage_name[0] ==
				 mailbox_list_get_hierarchy_sep(_list));
			storage_name++;
		}
	}
	return mailbox_list_default_get_vname(_list, storage_name);
}
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);
}
Exemple #13
0
const char *imapc_list_to_remote(struct imapc_mailbox_list *list, const char *name)
{
	return mailbox_list_unescape_name_params(name, "", list->root_sep,
				mailbox_list_get_hierarchy_sep(&list->list),
				list->list.set.escape_char);
}
Exemple #14
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;
	}