From: Timo Sirainen Date: Sun, 23 Jan 2011 20:57:01 +0000 (+0200) Subject: imapc: Handle properly mailbox changes while it's selected. Added support for IDLE. X-Git-Tag: 2.1.alpha1~413 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6f9a5ecb55d8c024a0953647b77711b5622e9bbb;p=thirdparty%2Fdovecot%2Fcore.git imapc: Handle properly mailbox changes while it's selected. Added support for IDLE. --- diff --git a/src/lib-storage/index/imapc/imapc-client.c b/src/lib-storage/index/imapc/imapc-client.c index f963520c1c..d645b6c95e 100644 --- a/src/lib-storage/index/imapc/imapc-client.c +++ b/src/lib-storage/index/imapc/imapc-client.c @@ -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); +} diff --git a/src/lib-storage/index/imapc/imapc-client.h b/src/lib-storage/index/imapc/imapc-client.h index ade9fe96dd..b4217019bb 100644 --- a/src/lib-storage/index/imapc/imapc-client.h +++ b/src/lib-storage/index/imapc/imapc-client.h @@ -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 diff --git a/src/lib-storage/index/imapc/imapc-connection.c b/src/lib-storage/index/imapc/imapc-connection.c index f701cc2b14..82cf7704b3 100644 --- a/src/lib-storage/index/imapc/imapc-connection.c +++ b/src/lib-storage/index/imapc/imapc-connection.c @@ -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; +} diff --git a/src/lib-storage/index/imapc/imapc-connection.h b/src/lib-storage/index/imapc/imapc-connection.h index f5ad141d53..6abc712d4e 100644 --- a/src/lib-storage/index/imapc/imapc-connection.h +++ b/src/lib-storage/index/imapc/imapc-connection.h @@ -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 diff --git a/src/lib-storage/index/imapc/imapc-mailbox.c b/src/lib-storage/index/imapc/imapc-mailbox.c index 4f3d730487..ce4fd75fbd 100644 --- a/src/lib-storage/index/imapc/imapc-mailbox.c +++ b/src/lib-storage/index/imapc/imapc-mailbox.c @@ -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 diff --git a/src/lib-storage/index/imapc/imapc-storage.c b/src/lib-storage/index/imapc/imapc-storage.c index 7f99575619..57b39daf2a 100644 --- a/src/lib-storage/index/imapc/imapc-storage.c +++ b/src/lib-storage/index/imapc/imapc-storage.c @@ -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 = { diff --git a/src/lib-storage/index/imapc/imapc-storage.h b/src/lib-storage/index/imapc/imapc-storage.h index f51a3008c7..10ce62cdb4 100644 --- a/src/lib-storage/index/imapc/imapc-storage.h +++ b/src/lib-storage/index/imapc/imapc-storage.h @@ -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; diff --git a/src/lib-storage/index/imapc/imapc-sync.c b/src/lib-storage/index/imapc/imapc-sync.c index 2ac6b75ee6..000d572555 100644 --- a/src/lib-storage/index/imapc/imapc-sync.c +++ b/src/lib-storage/index/imapc/imapc-sync.c @@ -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; }