# HG changeset patch # User Timo Sirainen # Date 1295969032 -7200 # Node ID 1413a0fa9ae77dd8f93df50117b3641e7ed61802 # Parent ba3c0ee558f592f08a03459db2d2cf1bd90a2462 imapc: Added support for changing flags and expunging. diff -r ba3c0ee558f5 -r 1413a0fa9ae7 src/lib-storage/index/imapc/imapc-client.c --- a/src/lib-storage/index/imapc/imapc-client.c Tue Jan 25 17:15:54 2011 +0200 +++ b/src/lib-storage/index/imapc/imapc-client.c Tue Jan 25 17:23:52 2011 +0200 @@ -19,6 +19,7 @@ { "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS }, { "QRESYNC", IMAPC_CAPABILITY_QRESYNC }, { "IDLE", IMAPC_CAPABILITY_IDLE }, + { "UIDPLUS", IMAPC_CAPABILITY_UIDPLUS }, { "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 }, { NULL, 0 } @@ -195,6 +196,33 @@ i_free(ctx); } +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; + + ctx = i_new(struct imapc_client_command_context, 1); + ctx->box = box; + ctx->callback = callback; + 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, ...) @@ -202,13 +230,7 @@ struct imapc_client_command_context *ctx; va_list args; - ctx = i_new(struct imapc_client_command_context, 1); - ctx->box = box; - ctx->callback = callback; - ctx->context = context; - - box->pending_box_command_count++; - + 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); diff -r ba3c0ee558f5 -r 1413a0fa9ae7 src/lib-storage/index/imapc/imapc-client.h --- a/src/lib-storage/index/imapc/imapc-client.h Tue Jan 25 17:15:54 2011 +0200 +++ b/src/lib-storage/index/imapc/imapc-client.h Tue Jan 25 17:23:52 2011 +0200 @@ -13,6 +13,7 @@ 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_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, ...) diff -r ba3c0ee558f5 -r 1413a0fa9ae7 src/lib-storage/index/imapc/imapc-connection.c --- a/src/lib-storage/index/imapc/imapc-connection.c Tue Jan 25 17:15:54 2011 +0200 +++ b/src/lib-storage/index/imapc/imapc-connection.c Tue Jan 25 17:23:52 2011 +0200 @@ -614,7 +614,7 @@ 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) diff -r ba3c0ee558f5 -r 1413a0fa9ae7 src/lib-storage/index/imapc/imapc-sync.c --- a/src/lib-storage/index/imapc/imapc-sync.c Tue Jan 25 17:15:54 2011 +0200 +++ b/src/lib-storage/index/imapc/imapc-sync.c Tue Jan 25 17:23:52 2011 +0200 @@ -2,41 +2,211 @@ #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 @@ 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 @@ 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); } diff -r ba3c0ee558f5 -r 1413a0fa9ae7 src/lib-storage/index/imapc/imapc-sync.h --- a/src/lib-storage/index/imapc/imapc-sync.h Tue Jan 25 17:15:54 2011 +0200 +++ b/src/lib-storage/index/imapc/imapc-sync.h Tue Jan 25 17:23:52 2011 +0200 @@ -8,6 +8,10 @@ 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 *