diff -r 67235781cc08 src/imap/cmd-list.c
--- a/src/imap/cmd-list.c	Thu Aug 30 21:56:43 2012 +0300
+++ b/src/imap/cmd-list.c	Fri Aug 31 15:59:54 2012 +0300
@@ -13,24 +13,15 @@
 
 struct cmd_list_context {
 	struct client_command_context *cmd;
-	const char *ref;
-	const char *const *patterns;
+	struct mail_user *user;
+
 	enum mailbox_list_iter_flags list_flags;
 	struct imap_status_items status_items;
-	enum mailbox_info_flags inbox_flags;
 
-	struct mail_namespace *ns;
 	struct mailbox_list_iterate_context *list_iter;
 
-	ARRAY_DEFINE(ns_prefixes_listed, struct mail_namespace *);
-
 	unsigned int lsub:1;
 	unsigned int lsub_no_unsubscribed:1;
-	unsigned int inbox_found:1;
-	unsigned int seen_inbox_namespace:1;
-	unsigned int cur_ns_match_inbox:1;
-	unsigned int cur_ns_send_prefix:1;
-	unsigned int cur_ns_skip_trailing_sep:1;
 	unsigned int used_listext:1;
 	unsigned int used_status:1;
 };
@@ -181,55 +172,6 @@
 	return TRUE;
 }
 
-static enum mailbox_info_flags
-list_get_inbox_flags(struct cmd_list_context *ctx)
-{
-	struct mail_namespace *ns;
-	struct mailbox_list_iterate_context *list_iter;
-	const struct mailbox_info *info;
-	enum mailbox_info_flags flags = MAILBOX_UNMARKED;
-
-	if (ctx->seen_inbox_namespace &&
-	    (ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0) {
-		/* INBOX doesn't exist. use the default INBOX flags */
-		return flags;
-	}
-
-	/* find the INBOX flags */
-	ns = mail_namespace_find_inbox(ctx->cmd->client->user->namespaces);
-	list_iter = mailbox_list_iter_init(ns->list, "INBOX", 0);
-	info = mailbox_list_iter_next(list_iter);
-	if (info != NULL) {
-		i_assert(strcasecmp(info->name, "INBOX") == 0);
-		flags = info->flags;
-	}
-	(void)mailbox_list_iter_deinit(&list_iter);
-	return flags;
-}
-
-static bool list_namespace_has_children(struct cmd_list_context *ctx)
-{
-	enum mailbox_list_iter_flags list_flags =
-		MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
-	struct mailbox_list_iterate_context *list_iter;
-	const struct mailbox_info *info;
-	bool ret = FALSE;
-
-	if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
-		list_flags |= MAILBOX_LIST_ITER_SELECT_SUBSCRIBED;
-
-	list_iter = mailbox_list_iter_init(ctx->ns->list,
-		t_strconcat(ctx->ns->prefix, "%", NULL), list_flags);
-	info = mailbox_list_iter_next(list_iter);
-	if (info != NULL)
-		ret = TRUE;
-	if (mailbox_list_iter_deinit(&list_iter) < 0) {
-		/* safer to answer TRUE in error conditions */
-		ret = TRUE;
-	}
-	return ret;
-}
-
 static const char *ns_prefix_mutf7(struct mail_namespace *ns)
 {
 	string_t *str;
@@ -243,41 +185,6 @@
 	return str_c(str);
 }
 
-static const char *ns_get_listed_prefix(struct cmd_list_context *ctx)
-{
-	struct imap_match_glob *glob;
-	enum imap_match_result match;
-	const char *ns_prefix, *p;
-	bool inboxcase;
-	unsigned int skip_len;
-
-	skip_len = strlen(ctx->ref);
-	if (strncmp(ctx->ns->prefix, ctx->ref, skip_len) != 0)
-		skip_len = 0;
-
-	inboxcase = strncasecmp(ctx->ns->prefix, "INBOX", 5) == 0 &&
-		ctx->ns->prefix[5] == mail_namespace_get_sep(ctx->ns);
-	glob = imap_match_init_multiple(pool_datastack_create(),
-					ctx->patterns, inboxcase,
-					mail_namespace_get_sep(ctx->ns));
-	ns_prefix = ctx->ns->prefix + skip_len;
-	match = imap_match(glob, ns_prefix);
-	if (match == IMAP_MATCH_YES) {
-		return !ctx->cur_ns_skip_trailing_sep ? ctx->ns->prefix :
-			t_strndup(ctx->ns->prefix, strlen(ctx->ns->prefix)-1);
-	}
-
-	while ((match & IMAP_MATCH_PARENT) != 0) {
-		p = strrchr(ns_prefix, mail_namespace_get_sep(ctx->ns));
-		i_assert(p != NULL);
-		ns_prefix = t_strdup_until(ns_prefix, p);
-		match = imap_match(glob, ns_prefix);
-	}
-	i_assert(match == IMAP_MATCH_YES);
-	return t_strconcat(t_strndup(ctx->ns->prefix, skip_len),
-			   ns_prefix, NULL);
-}
-
 static void list_reply_append_ns_sep_param(string_t *str, char sep)
 {
 	str_append_c(str, '"');
@@ -289,105 +196,6 @@
 }
 
 static void
-list_namespace_send_prefix(struct cmd_list_context *ctx, bool have_children)
-{
-	struct mail_namespace *const *listed;
-	const struct mailbox_settings *mailbox_set;
-	struct mailbox *box;
-	enum mailbox_existence existence;
-	unsigned int len;
-	enum mailbox_info_flags flags;
-	const char *name;
-	string_t *str, *mutf7_name;
-	bool same_ns, ends_with_sep;
-	char ns_sep = mail_namespace_get_sep(ctx->ns);
-
-	ctx->cur_ns_send_prefix = FALSE;
-
-	/* see if we already listed this as a valid mailbox in another
-	   namespace */
-	array_foreach(&ctx->ns_prefixes_listed, listed) {
-		if (*listed == ctx->ns)
-			return;
-	}
-
-	name = ns_get_listed_prefix(ctx);
-	len = strlen(ctx->ns->prefix);
-	ends_with_sep = ctx->ns->prefix[len-1] == ns_sep;
-
-	/* we may be listing namespace's parent. in such case we always want to
-	   set the name as nonexistent. */
-	same_ns = strcmp(name, ctx->ns->prefix) == 0 ||
-		(strncmp(name, ctx->ns->prefix, len - 1) == 0 && ends_with_sep);
-	if (len == 6 && strncasecmp(ctx->ns->prefix, "INBOX", len-1) == 0 &&
-	    ends_with_sep) {
-		/* INBOX namespace needs to be handled specially. */
-		if (ctx->inbox_found) {
-			/* we're just now going to send it */
-			return;
-		}
-
-		ctx->inbox_found = TRUE;
-		flags = list_get_inbox_flags(ctx);
-	} else if (!same_ns) {
-		/* parent */
-		flags = MAILBOX_NONEXISTENT;
-	} else {
-		/* see if namespace prefix is selectable */
-		box = mailbox_alloc(ctx->ns->list, name, 0);
-		if (mailbox_exists(box, TRUE, &existence) == 0 &&
-		    existence == MAILBOX_EXISTENCE_SELECT)
-			flags = MAILBOX_SELECT;
-		else
-			flags = MAILBOX_NONEXISTENT;
-		mailbox_free(&box);
-	}
-
-	if ((flags & MAILBOX_CHILDREN) == 0) {
-		if (have_children || list_namespace_has_children(ctx)) {
-			flags |= MAILBOX_CHILDREN;
-			flags &= ~MAILBOX_NOCHILDREN;
-		} else {
-			flags |= MAILBOX_NOCHILDREN;
-		}
-	}
-
-	if ((ctx->ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 ||
-	    (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
-		if (have_children) {
-			/* children are going to be listed. */
-			return;
-		}
-		if ((flags & MAILBOX_CHILDREN) == 0) {
-			/* namespace has no children. don't show it. */
-			return;
-		}
-		/* namespace has children but they don't match the list
-		   pattern. the prefix itself matches though, so show it. */
-	}
-
-	str = t_str_new(128);
-	str_printfa(str, "* %s (", ctx->lsub ? "LSUB" : "LIST");
-	if (ctx->lsub)
-		flags |= MAILBOX_NONEXISTENT;
-	mailbox_set = (ctx->list_flags & MAILBOX_LIST_ITER_RETURN_SPECIALUSE) == 0 ? NULL :
-		mailbox_settings_find(ctx->cmd->client->user, name);
-	mailbox_flags2str(ctx, str, mailbox_set == NULL ? NULL :
-			  mailbox_set->special_use, flags);
-	str_append(str, ") ");
-	list_reply_append_ns_sep_param(str, ns_sep);
-	str_append_c(str, ' ');
-
-	mutf7_name = t_str_new(64);
-	if (imap_utf8_to_utf7(name, mutf7_name) < 0)
-		i_panic("Namespace prefix not UTF-8: %s", name);
-	imap_quote_append_string(str, str_c(mutf7_name), FALSE);
-	mailbox_childinfo2str(ctx, str, flags);
-
-	client_send_line(ctx->cmd->client, str_c(str));
-}
-
-static void
 list_send_status(struct cmd_list_context *ctx, const char *name,
 		 const char *mutf7_name, enum mailbox_info_flags flags)
 {
@@ -407,7 +215,7 @@
 
 	/* if we're listing subscriptions and there are subscriptions=no
 	   namespaces, ctx->ns may not point to correct one */
-	ns = mail_namespace_find(ctx->ns->user->namespaces, name);
+	ns = mail_namespace_find(ctx->user->namespaces, name);
 	if (imap_status_get(ctx->cmd, ns, name,
 			    &ctx->status_items, &result, &error) < 0) {
 		client_send_line(ctx->cmd->client,
@@ -419,71 +227,26 @@
 			 &ctx->status_items, &result);
 }
 
-static bool list_has_empty_prefix_ns(struct mail_user *user)
+static bool cmd_list_continue(struct client_command_context *cmd)
 {
-	struct mail_namespace *ns;
-
-	ns = mail_namespace_find_prefix(user->namespaces, "");
-	return ns != NULL && (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
-					   NAMESPACE_FLAG_LIST_CHILDREN)) != 0;
-}
-
-static int
-list_namespace_mailboxes(struct cmd_list_context *ctx)
-{
+        struct cmd_list_context *ctx = cmd->context;
 	const struct mailbox_info *info;
-	struct mail_namespace *ns;
 	enum mailbox_info_flags flags;
 	string_t *str, *mutf7_name;
 	const char *name;
 	int ret = 0;
 
+	if (cmd->cancel) {
+		if (ctx->list_iter != NULL)
+			(void)mailbox_list_iter_deinit(&ctx->list_iter);
+		return TRUE;
+	}
 	str = t_str_new(256);
 	mutf7_name = t_str_new(128);
 	while ((info = mailbox_list_iter_next(ctx->list_iter)) != NULL) {
 		name = info->name;
 		flags = info->flags;
 
-		if (strcasecmp(name, "INBOX") == 0) {
-			if (ctx->inbox_found) {
-				/* we already listed this at the beginning
-				   of handling INBOX/ namespace */
-				continue;
-			}
-			if ((ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0) {
-				/* INBOX is in non-empty prefix namespace,
-				   and we're now listing prefixless namespace
-				   that contains INBOX. There's no way we can
-				   show this mailbox. */
-				ctx->inbox_flags = flags &
-					(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN);
-				continue;
-			}
-
-			if (*info->ns->prefix != '\0' &&
-			    list_has_empty_prefix_ns(info->ns->user)) {
-				/* INBOX is in its own namespace, while a
-				   namespace with prefix="" has its children. */
-				flags &= ~(MAILBOX_CHILDREN|MAILBOX_NOCHILDREN|
-					   MAILBOX_NOINFERIORS);
-				flags |= ctx->inbox_flags;
-			}
-			ctx->inbox_found = TRUE;
-		}
-		if (ctx->cur_ns_send_prefix)
-			list_namespace_send_prefix(ctx, TRUE);
-
-		/* if there's a list=yes namespace with this name, list it as
-		   having children */
-		ns = mail_namespace_find_prefix_nosep(ctx->ns, name);
-		if (ns != NULL &&
-		    (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
-				  NAMESPACE_FLAG_LIST_CHILDREN)) != 0) {
-			flags |= MAILBOX_CHILDREN;
-			flags &= ~MAILBOX_NOCHILDREN;
-			array_append(&ctx->ns_prefixes_listed, &ns, 1);
-		}
-
 		if ((flags & MAILBOX_CHILD_SUBSCRIBED) != 0 &&
 		    (flags & MAILBOX_SUBSCRIBED) == 0 &&
 		    ctx->lsub_no_unsubscribed) {
@@ -501,7 +264,7 @@
 		mailbox_flags2str(ctx, str, info->special_use, flags);
 		str_append(str, ") ");
 		list_reply_append_ns_sep_param(str,
-			mail_namespace_get_sep(ctx->ns));
+			mail_namespace_get_sep(info->ns));
 		str_append_c(str, ' ');
 		imap_quote_append_string(str, str_c(mutf7_name), FALSE);
 		mailbox_childinfo2str(ctx, str, flags);
@@ -512,369 +275,56 @@
 		} T_END;
 		if (ret == 0) {
 			/* buffer is full, continue later */
-			return 0;
-		}
-	}
-
-	if (mailbox_list_iter_deinit(&ctx->list_iter) < 0)
-		ret = -1;
-
-	return ret < 0 ? -1 : 1;
-}
-
-static bool list_pattern_has_wildcards(const char *pattern)
-{
-	for (; *pattern != '\0'; pattern++) {
-		if (*pattern == '%' || *pattern == '*')
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static void
-skip_namespace_prefix(const char **prefix, const char **pattern,
-		      bool inbox_check, char sep)
-{
-	size_t pattern_len, prefix_len, min_len;
-	bool match;
-
-	prefix_len = strlen(*prefix);
-	pattern_len = strlen(*pattern);
-	min_len = I_MIN(prefix_len, pattern_len);
-
-	match = strncmp(*prefix, *pattern, min_len) == 0;
-	if (!match && inbox_check) {
-		/* try INBOX check. */
-		match = prefix_len >= 5 &&
-			strncasecmp(*prefix, *pattern, 5) == 0 &&
-			strncmp(*prefix + 5, *pattern + 5,
-				prefix_len - 5) == 0 &&
-			strncasecmp(*prefix, "INBOX", 5) == 0 &&
-			((*prefix)[5] == sep || (*prefix)[5] == '\0');
-	}
-
-	if (match) {
-		*prefix += min_len;
-		*pattern += min_len;
-	}
-}
-
-static bool
-skip_namespace_prefix_ref(struct cmd_list_context *ctx,
-			  const char **cur_ns_prefix_p,
-			  const char **cur_ref_p)
-{
-	const char *cur_ns_prefix = *cur_ns_prefix_p;
-	const char *cur_ref = *cur_ref_p;
-
-	if (*cur_ref != '\0') {
-		/* reference argument given. skip namespace prefix using it.
-
-		   cur_ns_prefix = foo/bar/
-		   cur_ref = foo/
-		     -> cur_ns_prefix=bar/, cur_ref=""
-		   cur_ref = foo/bar/baz
-		     -> cur_ns_prefix="", cur_ref="baz"
-		   */
-		skip_namespace_prefix(&cur_ns_prefix, &cur_ref, TRUE,
-				      mail_namespace_get_sep(ctx->ns));
-
-		if (*cur_ref != '\0' && *cur_ns_prefix != '\0') {
-			/* reference parameter didn't match with
-			   namespace prefix. skip this. */
 			return FALSE;
 		}
 	}
 
-	*cur_ns_prefix_p = cur_ns_prefix;
-	*cur_ref_p = cur_ref;
-	return TRUE;
-}
-
-static void
-skip_namespace_prefix_pattern(struct cmd_list_context *ctx,
-			      const char **cur_ns_prefix_p,
-			      const char *cur_ref, const char **cur_pattern_p)
-{
-	const char *cur_ns_prefix = *cur_ns_prefix_p;
-	const char *cur_pattern = *cur_pattern_p;
-	const char *old_ns_prefix = cur_ns_prefix;
-	const char *old_pattern = cur_pattern;
-
-	if (*cur_ns_prefix == '\0')
-		return;
-
-	/* skip namespace prefix using pattern */
-	i_assert(*cur_ref == '\0');
-
-	skip_namespace_prefix(&cur_ns_prefix, &cur_pattern,
-			      cur_ref == ctx->ref,
-			      mail_namespace_get_sep(ctx->ns));
-
-	if (*cur_pattern == '\0' && *cur_ns_prefix == '\0') {
-		/* trying to list the namespace prefix itself. */
-		cur_ns_prefix = old_ns_prefix;
-		cur_pattern = old_pattern;
-	}
-
-	*cur_ns_prefix_p = cur_ns_prefix;
-	*cur_pattern_p = cur_pattern;
-}
-
-static enum imap_match_result
-list_use_inboxcase(struct cmd_list_context *ctx)
-{
-	struct imap_match_glob *inbox_glob;
-	const char *const *pat;
-	enum imap_match_result match, ret;
-
-	if (*ctx->ns->prefix != '\0' &&
-	    (ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
-		return IMAP_MATCH_NO;
-
-	/* if the original reference and pattern combined produces something
-	   that matches INBOX, the INBOX casing is on. */
-	ret = IMAP_MATCH_NO;
-	for (pat = ctx->patterns; *pat != NULL; pat++) {
-		inbox_glob =
-			imap_match_init(pool_datastack_create(),
-					t_strconcat(ctx->ref, *pat, NULL),
-					TRUE, mail_namespace_get_sep(ctx->ns));
-		match = imap_match(inbox_glob, "INBOX");
-
-		if (match == IMAP_MATCH_YES)
-			return IMAP_MATCH_YES;
-		if ((match & IMAP_MATCH_PARENT) != 0)
-			ret = IMAP_MATCH_PARENT;
-	}
-	return ret;
-}
-
-static bool
-list_want_send_prefix(struct cmd_list_context *ctx, const char *pattern)
-{
-	/* don't send the prefix if we're listing subscribed mailboxes */
-	if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0)
-		return FALSE;
-
-	/* send the prefix if namespace is listable. if children are listable
-	   we may or may not need to send it. */
-	if ((ctx->ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
-			       NAMESPACE_FLAG_LIST_CHILDREN)) != 0)
-		return TRUE;
-
-	/* ..or if pattern is exactly the same as namespace prefix.
-	   some clients (mutt) want to do LIST "" prefix. */
-	for (; *pattern != '\0'; pattern++) {
-		if (*pattern == '*' || *pattern == '%')
-			break;
-	}
-	return *pattern == '\0';
-}
-
-static bool
-list_namespace_match_pattern(struct cmd_list_context *ctx, bool inboxcase,
-			     const char *cur_ref, const char *cur_ns_prefix,
-			     const char *cur_pattern)
-{
-	const char *orig_cur_pattern = cur_pattern;
-	struct mail_namespace *ns = ctx->ns;
-	struct imap_match_glob *pat_glob;
-	enum imap_match_result match;
-	const char *p;
-	size_t len;
-
-	skip_namespace_prefix_pattern(ctx, &cur_ns_prefix,
-				      cur_ref, &cur_pattern);
-	if (*cur_ns_prefix == '\0') {
-		if (*ns->prefix == '\0') {
-			/* no namespace prefix: if list=no we don't want to
-			   show anything, except when the client does e.g.
-			   LIST "" mailbox. prefix="", list=no namespace is
-			   mainly useful for working around client bugs. */
-			if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0 &&
-			    list_pattern_has_wildcards(cur_pattern))
-				return FALSE;
-		}
+	if (mailbox_list_iter_deinit(&ctx->list_iter) < 0) {
+		client_send_list_error(cmd, ctx->user->namespaces->list);
 		return TRUE;
 	}
-
-	/* namespace prefix still wasn't completely skipped over.
-	   for example cur_ns_prefix=INBOX/, pattern=%/% or pattern=IN%.
-	   Check that pattern matches namespace prefix. */
-	i_assert(*cur_ref == '\0');
-
-	/* drop the trailing separator in namespace prefix.
-	   don't do it if we're listing only the prefix itself
-	   (LIST "" foo/ needs to return "foo/" entry) */
-	len = strlen(cur_ns_prefix);
-	if (cur_ns_prefix[len-1] == mail_namespace_get_sep(ns) &&
-	    strcmp(cur_pattern, cur_ns_prefix) != 0) {
-		ctx->cur_ns_skip_trailing_sep = TRUE;
-		cur_ns_prefix = t_strndup(cur_ns_prefix, len-1);
-	}
-
-	/* hidden and non-listable namespaces are invisible to wildcards */
-	if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
-			  NAMESPACE_FLAG_LIST_CHILDREN)) == 0 &&
-	    list_pattern_has_wildcards(cur_pattern))
-		return FALSE;
-
-	/* check if this namespace prefix matches the current pattern */
-	pat_glob = imap_match_init(pool_datastack_create(), orig_cur_pattern,
-				   inboxcase, mail_namespace_get_sep(ns));
-	match = imap_match(pat_glob, cur_ns_prefix);
-	if (match == IMAP_MATCH_YES) {
-		if (list_want_send_prefix(ctx, orig_cur_pattern))
-			ctx->cur_ns_send_prefix = TRUE;
-
-		/* if the pattern contains '*' characters, we'll need to
-		   check our children too */
-		for (p = orig_cur_pattern; *p != '\0'; p++) {
-			if (*p == '*')
-				return TRUE;
-		}
-		if ((ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) != 0) {
-			/* the namespace prefix itself may be subscribed. */
-			return TRUE;
-		}
-		return FALSE;
-	} else {
-		while ((match & IMAP_MATCH_PARENT) != 0) {
-			p = strrchr(cur_ns_prefix, mail_namespace_get_sep(ns));
-			if (p == NULL)
-				break;
-			cur_ns_prefix = t_strdup_until(cur_ns_prefix, p);
-			match = imap_match(pat_glob, cur_ns_prefix);
-		}
-		if (match == IMAP_MATCH_YES &&
-		    mail_namespace_find_prefix_nosep(ns->user->namespaces,
-						     cur_ns_prefix) == NULL) {
-			/* ns prefix="foo/bar/" and we're listing e.g. % */
-			if (list_want_send_prefix(ctx, orig_cur_pattern))
-				ctx->cur_ns_send_prefix = TRUE;
-		}
-		return match != IMAP_MATCH_NO;
-	}
-}
-
-static void list_namespace_init(struct cmd_list_context *ctx)
-{
-	struct mail_namespace *ns = ctx->ns;
-	const char *cur_ns_prefix, *cur_ref, *const *pat, *pattern;
-	enum imap_match_result inbox_match;
-	ARRAY_DEFINE(used_patterns, const char *);
-	bool inboxcase;
-
-	cur_ns_prefix = ns->prefix;
-	cur_ref = ctx->ref;
-
-	ctx->cur_ns_skip_trailing_sep = FALSE;
-
-	if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0)
-		ctx->seen_inbox_namespace = TRUE;
-
-	if (*cur_ns_prefix != '\0') {
-		/* namespace has a prefix. see if we can skip over it. */
-		if (!skip_namespace_prefix_ref(ctx, &cur_ns_prefix, &cur_ref))
-			return;
-	}
-
-	inbox_match = list_use_inboxcase(ctx);
-	ctx->cur_ns_match_inbox = inbox_match == IMAP_MATCH_YES;
-	inboxcase = (inbox_match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) != 0;
-
-	t_array_init(&used_patterns, 16);
-	for (pat = ctx->patterns; *pat != NULL; pat++) {
-		pattern = *pat;
-		/* see if pattern even has a chance of matching the
-		   namespace prefix */
-		if (list_namespace_match_pattern(ctx, inboxcase, cur_ref,
-						 cur_ns_prefix, pattern)) {
-			pattern = mailbox_list_join_refpattern(ns->list,
-							ctx->ref, pattern);
-			array_append(&used_patterns, &pattern, 1);
-		}
-	}
-
-	if (array_count(&used_patterns) == 0) {
-		if (!ctx->cur_ns_match_inbox) {
-			/* it's possible that the namespace prefix matched,
-			   even though its children didn't */
-			if (ctx->cur_ns_send_prefix)
-				list_namespace_send_prefix(ctx, FALSE);
-			return;
-		}
-		/* we should still list INBOX */
-		pattern = "INBOX";
-		array_append(&used_patterns, &pattern, 1);
-	}
-	(void)array_append_space(&used_patterns); /* NULL-terminate */
-	pat = array_idx(&used_patterns, 0);
-
-	ctx->list_iter = mailbox_list_iter_init_multiple(ns->list, pat,
-							 ctx->list_flags);
-}
-
-static void list_inbox(struct cmd_list_context *ctx)
-{
-	string_t *str;
-
-	/* INBOX always exists */
-	if (!ctx->inbox_found && ctx->cur_ns_match_inbox &&
-	    (ctx->ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
-	    (ctx->list_flags & MAILBOX_LIST_ITER_SELECT_SUBSCRIBED) == 0) {
-		str = t_str_new(64);
-		str_append(str, "* LIST (\\Unmarked) ");
-		list_reply_append_ns_sep_param(str,
-			mail_namespace_get_sep(ctx->ns));
-		str_append(str, " \"INBOX\"");
-		client_send_line(ctx->cmd->client, str_c(str));
-	}
-}
-
-static bool cmd_list_continue(struct client_command_context *cmd)
-{
-        struct cmd_list_context *ctx = cmd->context;
-	int ret;
-
-	if (cmd->cancel) {
-		if (ctx->list_iter != NULL)
-			(void)mailbox_list_iter_deinit(&ctx->list_iter);
-		return TRUE;
-	}
-	for (; ctx->ns != NULL; ctx->ns = ctx->ns->next) {
-		if (ctx->list_iter == NULL) {
-			T_BEGIN {
-				list_namespace_init(ctx);
-			} T_END;
-			if (ctx->list_iter == NULL)
-				continue;
-		}
-
-		T_BEGIN {
-			ret = list_namespace_mailboxes(ctx);
-		} T_END;
-		if (ret < 0) {
-			client_send_list_error(cmd, ctx->ns->list);
-			return TRUE;
-		}
-		if (ret == 0)
-			return FALSE;
-
-		if (ctx->cur_ns_send_prefix) {
-			/* no mailboxes in this namespace */
-			list_namespace_send_prefix(ctx, FALSE);
-		}
-		list_inbox(ctx);
-	}
-
 	client_send_tagline(cmd, !ctx->lsub ?
 			    "OK List completed." :
 			    "OK Lsub completed.");
 	return TRUE;
 }
 
+static const char *const *
+list_get_ref_patterns(struct cmd_list_context *ctx, const char *ref,
+		      const char *const *patterns)
+{
+	struct mail_namespace *ns;
+	const char *const *pat, *pattern;
+	ARRAY_DEFINE(full_patterns, const char *);
+
+	if (*ref == '\0')
+		return patterns;
+
+	ns = mail_namespace_find(ctx->user->namespaces, ref);
+	if (ns == NULL)
+		return patterns;
+
+	t_array_init(&full_patterns, 16);
+	for (pat = patterns; *pat != NULL; pat++) {
+		pattern = mailbox_list_join_refpattern(ns->list, ref, *pat);
+		array_append(&full_patterns, &pattern, 1);
+	}
+	(void)array_append_space(&full_patterns); /* NULL-terminate */
+	return array_idx(&full_patterns, 0);
+}
+
+static void cmd_list_init(struct cmd_list_context *ctx,
+			  const char *const *patterns)
+{
+	enum namespace_type type_mask =
+		NAMESPACE_PRIVATE | NAMESPACE_SHARED | NAMESPACE_PUBLIC;
+
+	ctx->list_iter =
+		mailbox_list_iter_init_namespaces(ctx->user->namespaces,
+						  patterns, type_mask,
+						  ctx->list_flags);
+}
+
 static void cmd_list_ref_root(struct client *client, const char *ref)
 {
 	struct mail_namespace *ns;
@@ -927,7 +377,7 @@
 	unsigned int arg_count;
 	struct cmd_list_context *ctx;
 	ARRAY_DEFINE(patterns, const char *) = ARRAY_INIT;
-	const char *pattern, *const *patterns_strarr;
+	const char *ref, *pattern, *const *patterns_strarr;
 	string_t *str;
 
 	/* [(<selection options>)] <reference> <pattern>|(<pattern list>)
@@ -937,8 +387,8 @@
 
 	ctx = p_new(cmd->pool, struct cmd_list_context, 1);
 	ctx->cmd = cmd;
-	ctx->ns = client->user->namespaces;
 	ctx->lsub = lsub;
+	ctx->user = client->user;
 
 	cmd->context = ctx;
 
@@ -950,13 +400,13 @@
 		args++;
 	}
 
-	if (!imap_arg_get_astring(&args[0], &ctx->ref)) {
+	if (!imap_arg_get_astring(&args[0], &ref)) {
 		client_send_command_error(cmd, "Invalid reference.");
 		return TRUE;
 	}
 	str = t_str_new(64);
-	if (imap_utf7_to_utf8(ctx->ref, str) == 0)
-		ctx->ref = p_strdup(cmd->pool, str_c(str));
+	if (imap_utf7_to_utf8(ref, str) == 0)
+		ref = p_strdup(cmd->pool, str_c(str));
 	str_truncate(str, 0);
 
 	if (imap_arg_get_list_full(&args[1], &list_args, &arg_count)) {
@@ -1016,7 +466,6 @@
 		ctx->list_flags |= MAILBOX_LIST_ITER_RETURN_CHILDREN |
 			MAILBOX_LIST_ITER_RETURN_SPECIALUSE;
 	}
-	ctx->list_flags |= MAILBOX_LIST_ITER_SHOW_EXISTING_PARENT;
 
 	if (!IMAP_ARG_IS_EOL(args)) {
 		client_send_command_error(cmd, "Extra arguments.");
@@ -1027,11 +476,12 @@
 	patterns_strarr = array_idx(&patterns, 0);
 	if (!ctx->used_listext && !lsub && *patterns_strarr[0] == '\0') {
 		/* Only LIST ref "" gets us here */
-		cmd_list_ref_root(client, ctx->ref);
+		cmd_list_ref_root(client, ref);
 		client_send_tagline(cmd, "OK List completed.");
 	} else {
-		ctx->patterns = patterns_strarr;
-		p_array_init(&ctx->ns_prefixes_listed, cmd->pool, 8);
+		patterns_strarr =
+			list_get_ref_patterns(ctx, ref, patterns_strarr);
+		cmd_list_init(ctx, patterns_strarr);
 
 		if (!cmd_list_continue(cmd)) {
 			/* unfinished */
diff -r 67235781cc08 src/lib-storage/mailbox-list-iter.c
--- a/src/lib-storage/mailbox-list-iter.c	Thu Aug 30 21:56:43 2012 +0300
+++ b/src/lib-storage/mailbox-list-iter.c	Fri Aug 31 15:59:54 2012 +0300
@@ -35,17 +35,26 @@
 struct ns_list_iterate_context {
 	struct mailbox_list_iterate_context ctx;
 	struct mailbox_list_iterate_context *backend_ctx;
-	struct mail_namespace *namespaces;
+	struct mail_namespace *namespaces, *cur_ns;
+	struct mailbox_list *error_list;
 	pool_t pool;
 	const char **patterns, **patterns_ns_match;
 	enum namespace_type type_mask;
 
-	struct mail_namespace *iter_namespaces;
 	struct mailbox_info ns_info;
+	struct mailbox_info inbox_info;
+	const struct mailbox_info *pending_backend_info;
+
+	unsigned int cur_ns_prefix_sent:1;
+	unsigned int inbox_list:1;
+	unsigned int inbox_listed:1;
 };
 
 static bool ns_match_next(struct ns_list_iterate_context *ctx, 
 			  struct mail_namespace *ns, const char *pattern);
+static int mailbox_list_match_anything(struct ns_list_iterate_context *ctx,
+				       struct mail_namespace *ns,
+				       const char *prefix);
 
 struct mailbox_list_iterate_context *
 mailbox_list_iter_init(struct mailbox_list *list, const char *pattern,
@@ -177,23 +186,22 @@
 }
 
 static bool
-ns_match_inbox(struct mail_namespace *ns, const char *pattern)
-{
-	struct imap_match_glob *glob;
-
-	if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) == 0)
-		return FALSE;
-
-	glob = imap_match_init(pool_datastack_create(), pattern,
-			       TRUE, mail_namespace_get_sep(ns));
-	return imap_match(glob, "INBOX") == IMAP_MATCH_YES;
-}
-
-static bool
 ns_is_match_within_ns(struct ns_list_iterate_context *ctx, 
 		      struct mail_namespace *ns, const char *prefix_without_sep,
 		      const char *pattern, enum imap_match_result result)
 {
+	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) == 0) {
+		switch (result) {
+		case IMAP_MATCH_YES:
+		case IMAP_MATCH_CHILDREN:
+			return TRUE;
+		case IMAP_MATCH_NO:
+		case IMAP_MATCH_PARENT:
+			break;
+		}
+		return FALSE;
+	}
+
 	switch (result) {
 	case IMAP_MATCH_YES:
 		/* allow matching prefix only when it's done without
@@ -253,25 +261,14 @@
 		result = imap_match(glob, prefix_without_sep);
 	}
 
-	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) {
-		return ns_is_match_within_ns(ctx, ns, prefix_without_sep,
-					     pattern, result);
-	} else {
-		switch (result) {
-		case IMAP_MATCH_YES:
-		case IMAP_MATCH_CHILDREN:
-			return TRUE;
-		case IMAP_MATCH_NO:
-		case IMAP_MATCH_PARENT:
-			break;
-		}
-		return FALSE;
-	}
+	return ns_is_match_within_ns(ctx, ns, prefix_without_sep,
+				     pattern, result);
 }
 
 static bool
-ns_match(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
+mailbox_list_ns_match_patterns(struct ns_list_iterate_context *ctx)
 {
+	struct mail_namespace *ns = ctx->cur_ns;
 	unsigned int i;
 
 	if (!ns_match_simple(ctx, ns))
@@ -282,8 +279,6 @@
 	   is slower than necessary, but this shouldn't matter much */
 	T_BEGIN {
 		for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
-			if (ns_match_inbox(ns, ctx->patterns_ns_match[i]))
-				break;
 			if (ns_match_next(ctx, ns, ctx->patterns_ns_match[i]))
 				break;
 		}
@@ -292,16 +287,6 @@
 	return ctx->patterns_ns_match[i] != NULL;
 }
 
-static struct mail_namespace *
-ns_next(struct ns_list_iterate_context *ctx, struct mail_namespace *ns)
-{
-	for (; ns != NULL; ns = ns->next) {
-		if (ns_match(ctx, ns))
-			break;
-	}
-	return ns;
-}
-
 static bool
 iter_next_try_prefix_pattern(struct ns_list_iterate_context *ctx,
 			     struct mail_namespace *ns, const char *pattern)
@@ -328,18 +313,13 @@
 				      pattern, result);
 }
 
-static bool iter_next_try_prefix(struct ns_list_iterate_context *ctx,
-				 struct mail_namespace *ns)
+static bool
+mailbox_list_ns_prefix_match(struct ns_list_iterate_context *ctx,
+			     struct mail_namespace *ns)
 {
 	unsigned int i;
 	bool ret = FALSE;
 
-	if (strncasecmp(ns->prefix, "INBOX", ns->prefix_len-1) == 0) {
-		/* INBOX is going to be listed in any case,
-		   don't duplicate it */
-		return FALSE;
-	}
-
 	for (i = 0; ctx->patterns_ns_match[i] != NULL; i++) {
 		T_BEGIN {
 			ret = iter_next_try_prefix_pattern(ctx, ns,
@@ -351,39 +331,274 @@
 	return ret;
 }
 
+static bool
+ns_prefix_is_visible(struct ns_list_iterate_context *ctx,
+		     struct mail_namespace *ns)
+{
+	if ((ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0)
+		return TRUE;
+	if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) {
+		if (mailbox_list_match_anything(ctx, ns, ns->prefix))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static bool
+ns_prefix_has_visible_child_namespace(struct ns_list_iterate_context *ctx,
+				      const char *prefix)
+{
+	struct mail_namespace *ns;
+	unsigned int prefix_len = strlen(prefix);
+
+	for (ns = ctx->namespaces; ns != NULL; ns = ns->next) {
+		if (ns->prefix_len > prefix_len &&
+		    strncmp(ns->prefix, prefix, prefix_len) == 0 &&
+		    ns_prefix_is_visible(ctx, ns))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static bool
+mailbox_ns_prefix_is_shared_inbox(struct mail_namespace *ns)
+{
+	return ns->type == NAMESPACE_SHARED &&
+		(ns->flags & NAMESPACE_FLAG_INBOX_ANY) != 0 &&
+		!ns->list->mail_set->mail_shared_explicit_inbox;
+}
+
+static bool
+mailbox_is_shared_inbox(struct mail_namespace *ns, const char *vname)
+{
+	return mailbox_ns_prefix_is_shared_inbox(ns) &&
+		strncmp(ns->prefix, vname, ns->prefix_len-1) == 0 &&
+		vname[ns->prefix_len-1] == '\0';
+}
+
+static int
+mailbox_list_match_anything(struct ns_list_iterate_context *ctx,
+			    struct mail_namespace *ns, const char *prefix)
+{
+	enum mailbox_list_iter_flags list_flags =
+		MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
+	struct mailbox_list_iterate_context *list_iter;
+	const struct mailbox_info *info;
+	const char *pattern;
+	int ret;
+
+	if (ns_prefix_has_visible_child_namespace(ctx, prefix))
+		return 1;
+
+	pattern = t_strconcat(prefix, "%", NULL);
+	list_iter = mailbox_list_iter_init(ns->list, pattern, list_flags);
+	info = mailbox_list_iter_next(list_iter);
+	if (info != NULL && mailbox_ns_prefix_is_shared_inbox(ns) &&
+	    mailbox_is_shared_inbox(ns, info->name)) {
+		/* we don't want to see this, try the next one */
+		info = mailbox_list_iter_next(list_iter);
+	}
+	ret = info != NULL ? 1 : 0;
+	if (mailbox_list_iter_deinit(&list_iter) < 0) {
+		if (ret == 0)
+			ret = -1;
+	}
+	return ret;
+}
+
+static bool
+mailbox_list_ns_prefix_return(struct ns_list_iterate_context *ctx,
+			      struct mail_namespace *ns, bool has_children)
+{
+	struct mailbox *box;
+	enum mailbox_existence existence;
+	int ret;
+
+	if (strncasecmp(ns->prefix, "INBOX", 5) == 0 &&
+	    ns->prefix[5] == mail_namespace_get_sep(ns)) {
+		/* prefix=INBOX/ (or prefix=INBOX/something/) namespace exists.
+		   so we can create children to INBOX. */
+		ctx->inbox_info.flags &= ~MAILBOX_NOINFERIORS;
+	}
+
+	if (ns->prefix_len == 0 || !mailbox_list_ns_prefix_match(ctx, ns))
+		return FALSE;
+
+	memset(&ctx->ns_info, 0, sizeof(ctx->ns_info));
+	ctx->ns_info.ns = ns;
+	ctx->ns_info.name = p_strndup(ctx->pool, ns->prefix,
+				      ns->prefix_len-1);
+
+	if (strcasecmp(ctx->ns_info.name, "INBOX") == 0) {
+		i_assert(!ctx->inbox_listed);
+		ctx->inbox_listed = TRUE;
+		ctx->ns_info.flags |= ctx->inbox_info.flags | MAILBOX_SELECT;
+	}
+
+	/* see if the namespace has children */
+	if (has_children)
+		ctx->ns_info.flags |= MAILBOX_CHILDREN;
+	else if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_CHILDREN) != 0 ||
+		 (ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0) {
+		/* need to check this explicitly */
+		if ((ret = mailbox_list_match_anything(ctx, ns, ns->prefix)) > 0)
+			ctx->ns_info.flags |= MAILBOX_CHILDREN;
+		else if (ret == 0) {
+			if ((ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 &&
+			    !mailbox_ns_prefix_is_shared_inbox(ns)) {
+				/* no children -> not visible */
+				return FALSE;
+			}
+			ctx->ns_info.flags |= MAILBOX_NOCHILDREN;
+		}
+	}
+
+	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) == 0 &&
+	    (ctx->ns_info.flags & MAILBOX_SELECT) == 0) {
+		/* see if namespace prefix is selectable */
+		box = mailbox_alloc(ns->list, ctx->ns_info.name, 0);
+		if (mailbox_exists(box, TRUE, &existence) == 0 &&
+		    existence == MAILBOX_EXISTENCE_SELECT)
+			ctx->ns_info.flags |= MAILBOX_SELECT;
+		else
+			ctx->ns_info.flags |= MAILBOX_NONEXISTENT;
+		mailbox_free(&box);
+	}
+	return TRUE;
+}
+
+static void inbox_set_children_flags(struct ns_list_iterate_context *ctx)
+{
+	const char *prefix;
+	int ret;
+
+	if ((ctx->ctx.flags & MAILBOX_LIST_ITER_RETURN_NO_FLAGS) != 0)
+		return;
+	if ((ctx->inbox_info.flags & (MAILBOX_CHILDREN | MAILBOX_NOINFERIORS |
+				      MAILBOX_NOCHILDREN)) != 0)
+		return;
+
+	if (mail_namespace_find_prefix(ctx->namespaces, "") == NULL) {
+		/* prefix="" namespace doesn't exist, and neither does
+		   anything beginning with prefix=INBOX/ (we checked this
+		   earlier). there's no way to create children for INBOX. */
+		ctx->inbox_info.flags |= MAILBOX_NOINFERIORS;
+		return;
+	}
+
+ 	/* INBOX namespace doesn't exist and we didn't see any children listed
+	   for INBOX. this could be because there truly aren't any children,
+	   or that the list patterns just didn't match them. */
+	prefix = t_strdup_printf("INBOX%c",
+				 mail_namespace_get_sep(ctx->inbox_info.ns));
+	ret = mailbox_list_match_anything(ctx, ctx->inbox_info.ns, prefix);
+	if (ret > 0)
+		ctx->inbox_info.flags |= MAILBOX_CHILDREN;
+	else if (ret == 0)
+		ctx->inbox_info.flags |= MAILBOX_NOCHILDREN;
+}
+
+static bool
+mailbox_list_ns_iter_try_next(struct mailbox_list_iterate_context *_ctx,
+			      const struct mailbox_info **info_r)
+{
+	struct ns_list_iterate_context *ctx =
+		(struct ns_list_iterate_context *)_ctx;
+	struct mail_namespace *ns;
+	const struct mailbox_info *info;
+	enum mail_error error;
+	const char *errstr;
+	bool has_children;
+
+	if (ctx->cur_ns == NULL) {
+		if (!ctx->inbox_listed && ctx->inbox_list) {
+			/* send delayed INBOX reply */
+			ctx->inbox_listed = TRUE;
+			inbox_set_children_flags(ctx);
+			*info_r = &ctx->inbox_info;
+			return TRUE;
+		}
+		*info_r = NULL;
+		return TRUE;
+	}
+
+	if (ctx->backend_ctx == NULL) {
+		i_assert(ctx->pending_backend_info == NULL);
+		if (!mailbox_list_ns_match_patterns(ctx)) {
+			/* namespace's children don't match the patterns,
+			   but the namespace prefix itself might */
+			ns = ctx->cur_ns;
+			ctx->cur_ns = ctx->cur_ns->next;
+			if (mailbox_list_ns_prefix_return(ctx, ns, FALSE)) {
+				*info_r = &ctx->ns_info;
+				return TRUE;
+			}
+			return FALSE;
+		}
+		/* start listing this namespace's mailboxes */
+		ctx->backend_ctx =
+			mailbox_list_iter_init_multiple(ctx->cur_ns->list,
+							ctx->patterns,
+							_ctx->flags);
+		ctx->cur_ns_prefix_sent = FALSE;
+	}
+	if (ctx->pending_backend_info == NULL)
+		info = mailbox_list_iter_next(ctx->backend_ctx);
+	else {
+		info = ctx->pending_backend_info;
+		ctx->pending_backend_info = NULL;
+	}
+	if (!ctx->cur_ns_prefix_sent) {
+		/* delayed sending of namespace prefix */
+		ctx->cur_ns_prefix_sent = TRUE;
+		has_children = info != NULL &&
+			!mailbox_is_shared_inbox(info->ns, info->name);
+		if (mailbox_list_ns_prefix_return(ctx, ctx->cur_ns,
+						  has_children)) {
+			ctx->pending_backend_info = info;
+			*info_r = &ctx->ns_info;
+			return TRUE;
+		}
+	}
+	if (info != NULL) {
+		if (strcasecmp(info->name, "INBOX") == 0 && ctx->inbox_list) {
+			/* delay sending INBOX reply. we already saved its
+			   flags at init stage */
+			return FALSE;
+		}
+		if (strncasecmp(info->name, "INBOX", 5) == 0 &&
+		    info->name[5] == mail_namespace_get_sep(info->ns)) {
+			/* we know now that INBOX has children */
+			ctx->inbox_info.flags |= MAILBOX_CHILDREN;
+			ctx->inbox_info.flags &= ~MAILBOX_NOINFERIORS;
+		}
+		if (mailbox_is_shared_inbox(info->ns, info->name)) {
+			/* listing shared/$user/INBOX as shared/$user, which
+			   we already listed as namespace prefix */
+			return FALSE;
+		}
+
+		*info_r = info;
+		return TRUE;
+	}
+
+	/* finished with this namespace */
+	if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) {
+		errstr = mailbox_list_get_last_error(ctx->cur_ns->list,
+						     &error);
+		mailbox_list_set_error(ctx->error_list, error, errstr);
+		_ctx->failed = TRUE;
+	}
+	ctx->cur_ns = ctx->cur_ns->next;
+	return FALSE;
+}
+
 static const struct mailbox_info *
 mailbox_list_ns_iter_next(struct mailbox_list_iterate_context *_ctx)
 {
-	struct ns_list_iterate_context *ctx =
-		(struct ns_list_iterate_context *)_ctx;
-	const struct mailbox_info *info;
+	const struct mailbox_info *info = NULL;
 
-	while (ctx->iter_namespaces != NULL) {
-		struct mail_namespace *ns = ctx->iter_namespaces;
-
-		ctx->iter_namespaces = ns->next;
-		if (ns->prefix_len > 0 && iter_next_try_prefix(ctx, ns)) {
-			ctx->ns_info.ns = ns;
-			ctx->ns_info.name = p_strndup(ctx->pool, ns->prefix,
-						      ns->prefix_len-1);
-			return &ctx->ns_info;
-		}
-	}
-
-	info = ctx->backend_ctx == NULL ? NULL :
-		mailbox_list_iter_next(ctx->backend_ctx);
-	if (info == NULL && ctx->namespaces != NULL) {
-		/* go to the next namespace */
-		if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0)
-			_ctx->failed = TRUE;
-		ctx->ctx.list->ns = ctx->namespaces;
-		ctx->backend_ctx =
-			mailbox_list_iter_init_multiple(ctx->namespaces->list,
-							ctx->patterns,
-							_ctx->flags);
-		ctx->namespaces = ns_next(ctx, ctx->namespaces->next);
-		return mailbox_list_ns_iter_next(_ctx);
-	}
+	while (!mailbox_list_ns_iter_try_next(_ctx, &info)) ;
 	return info;
 }
 
@@ -392,11 +607,17 @@
 {
 	struct ns_list_iterate_context *ctx =
 		(struct ns_list_iterate_context *)_ctx;
+	enum mail_error error;
+	const char *errstr;
 	int ret;
 
 	if (ctx->backend_ctx != NULL) {
-		if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0)
+		if (mailbox_list_iter_deinit(&ctx->backend_ctx) < 0) {
+			errstr = mailbox_list_get_last_error(ctx->cur_ns->list,
+							     &error);
+			mailbox_list_set_error(ctx->error_list, error, errstr);
 			_ctx->failed = TRUE;
+		}
 	}
 	ret = _ctx->failed ? -1 : 0;
 	pool_unref(&ctx->pool);
@@ -423,6 +644,31 @@
 	return dup;
 }
 
+static bool
+patterns_match_inbox(struct mail_namespace *namespaces,
+		     const char *const *patterns)
+{
+	struct mail_namespace *ns = mail_namespace_find_inbox(namespaces);
+	struct imap_match_glob *glob;
+
+	glob = imap_match_init_multiple(pool_datastack_create(), patterns,
+					TRUE, mail_namespace_get_sep(ns));
+	return imap_match(glob, "INBOX") == IMAP_MATCH_YES;
+}
+
+static void inbox_info_init(struct ns_list_iterate_context *ctx,
+			    struct mail_namespace *namespaces)
+{
+	enum mailbox_info_flags flags;
+
+	ctx->inbox_info.name = "INBOX";
+	ctx->inbox_info.ns = mail_namespace_find_inbox(namespaces);
+	i_assert(ctx->inbox_info.ns != NULL);
+
+	if (mailbox_list_mailbox(ctx->inbox_info.ns->list, "INBOX", &flags) > 0)
+		ctx->inbox_info.flags = flags;
+}
+
 struct mailbox_list_iterate_context *
 mailbox_list_iter_init_namespaces(struct mail_namespace *namespaces,
 				  const char *const *patterns,
@@ -443,29 +689,33 @@
 	ctx->ctx.list = p_new(pool, struct mailbox_list, 1);
 	ctx->ctx.list->v.iter_next = mailbox_list_ns_iter_next;
 	ctx->ctx.list->v.iter_deinit = mailbox_list_ns_iter_deinit;
-	ctx->iter_namespaces = namespaces;
+	ctx->namespaces = namespaces;
+	ctx->error_list = namespaces->list;
 
 	count = str_array_length(patterns);
 	ctx->patterns = p_new(pool, const char *, count + 1);
 	for (i = 0; i < count; i++)
 		ctx->patterns[i] = p_strdup(pool, patterns[i]);
+	if (patterns_match_inbox(namespaces, ctx->patterns)) {
+		/* we're going to list the INBOX. get its own flags (i.e. not
+		   [no]children) immediately, so if we end up seeing something
+		   else called INBOX (e.g. namespace prefix) we can show it
+		   immediately with the proper flags. */
+		ctx->inbox_list = TRUE;
+		inbox_info_init(ctx, namespaces);
+	}
 
 	if ((flags & MAILBOX_LIST_ITER_STAR_WITHIN_NS) != 0) {
-		/* create copies of patterns with '*' wildcard changed to '%' */
+		/* create copies of patterns with '*' wildcard changed to '%'.
+		   this is used only when checking which namespaces to list */
 		ctx->patterns_ns_match =
 			dup_patterns_without_stars(pool, ctx->patterns, count);
 	} else {
 		ctx->patterns_ns_match = ctx->patterns;
 	}
 
-	namespaces = ns_next(ctx, namespaces);
+	ctx->cur_ns = namespaces;
 	ctx->ctx.list->ns = namespaces;
-	if (namespaces != NULL) {
-		ctx->backend_ctx =
-			mailbox_list_iter_init_multiple(namespaces->list,
-							patterns, flags);
-		ctx->namespaces = ns_next(ctx, namespaces->next);
-	}
 	return &ctx->ctx;
 }
 
