mirror of
https://github.com/chatmail/core.git
synced 2026-05-07 08:56:30 +03:00
refactor(imap): move prefetch() to Session
This commit is contained in:
58
src/imap.rs
58
src/imap.rs
@@ -4,7 +4,6 @@
|
|||||||
//! to implement connect, fetch, delete functionality with standard IMAP servers.
|
//! to implement connect, fetch, delete functionality with standard IMAP servers.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp,
|
|
||||||
cmp::max,
|
cmp::max,
|
||||||
collections::{BTreeMap, BTreeSet, HashMap},
|
collections::{BTreeMap, BTreeSet, HashMap},
|
||||||
iter::Peekable,
|
iter::Peekable,
|
||||||
@@ -22,7 +21,7 @@ use tokio::sync::RwLock;
|
|||||||
|
|
||||||
use crate::chat::{self, ChatId, ChatIdBlocked};
|
use crate::chat::{self, ChatId, ChatIdBlocked};
|
||||||
use crate::config::Config;
|
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::contact::{normalize_name, Contact, ContactAddress, ContactId, Modifier, Origin};
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
@@ -63,21 +62,6 @@ pub enum ImapActionResult {
|
|||||||
Success,
|
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 (\
|
const RFC724MID_UID: &str = "(UID BODY.PEEK[HEADER.FIELDS (\
|
||||||
MESSAGE-ID \
|
MESSAGE-ID \
|
||||||
X-MICROSOFT-ORIGINAL-MESSAGE-ID\
|
X-MICROSOFT-ORIGINAL-MESSAGE-ID\
|
||||||
@@ -758,12 +742,14 @@ impl Imap {
|
|||||||
let uid_validity = get_uidvalidity(context, folder).await?;
|
let uid_validity = get_uidvalidity(context, folder).await?;
|
||||||
let old_uid_next = get_uid_next(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 {
|
let msgs = if fetch_existing_msgs {
|
||||||
self.prefetch_existing_msgs()
|
session
|
||||||
|
.prefetch_existing_msgs()
|
||||||
.await
|
.await
|
||||||
.context("prefetch_existing_msgs")?
|
.context("prefetch_existing_msgs")?
|
||||||
} else {
|
} else {
|
||||||
self.prefetch(old_uid_next).await.context("prefetch")?
|
session.prefetch(old_uid_next).await.context("prefetch")?
|
||||||
};
|
};
|
||||||
let read_cnt = msgs.len();
|
let read_cnt = msgs.len();
|
||||||
|
|
||||||
@@ -1386,40 +1372,6 @@ impl Imap {
|
|||||||
Ok(result)
|
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<Vec<(u32, async_imap::types::Fetch)>> {
|
|
||||||
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 <https://tools.ietf.org/html/rfc3501#page-61> 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.
|
/// Fetches a list of messages by server UID.
|
||||||
///
|
///
|
||||||
/// Returns the last UID fetched successfully and the info about each downloaded message.
|
/// Returns the last UID fetched successfully and the info about each downloaded message.
|
||||||
|
|||||||
@@ -1,13 +1,32 @@
|
|||||||
|
use std::cmp;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Context as _, Result};
|
||||||
use async_imap::types::Mailbox;
|
use async_imap::types::Mailbox;
|
||||||
use async_imap::Session as ImapSession;
|
use async_imap::Session as ImapSession;
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
|
|
||||||
|
use crate::constants::DC_FETCH_EXISTING_MSGS_COUNT;
|
||||||
use crate::imap::capabilities::Capabilities;
|
use crate::imap::capabilities::Capabilities;
|
||||||
use crate::net::session::SessionStream;
|
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)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Session {
|
pub(crate) struct Session {
|
||||||
pub(super) inner: ImapSession<Box<dyn SessionStream>>,
|
pub(super) inner: ImapSession<Box<dyn SessionStream>>,
|
||||||
@@ -77,8 +96,42 @@ impl Session {
|
|||||||
Ok(list)
|
Ok(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like fetch_after(), but not for new messages but existing ones (the DC_FETCH_EXISTING_MSGS_COUNT newest messages)
|
/// Prefetch all messages greater than or equal to `uid_next`. Returns a list of fetch results
|
||||||
async fn prefetch_existing_msgs(&mut self) -> Result<Vec<(u32, async_imap::types::Fetch)>> {
|
/// in the order of ascending delivery time to the server (INTERNALDATE).
|
||||||
|
pub(crate) async fn prefetch(
|
||||||
|
&mut self,
|
||||||
|
uid_next: u32,
|
||||||
|
) -> Result<Vec<(u32, async_imap::types::Fetch)>> {
|
||||||
|
// 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 <https://tools.ietf.org/html/rfc3501#page-61> 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<Vec<(u32, async_imap::types::Fetch)>> {
|
||||||
let exists: i64 = {
|
let exists: i64 = {
|
||||||
let mailbox = self.selected_mailbox.as_ref().context("no mailbox")?;
|
let mailbox = self.selected_mailbox.as_ref().context("no mailbox")?;
|
||||||
mailbox.exists.into()
|
mailbox.exists.into()
|
||||||
|
|||||||
Reference in New Issue
Block a user