]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
imapc: Added support for changing flags and expunging.
authorTimo Sirainen <tss@iki.fi>
Tue, 25 Jan 2011 15:23:52 +0000 (17:23 +0200)
committerTimo Sirainen <tss@iki.fi>
Tue, 25 Jan 2011 15:23:52 +0000 (17:23 +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-sync.c
src/lib-storage/index/imapc/imapc-sync.h

index df692e70b38a384573c93d9b94cf577fd505a540..23425e9e06a5706e1e13a065d4880ec0d4a4560f 100644 (file)
@@ -19,6 +19,7 @@ const struct imapc_capability_name imapc_capability_names[] = {
        { "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS },
        { "QRESYNC", IMAPC_CAPABILITY_QRESYNC },
        { "IDLE", IMAPC_CAPABILITY_IDLE },
+       { "UIDPLUS", IMAPC_CAPABILITY_UIDPLUS },
 
        { "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 },
        { NULL, 0 }
@@ -195,12 +196,12 @@ static void imapc_client_mailbox_cmd_cb(const struct imapc_command_reply *reply,
        i_free(ctx);
 }
 
-void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
-                              imapc_command_callback_t *callback,
-                              void *context, const char *cmd_fmt, ...)
+static struct imapc_client_command_context *
+imapc_client_mailbox_cmd_common(struct imapc_client_mailbox *box,
+                               imapc_command_callback_t *callback,
+                               void *context)
 {
        struct imapc_client_command_context *ctx;
-       va_list args;
 
        ctx = i_new(struct imapc_client_command_context, 1);
        ctx->box = box;
@@ -208,7 +209,28 @@ void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
        ctx->context = context;
 
        box->pending_box_command_count++;
+       return ctx;
+}
+
+void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
+                             const char *cmd,
+                             imapc_command_callback_t *callback,
+                             void *context)
+{
+       struct imapc_client_command_context *ctx;
+
+       ctx = imapc_client_mailbox_cmd_common(box, callback, context);
+       imapc_connection_cmd(box->conn, cmd, imapc_client_mailbox_cmd_cb, ctx);
+}
+
+void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
+                              imapc_command_callback_t *callback,
+                              void *context, const char *cmd_fmt, ...)
+{
+       struct imapc_client_command_context *ctx;
+       va_list args;
 
+       ctx = imapc_client_mailbox_cmd_common(box, callback, context);
        va_start(args, cmd_fmt);
        imapc_connection_cmdvf(box->conn, imapc_client_mailbox_cmd_cb,
                               ctx, cmd_fmt, args);
index b4217019bbaacc0aaba3bf96da6d27073928f8c5..dd6ab9d99b5bffbab5a2301f9e7b8d3fecb21126 100644 (file)
@@ -13,6 +13,7 @@ enum imapc_capability {
        IMAPC_CAPABILITY_LITERALPLUS    = 0x02,
        IMAPC_CAPABILITY_QRESYNC        = 0x04,
        IMAPC_CAPABILITY_IDLE           = 0x08,
+       IMAPC_CAPABILITY_UIDPLUS        = 0x10,
 
        IMAPC_CAPABILITY_IMAP4REV1      = 0x400000000
 };
@@ -91,6 +92,10 @@ imapc_client_mailbox_open(struct imapc_client *client, const char *name,
                          imapc_command_callback_t *callback, void *context,
                          void *untagged_box_context);
 void imapc_client_mailbox_close(struct imapc_client_mailbox **box);
+void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
+                             const char *cmd,
+                             imapc_command_callback_t *callback,
+                             void *context);
 void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
                               imapc_command_callback_t *callback,
                               void *context, const char *cmd_fmt, ...)
index 12d1068a79348b1c7b21abcb0c8cad55ea2b7cfd..136ed2408fd72bc3b4dcda7ec465bf09c8ccdb16 100644 (file)
@@ -614,7 +614,7 @@ static int imapc_connection_input_tagged(struct imapc_connection *conn)
        imapc_connection_input_reset(conn);
        cmd->callback(&reply, cmd->context);
        pool_unref(&cmd->pool);
-       return 0;
+       return 1;
 }
 
 static int imapc_connection_input_one(struct imapc_connection *conn)
index 84a1d297685b404b2a3f723b4ee022bedecfb59c..3031df12ce4e2cdd58637f867e2b7c7c988d1601 100644 (file)
 
 #include "lib.h"
 #include "ioloop.h"
+#include "str.h"
+#include "imap-util.h"
 #include "index-sync-private.h"
 #include "imapc-storage.h"
 #include "imapc-client.h"
 #include "imapc-seqmap.h"
 #include "imapc-sync.h"
 
+static void imapc_sync_callback(const struct imapc_command_reply *reply,
+                               void *context)
+{
+       struct imapc_sync_context *ctx = context;
+
+       i_assert(ctx->sync_command_count > 0);
+
+       if (reply->state == IMAPC_COMMAND_STATE_OK)
+               ;
+       else if (reply->state == IMAPC_COMMAND_STATE_NO) {
+               /* maybe the message was expunged already.
+                  some servers fail STOREs with NO in such situation. */
+       } else {
+               mail_storage_set_critical(&ctx->mbox->storage->storage,
+                       "imapc: Sync command failed: %s", reply->text_full);
+       }
+       
+       if (--ctx->sync_command_count == 0)
+               imapc_client_stop(ctx->mbox->storage->client);
+}
+
+static void imapc_sync_cmd(struct imapc_sync_context *ctx, const char *cmd)
+{
+       ctx->sync_command_count++;
+       imapc_client_mailbox_cmd(ctx->mbox->client_box, cmd,
+                                imapc_sync_callback, ctx);
+}
+
+static void
+imapc_sync_add_missing_deleted_flags(struct imapc_sync_context *ctx,
+                                    uint32_t seq1, uint32_t seq2)
+{
+       const struct mail_index_record *rec;
+       uint32_t seq, uid1, uid2;
+       const char *cmd;
+
+       /* if any of them has a missing \Deleted flag,
+          just add it to all of them. */
+       for (seq = seq1; seq <= seq2; seq++) {
+               rec = mail_index_lookup(ctx->sync_view, seq);
+               if ((rec->flags & MAIL_DELETED) == 0)
+                       break;
+       }
+
+       if (seq <= seq2) {
+               mail_index_lookup_uid(ctx->sync_view, seq1, &uid1);
+               mail_index_lookup_uid(ctx->sync_view, seq2, &uid2);
+               cmd = t_strdup_printf("UID STORE %u:%u +FLAGS \\Deleted",
+                                     uid1, uid2);
+               imapc_sync_cmd(ctx, cmd);
+       }
+}
+
+static void imapc_sync_index_flags(struct imapc_sync_context *ctx,
+                                  const struct mail_index_sync_rec *sync_rec)
+{
+       string_t *str = t_str_new(128);
+
+       i_assert(sync_rec->type == MAIL_INDEX_SYNC_TYPE_FLAGS);
+
+       if (sync_rec->add_flags != 0) {
+               str_printfa(str, "UID STORE %u:%u +FLAGS (",
+                           sync_rec->uid1, sync_rec->uid2);
+               imap_write_flags(str, sync_rec->add_flags, NULL);
+               str_append_c(str, ')');
+               imapc_sync_cmd(ctx, str_c(str));
+       }
+
+       if (sync_rec->remove_flags != 0) {
+               str_truncate(str, 0);
+               str_printfa(str, "UID STORE %u:%u -FLAGS (",
+                           sync_rec->uid1, sync_rec->uid2);
+               imap_write_flags(str, sync_rec->remove_flags, NULL);
+               str_append_c(str, ')');
+               imapc_sync_cmd(ctx, str_c(str));
+       }
+}
+
+static void
+imapc_sync_index_keyword(struct imapc_sync_context *ctx,
+                        const struct mail_index_sync_rec *sync_rec)
+{
+       string_t *str = t_str_new(128);
+       const char *const *kw_p;
+       char change_char;
+
+       switch (sync_rec->type) {
+       case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
+               change_char = '+';
+               break;
+       case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
+               change_char = '-';
+               break;
+       default:
+               i_unreached();
+       }
+
+       str_printfa(str, "UID STORE %u:%u %cFLAGS (",
+                   sync_rec->uid1, sync_rec->uid2, change_char);
+
+       kw_p = array_idx(ctx->keywords, sync_rec->keyword_idx);
+       str_append(str, *kw_p);
+       str_append_c(str, ')');
+       imapc_sync_cmd(ctx, str_c(str));
+}
+
+static void
+imapc_sync_index_keyword_reset(struct imapc_sync_context *ctx,
+                              uint32_t seq1, uint32_t seq2)
+{
+       const struct mail_index_record *rec;
+       string_t *str = t_str_new(128);
+       uint32_t seq;
+
+       for (seq = seq1; seq <= seq2; seq++) {
+               rec = mail_index_lookup(ctx->sync_view, seq);
+
+               str_truncate(str, 0);
+               str_printfa(str, "UID STORE %u FLAGS (", rec->uid);
+               imap_write_flags(str, rec->flags, NULL);
+               str_append_c(str, ')');
+               imapc_sync_cmd(ctx, str_c(str));
+       }
+}
+
+static void imapc_sync_expunge_finish(struct imapc_sync_context *ctx)
+{
+       string_t *str;
+       enum imapc_capability caps;
+       const struct seq_range *range;
+       unsigned int i, count;
+
+       if (array_count(&ctx->expunged_uids) == 0)
+               return;
+
+       caps = imapc_client_get_capabilities(ctx->mbox->storage->client);
+       if ((caps & IMAPC_CAPABILITY_UIDPLUS) == 0) {
+               /* just expunge everything */
+               imapc_sync_cmd(ctx, "EXPUNGE");
+               return;
+       }
+
+       /* build a list of UIDs to expunge */
+       str = t_str_new(128);
+       str_append(str, "UID EXPUNGE");
+
+       range = array_get(&ctx->expunged_uids, &count);
+       for (i = 0; i < count; i++) {
+               str_printfa(str, " %u", range[i].seq1);
+               if (range[i].seq1 == range[i].seq2)
+                       str_printfa(str, ":%u", range[i].seq2);
+       }
+       imapc_sync_cmd(ctx, str_c(str));
+}
+
 static void imapc_sync_index(struct imapc_sync_context *ctx)
 {
        struct mailbox *box = &ctx->mbox->box;
        struct mail_index_sync_rec sync_rec;
        uint32_t seq1, seq2;
 
-       while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) {
+       i_array_init(&ctx->expunged_uids, 64);
+       ctx->keywords = mail_index_get_keywords(box->index);
+
+       while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) T_BEGIN {
                if (!mail_index_lookup_seq_range(ctx->sync_view,
                                                 sync_rec.uid1, sync_rec.uid2,
                                                 &seq1, &seq2)) {
                        /* already expunged, nothing to do. */
-                       continue;
-               }
-
-               switch (sync_rec.type) {
+               } else switch (sync_rec.type) {
                case MAIL_INDEX_SYNC_TYPE_APPEND:
                        /* don't care */
                        break;
                case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
-                       //imapc_sync_expunge(ctx, seq1, seq2);
+                       imapc_sync_add_missing_deleted_flags(ctx, seq1, seq2);
+                       seq_range_array_add_range(&ctx->expunged_uids,
+                                                 sync_rec.uid1, sync_rec.uid2);
                        break;
                case MAIL_INDEX_SYNC_TYPE_FLAGS:
+                       imapc_sync_index_flags(ctx, &sync_rec);
+                       break;
                case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
                case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
+                       imapc_sync_index_keyword(ctx, &sync_rec);
+                       break;
                case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET:
-                       /* FIXME: should be bother calling sync_notify()? */
+                       imapc_sync_index_keyword_reset(ctx, seq1, seq2);
                        break;
                }
-       }
+       } T_END;
+
+       if (ctx->sync_command_count > 0)
+               imapc_client_run(ctx->mbox->storage->client);
+       imapc_sync_expunge_finish(ctx);
+       if (ctx->sync_command_count > 0)
+               imapc_client_run(ctx->mbox->storage->client);
+       array_free(&ctx->expunged_uids);
 
        if (box->v.sync_notify != NULL)
                box->v.sync_notify(box, 0, 0);
@@ -69,7 +239,15 @@ imapc_sync_begin(struct imapc_mailbox *mbox,
                return ret;
        }
 
+       i_assert(mbox->delayed_sync_trans == NULL);
+       mbox->delayed_sync_view = ctx->sync_view;
+       mbox->delayed_sync_trans = ctx->trans;
+
        imapc_sync_index(ctx);
+
+       mbox->delayed_sync_view = NULL;
+       mbox->delayed_sync_trans = NULL;
+
        *ctx_r = ctx;
        return 0;
 }
@@ -147,14 +325,17 @@ int imapc_mailbox_sync_deinit(struct mailbox_sync_context *ctx,
        struct index_mailbox_sync_context *ictx =
                (struct index_mailbox_sync_context *)ctx;
        struct imapc_mailbox *mbox = (struct imapc_mailbox *)ctx->box;
+       enum mailbox_sync_flags flags = ictx->flags;
        struct imapc_seqmap *seqmap;
        int ret;
 
        ret = index_mailbox_sync_deinit(ctx, status_r);
+       ctx = NULL;
+
        if (mbox->client_box == NULL)
                return ret;
 
-       if ((ictx->flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) == 0 && ret == 0) {
+       if ((flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) == 0 && ret == 0) {
                seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box);
                imapc_seqmap_reset(seqmap);
        }
index 6343de1b087d5538f5bdccc92476f01737085936..c56bcdc10b67b25ad70ad4e4fd66d97b5306073c 100644 (file)
@@ -8,6 +8,10 @@ struct imapc_sync_context {
         struct mail_index_sync_ctx *index_sync_ctx;
        struct mail_index_view *sync_view;
        struct mail_index_transaction *trans;
+
+       const ARRAY_TYPE(keywords) *keywords;
+       ARRAY_TYPE(seq_range) expunged_uids;
+       unsigned int sync_command_count;
 };
 
 struct mailbox_sync_context *