changeset 7057:81f4c9689c18 HEAD

FLAGS/PERMENENTFLAGS weren't always sent to client early enough. Also optimized sending keywords with FETCH FLAGS.
author Timo Sirainen <tss@iki.fi>
date Sat, 29 Dec 2007 07:11:12 +0200
parents 097c70cfa55e
children cf4cee852a05
files src/imap/client.c src/imap/client.h src/imap/cmd-close.c src/imap/cmd-select.c src/imap/cmd-unselect.c src/imap/commands-util.c src/imap/commands-util.h src/imap/imap-fetch.c src/imap/imap-fetch.h src/imap/imap-sync.c
diffstat 10 files changed, 79 insertions(+), 109 deletions(-) [+]
line wrap: on
line diff
--- a/src/imap/client.c	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/client.c	Sat Dec 29 07:11:12 2007 +0200
@@ -41,7 +41,6 @@
         client->last_input = ioloop_time;
 
 	client->command_pool = pool_alloconly_create("client command", 8192);
-	client->keywords.pool = pool_alloconly_create("mailbox_keywords", 512);
 	client->namespaces = namespaces;
 
 	while (namespaces != NULL) {
@@ -144,7 +143,6 @@
 			i_error("close(client out) failed: %m");
 	}
 
-	pool_unref(&client->keywords.pool);
 	pool_unref(&client->command_pool);
 	i_free(client);
 
--- a/src/imap/client.h	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/client.h	Sat Dec 29 07:11:12 2007 +0200
@@ -11,9 +11,13 @@
 struct imap_arg;
 
 struct mailbox_keywords {
-	pool_t pool; /* will be p_clear()ed when changed */
-
-	ARRAY_DEFINE(keywords, const char *);
+	/* All keyword names. The array itself exists in mail_index.
+	   Keywords are currently only appended, they're never removed. */
+	const ARRAY_TYPE(keywords) *names;
+	/* Number of keywords announced to client via FLAGS/PERMANENTFLAGS.
+	   This relies on keywords not being removed while mailbox is
+	   selected. */
+	unsigned int announce_count;
 };
 
 struct client_command_context {
--- a/src/imap/cmd-close.c	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/cmd-close.c	Sat Dec 29 07:11:12 2007 +0200
@@ -21,6 +21,7 @@
 
 	if (mailbox_close(&mailbox) < 0)
                 client_send_untagged_storage_error(client, storage);
+	client_update_mailbox_flags(client, NULL);
 
 	client_send_tagline(cmd, "OK Close completed.");
 	return TRUE;
--- a/src/imap/cmd-select.c	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/cmd-select.c	Sat Dec 29 07:11:12 2007 +0200
@@ -45,17 +45,17 @@
 		return TRUE;
 	}
 
-	client_save_keywords(&client->keywords, status.keywords);
-	client->messages_count = status.messages;
-	client->recent_count = status.recent;
-	client->uidvalidity = status.uidvalidity;
-
 	/* set client's mailbox only after getting status to make sure
 	   we're not sending any expunge/exists replies too early to client */
 	client->mailbox = box;
 	client->select_counter++;
 
-	client_send_mailbox_flags(client, box, status.keywords);
+	client->messages_count = status.messages;
+	client->recent_count = status.recent;
+	client->uidvalidity = status.uidvalidity;
+
+	client_update_mailbox_flags(client, status.keywords);
+	client_send_mailbox_flags(client, TRUE);
 
 	client_send_line(client,
 		t_strdup_printf("* %u EXISTS", status.messages));
--- a/src/imap/cmd-unselect.c	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/cmd-unselect.c	Sat Dec 29 07:11:12 2007 +0200
@@ -17,6 +17,7 @@
 	storage = mailbox_get_storage(mailbox);
 	if (mailbox_close(&mailbox) < 0)
 		client_send_untagged_storage_error(client, storage);
+	client_update_mailbox_flags(client, NULL);
 
 	client_send_tagline(cmd, "OK Unselect completed.");
 	return TRUE;
--- a/src/imap/commands-util.c	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/commands-util.c	Sat Dec 29 07:11:12 2007 +0200
@@ -251,9 +251,6 @@
 	const char *const *names;
 	unsigned int i, count;
 
-	if (array_count(keywords) == 0)
-		return "";
-
 	str = t_str_new(256);
 	names = array_get(keywords, &count);
 	for (i = 0; i < count; i++) {
@@ -265,65 +262,62 @@
 
 #define SYSTEM_FLAGS "\\Answered \\Flagged \\Deleted \\Seen \\Draft"
 
-void client_send_mailbox_flags(struct client *client, struct mailbox *box,
-			       const ARRAY_TYPE(keywords) *keywords)
+void client_send_mailbox_flags(struct client *client, bool selecting)
 {
+	unsigned int count = array_count(client->keywords.names);
 	const char *str;
 
-	str = get_keywords_string(keywords);
+	if (!selecting && count == client->keywords.announce_count) {
+		/* no changes to keywords and we're not selecting a mailbox */
+		return;
+	}
+
+	client->keywords.announce_count = count;
+	str = count == 0 ? "" : get_keywords_string(client->keywords.names);
 	client_send_line(client,
 		t_strconcat("* FLAGS ("SYSTEM_FLAGS, str, ")", NULL));
 
-	if (mailbox_is_readonly(box)) {
+	if (mailbox_is_readonly(client->mailbox)) {
 		client_send_line(client, "* OK [PERMANENTFLAGS ()] "
 				 "Read-only mailbox.");
 	} else {
+		bool star = mailbox_allow_new_keywords(client->mailbox);
+
 		client_send_line(client,
 			t_strconcat("* OK [PERMANENTFLAGS ("SYSTEM_FLAGS, str,
-				    mailbox_allow_new_keywords(box) ?
-				    " \\*" : "", ")] Flags permitted.", NULL));
+				    star ? " \\*" : "",
+				    ")] Flags permitted.", NULL));
 	}
 }
 
-bool client_save_keywords(struct mailbox_keywords *dest,
-			  const ARRAY_TYPE(keywords) *keywords)
+void client_update_mailbox_flags(struct client *client,
+				 const ARRAY_TYPE(keywords) *keywords)
 {
-	const char *const *names, *const *old_names;
-	unsigned int i, count, old_count;
-	bool changed;
-
-	names = array_get(keywords, &count);
+	client->keywords.names = keywords;
+	client->keywords.announce_count = 0;
+}
 
-	/* first check if anything changes */
-	if (!array_is_created(&dest->keywords))
-		changed = count != 0;
-	else {
-		old_names = array_get(&dest->keywords, &old_count);
-		if (count != old_count)
-			changed = TRUE;
-		else {
-			changed = FALSE;
-			for (i = 0; i < count; i++) {
-				if (strcmp(names[i], old_names[i]) != 0) {
-					changed = TRUE;
-					break;
-				}
-			}
-		}
+const char *const *
+client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
+			 const ARRAY_TYPE(keyword_indexes) *src)
+{
+	const unsigned int *kw_indexes;
+	const char *const *all_names;
+	unsigned int i, kw_count, all_count;
+
+	client_send_mailbox_flags(client, FALSE);
+
+	all_names = array_get(client->keywords.names, &all_count);
+	kw_indexes = array_get(src, &kw_count);
+
+	/* convert indexes to names */
+	for (i = 0; i < kw_count; i++) {
+		i_assert(kw_indexes[i] < all_count);
+		array_append(dest, &all_names[kw_indexes[i]], 1);
 	}
 
-	if (!changed)
-		return FALSE;
-
-	p_clear(dest->pool);
-	p_array_init(&dest->keywords, dest->pool, array_count(keywords));
-
-	for (i = 0; i < count; i++) {
-		const char *name = p_strdup(dest->pool, names[i]);
-
-		array_append(&dest->keywords, &name, 1);
-	}
-	return TRUE;
+	(void)array_append_space(dest);
+	return array_idx(dest, 0);
 }
 
 bool mailbox_equals(struct mailbox *box1, struct mail_storage *storage2,
--- a/src/imap/commands-util.h	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/commands-util.h	Sat Dec 29 07:11:12 2007 +0200
@@ -50,14 +50,16 @@
 			     enum mail_flags *flags_r,
 			     const char *const **keywords_r);
 
-/* Send FLAGS + PERMANENTFLAGS to client. */
-void client_send_mailbox_flags(struct client *client, struct mailbox *box,
-			       const ARRAY_TYPE(keywords) *keywords);
-
-/* Copy keywords into dest. dest must have been initialized. Returns TRUE if
-   keywords changed. */
-bool client_save_keywords(struct mailbox_keywords *dest,
-			  const ARRAY_TYPE(keywords) *keywords);
+/* Send FLAGS + PERMANENTFLAGS to client if they have changed,
+   or if selecting=TRUE. */
+void client_send_mailbox_flags(struct client *client, bool selecting);
+/* Update client->keywords array. Use keywords=NULL when unselecting. */
+void client_update_mailbox_flags(struct client *client,
+				 const ARRAY_TYPE(keywords) *keywords);
+/* Convert keyword indexes to keyword names in selected mailbox. */
+const char *const *
+client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
+			 const ARRAY_TYPE(keyword_indexes) *src);
 
 bool mailbox_equals(struct mailbox *box1, struct mail_storage *storage2,
 		    const char *name2);
--- a/src/imap/imap-fetch.c	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/imap-fetch.c	Sat Dec 29 07:11:12 2007 +0200
@@ -99,6 +99,8 @@
 	ctx->cur_str = str_new(default_pool, 8192);
 	ctx->all_headers_buf = buffer_create_dynamic(cmd->pool, 128);
 	p_array_init(&ctx->handlers, cmd->pool, 16);
+	p_array_init(&ctx->tmp_keywords, cmd->pool,
+		     client->keywords.announce_count + 8);
 	ctx->line_finished = TRUE;
 	return ctx;
 }
@@ -514,8 +516,6 @@
 	const char *const *keywords;
 
 	flags = mail_get_flags(mail);
-	keywords = mail_get_keywords(mail);
-
 	if (ctx->flags_update_seen && (flags & MAIL_SEEN) == 0) {
 		/* Add \Seen flag */
 		ctx->seen_flags_changed = TRUE;
@@ -525,6 +525,9 @@
 		return 1;
 	}
 
+	keywords = client_get_keyword_names(ctx->client, &ctx->tmp_keywords,
+			mail_get_keyword_indexes(mail));
+
 	str_append(ctx->cur_str, "FLAGS (");
 	imap_write_flags(ctx->cur_str, flags, keywords);
 	str_append(ctx->cur_str, ") ");
--- a/src/imap/imap-fetch.h	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/imap-fetch.h	Sat Dec 29 07:11:12 2007 +0200
@@ -51,6 +51,7 @@
 	bool skip_cr;
 	int (*cont_handler)(struct imap_fetch_context *ctx);
 
+	ARRAY_TYPE(keywords) tmp_keywords;
 	unsigned int select_counter;
 
 	unsigned int flags_have_handler:1;
--- a/src/imap/imap-sync.c	Sat Dec 29 06:54:40 2007 +0200
+++ b/src/imap/imap-sync.c	Sat Dec 29 07:11:12 2007 +0200
@@ -21,10 +21,8 @@
 	struct mailbox_sync_context *sync_ctx;
 	struct mail *mail;
 
-	const ARRAY_TYPE(keywords) *keywords;
-	unsigned int keyword_announce_count;
-
 	struct mailbox_sync_rec sync_rec;
+	ARRAY_TYPE(keywords) tmp_keywords;
 	uint32_t seq;
 
 	unsigned int messages_count;
@@ -33,19 +31,11 @@
 	unsigned int no_newmail:1;
 };
 
-static void imap_sync_send_changed_keywords(struct imap_sync_context *ctx)
-{
-	ctx->keyword_announce_count = array_count(ctx->keywords);
-	if (client_save_keywords(&ctx->client->keywords, ctx->keywords))
-		client_send_mailbox_flags(ctx->client, ctx->box, ctx->keywords);
-}
-
 struct imap_sync_context *
 imap_sync_init(struct client *client, struct mailbox *box,
 	       enum imap_sync_flags imap_flags, enum mailbox_sync_flags flags)
 {
 	struct imap_sync_context *ctx;
-	struct mailbox_status status;
 
 	i_assert(client->mailbox == box);
 
@@ -58,10 +48,9 @@
 	ctx->t = mailbox_transaction_begin(box, 0);
 	ctx->mail = mail_alloc(ctx->t, MAIL_FETCH_FLAGS, 0);
 	ctx->messages_count = client->messages_count;
+	i_array_init(&ctx->tmp_keywords, client->keywords.announce_count + 8);
 
-	mailbox_get_status(ctx->box, STATUS_KEYWORDS, &status);
-	ctx->keywords = status.keywords;
-	imap_sync_send_changed_keywords(ctx);
+	client_send_mailbox_flags(client, FALSE);
 	return ctx;
 }
 
@@ -103,41 +92,20 @@
 		}
 	}
 
+	array_free(&ctx->tmp_keywords);
 	i_free(ctx);
 	return ret;
 }
 
-static int imap_sync_send_flags(struct imap_sync_context *ctx, string_t *str,
-				ARRAY_TYPE(keywords) *keyword_names)
+static int imap_sync_send_flags(struct imap_sync_context *ctx, string_t *str)
 {
 	enum mail_flags flags;
-	const ARRAY_TYPE(keyword_indexes) *keywords;
-	const unsigned int *kw_indexes;
-	const char *const *all_names;
-	unsigned int i, kw_count, all_count;
+	const char *const *keywords;
 
 	mail_set_seq(ctx->mail, ctx->seq);
 	flags = mail_get_flags(ctx->mail);
-
-	all_names = array_get(ctx->keywords, &all_count);
-	keywords = mail_get_keyword_indexes(ctx->mail);
-	kw_indexes = array_get(keywords, &kw_count);
-
-	if (!array_is_created(keyword_names))
-		t_array_init(keyword_names, I_MAX(kw_count, 16));
-	else
-		array_clear(keyword_names);
-	/* convert indexes to names */
-	for (i = 0; i < kw_count; i++) {
-		if (kw_indexes[i] >= ctx->keyword_announce_count) {
-			/* a new keyword. notify the client. */
-			imap_sync_send_changed_keywords(ctx);
-			all_names = array_get(ctx->keywords, &all_count);
-		}
-		i_assert(kw_indexes[i] < all_count);
-		array_append(keyword_names, &all_names[kw_indexes[i]], 1);
-	}
-	(void)array_append_space(keyword_names);
+	keywords = client_get_keyword_names(ctx->client, &ctx->tmp_keywords,
+			mail_get_keyword_indexes(ctx->mail));
 
 	str_truncate(str, 0);
 	str_printfa(str, "* %u FETCH (", ctx->seq);
@@ -145,14 +113,13 @@
 		str_printfa(str, "UID %u ", ctx->mail->uid);
 
 	str_append(str, "FLAGS (");
-	imap_write_flags(str, flags, array_idx(keyword_names, 0));
+	imap_write_flags(str, flags, keywords);
 	str_append(str, "))");
 	return client_send_line(ctx->client, str_c(str));
 }
 
 int imap_sync_more(struct imap_sync_context *ctx)
 {
-	ARRAY_TYPE(keywords) keyword_names = ARRAY_INIT;
 	string_t *str;
 	int ret = 1;
 
@@ -188,8 +155,7 @@
 				if (ret <= 0)
 					break;
 
-				ret = imap_sync_send_flags(ctx, str,
-							   &keyword_names);
+				ret = imap_sync_send_flags(ctx, str);
 			}
 			break;
 		case MAILBOX_SYNC_TYPE_EXPUNGE: