changeset 12592:475050722f54

imapc: Handle properly mailbox changes while it's selected. Added support for IDLE.
author Timo Sirainen <tss@iki.fi>
date Sun, 23 Jan 2011 22:57:01 +0200
parents 868f2de3898b
children ad3abe06954b
files src/lib-storage/index/imapc/imapc-client.c src/lib-storage/index/imapc/imapc-client.h src/lib-storage/index/imapc/imapc-connection.c src/lib-storage/index/imapc/imapc-connection.h src/lib-storage/index/imapc/imapc-mailbox.c src/lib-storage/index/imapc/imapc-storage.c src/lib-storage/index/imapc/imapc-storage.h src/lib-storage/index/imapc/imapc-sync.c
diffstat 8 files changed, 119 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/imapc/imapc-client.c	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client.c	Sun Jan 23 22:57:01 2011 +0200
@@ -18,6 +18,7 @@
 	{ "SASL-IR", IMAPC_CAPABILITY_SASL_IR },
 	{ "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS },
 	{ "QRESYNC", IMAPC_CAPABILITY_QRESYNC },
+	{ "IDLE", IMAPC_CAPABILITY_IDLE },
 
 	{ "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 },
 	{ NULL, 0 }
@@ -88,7 +89,8 @@
 
 void imapc_client_stop(struct imapc_client *client)
 {
-	io_loop_stop(client->ioloop);
+	if (client->ioloop != NULL)
+		io_loop_stop(client->ioloop);
 }
 
 static void
@@ -225,3 +227,17 @@
 {
 	return box->seqmap;
 }
+
+void imapc_client_mailbox_idle(struct imapc_client_mailbox *box)
+{
+	imapc_connection_idle(box->conn);
+}
+
+enum imapc_capability
+imapc_client_get_capabilities(struct imapc_client *client)
+{
+	struct imapc_client_connection *const *connp;
+
+	connp = array_idx(&client->conns, 0);
+	return imapc_connection_get_capabilities((*connp)->conn);
+}
--- a/src/lib-storage/index/imapc/imapc-client.h	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-client.h	Sun Jan 23 22:57:01 2011 +0200
@@ -12,6 +12,7 @@
 	IMAPC_CAPABILITY_SASL_IR	= 0x01,
 	IMAPC_CAPABILITY_LITERALPLUS	= 0x02,
 	IMAPC_CAPABILITY_QRESYNC	= 0x04,
+	IMAPC_CAPABILITY_IDLE		= 0x08,
 
 	IMAPC_CAPABILITY_IMAP4REV1	= 0x400000000
 };
@@ -97,4 +98,9 @@
 struct imapc_seqmap *
 imapc_client_mailbox_get_seqmap(struct imapc_client_mailbox *box);
 
+void imapc_client_mailbox_idle(struct imapc_client_mailbox *box);
+
+enum imapc_capability
+imapc_client_get_capabilities(struct imapc_client *client);
+
 #endif
--- a/src/lib-storage/index/imapc/imapc-connection.c	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-connection.c	Sun Jan 23 22:57:01 2011 +0200
@@ -67,6 +67,10 @@
 
 	unsigned int ips_count, prev_connect_idx;
 	struct ip_addr *ips;
+
+	unsigned int idling:1;
+	unsigned int idle_stopping:1;
+	unsigned int idle_plus_waiting:1;
 };
 
 static void imapc_connection_disconnect(struct imapc_connection *conn);
@@ -521,6 +525,13 @@
 {
 	struct imapc_command *const *cmd_p;
 
+	if (conn->idle_plus_waiting) {
+		/* "+ idling" reply for IDLE command */
+		conn->idle_plus_waiting = FALSE;
+		conn->idling = TRUE;
+		return imapc_connection_skip_line(conn);
+	}
+
 	if (array_count(&conn->cmd_send_queue) == 0) {
 		imapc_connection_input_error(conn, "Unexpected '+'");
 		return -1;
@@ -558,9 +569,11 @@
 		reply.state = IMAPC_COMMAND_STATE_OK;
 	else if (strcasecmp(line, "no") == 0)
 		reply.state = IMAPC_COMMAND_STATE_NO;
-	else if (strcasecmp(line, "bad") == 0)
+	else if (strcasecmp(line, "bad") == 0) {
+		i_error("imapc(%s): Command failed with BAD: %u %s",
+			conn->name, conn->cur_tag, line);
 		reply.state = IMAPC_COMMAND_STATE_BAD;
-	else if (strcasecmp(line, "bad") == 0) {
+	} else {
 		imapc_connection_input_error(conn,
 			"Invalid state in tagged reply: %u %s",
 			conn->cur_tag, line);
@@ -885,6 +898,10 @@
 static void imapc_command_send(struct imapc_connection *conn,
 			       struct imapc_command *cmd)
 {
+	if ((conn->idling || conn->idle_plus_waiting) && !conn->idle_stopping) {
+		conn->idle_stopping = TRUE;
+		o_stream_send_str(conn->output, "DONE\r\n");
+	}
 	switch (conn->state) {
 	case IMAPC_CONNECTION_STATE_AUTHENTICATING:
 		array_insert(&conn->cmd_send_queue, 0, &cmd, 1);
@@ -986,6 +1003,12 @@
 	return conn->state;
 }
 
+enum imapc_capability
+imapc_connection_get_capabilities(struct imapc_connection *conn)
+{
+	return conn->capabilities;
+}
+
 void imapc_connection_select(struct imapc_client_mailbox *box, const char *name,
 			     imapc_command_callback_t *callback, void *context)
 {
@@ -1006,3 +1029,27 @@
 
 	imapc_connection_cmdf(conn, callback, context, "SELECT %s", name);
 }
+
+static void
+imapc_connection_idle_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
+			       void *context)
+{
+	struct imapc_connection *conn = context;
+
+	conn->idling = FALSE;
+	conn->idle_plus_waiting = FALSE;
+	conn->idle_stopping = FALSE;
+}
+
+void imapc_connection_idle(struct imapc_connection *conn)
+{
+	if (array_count(&conn->cmd_send_queue) != 0 ||
+	    array_count(&conn->cmd_wait_list) != 0 ||
+	    conn->idling || conn->idle_plus_waiting ||
+	    (conn->capabilities & IMAPC_CAPABILITY_IDLE) == 0)
+		return;
+
+	imapc_connection_cmd(conn, "IDLE",
+			     imapc_connection_idle_callback, conn);
+	conn->idle_plus_waiting = TRUE;
+}
--- a/src/lib-storage/index/imapc/imapc-connection.h	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-connection.h	Sun Jan 23 22:57:01 2011 +0200
@@ -45,5 +45,9 @@
 
 enum imapc_connection_state
 imapc_connection_get_state(struct imapc_connection *conn);
+enum imapc_capability
+imapc_connection_get_capabilities(struct imapc_connection *conn);
+
+void imapc_connection_idle(struct imapc_connection *conn);
 
 #endif
--- a/src/lib-storage/index/imapc/imapc-mailbox.c	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-mailbox.c	Sun Jan 23 22:57:01 2011 +0200
@@ -1,12 +1,15 @@
 /* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "ioloop.h"
 #include "imap-arg.h"
 #include "imap-util.h"
 #include "imapc-client.h"
 #include "imapc-seqmap.h"
 #include "imapc-storage.h"
 
+#define NOTIFY_DELAY_MSECS 500
+
 static void imapc_untagged_exists(const struct imapc_untagged_reply *reply,
 				  struct imapc_mailbox *mbox)
 {
@@ -32,6 +35,21 @@
 				  hdr->next_uid);
 }
 
+static void imapc_mailbox_idle_timeout(struct imapc_mailbox *mbox)
+{
+	timeout_remove(&mbox->to_idle);
+	if (mbox->box.notify_callback != NULL)
+		mbox->box.notify_callback(&mbox->box, mbox->box.notify_context);
+}
+
+static void imapc_mailbox_idle_notify(struct imapc_mailbox *mbox)
+{
+	if (mbox->box.notify_callback != NULL && mbox->to_idle == NULL) {
+		mbox->to_idle =
+			timeout_add(NOTIFY_DELAY_MSECS,
+				    imapc_mailbox_idle_timeout, mbox);
+	}
+}
 
 static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply,
 				 struct imapc_mailbox *mbox)
@@ -92,6 +110,7 @@
 		mail_index_update_flags(mbox->delayed_sync_trans, seq,
 					MODIFY_REPLACE, flags);
 	}
+	imapc_mailbox_idle_notify(mbox);
 }
 
 static void imapc_untagged_expunge(const struct imapc_untagged_reply *reply,
@@ -106,6 +125,9 @@
 	seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
 	lseq = imapc_seqmap_rseq_to_lseq(seqmap, rseq);
 	mail_index_expunge(mbox->delayed_sync_trans, lseq);
+	imapc_seqmap_expunge(seqmap, rseq);
+
+	imapc_mailbox_idle_notify(mbox);
 }
 
 static void
--- a/src/lib-storage/index/imapc/imapc-storage.c	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-storage.c	Sun Jan 23 22:57:01 2011 +0200
@@ -1,6 +1,7 @@
 /* Copyright (c) 2011 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "ioloop.h"
 #include "str.h"
 #include "imap-resp-code.h"
 #include "mail-copy.h"
@@ -302,9 +303,12 @@
 {
 	struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
 
+	imapc_client_mailbox_close(&mbox->client_box);
 	mail_index_view_close(&mbox->delayed_sync_view);
 	if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0)
 		mail_storage_set_index_error(&mbox->box);
+	if (mbox->to_idle != NULL)
+		timeout_remove(&mbox->to_idle);
 	return index_storage_mailbox_close(box);
 }
 
@@ -416,18 +420,17 @@
 }
 
 static int imapc_mailbox_get_metadata(struct mailbox *box,
-				      enum mailbox_metadata_items items,
-				      struct mailbox_metadata *metadata_r)
+				      enum mailbox_metadata_items items ATTR_UNUSED,
+				      struct mailbox_metadata *metadata_r ATTR_UNUSED)
 {
 	mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
 			       "Not supported");
 	return -1;
 }
 
-static void imapc_notify_changes(struct mailbox *box)
+static void imapc_notify_changes(struct mailbox *box ATTR_UNUSED)
 {
-	struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
-
+	/* we're doing IDLE all the time anyway - nothing to do here */
 }
 
 struct mail_storage imapc_storage = {
--- a/src/lib-storage/index/imapc/imapc-storage.h	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-storage.h	Sun Jan 23 22:57:01 2011 +0200
@@ -41,6 +41,7 @@
 
 	struct mail_index_transaction *delayed_sync_trans;
 	struct mail_index_view *delayed_sync_view;
+	struct timeout *to_idle;
 
 	struct mail *cur_fetch_mail;
 
--- a/src/lib-storage/index/imapc/imapc-sync.c	Sun Jan 23 20:26:02 2011 +0200
+++ b/src/lib-storage/index/imapc/imapc-sync.c	Sun Jan 23 22:57:01 2011 +0200
@@ -107,6 +107,7 @@
 imapc_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
 {
 	struct imapc_mailbox *mbox = (struct imapc_mailbox *)box;
+	enum imapc_capability capabilities;
 	int ret = 0;
 
 	if (!box->opened) {
@@ -114,6 +115,16 @@
 			ret = -1;
 	}
 
+	capabilities = imapc_client_get_capabilities(mbox->storage->client);
+	if ((capabilities & IMAPC_CAPABILITY_IDLE) == 0) {
+		/* IDLE not supported. do NOOP to get latest changes
+		   before starting sync. */
+		imapc_client_mailbox_cmdf(mbox->client_box,
+					  imapc_async_stop_callback,
+					  mbox->storage, "NOOP");
+		imapc_client_run(mbox->storage->client);
+	}
+
 	mail_index_view_close(&mbox->delayed_sync_view);
 	if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0) {
 		// FIXME: mark inconsistent
@@ -148,5 +159,6 @@
 		seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
 		imapc_seqmap_reset(seqmap);
 	}
+	imapc_client_mailbox_idle(mbox->client_box);
 	return ret;
 }