]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imapc: Handle properly mailbox changes while it's selected. Added support for IDLE.
authorTimo Sirainen <tss@iki.fi>
Sun, 23 Jan 2011 20:57:01 +0000 (22:57 +0200)
committerTimo Sirainen <tss@iki.fi>
Sun, 23 Jan 2011 20:57:01 +0000 (22:57 +0200)
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

index f963520c1c79c0c68ab4a6d5e184503d4456bb41..d645b6c95eff9a33405df1cbf5aff29f6d4e543d 100644 (file)
@@ -18,6 +18,7 @@ const struct imapc_capability_name imapc_capability_names[] = {
        { "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_run(struct imapc_client *client)
 
 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 @@ imapc_client_mailbox_get_seqmap(struct imapc_client_mailbox *box)
 {
        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);
+}
index ade9fe96dd1729281a8ff44bf05986ed1b5b0ac9..b4217019bbaacc0aaba3bf96da6d27073928f8c5 100644 (file)
@@ -12,6 +12,7 @@ enum imapc_capability {
        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 @@ void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
 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
index f701cc2b1495dc05dcec6033faaea27a10e2c015..82cf7704b34001c46f1eab5877717d8e2b54620e 100644 (file)
@@ -67,6 +67,10 @@ struct imapc_connection {
 
        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 @@ static int imapc_connection_input_plus(struct imapc_connection *conn)
 {
        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 @@ static int imapc_connection_input_tagged(struct imapc_connection *conn)
                reply.state = IMAPC_COMMAND_STATE_OK;
        else if (strcasecmp(line, "no") == 0)
                reply.state = IMAPC_COMMAND_STATE_NO;
-       else if (strcasecmp(line, "bad") == 0)
-               reply.state = IMAPC_COMMAND_STATE_BAD;
        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 {
                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_more(struct imapc_connection *conn,
 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 @@ imapc_connection_get_state(struct imapc_connection *conn)
        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 @@ void imapc_connection_select(struct imapc_client_mailbox *box, const char *name,
 
        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;
+}
index f5ad141d536cb5120d04d9507fa83fa457bbe539..6abc712d4e0776c45fc3e5721ffacb593936af9b 100644 (file)
@@ -45,5 +45,9 @@ void imapc_connection_select(struct imapc_client_mailbox *box, const char *name,
 
 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
index 4f3d7304872920112121d0b65dfcbd59ad058f76..ce4fd75fbd7b06f48f1935208f98ad2d377cd59e 100644 (file)
@@ -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 @@ static void imapc_untagged_exists(const struct imapc_untagged_reply *reply,
                                  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 @@ static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply,
                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 @@ static void imapc_untagged_expunge(const struct imapc_untagged_reply *reply,
        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
index 7f995756192dba16812b7468d8cae7e0040a49cb..57b39daf2ae56d7aeef8094ee0dac5f7a28a0eec 100644 (file)
@@ -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 @@ static void imapc_mailbox_close(struct mailbox *box)
 {
        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_status(struct mailbox *box,
 }
 
 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 = {
index f51a3008c7721019b756b76a608fc663a0fc1480..10ce62cdb4845462b71d3f69e9515d16eb40b982 100644 (file)
@@ -41,6 +41,7 @@ struct imapc_mailbox {
 
        struct mail_index_transaction *delayed_sync_trans;
        struct mail_index_view *delayed_sync_view;
+       struct timeout *to_idle;
 
        struct mail *cur_fetch_mail;
 
index 2ac6b75ee6781f3d06c9be4a975f373872e8c9c4..000d5725550ce85dc3ebfe8d4edbbf8f4ef30486 100644 (file)
@@ -107,6 +107,7 @@ struct mailbox_sync_context *
 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 @@ imapc_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
                        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 @@ int imapc_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
                seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
                imapc_seqmap_reset(seqmap);
        }
+       imapc_client_mailbox_idle(mbox->client_box);
        return ret;
 }