changeset 1244:40bae47bdba1 HEAD

Added support for CHILDREN and LISTEXT extensions.
author Timo Sirainen <tss@iki.fi>
date Mon, 24 Feb 2003 19:39:31 +0200
parents 478471737964
children 06b1b95ae756
files configure.in src/imap/cmd-list.c src/imap/commands.h src/lib-storage/index/maildir/maildir-list.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/mbox/mbox-list.c src/lib-storage/mail-storage.h
diffstat 7 files changed, 172 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/configure.in	Mon Feb 24 03:16:30 2003 +0200
+++ b/configure.in	Mon Feb 24 19:39:31 2003 +0200
@@ -871,7 +871,7 @@
 dnl ** capabilities
 dnl **
 
-capability="IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE"
+capability="IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN LISTEXT LIST-SUBSCRIBED"
 AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
 
 CFLAGS="$CFLAGS $EXTRA_CFLAGS"
--- a/src/imap/cmd-list.c	Mon Feb 24 03:16:30 2003 +0200
+++ b/src/imap/cmd-list.c	Mon Feb 24 19:39:31 2003 +0200
@@ -19,14 +19,36 @@
 	struct mail_storage *storage;
 };
 
-static const char *mailbox_flags2str(enum mailbox_flags flags)
+struct list_send_context {
+	struct client *client;
+	const char *response_name;
+	const char *sep;
+	struct imap_match_glob *glob;
+	int listext;
+};
+
+static const char *mailbox_flags2str(enum mailbox_flags flags, int listext)
 {
 	const char *str;
 
-	if (flags == MAILBOX_PLACEHOLDER)
-		flags = MAILBOX_NOSELECT;
+	if (flags & MAILBOX_PLACEHOLDER) {
+		if (flags == MAILBOX_PLACEHOLDER) {
+			if (!listext)
+				flags = MAILBOX_NOSELECT;
+		} else {
+			/* it was at one point, but then we got better specs */
+			flags &= ~MAILBOX_PLACEHOLDER;
+		}
+		flags |= MAILBOX_CHILDREN;
+	}
+	if ((flags & MAILBOX_NONEXISTENT) != 0 && !listext)
+		flags |= MAILBOX_NOSELECT;
 
 	str = t_strconcat((flags & MAILBOX_NOSELECT) ? " \\Noselect" : "",
+			  (flags & MAILBOX_NONEXISTENT) ? " \\NonExistent" : "",
+			  (flags & MAILBOX_PLACEHOLDER) ? " \\PlaceHolder" : "",
+			  (flags & MAILBOX_CHILDREN) ? " \\Children" : "",
+			  (flags & MAILBOX_NOCHILDREN) ? " \\NoChildren" : "",
 			  (flags & MAILBOX_NOINFERIORS) ? " \\NoInferiors" : "",
 			  (flags & MAILBOX_MARKED) ? " \\Marked" : "",
 			  (flags & MAILBOX_UNMARKED) ? " \\UnMarked" : "",
@@ -35,8 +57,9 @@
 	return *str == '\0' ? "" : str+1;
 }
 
-static struct list_node *list_node_get(pool_t pool, struct list_node **node,
-				       const char *path, char separator)
+static void list_node_update(pool_t pool, struct list_node **node,
+			     const char *path, char separator,
+			     enum mailbox_flags flags)
 {
 	const char *name, *parent;
 
@@ -64,7 +87,11 @@
 			/* not found, create it */
 			*node = p_new(pool, struct list_node, 1);
 			(*node)->name = p_strdup(pool, name);
-			(*node)->flags = MAILBOX_PLACEHOLDER;
+			(*node)->flags = *path == '\0' ?
+				flags : MAILBOX_PLACEHOLDER;
+		} else {
+			if (*path == '\0')
+				(*node)->flags |= flags;
 		}
 
 		t_pop();
@@ -76,15 +103,12 @@
 		parent = (*node)->name;
 		node = &(*node)->children;
 	}
-
-	return *node;
 }
 
-static void list_send(struct client *client, struct list_node *node,
-		      const char *cmd, const char *path, const char *sep,
-		      struct imap_match_glob *glob)
+static void list_send(struct list_send_context *ctx, struct list_node *node,
+		      const char *path)
 {
-	const char *name, *send_name, *str;
+	const char *name, *send_name, *str, *flagstr;
 	enum imap_match_result match;
 
 	for (; node != NULL; node = node->next) {
@@ -92,7 +116,7 @@
 
 		/* Send INBOX always uppercased */
 		if (path != NULL)
-			name = t_strconcat(path, sep, node->name, NULL);
+			name = t_strconcat(path, ctx->sep, node->name, NULL);
 		else if (strcasecmp(node->name, "INBOX") == 0)
 			name = "INBOX";
 		else
@@ -106,24 +130,25 @@
 			const char *buf;
 
 			buf = str_unescape(t_strdup_noconst(name));
-			match = imap_match(glob, buf);
+			match = imap_match(ctx->glob, buf);
 			if (match == IMAP_MATCH_CHILDREN) {
-				send_name = t_strconcat(name, sep, NULL);
+				send_name = t_strconcat(name, ctx->sep, NULL);
 				buf = str_unescape(t_strdup_noconst(send_name));
-				match = imap_match(glob, buf);
+				match = imap_match(ctx->glob, buf);
 			}
 		}
 
 		if (match == IMAP_MATCH_YES) {
 			/* node->name should already be escaped */
-			str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
-					      mailbox_flags2str(node->flags),
-					      sep, send_name);
-			client_send_line(client, str);
+			flagstr = mailbox_flags2str(node->flags, ctx->listext);
+			str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"",
+					      ctx->response_name, flagstr,
+					      ctx->sep, send_name);
+			client_send_line(ctx->client, str);
 		}
 
 		if (node->children != NULL)
-			list_send(client, node->children, cmd, name, sep, glob);
+			list_send(ctx, node->children,  name);
 
 		t_pop();
 	}
@@ -131,32 +156,38 @@
 
 static void list_and_sort(struct client *client,
 			  struct mailbox_list_context *ctx,
-			  const char *cmd, const char *sep, const char *mask)
+			  const char *response_name,
+			  const char *sep, const char *mask, int listext)
 {
 	struct mailbox_list *list;
-	struct list_node *nodes, *node;
-	struct imap_match_glob *glob;
+	struct list_node *nodes;
+	struct list_send_context send_ctx;
 	pool_t pool;
 
 	pool = pool_alloconly_create("list_mailboxes", 10240);
 	nodes = NULL;
 
 	while ((list = client->storage->list_mailbox_next(ctx)) != NULL) {
-		node = list_node_get(pool, &nodes, list->name,
-				     client->storage->hierarchy_sep);
-		node->flags |= list->flags;
+		list_node_update(pool, &nodes, list->name,
+				 client->storage->hierarchy_sep,
+				 list->flags);
 	}
 
-	glob = imap_match_init(data_stack_pool, mask, TRUE,
-			       client->storage->hierarchy_sep);
-	list_send(client, nodes, cmd, NULL, sep, glob);
-	imap_match_deinit(glob);
+	send_ctx.client = client;
+	send_ctx.response_name = response_name;
+	send_ctx.sep = sep;
+	send_ctx.glob = imap_match_init(data_stack_pool, mask, TRUE,
+					client->storage->hierarchy_sep);
+	send_ctx.listext = listext;
+
+	list_send(&send_ctx, nodes, NULL);
+	imap_match_deinit(send_ctx.glob);
 	pool_unref(pool);
 }
 
 static void list_unsorted(struct client *client,
 			  struct mailbox_list_context *ctx,
-			  const char *cmd, const char *sep)
+			  const char *reply, const char *sep, int listext)
 {
 	struct mailbox_list *list;
 	const char *name, *str;
@@ -167,42 +198,50 @@
 			name = "INBOX";
 		else
 			name = str_escape(list->name);
-		str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
-				      mailbox_flags2str(list->flags),
+		str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", reply,
+				      mailbox_flags2str(list->flags, listext),
 				      sep, name);
 		client_send_line(client, str);
 		t_pop();
 	}
 }
 
-static int list_mailboxes(struct client *client, const char *mask,
-			  int subscribed, const char *sep)
+static int parse_list_flags(struct client *client, struct imap_arg *args,
+			    enum mailbox_list_flags *list_flags)
 {
-	struct mailbox_list_context *ctx;
-	const char *cmd;
-	int sorted;
+	const char *atom;
+
+	while (args->type != IMAP_ARG_EOL) {
+		if (args->type != IMAP_ARG_ATOM) {
+			client_send_command_error(client,
+				"List options contains non-atoms.");
+			return FALSE;
+		}
 
-	ctx = client->storage->
-		list_mailbox_init(client->storage, mask,
-				  subscribed ? MAILBOX_LIST_SUBSCRIBED : 0,
-				  &sorted);
-	if (ctx == NULL)
-		return FALSE;
+		atom = IMAP_ARG_STR(args);
 
-        cmd = subscribed ? "LSUB" : "LIST";
-	if (sorted)
-		list_unsorted(client, ctx, cmd, sep);
-	else
-		list_and_sort(client, ctx, cmd, sep, mask);
-
-	return client->storage->list_mailbox_deinit(ctx);
+		if (strcasecmp(atom, "SUBSCRIBED") == 0)
+			*list_flags |= MAILBOX_LIST_SUBSCRIBED;
+		else if (strcasecmp(atom, "CHILDREN") == 0)
+			*list_flags |= MAILBOX_LIST_CHILDREN;
+		else {
+			client_send_tagline(client, t_strconcat(
+				"BAD Invalid list option ", atom, NULL));
+			return FALSE;
+		}
+		args++;
+	}
+	return TRUE;
 }
 
-int _cmd_list_full(struct client *client, int subscribed)
+int _cmd_list_full(struct client *client, int lsub)
 {
+	struct imap_arg *args;
+        enum mailbox_list_flags list_flags;
+	struct mailbox_list_context *ctx;
 	const char *ref, *mask;
 	char sep_chr, sep[3];
-	int failed;
+	int failed, sorted, listext;
 
 	sep_chr = client->storage->hierarchy_sep;
 	if (IS_ESCAPED_CHAR(sep_chr)) {
@@ -214,11 +253,34 @@
 		sep[1] = '\0';
 	}
 
-	/* <reference> <mailbox wildcards> */
-	if (!client_read_string_args(client, 2, &ref, &mask))
+	/* [(<options>)] <reference> <mailbox wildcards> */
+	if (!client_read_args(client, 0, 0, &args))
 		return FALSE;
 
-	if (*mask == '\0' && !subscribed) {
+	listext = FALSE;
+	if (lsub)
+		list_flags = MAILBOX_LIST_SUBSCRIBED | MAILBOX_LIST_FAST_FLAGS;
+	else {
+		list_flags = 0;
+		if (args[0].type == IMAP_ARG_LIST) {
+			listext = TRUE;
+			if (!parse_list_flags(client,
+					      IMAP_ARG_LIST(&args[0])->args,
+					      &list_flags))
+				return TRUE;
+			args++;
+		}
+	}
+
+	ref = imap_arg_string(&args[0]);
+	mask = imap_arg_string(&args[1]);
+
+	if (ref == NULL || mask == NULL) {
+		client_send_command_error(client, "Invalid FETCH arguments.");
+		return TRUE;
+	}
+
+	if (*mask == '\0' && !lsub) {
 		/* special request to return the hierarchy delimiter */
 		client_send_line(client, t_strconcat(
 			"* LIST (\\Noselect) \"", sep, "\" \"\"", NULL));
@@ -240,13 +302,29 @@
 			}
 		}
 
-		failed = !list_mailboxes(client, mask, subscribed, sep);
+		ctx = client->storage->list_mailbox_init(client->storage, mask,
+							 list_flags, &sorted);
+		if (ctx == NULL)
+			failed = TRUE;
+		else {
+			const char *response_name = lsub ? "LSUB" : "LIST";
+
+			if (sorted) {
+				list_unsorted(client, ctx, response_name, sep,
+					      listext);
+			} else {
+				list_and_sort(client, ctx, response_name, sep,
+					      mask, listext);
+			}
+
+			failed = !client->storage->list_mailbox_deinit(ctx);
+		}
 	}
 
 	if (failed)
 		client_send_storage_error(client);
 	else {
-		client_send_tagline(client, subscribed ?
+		client_send_tagline(client, lsub ?
 				    "OK Lsub completed." :
 				    "OK List completed.");
 	}
--- a/src/imap/commands.h	Mon Feb 24 03:16:30 2003 +0200
+++ b/src/imap/commands.h	Mon Feb 24 19:39:31 2003 +0200
@@ -47,7 +47,7 @@
 int cmd_idle(struct client *client);
 
 /* private: */
-int _cmd_list_full(struct client *client, int subscribed);
+int _cmd_list_full(struct client *client, int lsub);
 int _cmd_select_full(struct client *client, int readonly);
 int _cmd_subscribe_full(struct client *client, int subscribe);
 
--- a/src/lib-storage/index/maildir/maildir-list.c	Mon Feb 24 03:16:30 2003 +0200
+++ b/src/lib-storage/index/maildir/maildir-list.c	Mon Feb 24 19:39:31 2003 +0200
@@ -147,9 +147,6 @@
 	ctx->list.flags = 0;
 	ctx->list.name = name;
 
-	if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) != 0)
-		return &ctx->list;
-
 	if (match == IMAP_MATCH_PARENT) {
 		/* placeholder */
 		ctx->list.flags = MAILBOX_PLACEHOLDER;
@@ -163,6 +160,9 @@
 		i_unreached();
 	}
 
+	if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) != 0)
+		return &ctx->list;
+
 	t_push();
 	path = maildir_get_path(ctx->storage, ctx->list.name);
 	if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
@@ -171,7 +171,7 @@
 		if (strcasecmp(ctx->list.name, "INBOX") == 0)
 			ctx->list.flags = 0;
 		else
-			ctx->list.flags = MAILBOX_NOSELECT;
+			ctx->list.flags = MAILBOX_NONEXISTENT;
 	}
 	t_pop();
 	return &ctx->list;
@@ -253,8 +253,8 @@
 		}
 
 		p_clear(ctx->list_pool);
-		if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) == 0)
-			ctx->list.flags = maildir_get_marked_flags(path);
+                ctx->list.flags = (ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0 ?
+			maildir_get_marked_flags(path) : 0;
 		ctx->list.name = p_strconcat(ctx->list_pool,
 					     ctx->prefix, fname, NULL);
 		return &ctx->list;
@@ -270,7 +270,8 @@
 	if (imap_match(ctx->glob, "INBOX") > 0) {
 		const char *path = maildir_get_path(ctx->storage, "INBOX");
 
-		ctx->list.flags = maildir_get_marked_flags(path);
+		ctx->list.flags = (ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0 ?
+			maildir_get_marked_flags(path) : 0;
 		ctx->list.name = "INBOX";
 		return &ctx->list;
 	}
--- a/src/lib-storage/index/maildir/maildir-storage.c	Mon Feb 24 03:16:30 2003 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Mon Feb 24 19:39:31 2003 +0200
@@ -416,7 +416,7 @@
 
 	ctx = storage->list_mailbox_init(storage,
 					 t_strconcat(oldname, ".*", NULL),
-					 MAILBOX_LIST_NO_FLAGS, &sorted);
+					 MAILBOX_LIST_FAST_FLAGS, &sorted);
 	while ((list = maildir_list_mailbox_next(ctx)) != NULL) {
 		i_assert(oldnamelen <= strlen(list->name));
 
--- a/src/lib-storage/index/mbox/mbox-list.c	Mon Feb 24 03:16:30 2003 +0200
+++ b/src/lib-storage/index/mbox/mbox-list.c	Mon Feb 24 19:39:31 2003 +0200
@@ -233,13 +233,13 @@
 		path = t_strconcat(list_path, "/", NULL);
 		match2 = imap_match(ctx->glob, path);
 
-		if (match > 0) {
-			ctx->list.flags = MAILBOX_NOSELECT;
+		ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
+		if (match > 0)
 			ctx->list.name = p_strdup(ctx->list_pool, list_path);
-		} else if (match2 > 0) {
-			ctx->list.flags = MAILBOX_NOSELECT;
+		else if (match2 > 0)
 			ctx->list.name = p_strdup(ctx->list_pool, path);
-		}
+		else
+			ctx->list.name = NULL;
 
 		ret = match2 < 0 ? 0 :
 			list_opendir(ctx->storage, real_path, FALSE, &dirp);
@@ -286,9 +286,6 @@
 	ctx->list.flags = 0;
 	ctx->list.name = name;
 
-	if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) != 0)
-		return &ctx->list;
-
 	if (match == IMAP_MATCH_PARENT) {
 		/* placeholder */
 		ctx->list.flags = MAILBOX_PLACEHOLDER;
@@ -302,11 +299,14 @@
 		i_unreached();
 	}
 
+	if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) != 0)
+		return &ctx->list;
+
 	t_push();
 	path = mbox_get_path(ctx->storage, ctx->list.name);
 	if (stat(path, &st) == 0) {
 		if (S_ISDIR(st.st_mode))
-			ctx->list.flags = MAILBOX_NOSELECT;
+			ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
 		else {
 			ctx->list.flags = MAILBOX_NOINFERIORS |
 				STAT_GET_MARKED(st);
@@ -315,7 +315,7 @@
 		if (strcasecmp(ctx->list.name, "INBOX") == 0)
 			ctx->list.flags = MAILBOX_UNMARKED;
 		else
-			ctx->list.flags = MAILBOX_NOSELECT;
+			ctx->list.flags = MAILBOX_NONEXISTENT;
 	}
 	t_pop();
 	return &ctx->list;
@@ -332,7 +332,7 @@
 
 	/* INBOX exists always, even if the file doesn't. */
 	ctx->list.flags = MAILBOX_NOINFERIORS;
-	if ((ctx->flags & MAILBOX_LIST_NO_FLAGS) == 0) {
+	if ((ctx->flags & MAILBOX_LIST_FAST_FLAGS) == 0) {
 		if (stat(ctx->storage->inbox_file, &st) < 0)
 			ctx->list.flags |= MAILBOX_UNMARKED;
 		else
@@ -347,7 +347,7 @@
 {
 	ctx->next = mbox_list_next;
 
-	ctx->list.flags = MAILBOX_NOSELECT;
+	ctx->list.flags = MAILBOX_NOSELECT | MAILBOX_CHILDREN;
 	ctx->list.name = p_strconcat(ctx->list_pool,
 				     ctx->dir->virtual_path, "/", NULL);
 
--- a/src/lib-storage/mail-storage.h	Mon Feb 24 03:16:30 2003 +0200
+++ b/src/lib-storage/mail-storage.h	Mon Feb 24 19:39:31 2003 +0200
@@ -6,20 +6,22 @@
 #include "imap-util.h"
 
 enum mailbox_list_flags {
-	MAILBOX_LIST_SUBSCRIBED	= 0x01, /* show only subscribed */
-	MAILBOX_LIST_NO_FLAGS	= 0x02  /* don't set mailbox_flags */
+	MAILBOX_LIST_SUBSCRIBED	= 0x01,
+	MAILBOX_LIST_FAST_FLAGS	= 0x02,
+	MAILBOX_LIST_CHILDREN	= 0x04
 };
 
 enum mailbox_flags {
-	MAILBOX_NOSELECT	= 0x01,
-	MAILBOX_CHILDREN	= 0x02,
-	MAILBOX_NOCHILDREN	= 0x04,
-	MAILBOX_NOINFERIORS	= 0x08,
-	MAILBOX_MARKED		= 0x10,
-	MAILBOX_UNMARKED	= 0x20,
-	MAILBOX_PLACEHOLDER	= 0x40,
+	MAILBOX_NOSELECT	= 0x001,
+	MAILBOX_NONEXISTENT	= 0x002,
+	MAILBOX_PLACEHOLDER	= 0x004,
+	MAILBOX_CHILDREN	= 0x008,
+	MAILBOX_NOCHILDREN	= 0x010,
+	MAILBOX_NOINFERIORS	= 0x020,
+	MAILBOX_MARKED		= 0x040,
+	MAILBOX_UNMARKED	= 0x080,
 
-	MAILBOX_READONLY	= 0x40
+	MAILBOX_READONLY	= 0x100
 };
 
 enum mailbox_status_items {