dovecot: Handle listing flags correctly for all namespace prefixes.

dovecot at dovecot.org dovecot at dovecot.org
Wed Jun 27 20:21:05 EEST 2007


details:   http://hg.dovecot.org/dovecot/rev/e0b451e0c190
changeset: 5811:e0b451e0c190
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Jun 27 20:21:00 2007 +0300
description:
Handle listing flags correctly for all namespace prefixes.

diffstat:

1 file changed, 185 insertions(+), 78 deletions(-)
src/imap/cmd-list.c |  263 +++++++++++++++++++++++++++++++++++----------------

diffs (truncated from 446 to 300 lines):

diff -r f56a71347378 -r e0b451e0c190 src/imap/cmd-list.c
--- a/src/imap/cmd-list.c	Wed Jun 27 20:19:49 2007 +0300
+++ b/src/imap/cmd-list.c	Wed Jun 27 20:21:00 2007 +0300
@@ -1,6 +1,7 @@
-/* Copyright (C) 2002-2004 Timo Sirainen */
+/* Copyright (C) 2002-2007 Timo Sirainen */
 
 #include "common.h"
+#include "array.h"
 #include "str.h"
 #include "strescape.h"
 #include "imap-quote.h"
@@ -14,6 +15,7 @@ enum {
 };
 
 struct cmd_list_context {
+	struct client_command_context *cmd;
 	const char *ref;
 	const char *mask;
 	enum mailbox_list_flags list_flags;
@@ -22,9 +24,14 @@ struct cmd_list_context {
 	struct mailbox_list_iterate_context *list_iter;
 	struct imap_match_glob *glob;
 
+	ARRAY_DEFINE(ns_prefixes_listed, struct mail_namespace *);
+
 	unsigned int lsub:1;
 	unsigned int inbox_found:1;
-	unsigned int match_inbox: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;
 };
 
 static void
@@ -48,10 +55,12 @@ mailbox_flags2str(string_t *str, enum ma
 		str_append(str, "\\NonExistent ");
 	if ((flags & MAILBOX_CHILDREN) != 0)
 		str_append(str, "\\HasChildren ");
-	if ((flags & MAILBOX_NOCHILDREN) != 0)
-		str_append(str, "\\HasNoChildren ");
-	if ((flags & MAILBOX_NOINFERIORS) != 0)
-		str_append(str, "\\NoInferiors ");
+	else {
+		if ((flags & MAILBOX_NOCHILDREN) != 0)
+			str_append(str, "\\HasNoChildren ");
+		if ((flags & MAILBOX_NOINFERIORS) != 0)
+			str_append(str, "\\NoInferiors ");
+	}
 	if ((flags & MAILBOX_MARKED) != 0)
 		str_append(str, "\\Marked ");
 	if ((flags & MAILBOX_UNMARKED) != 0)
@@ -90,19 +99,102 @@ parse_list_flags(struct client_command_c
 	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) == 0) {
+		/* INBOX doesn't exist. use the default INBOX flags */
+		return flags;
+	}
+
+	/* find the INBOX flags */
+	ns = mail_namespace_find_inbox(ctx->cmd->client->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)
+{
+	struct mailbox_list_iterate_context *list_iter;
+	const struct mailbox_info *info;
+	bool ret = FALSE;
+
+	list_iter = mailbox_list_iter_init(ctx->ns->list, "%",
+					   MAILBOX_LIST_ITER_FAST_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 void
-list_namespace_inbox(struct client *client, struct cmd_list_context *ctx)
-{
-	const char *str;
-
-	if (!ctx->inbox_found && ctx->match_inbox &&
-	    (ctx->ns->flags & NAMESPACE_FLAG_INBOX) != 0 &&
-	    (ctx->list_flags & MAILBOX_LIST_ITER_SUBSCRIBED) == 0) {
-		/* INBOX always exists */
-		str = t_strdup_printf("* LIST (\\Unmarked) \"%s\" \"INBOX\"",
-				      ctx->ns->sep_str);
-		client_send_line(client, str);
-	}
+list_namespace_send_prefix(struct cmd_list_context *ctx, bool have_children)
+{
+	struct mail_namespace *const *listed;
+	unsigned int i, count, len;
+	enum mailbox_info_flags flags;
+	const char *name;
+	string_t *str;
+	
+	ctx->cur_ns_send_prefix = FALSE;
+
+	/* see if we already listed this as a valid mailbox in another
+	   namespace */
+	listed = array_get(&ctx->ns_prefixes_listed, &count);
+	for (i = 0; i < count; i++) {
+		if (listed[i] == ctx->ns)
+			return;
+	}
+
+	len = strlen(ctx->ns->prefix);
+	if (len == 6 && strncasecmp(ctx->ns->prefix, "INBOX", len-1) == 0 &&
+	    ctx->ns->prefix[len-1] == ctx->ns->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 {
+		flags = MAILBOX_NONEXISTENT;
+	}
+
+	if ((flags & MAILBOX_CHILDREN) == 0) {
+		if (have_children || list_namespace_has_children(ctx)) {
+			flags |= MAILBOX_CHILDREN;
+			flags &= ~MAILBOX_NOCHILDREN;
+		} else {
+			flags |= MAILBOX_NOCHILDREN;
+		}
+	}
+	
+	str = t_str_new(128);
+	str_append(str, "* LIST (");
+	mailbox_flags2str(str, flags, ctx->list_flags);
+	str_printfa(str, ") \"%s\" ", ctx->ns->sep_str);
+
+	name = ctx->cur_ns_skip_trailing_sep ?
+		t_strndup(ctx->ns->prefix, len-1) : ctx->ns->prefix;
+	imap_quote_append_string(str, name, FALSE);
+	client_send_line(ctx->cmd->client, str_c(str));
 }
 
 static bool
@@ -117,7 +209,7 @@ list_insert_ns_prefix(string_t *name_str
 			/* no namespace prefix, we can't list this */
 			return FALSE;
 		}
-	} else if (!ctx->match_inbox) {
+	} else if (!ctx->cur_ns_match_inbox) {
 		/* The mask doesn't match INBOX (eg. prefix.%).
 		   We still want to list prefix.INBOX if it has
 		   children. Otherwise we don't want to list
@@ -134,17 +226,14 @@ list_insert_ns_prefix(string_t *name_str
 }
 
 static int
-list_namespace_mailboxes(struct client *client, struct cmd_list_context *ctx)
+list_namespace_mailboxes(struct cmd_list_context *ctx)
 {
 	const struct mailbox_info *info;
+	struct mail_namespace *ns;
+	enum mailbox_info_flags flags;
+	string_t *str, *name_str;
 	const char *name;
-	string_t *str, *name_str;
 	int ret = 0;
-
-	if (ctx->list_iter == NULL) {
-		list_namespace_inbox(client, ctx);
-		return 1;
-	}
 
 	t_push();
 	str = t_str_new(256);
@@ -164,6 +253,7 @@ list_namespace_mailboxes(struct client *
 			}
 		}
 		name = str_c(name_str);
+		flags = info->flags;
 
 		if (*ctx->ns->prefix != '\0') {
 			/* With masks containing '*' we do the checks here
@@ -173,19 +263,32 @@ list_namespace_mailboxes(struct client *
 				continue;
 		}
 		if (strcasecmp(name, "INBOX") == 0) {
-			if ((ctx->ns->flags & NAMESPACE_FLAG_INBOX) == 0)
+			i_assert((ctx->ns->flags & NAMESPACE_FLAG_INBOX) != 0);
+			if (ctx->inbox_found) {
+				/* we already listed this at the beginning
+				   of handling INBOX/ namespace */
 				continue;
-
-			name = "INBOX";
+			}
 			ctx->inbox_found = TRUE;
+		}
+		if (ctx->cur_ns_send_prefix)
+			list_namespace_send_prefix(ctx, TRUE);
+
+		/* if there's a namespace with this name, list it as
+		   having children */
+		ns = mail_namespace_find_prefix_nosep(ctx->ns, name);
+		if (ns != NULL) {
+			flags |= MAILBOX_CHILDREN;
+			flags &= ~MAILBOX_NOCHILDREN;
+			array_append(&ctx->ns_prefixes_listed, &ns, 1);
 		}
 
 		str_truncate(str, 0);
 		str_printfa(str, "* %s (", ctx->lsub ? "LSUB" : "LIST");
-		mailbox_flags2str(str, info->flags, ctx->list_flags);
+		mailbox_flags2str(str, flags, ctx->list_flags);
 		str_printfa(str, ") \"%s\" ", ctx->ns->sep_str);
 		imap_quote_append_string(str, name, FALSE);
-		if (client_send_line(client, str_c(str)) == 0) {
+		if (client_send_line(ctx->cmd->client, str_c(str)) == 0) {
 			/* buffer is full, continue later */
 			t_pop();
 			return 0;
@@ -194,9 +297,6 @@ list_namespace_mailboxes(struct client *
 
 	if (mailbox_list_iter_deinit(&ctx->list_iter) < 0)
 		ret = -1;
-
-	if (ret == 0)
-		list_namespace_inbox(client, ctx);
 
 	t_pop();
 	return ret < 0 ? -1 : 1;
@@ -297,8 +397,7 @@ skip_namespace_prefix_refmask(struct cmd
 }
 
 static enum imap_match_result
-list_use_inboxcase(struct client_command_context *cmd,
-		   struct cmd_list_context *ctx)
+list_use_inboxcase(struct cmd_list_context *ctx)
 {
 	struct imap_match_glob *inbox_glob;
 
@@ -308,7 +407,7 @@ list_use_inboxcase(struct client_command
 
 	/* if the original reference and mask combined produces something
 	   that matches INBOX, the INBOX casing is on. */
-	inbox_glob = imap_match_init(cmd->pool,
+	inbox_glob = imap_match_init(ctx->cmd->pool,
 				     t_strconcat(ctx->ref, ctx->mask, NULL),
 				     TRUE, ctx->ns->sep);
 	return imap_match(inbox_glob, "INBOX");
@@ -349,29 +448,31 @@ skip_mask_wildcard_prefix(const char *cu
 	*cur_mask_p = cur_mask;
 }
 
-static void
-list_namespace_init(struct client_command_context *cmd,
-		    struct cmd_list_context *ctx)
-{
-        struct client *client = cmd->client;
+static void list_namespace_init(struct cmd_list_context *ctx)
+{
 	struct mail_namespace *ns = ctx->ns;
 	const char *cur_ns_prefix, *cur_ref, *cur_mask;
 	enum imap_match_result match;
 	enum imap_match_result inbox_match;
 	size_t len;
 
-	cur_ns_prefix = ctx->ns->prefix;
+	cur_ns_prefix = ns->prefix;
 	cur_ref = ctx->ref;
 	cur_mask = ctx->mask;
+
+	ctx->cur_ns_skip_trailing_sep = FALSE;
+
+	if ((ns->flags & NAMESPACE_FLAG_INBOX) != 0)
+		ctx->seen_inbox_namespace = TRUE;


More information about the dovecot-cvs mailing list