diff --git a/src/imap.rs b/src/imap.rs index 85170e229..17d225de8 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -4,7 +4,6 @@ //! to implement connect, fetch, delete functionality with standard IMAP servers. use std::{ - cmp, cmp::max, collections::{BTreeMap, BTreeSet, HashMap}, iter::Peekable, @@ -22,7 +21,7 @@ use tokio::sync::RwLock; use crate::chat::{self, ChatId, ChatIdBlocked}; use crate::config::Config; -use crate::constants::{self, Blocked, Chattype, ShowEmails, DC_FETCH_EXISTING_MSGS_COUNT}; +use crate::constants::{self, Blocked, Chattype, ShowEmails}; use crate::contact::{normalize_name, Contact, ContactAddress, ContactId, Modifier, Origin}; use crate::context::Context; use crate::events::EventType; @@ -63,21 +62,6 @@ pub enum ImapActionResult { Success, } -/// Prefetch: -/// - Message-ID to check if we already have the message. -/// - In-Reply-To and References to check if message is a reply to chat message. -/// - Chat-Version to check if a message is a chat message -/// - Autocrypt-Setup-Message to check if a message is an autocrypt setup message, -/// not necessarily sent by Delta Chat. -const PREFETCH_FLAGS: &str = "(UID INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\ - MESSAGE-ID \ - DATE \ - X-MICROSOFT-ORIGINAL-MESSAGE-ID \ - FROM \ - IN-REPLY-TO REFERENCES \ - CHAT-VERSION \ - AUTOCRYPT-SETUP-MESSAGE\ - )])"; const RFC724MID_UID: &str = "(UID BODY.PEEK[HEADER.FIELDS (\ MESSAGE-ID \ X-MICROSOFT-ORIGINAL-MESSAGE-ID\ @@ -758,12 +742,14 @@ impl Imap { let uid_validity = get_uidvalidity(context, folder).await?; let old_uid_next = get_uid_next(context, folder).await?; + let session = self.session.as_mut().context("No IMAP session")?; let msgs = if fetch_existing_msgs { - self.prefetch_existing_msgs() + session + .prefetch_existing_msgs() .await .context("prefetch_existing_msgs")? } else { - self.prefetch(old_uid_next).await.context("prefetch")? + session.prefetch(old_uid_next).await.context("prefetch")? }; let read_cnt = msgs.len(); @@ -1386,40 +1372,6 @@ impl Imap { Ok(result) } - /// Prefetch all messages greater than or equal to `uid_next`. Returns a list of fetch results - /// in the order of ascending delivery time to the server (INTERNALDATE). - async fn prefetch(&mut self, uid_next: u32) -> Result> { - let session = self - .session - .as_mut() - .context("no IMAP connection established")?; - - // fetch messages with larger UID than the last one seen - let set = format!("{uid_next}:*"); - let mut list = session - .uid_fetch(set, PREFETCH_FLAGS) - .await - .context("IMAP could not fetch")?; - - let mut msgs = BTreeMap::new(); - while let Some(msg) = list.try_next().await? { - if let Some(msg_uid) = msg.uid { - // If the mailbox is not empty, results always include - // at least one UID, even if last_seen_uid+1 is past - // the last UID in the mailbox. It happens because - // uid:* is interpreted the same way as *:uid. - // See for - // standard reference. Therefore, sometimes we receive - // already seen messages and have to filter them out. - if msg_uid >= uid_next { - msgs.insert((msg.internal_date(), msg_uid), msg); - } - } - } - - Ok(msgs.into_iter().map(|((_, uid), msg)| (uid, msg)).collect()) - } - /// Fetches a list of messages by server UID. /// /// Returns the last UID fetched successfully and the info about each downloaded message. diff --git a/src/imap/session.rs b/src/imap/session.rs index b15472d82..c3a5bb778 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -1,13 +1,32 @@ +use std::cmp; +use std::collections::BTreeMap; use std::ops::{Deref, DerefMut}; -use anyhow::Result; +use anyhow::{Context as _, Result}; use async_imap::types::Mailbox; use async_imap::Session as ImapSession; use futures::TryStreamExt; +use crate::constants::DC_FETCH_EXISTING_MSGS_COUNT; use crate::imap::capabilities::Capabilities; use crate::net::session::SessionStream; +/// Prefetch: +/// - Message-ID to check if we already have the message. +/// - In-Reply-To and References to check if message is a reply to chat message. +/// - Chat-Version to check if a message is a chat message +/// - Autocrypt-Setup-Message to check if a message is an autocrypt setup message, +/// not necessarily sent by Delta Chat. +const PREFETCH_FLAGS: &str = "(UID INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\ + MESSAGE-ID \ + DATE \ + X-MICROSOFT-ORIGINAL-MESSAGE-ID \ + FROM \ + IN-REPLY-TO REFERENCES \ + CHAT-VERSION \ + AUTOCRYPT-SETUP-MESSAGE\ + )])"; + #[derive(Debug)] pub(crate) struct Session { pub(super) inner: ImapSession>, @@ -77,8 +96,42 @@ impl Session { Ok(list) } - /// Like fetch_after(), but not for new messages but existing ones (the DC_FETCH_EXISTING_MSGS_COUNT newest messages) - async fn prefetch_existing_msgs(&mut self) -> Result> { + /// Prefetch all messages greater than or equal to `uid_next`. Returns a list of fetch results + /// in the order of ascending delivery time to the server (INTERNALDATE). + pub(crate) async fn prefetch( + &mut self, + uid_next: u32, + ) -> Result> { + // fetch messages with larger UID than the last one seen + let set = format!("{uid_next}:*"); + let mut list = self + .uid_fetch(set, PREFETCH_FLAGS) + .await + .context("IMAP could not fetch")?; + + let mut msgs = BTreeMap::new(); + while let Some(msg) = list.try_next().await? { + if let Some(msg_uid) = msg.uid { + // If the mailbox is not empty, results always include + // at least one UID, even if last_seen_uid+1 is past + // the last UID in the mailbox. It happens because + // uid:* is interpreted the same way as *:uid. + // See for + // standard reference. Therefore, sometimes we receive + // already seen messages and have to filter them out. + if msg_uid >= uid_next { + msgs.insert((msg.internal_date(), msg_uid), msg); + } + } + } + + Ok(msgs.into_iter().map(|((_, uid), msg)| (uid, msg)).collect()) + } + + /// Like prefetch(), but not for new messages but existing ones (the DC_FETCH_EXISTING_MSGS_COUNT newest messages) + pub(crate) async fn prefetch_existing_msgs( + &mut self, + ) -> Result> { let exists: i64 = { let mailbox = self.selected_mailbox.as_ref().context("no mailbox")?; mailbox.exists.into()