From 48eb400a69f490b8cad7ed01bc58f380e69a63e5 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sun, 30 Jan 2022 20:12:02 +0000 Subject: [PATCH] imap: skip sync flags update if highest modseq has not increased --- CHANGELOG.md | 1 + Cargo.lock | 1 + Cargo.toml | 1 + src/imap.rs | 43 +++++++++++++++++++++++++++++++++++++------ src/imap/idle.rs | 8 ++++++++ 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5615201b..6b5841919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - remove direct dependency on `byteorder` crate #3031 - make it possible to cancel message sending by removing the message #3034, this was previosuly removed in 1.71.0 #2939 +- always skip Seen flag synchronization when there are no updates #3039 ### Fixes - fix splitting off text from webxdc messages #3032 diff --git a/Cargo.lock b/Cargo.lock index bf7ae2219..768b0025f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1090,6 +1090,7 @@ dependencies = [ "hex", "humansize", "image", + "imap-proto", "kamadak-exif", "lettre_email", "libc", diff --git a/Cargo.toml b/Cargo.toml index abfd18cb9..38c658d3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ escaper = "0.1" futures = "0.3" hex = "0.4.0" image = { version = "0.23.5", default-features=false, features = ["gif", "jpeg", "ico", "png", "pnm", "webp", "bmp"] } +imap-proto = "0.14.3" kamadak-exif = "0.5" lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" } libc = "0.2" diff --git a/src/imap.rs b/src/imap.rs index f68c10873..76719eca1 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -1023,23 +1023,33 @@ impl Imap { .as_ref() .with_context(|| format!("No mailbox selected, folder: {}", folder))?; - // Check if the mailbox supports MODSEQ. - // We are not interested in actual value of HIGHESTMODSEQ. - if mailbox.highest_modseq.is_none() { + let remote_highest_modseq = if let Some(remote_highest_modseq) = mailbox.highest_modseq { + remote_highest_modseq + } else { info!( context, "Mailbox {} does not support mod-sequences, skipping flag synchronization.", folder ); return Ok(()); + }; + + let mut highest_modseq = get_modseq(context, folder) + .await + .with_context(|| format!("failed to get MODSEQ for folder {}", folder))?; + if highest_modseq >= remote_highest_modseq { + info!( + context, + "MODSEQ {} is already new, HIGHESTMODSEQ={}, skipping seen flag update", + highest_modseq, + remote_highest_modseq + ); + return Ok(()); } let mut updated_chat_ids = BTreeSet::new(); let uid_validity = get_uidvalidity(context, folder) .await .with_context(|| format!("failed to get UID validity for folder {}", folder))?; - let mut highest_modseq = get_modseq(context, folder) - .await - .with_context(|| format!("failed to get MODSEQ for folder {}", folder))?; let mut list = session .uid_fetch("1:*", format!("(FLAGS) (CHANGEDSINCE {})", highest_modseq)) .await @@ -1074,6 +1084,10 @@ impl Imap { } } + if remote_highest_modseq > highest_modseq { + // We haven't seen the message with the highest MODSEQ, maybe it was deleted already. + highest_modseq = remote_highest_modseq; + } set_modseq(context, folder, highest_modseq) .await .with_context(|| format!("failed to set MODSEQ for folder {}", folder))?; @@ -1570,6 +1584,23 @@ impl Imap { Ok(()) } + /// Update HIGHESTMODSEQ on selected mailbox. + /// + /// Should be called when MODSEQ is seen on the response, such as IDLE response. + pub(crate) fn update_modseq(&mut self, modseq: u64) { + self.config.selected_mailbox = + self.config + .selected_mailbox + .as_ref() + .map(|mailbox| Mailbox { + highest_modseq: Some(std::cmp::max( + mailbox.highest_modseq.unwrap_or_default(), + modseq, + )), + ..mailbox.clone() + }); + } + /// Return whether the server sent an unsolicited EXISTS response. /// Drains all responses from `session.unsolicited_responses` in the process. /// If this returns `true`, this means that new emails arrived and you should diff --git a/src/imap/idle.rs b/src/imap/idle.rs index ffff87070..8a722168e 100644 --- a/src/imap/idle.rs +++ b/src/imap/idle.rs @@ -3,6 +3,7 @@ use super::Imap; use anyhow::{bail, Context as _, Result}; use async_imap::extensions::idle::IdleResponse; use async_std::prelude::*; +use imap_proto::types::{AttributeValue, Response}; use std::time::{Duration, SystemTime}; use crate::{context::Context, scheduler::InterruptInfo}; @@ -71,6 +72,13 @@ impl Imap { match fut.await { Ok(Event::IdleResponse(IdleResponse::NewData(x))) => { info!(context, "Idle has NewData {:?}", x); + if let Response::Fetch(_message, attrs) = x.parsed() { + for attr in attrs { + if let AttributeValue::ModSeq(modseq) = attr { + self.update_modseq(*modseq); + } + } + } } Ok(Event::IdleResponse(IdleResponse::Timeout)) => { info!(context, "Idle-wait timeout or interruption");