[dovecot-cvs] dovecot/src/imap cmd-list.c,1.48,1.49

cras at dovecot.org cras at dovecot.org
Sat Jan 14 18:48:54 EET 2006


Update of /var/lib/cvs/dovecot/src/imap
In directory talvi:/tmp/cvs-serv27153

Modified Files:
	cmd-list.c 
Log Message:
Cleaned up and added comments. Also added some kludging so that listing
namespace prefix itself with and without trailing separator works.



Index: cmd-list.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/imap/cmd-list.c,v
retrieving revision 1.48
retrieving revision 1.49
diff -u -d -r1.48 -r1.49
--- cmd-list.c	13 Jan 2006 20:25:59 -0000	1.48
+++ cmd-list.c	14 Jan 2006 16:48:52 -0000	1.49
@@ -23,7 +23,6 @@
 	struct imap_match_glob *glob;
 
 	unsigned int lsub:1;
-	unsigned int inbox:1;
 	unsigned int inbox_found:1;
 	unsigned int match_inbox:1;
 };
@@ -171,7 +170,18 @@
 	return ret < 0 ? -1 : 1;
 }
 
-static void skip_prefix(const char **prefix, const char **mask, bool inbox)
+static bool list_mask_has_wildcards(const char *mask)
+{
+	for (; *mask != '\0'; mask++) {
+		if (*mask == '%' || *mask == '*')
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static void
+skip_namespace_prefix(const char **prefix, const char **mask,
+		      bool inbox_check, char sep)
 {
 	size_t mask_len, prefix_len;
 	bool match;
@@ -185,11 +195,13 @@
 	}
 
 	match = strncmp(*prefix, *mask, prefix_len) == 0;
-	if (!match && inbox) {
+	if (!match && inbox_check) {
 		/* try INBOX check. */
 		match = prefix_len >= 5 &&
 			strncasecmp(*prefix, *mask, 5) == 0 &&
-			strncmp(*prefix + 5, *mask + 5, prefix_len - 5) == 0;
+			strncmp(*prefix + 5, *mask + 5, prefix_len - 5) == 0 &&
+			strncasecmp(*prefix, "INBOX", 5) == 0 &&
+			((*prefix)[5] == sep || (*prefix)[5] == '\0');
 	}
 
 	if (match) {
@@ -204,43 +216,79 @@
 {
         struct client *client = cmd->client;
 	struct namespace *ns = ctx->ns;
-	const char *cur_prefix, *cur_ref, *cur_mask;
+	const char *cur_ns_prefix, *cur_ref, *cur_mask;
 	enum imap_match_result match;
 	enum mailbox_list_flags list_flags;
 	unsigned int count;
 	size_t len;
 
-	cur_prefix = ns->prefix;
+	cur_ns_prefix = ns->prefix;
 	cur_ref = ctx->ref;
 	cur_mask = ctx->mask;
 
-	if (*ctx->ref != '\0') {
-		skip_prefix(&cur_prefix, &cur_ref, ctx->inbox);
+	if (*cur_ref != '\0' && *cur_ns_prefix != '\0') {
+		/* reference argument given. skip namespace prefix using it.
 
-		if (*cur_ref != '\0' && *cur_prefix != '\0') {
+		   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, ns->sep);
+
+		if (*cur_ref != '\0' && *cur_ns_prefix != '\0') {
 			/* reference parameter didn't match with
 			   namespace prefix. skip this. */
 			return;
 		}
 	}
 
-	if (*cur_ref == '\0' && *cur_prefix != '\0') {
+	if (*cur_ns_prefix != '\0') {
 		/* no reference parameter. skip namespace prefix from mask. */
-		skip_prefix(&cur_prefix, &cur_mask,
-			    ctx->inbox && cur_ref == ctx->ref);
+		const char *old_ns_prefix = cur_ns_prefix;
+		const char *old_mask = cur_mask;
+
+		i_assert(*cur_ref == '\0');
+
+		skip_namespace_prefix(&cur_ns_prefix, &cur_mask,
+				      cur_ref == ctx->ref, ns->sep);
+
+		if (*cur_mask == '\0' && *cur_ns_prefix == '\0') {
+			/* trying to list the namespace prefix itself. */
+			cur_ns_prefix = old_ns_prefix;
+			cur_mask = old_mask;
+		}
 	}
 
+	/* INBOX check is done only in the beginning of mask.
+	   Reference parameter doesn't affect it. */
 	ctx->glob = imap_match_init(cmd->pool, ctx->mask,
-				    cur_ref == ctx->ref, ns->sep);
+                                    cur_mask == ctx->mask, ns->sep);
+	ctx->match_inbox = imap_match(ctx->glob, "INBOX") == IMAP_MATCH_YES;
 
-	if (*cur_ref != '\0' || *cur_prefix == '\0')
-		match = IMAP_MATCH_CHILDREN;
-	else {
-		len = strlen(cur_prefix);
-		if (cur_prefix[len-1] == ns->sep)
-			cur_prefix = t_strndup(cur_prefix, len-1);
-		match = ns->hidden ? IMAP_MATCH_NO :
-		       imap_match(ctx->glob, cur_prefix);
+	if (*cur_ns_prefix != '\0') {
+		/* namespace prefix still wasn't completely skipped over.
+		   for example cur_ns_prefix=INBOX/, mask=%/% or mask=IN%.
+		   Check that mask matches namespace prefix. */
+		bool skip_trailing_sep = FALSE;
+		i_assert(*cur_ref == '\0');
+
+		/* drop the trailing separator in namespace prefix.
+		   don't do it if we're listing only the prefix itself. */
+		len = strlen(cur_ns_prefix);
+		if (cur_ns_prefix[len-1] == ns->sep &&
+		    strcmp(cur_mask, cur_ns_prefix) != 0) {
+			skip_trailing_sep = TRUE;
+			cur_ns_prefix = t_strndup(cur_ns_prefix, len-1);
+		}
+
+		/* hidden namespaces should still be seen without wildcards.
+		   some clients rely on this. */
+		match = (ns->hidden && list_mask_has_wildcards(cur_mask)) ?
+			IMAP_MATCH_NO : imap_match(ctx->glob, cur_ns_prefix);
+		if (match < 0)
+			return;
 
 		if (match == IMAP_MATCH_YES &&
 		    (ctx->list_flags & MAILBOX_LIST_SUBSCRIBED) == 0) {
@@ -261,46 +309,49 @@
 			str_printfa(str, "* LIST (%s) \"%s\" ",
 				    mailbox_flags2str(flags, ctx->list_flags),
 				    ns->sep_str);
-			imap_quote_append_string(str,
-				t_strndup(ns->prefix, len-1), FALSE);
+			imap_quote_append_string(str, skip_trailing_sep ?
+				t_strndup(ns->prefix, len-1) : ns->prefix,
+				FALSE);
 			client_send_line(client, str_c(str));
 		}
 	}
 
-	ctx->match_inbox = imap_match(ctx->glob, "INBOX") == IMAP_MATCH_YES;
 
-	if (match < 0)
-		return;
+	if (*cur_ns_prefix != '\0') {
+		/* We didn't skip over the whole namespace prefix. For example
+		   cur_ns_prefix=INBOX/ and mask=%/% or IN*.
 
-	count = 0;
-	if (*cur_prefix != '\0') {
-		/* we'll have to fix mask */
-		for (; *cur_prefix != '\0'; cur_prefix++) {
-			if (*cur_prefix == ns->sep)
+		   We have already verified that the mask matches the namespace
+		   prefix, so we'll just have to skip over as many hierarchies
+		   from mask as there exists in namespace prefix.
+
+		   The "INBOX" namespace match reply was already sent. We're
+		   only listing the actual mailboxes now. */
+		i_assert(*cur_ref == '\0');
+
+		for (count = 1; *cur_ns_prefix != '\0'; cur_ns_prefix++) {
+			if (*cur_ns_prefix == ns->sep)
 					count++;
 		}
-		if (count == 0)
-			count = 1;
 
-		while (count > 0) {
-			if (*cur_ref != '\0') {
-				while (*cur_ref != '\0' &&
-				       *cur_ref++ != ns->sep)
-					;
-			} else {
-				while (*cur_mask != '\0' && *cur_mask != '*' &&
-				       *cur_mask != ns->sep)
-					cur_mask++;
-
-				if (*cur_mask == '*') {
-					cur_mask = "*";
-					break;
-				}
-				if (*cur_mask == '\0')
-					break;
+		for (; count > 0; count--) {
+			/* skip over one hierarchy */
+			while (*cur_mask != '\0' && *cur_mask != '*' &&
+			       *cur_mask != ns->sep)
 				cur_mask++;
+
+			if (*cur_mask == '*') {
+				/* we'll just request "*" and filter it
+				   ourself. otherwise this gets too complex. */
+				cur_mask = "*";
+				break;
 			}
-			count--;
+			if (*cur_mask == '\0') {
+				/* mask ended too early. we won't be listing
+				   any mailboxes. */
+				break;
+			}
+			cur_mask++;
 		}
 	}
 
@@ -308,7 +359,7 @@
 		/* a) we don't have '*' in mask
 		   b) we want to display everything
 
-		   we don't need to do separate matching ourself */
+		   we don't need to do separate filtering ourself */
 		ctx->glob = NULL;
 	}
 
@@ -426,8 +477,6 @@
 		}
 		client_send_tagline(cmd, "OK List completed.");
 	} else {
-		const char *inbox_arg;
-
 		ctx = p_new(cmd->pool, struct cmd_list_context, 1);
 		ctx->ref = ref;
 		ctx->mask = mask;
@@ -435,10 +484,6 @@
 		ctx->lsub = lsub;
 		ctx->ns = client->namespaces;
 
-		inbox_arg = *ref != '\0' ? ref : mask;
-		ctx->inbox = strncasecmp(inbox_arg, "INBOX", 5) == 0 &&
-			(inbox_arg[5] == ctx->ns->sep || inbox_arg[5] == '\0');
-
 		cmd->context = ctx;
 		if (!cmd_list_continue(cmd)) {
 			/* unfinished */



More information about the dovecot-cvs mailing list