mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
fix: do not miss new messages while expunging the folder
This should fix flaky `test_verified_group_vs_delete_server_after`.
This commit is contained in:
@@ -29,9 +29,13 @@ impl Session {
|
||||
) -> Result<Self> {
|
||||
use futures::future::FutureExt;
|
||||
|
||||
self.select_folder(context, folder).await?;
|
||||
self.select_with_uidvalidity(context, folder).await?;
|
||||
|
||||
if self.server_sent_unsolicited_exists(context)? {
|
||||
self.new_mail = true;
|
||||
}
|
||||
|
||||
if self.new_mail {
|
||||
return Ok(self);
|
||||
}
|
||||
|
||||
@@ -92,6 +96,9 @@ impl Session {
|
||||
session.as_mut().set_read_timeout(Some(IMAP_TIMEOUT));
|
||||
self.inner = session;
|
||||
|
||||
// Fetch mail once we exit IDLE.
|
||||
self.new_mail = true;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ impl ImapSession {
|
||||
info!(context, "close/expunge succeeded");
|
||||
self.selected_folder = None;
|
||||
self.selected_folder_needs_expunge = false;
|
||||
self.new_mail = false;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -47,11 +48,7 @@ impl ImapSession {
|
||||
/// Selects a folder, possibly updating uid_validity and, if needed,
|
||||
/// expunging the folder to remove delete-marked messages.
|
||||
/// Returns whether a new folder was selected.
|
||||
pub(crate) async fn select_folder(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
) -> Result<NewlySelected> {
|
||||
async fn select_folder(&mut self, context: &Context, folder: &str) -> Result<NewlySelected> {
|
||||
// if there is a new folder and the new folder is equal to the selected one, there's nothing to do.
|
||||
// if there is _no_ new folder, we continue as we might want to expunge below.
|
||||
if let Some(selected_folder) = &self.selected_folder {
|
||||
@@ -119,13 +116,14 @@ impl ImapSession {
|
||||
/// When selecting a folder for the first time, sets the uid_next to the current
|
||||
/// mailbox.uid_next so that no old emails are fetched.
|
||||
///
|
||||
/// Returns Result<new_emails> (i.e. whether new emails arrived),
|
||||
/// if in doubt, returns new_emails=true so emails are fetched.
|
||||
/// Updates `self.new_mail` if folder was previously unselected
|
||||
/// and new mails are detected after selecting,
|
||||
/// i.e. UIDNEXT advanced while the folder was closed.
|
||||
pub(crate) async fn select_with_uidvalidity(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
folder: &str,
|
||||
) -> Result<bool> {
|
||||
) -> Result<()> {
|
||||
let newly_selected = self
|
||||
.select_or_create_folder(context, folder)
|
||||
.await
|
||||
@@ -182,28 +180,26 @@ impl ImapSession {
|
||||
mailbox.uid_next = new_uid_next;
|
||||
|
||||
if new_uid_validity == old_uid_validity {
|
||||
let new_emails = if newly_selected == NewlySelected::No {
|
||||
// The folder was not newly selected i.e. no SELECT command was run. This means that mailbox.uid_next
|
||||
// was not updated and may contain an incorrect value. So, just return true so that
|
||||
// the caller tries to fetch new messages (we could of course run a SELECT command now, but trying to fetch
|
||||
// new messages is only one command, just as a SELECT command)
|
||||
true
|
||||
} else if let Some(new_uid_next) = new_uid_next {
|
||||
if new_uid_next < old_uid_next {
|
||||
warn!(
|
||||
context,
|
||||
"The server illegally decreased the uid_next of folder {folder:?} from {old_uid_next} to {new_uid_next} without changing validity ({new_uid_validity}), resyncing UIDs...",
|
||||
);
|
||||
set_uid_next(context, folder, new_uid_next).await?;
|
||||
context.schedule_resync().await?;
|
||||
}
|
||||
new_uid_next != old_uid_next // If UIDNEXT changed, there are new emails
|
||||
} else {
|
||||
// We have no UIDNEXT and if in doubt, return true.
|
||||
true
|
||||
};
|
||||
if newly_selected == NewlySelected::Yes {
|
||||
if let Some(new_uid_next) = new_uid_next {
|
||||
if new_uid_next < old_uid_next {
|
||||
warn!(
|
||||
context,
|
||||
"The server illegally decreased the uid_next of folder {folder:?} from {old_uid_next} to {new_uid_next} without changing validity ({new_uid_validity}), resyncing UIDs...",
|
||||
);
|
||||
set_uid_next(context, folder, new_uid_next).await?;
|
||||
context.schedule_resync().await?;
|
||||
}
|
||||
|
||||
return Ok(new_emails);
|
||||
// If UIDNEXT changed, there are new emails.
|
||||
self.new_mail |= new_uid_next != old_uid_next;
|
||||
} else {
|
||||
warn!(context, "Folder {folder} was just selected but we failed to determine UIDNEXT, assume that it has new mail.");
|
||||
self.new_mail = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// UIDVALIDITY is modified, reset highest seen MODSEQ.
|
||||
@@ -214,6 +210,7 @@ impl ImapSession {
|
||||
let new_uid_next = new_uid_next.unwrap_or_default();
|
||||
set_uid_next(context, folder, new_uid_next).await?;
|
||||
set_uidvalidity(context, folder, new_uid_validity).await?;
|
||||
self.new_mail = true;
|
||||
|
||||
// Collect garbage entries in `imap` table.
|
||||
context
|
||||
@@ -236,7 +233,7 @@ impl ImapSession {
|
||||
old_uid_next,
|
||||
old_uid_validity,
|
||||
);
|
||||
Ok(false)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,11 @@ pub(crate) struct Session {
|
||||
pub selected_mailbox: Option<Mailbox>,
|
||||
|
||||
pub selected_folder_needs_expunge: bool,
|
||||
|
||||
/// True if currently selected folder has new messages.
|
||||
///
|
||||
/// Should be false if no folder is currently selected.
|
||||
pub new_mail: bool,
|
||||
}
|
||||
|
||||
impl Deref for Session {
|
||||
@@ -67,6 +72,7 @@ impl Session {
|
||||
selected_folder: None,
|
||||
selected_mailbox: None,
|
||||
selected_folder_needs_expunge: false,
|
||||
new_mail: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user