示例#1
0
static struct mailbox_node *
imapc_list_update_tree(struct imapc_mailbox_list *list,
		       struct mailbox_tree_context *tree,
		       const struct imap_arg *args)
{
	struct mailbox_node *node;
	const struct imap_arg *flags;
	const char *name, *flag;
	enum mailbox_info_flags info_flag, info_flags = 0;
	bool created;

	if (!imap_arg_get_list(&args[0], &flags) ||
	    args[1].type == IMAP_ARG_EOL ||
	    !imap_arg_get_astring(&args[2], &name))
		return NULL;

	while (imap_arg_get_atom(flags, &flag)) {
		if (imap_list_flag_parse(flag, &info_flag))
			info_flags |= info_flag;
		flags++;
	}

	T_BEGIN {
		const char *vname = imapc_list_to_vname(list, name);

		if ((info_flags & MAILBOX_NONEXISTENT) != 0)
			node = mailbox_tree_lookup(tree, vname);
		else
			node = mailbox_tree_get(tree, vname, &created);
	} T_END;
	if (node != NULL)
		node->flags = info_flags;
	return node;
}
static int
maildir_fill_inbox(struct maildir_list_iterate_context *ctx,
		   struct imap_match_glob *glob, const char *inbox_name,
		   bool update_only)
{
	struct mailbox_node *node;
	enum mailbox_info_flags flags;
	enum imap_match_result match;
	bool created;
	int ret;

	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_NO_AUTO_BOXES) == 0) {
		/* always show INBOX */
	} else {
		/* INBOX may be Maildir root or completely elsewhere.
		   show it only if it has already been created */
		ret = mailbox_list_mailbox(ctx->ctx.list, "INBOX", &flags);
		if (ret < 0)
			return -1;
		if ((flags & MAILBOX_NONEXISTENT) != 0)
			update_only = TRUE;
	}

	if (update_only) {
		node = mailbox_tree_lookup(ctx->tree_ctx, inbox_name);
		if (node != NULL)
			node->flags &= ~MAILBOX_NONEXISTENT;
		return 0;
	}

	/* add the INBOX only if it matches the patterns */
	match = imap_match(glob, inbox_name);
	if (match == IMAP_MATCH_PARENT)
		maildir_fill_parents(ctx, glob, FALSE, inbox_name);
	else if (match == IMAP_MATCH_YES) {
		node = mailbox_tree_get(ctx->tree_ctx, inbox_name, &created);
		if (created)
			node->flags = MAILBOX_NOCHILDREN;
		else
			node->flags &= ~MAILBOX_NONEXISTENT;
		node->flags |= MAILBOX_MATCHED;
	}
	return 0;
}
static void
maildir_fill_parents(struct maildir_list_iterate_context *ctx,
		     struct imap_match_glob *glob, bool update_only,
		     const char *vname)
{
	struct mail_namespace *ns = ctx->ctx.list->ns;
	struct mailbox_node *node;
	const char *p;
	unsigned int vname_len = strlen(vname);
	bool created;
	char ns_sep = mail_namespace_get_sep(ns);

	while ((p = strrchr(vname, ns_sep)) != NULL) {
		vname = t_strdup_until(vname, p);
		if (imap_match(glob, vname) != IMAP_MATCH_YES)
			continue;

		if (ns->prefix_len > 0 && vname_len == ns->prefix_len-1 &&
		    strncmp(vname, ns->prefix, ns->prefix_len - 1) == 0 &&
		    vname[ns->prefix_len-1] == ns_sep) {
			/* don't return matches to namespace prefix itself */
			continue;
		}

		created = FALSE;
		node = update_only ?
			mailbox_tree_lookup(ctx->tree_ctx, vname) :
			mailbox_tree_get(ctx->tree_ctx, vname, &created);
		if (node != NULL) {
			if (created) {
				/* we haven't yet seen this mailbox,
				   but we might see it later */
				node->flags = MAILBOX_NONEXISTENT;
			}
			if (!update_only)
				node->flags |= MAILBOX_MATCHED;
			node->flags |= MAILBOX_CHILDREN;
			node->flags &= ~MAILBOX_NOCHILDREN;
			node_fix_parents(node);
		}
	}
}
static int
mailbox_list_subscription_fill_one(struct mailbox_list *list,
				   struct mailbox_list *src_list,
				   const char *name)
{
	struct mail_namespace *ns, *default_ns = list->ns;
	struct mail_namespace *namespaces = default_ns->user->namespaces;
	struct mailbox_node *node;
	const char *vname, *ns_name, *error;
	unsigned int len;
	bool created;

	/* default_ns is whatever namespace we're currently listing.
	   if we have e.g. prefix="" and prefix=pub/ namespaces with
	   pub/ namespace having subscriptions=no, we want to:

	   1) when listing "" namespace we want to skip over any names
	   that begin with pub/. */
	if (src_list->ns->prefix_len == 0)
		ns_name = name;
	else {
		/* we could have two-level namespace: ns/ns2/ */
		ns_name = t_strconcat(src_list->ns->prefix, name, NULL);
	}
	ns = mail_namespace_find_unsubscribable(namespaces, ns_name);
	if (ns != NULL && ns != default_ns) {
		if (ns->prefix_len > 0)
			return 0;
		/* prefix="" namespace=no : catching this is basically the
		   same as not finding any namespace. */
		ns = NULL;
	}

	/* 2) when listing pub/ namespace, skip over entries that don't
	   begin with pub/. */
	if (ns == NULL &&
	    (default_ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0)
		return 0;

	/* When listing shared namespace's subscriptions, we need to
	   autocreate all the visible child namespaces. their subscriptions
	   are listed later. */
	if (ns != NULL && mail_namespace_is_shared_user_root(ns)) {
		/* we'll need to get the namespace autocreated.
		   one easy way is to just ask to join a reference and
		   pattern */
		(void)mailbox_list_join_refpattern(ns->list, ns_name, "");
	}

	/* When listing pub/ namespace, skip over the namespace
	   prefix in the name. the rest of the name is storage_name. */
	if (ns == NULL)
		ns = default_ns;
	else if (strncmp(ns_name, ns->prefix, ns->prefix_len) == 0) {
		ns_name += ns->prefix_len;
		name = ns_name;
	} else {
		/* "pub" entry - this shouldn't be possible normally, because
		   it should be saved as "pub/", but handle it anyway */
		i_assert(strncmp(ns_name, ns->prefix, ns->prefix_len-1) == 0 &&
			 ns_name[ns->prefix_len-1] == '\0');
		name = ns_name = "";
	}

	len = strlen(name);
	if (len > 0 && name[len-1] == mail_namespace_get_sep(ns)) {
		/* entry ends with hierarchy separator, remove it.
		   this exists mainly for backwards compatibility with old
		   Dovecot versions and non-Dovecot software that added them */
		name = t_strndup(name, len-1);
	}

	if (!mailbox_list_is_valid_name(list, name, &error)) {
		/* we'll only get into trouble if we show this */
		return -1;
	} else {
		vname = mailbox_list_get_vname(list, name);
		if (!uni_utf8_str_is_valid(vname))
			return -1;
		node = mailbox_tree_get(list->subscriptions, vname, &created);
		node->flags = MAILBOX_SUBSCRIBED;
	}
	return 0;
}
static int
maildir_fill_readdir_entry(struct maildir_list_iterate_context *ctx,
			   struct imap_match_glob *glob, const struct dirent *d,
			   bool update_only)
{
	struct mailbox_list *list = ctx->ctx.list;
	const char *fname, *storage_name, *vname;
	enum mailbox_info_flags flags;
	enum imap_match_result match;
	struct mailbox_node *node;
	bool created;
	int ret;

	fname = d->d_name;
	if (fname[0] == ctx->prefix_char)
		storage_name = fname + 1;
	else {
		if (ctx->prefix_char != '\0' || fname[0] == '.')
			return 0;
		storage_name = fname;
	}

	/* skip . and .. */
	if (fname[0] == '.' &&
	    (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0')))
		return 0;

	vname = mailbox_list_get_vname(list, storage_name);
	if (!uni_utf8_str_is_valid(vname)) {
		/* the storage_name is completely invalid, rename it to
		   something more sensible. we could do this for all names that
		   aren't valid mUTF-7, but that might lead to accidents in
		   future when UTF-8 storage names are used */
		const char *src = t_strdup_printf("%s/%s", ctx->dir, fname);
		string_t *destvname = t_str_new(128);
		string_t *dest = t_str_new(128);

		(void)uni_utf8_get_valid_data((const void *)fname,
					      strlen(fname), destvname);

		str_append(dest, ctx->dir);
		str_append_c(dest, '/');
		(void)imap_utf8_to_utf7(str_c(destvname), dest);

		if (rename(src, str_c(dest)) < 0 && errno != ENOENT)
			i_error("rename(%s, %s) failed: %m", src, str_c(dest));
		/* just skip this in this iteration, we'll see it on the
		   next list */
		return 0;
	}

	/* make sure the pattern matches */
	match = imap_match(glob, vname);
	if ((match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) == 0)
		return 0;

	/* check if this is an actual mailbox */
	if (maildir_delete_trash_dir(ctx, fname))
		return 0;

	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) {
		ret = mailbox_list_dirent_is_alias_symlink(list, ctx->dir, d);
		if (ret != 0)
			return ret < 0 ? -1 : 0;
	}
	T_BEGIN {
		ret = list->v.get_mailbox_flags(list, ctx->dir, fname,
				mailbox_list_get_file_type(d), &flags);
	} T_END;
	if (ret <= 0)
		return ret;

	/* we know the children flags ourself, so ignore if any of
	   them were set. */
	flags &= ~(MAILBOX_NOINFERIORS | MAILBOX_CHILDREN | MAILBOX_NOCHILDREN);

	if ((match & IMAP_MATCH_PARENT) != 0)
		maildir_fill_parents(ctx, glob, update_only, vname);
	else {
		created = FALSE;
		node = update_only ?
			mailbox_tree_lookup(ctx->tree_ctx, vname) :
			mailbox_tree_get(ctx->tree_ctx, vname, &created);

		if (node != NULL) {
			if (created)
				node->flags = MAILBOX_NOCHILDREN;
			else
				node->flags &= ~MAILBOX_NONEXISTENT;
			if (!update_only)
				node->flags |= MAILBOX_MATCHED;
			node->flags |= flags;
			node_fix_parents(node);
		} else {
			i_assert(update_only);
			maildir_set_children(ctx, vname);
		}
	}
	return 0;
}