--- imap/command.c | 47 +++++++++++++++++++++++------- imap/imap.c | 79 +++++++++++++++++++++++++++++++++++++++------------- imap/imap_private.h | 2 - imap/message.c | 6 +-- 4 files changed, 99 insertions(+), 35 deletions(-) --- imap/command.c +++ imap/command.c 2017-10-24 14:15:49.011057645 +0000 @@ -146,6 +146,7 @@ static void cmd_handle_fatal(struct Imap if ((idata->state >= IMAP_SELECTED) && (idata->reopen & IMAP_REOPEN_ALLOW)) { mx_fastclose_mailbox(idata->ctx); + mutt_socket_close(idata->conn); mutt_error(_("Mailbox closed")); mutt_sleep(1); idata->state = IMAP_DISCONNECTED; @@ -257,7 +258,7 @@ static void cmd_parse_expunge(struct Ima */ static void cmd_parse_fetch(struct ImapData *idata, char *s) { - unsigned int msn; + unsigned int msn, uid; struct Header *h = NULL; mutt_debug(3, "Handling FETCH\n"); @@ -288,19 +289,41 @@ static void cmd_parse_fetch(struct ImapD } s++; - if (mutt_strncasecmp("FLAGS", s, 5) != 0) + while (*s) { - mutt_debug(2, "Only handle FLAGS updates\n"); - return; - } + SKIPWS (s); - /* If server flags could conflict with mutt's flags, reopen the mailbox. */ - if (h->changed) - idata->reopen |= IMAP_EXPUNGE_PENDING; - else - { - imap_set_flags(idata, h, s); - idata->check_status = IMAP_FLAGS_PENDING; + if (mutt_strncasecmp ("FLAGS", s, 5) == 0) + { + /* If server flags could conflict with mutt's flags, reopen the mailbox. */ + if (h->changed) + idata->reopen |= IMAP_EXPUNGE_PENDING; + else + { + imap_set_flags (idata, h, s); + idata->check_status = IMAP_FLAGS_PENDING; + } + return; + } + else if (mutt_strncasecmp ("UID", s, 3) == 0) + { + s += 3; + SKIPWS (s); + uid = (unsigned int) atoi (s); + if (uid != HEADER_DATA(h)->uid) + { + mutt_debug(2, "FETCH UID vs MSN mismatch. Skipping update.\n"); + return; + } + s = imap_next_word (s); + } + else if (*s == ')') + s++; /* end of request */ + else if (*s) + { + mutt_debug(2, "Only handle FLAGS updates\n"); + return; + } } } --- imap/imap.c +++ imap/imap.c 2017-10-24 14:27:08.002351735 +0000 @@ -318,7 +318,26 @@ void imap_expunge_mailbox(struct ImapDat imap_free_header_data((struct ImapHeaderData **) &h->data); } else + { h->index = i; + /* Mutt has several places where it turns off h->active as a + * hack. For example to avoid FLAG updates, or to exclude from + * imap_exec_msgset. + * + * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING + * flag becomes set (e.g. a flag update to a modified header), + * this function will be called by imap_cmd_finish(). + * + * The mx_update_tables() will free and remove these "inactive" headers, + * despite that an EXPUNGE was not received for them. + * This would result in memory leaks and segfaults due to dangling + * pointers in the msn_index and uid_hash. + * + * So this is another hack to work around the hacks. We don't want to + * remove the messages, so make sure active is on. + */ + h->active = 1; + } } #ifdef USE_HCACHE @@ -1094,7 +1113,7 @@ out: * compare_flags - Compare local flags against the server * @retval 0 if mutt's flags match cached server flags */ -static bool compare_flags(struct Header *h) +static bool compare_flags_for_copy(struct Header *h) { struct ImapHeaderData *hd = (struct ImapHeaderData *) h->data; @@ -1106,16 +1125,17 @@ static bool compare_flags(struct Header return true; if (h->replied != hd->replied) return true; - if (h->deleted != hd->deleted) - return true; return false; } /** - * imap_sync_message - Update server to reflect the flags of a single message + * imap_sync_message - Update the IMAP server to reflect the flags for a single message before + * performing a "UID COPY". + * NOTE: This does not sync the "deleted" flag state, because it is not + * desirable to propagate that flag into the copy. */ -int imap_sync_message(struct ImapData *idata, struct Header *hdr, +int imap_sync_message_for_copy(struct ImapData *idata, struct Header *hdr, struct Buffer *cmd, int *err_continue) { char flags[LONG_STRING]; @@ -1123,9 +1143,13 @@ int imap_sync_message(struct ImapData *i hdr->changed = false; - if (!compare_flags(hdr)) + if (!compare_flags_for_copy(hdr)) { - idata->ctx->changed = false; + if (hdr->deleted == HEADER_DATA(hdr)->deleted) + { + hdr->changed = 0; + idata->ctx->changed = false; + } return 0; } @@ -1140,7 +1164,7 @@ int imap_sync_message(struct ImapData *i imap_set_flag(idata, MUTT_ACL_WRITE, hdr->old, "Old ", flags, sizeof(flags)); imap_set_flag(idata, MUTT_ACL_WRITE, hdr->flagged, "\\Flagged ", flags, sizeof(flags)); imap_set_flag(idata, MUTT_ACL_WRITE, hdr->replied, "\\Answered ", flags, sizeof(flags)); - imap_set_flag(idata, MUTT_ACL_DELETE, hdr->deleted, "\\Deleted ", flags, sizeof(flags)); + imap_set_flag(idata, MUTT_ACL_DELETE, HEADER_DATA(hdr)->deleted, "\\Deleted ", flags, sizeof(flags)); /* now make sure we don't lose custom tags */ if (mutt_bit_isset(idata->ctx->rights, MUTT_ACL_WRITE)) @@ -1156,7 +1180,7 @@ int imap_sync_message(struct ImapData *i imap_set_flag(idata, MUTT_ACL_WRITE, 1, "Old ", flags, sizeof(flags)); imap_set_flag(idata, MUTT_ACL_WRITE, 1, "\\Flagged ", flags, sizeof(flags)); imap_set_flag(idata, MUTT_ACL_WRITE, 1, "\\Answered ", flags, sizeof(flags)); - imap_set_flag(idata, MUTT_ACL_DELETE, 1, "\\Deleted ", flags, sizeof(flags)); + imap_set_flag (idata, MUTT_ACL_DELETE, !HEADER_DATA(hdr)->deleted, "\\Deleted ", flags, sizeof (flags)); mutt_remove_trailing_ws(flags); @@ -1178,11 +1202,17 @@ int imap_sync_message(struct ImapData *i { *err_continue = imap_continue("imap_sync_message: STORE failed", idata->buf); if (*err_continue != MUTT_YES) + { + hdr->active = 1; return -1; + } } - hdr->active = true; - idata->ctx->changed = false; + if (hdr->deleted == HEADER_DATA(hdr)->deleted) + { + hdr->active = true; + idata->ctx->changed = false; + } return 0; } @@ -1637,7 +1667,7 @@ int imap_buffy_check(int force, int chec { /* Send commands to previous server. Sorting the buffy list * may prevent some infelicitous interleavings */ - if (imap_exec(lastdata, NULL, IMAP_CMD_FAIL_OK) == -1) + if (imap_exec(lastdata, NULL, IMAP_CMD_FAIL_OK | IMAP_CMD_POLL) == -1) mutt_debug(1, "Error polling mailboxes\n"); lastdata = NULL; @@ -2214,9 +2244,11 @@ int imap_fast_trash(struct Context *ctx, char mbox[LONG_STRING]; char mmbox[LONG_STRING]; char prompt[LONG_STRING]; - int rc; + int n, rc; struct ImapMbox mx; bool triedcreate = false; + struct Buffer *sync_cmd = NULL; + int err_continue = MUTT_NO; idata = ctx->data; @@ -2238,16 +2270,24 @@ int imap_fast_trash(struct Context *ctx, strfcpy(mbox, "INBOX", sizeof(mbox)); imap_munge_mbox_name(idata, mmbox, sizeof(mmbox), mbox); - /* loop in case of TRYCREATE */ - do + sync_cmd = mutt_buffer_new (); + for (n = 0; n < ctx->msgcount; n++) { - rc = imap_exec_msgset(idata, "UID STORE", "+FLAGS.SILENT (\\Seen)", MUTT_TRASH, 0, 0); - if (rc < 0) + if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed && + ctx->hdrs[n]->deleted && !ctx->hdrs[n]->purge) { - mutt_debug(1, "imap_fast_trash: Unable to mark messages as seen\n"); - goto out; + rc = imap_sync_message_for_copy (idata, ctx->hdrs[n], sync_cmd, &err_continue); + if (rc < 0) + { + mutt_debug(1, "imap_fast_trash: could not sync\n"); + goto out; + } } + } + /* loop in case of TRYCREATE */ + do + { rc = imap_exec_msgset(idata, "UID COPY", mmbox, MUTT_TRASH, 0, 0); if (!rc) { @@ -2297,6 +2337,7 @@ int imap_fast_trash(struct Context *ctx, rc = 0; out: + mutt_buffer_free (&sync_cmd); FREE(&mx.mbox); return rc < 0 ? -1 : rc; --- imap/imap_private.h +++ imap/imap_private.h 2017-10-24 14:15:49.015057570 +0000 @@ -279,7 +279,7 @@ struct ImapData *imap_conn_find(const st int imap_read_literal(FILE *fp, struct ImapData *idata, long bytes, struct Progress *pbar); void imap_expunge_mailbox(struct ImapData *idata); void imap_logout(struct ImapData **idata); -int imap_sync_message(struct ImapData *idata, struct Header *hdr, struct Buffer *cmd, int *err_continue); +int imap_sync_message_for_copy(struct ImapData *idata, struct Header *hdr, struct Buffer *cmd, int *err_continue); bool imap_has_flag(struct ListHead *flag_list, const char *flag); /* auth.c */ --- imap/message.c +++ imap/message.c 2017-10-24 14:15:49.015057570 +0000 @@ -1261,7 +1261,7 @@ int imap_copy_messages(struct Context *c if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active && ctx->hdrs[n]->changed) { - rc = imap_sync_message(idata, ctx->hdrs[n], &sync_cmd, &err_continue); + rc = imap_sync_message_for_copy(idata, ctx->hdrs[n], &sync_cmd, &err_continue); if (rc < 0) { mutt_debug(1, "imap_copy_messages: could not sync\n"); @@ -1292,7 +1292,7 @@ int imap_copy_messages(struct Context *c if (h->active && h->changed) { - rc = imap_sync_message(idata, h, &sync_cmd, &err_continue); + rc = imap_sync_message_for_copy(idata, h, &sync_cmd, &err_continue); if (rc < 0) { mutt_debug(1, "imap_copy_messages: could not sync\n"); @@ -1444,7 +1444,7 @@ char *imap_set_flags(struct ImapData *id hd = h->data; newh.data = hd; - mutt_debug(2, "imap_fetch_message: parsing FLAGS\n"); + mutt_debug(2, "imap_set_flags: parsing FLAGS\n"); if ((s = msg_parse_flags(&newh, s)) == NULL) return NULL;