Fix IMAP: UID FETCH N:* could miss messages moved into a SELECTed mailbox by another connection
This commit is contained in:
@@ -15,6 +15,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If
|
||||
- DAV: `acl-principal-prop-set` REPORT enforced the wrong privilege.
|
||||
- JMAP: `Thread/get` did not filter by per-mailbox ACLs on shared accounts.
|
||||
- RFC2136 TSIG: regression related to multiplexer.
|
||||
- IMAP: `UID FETCH N:*` could miss messages moved into a SELECTed mailbox by another connection.
|
||||
|
||||
## [0.16.5] - 2026-05-11
|
||||
|
||||
|
||||
@@ -194,23 +194,28 @@ impl SelectedMailbox {
|
||||
if !sequence.is_saved_search() {
|
||||
let mut ids = AHashMap::new();
|
||||
let state = self.state.lock();
|
||||
let (id_to_imap, uid_max, total_messages) =
|
||||
if let Some(next) = state.next_state.as_ref() {
|
||||
(
|
||||
&next.next_state.id_to_imap,
|
||||
next.next_state.uid_max,
|
||||
next.next_state.total_messages,
|
||||
)
|
||||
} else {
|
||||
(&state.id_to_imap, state.uid_max, state.total_messages)
|
||||
};
|
||||
|
||||
if is_uid {
|
||||
let id_to_imap = state
|
||||
.next_state
|
||||
.as_ref()
|
||||
.map(|s| &s.next_state.id_to_imap)
|
||||
.unwrap_or(&state.id_to_imap);
|
||||
if !state.id_to_imap.is_empty() {
|
||||
if !id_to_imap.is_empty() {
|
||||
for (id, imap_id) in id_to_imap {
|
||||
if sequence.contains(imap_id.uid, state.uid_max) {
|
||||
if sequence.contains(imap_id.uid, uid_max) {
|
||||
ids.insert(*id, *imap_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !state.id_to_imap.is_empty() {
|
||||
for (id, imap_id) in &state.id_to_imap {
|
||||
if sequence.contains(imap_id.seqnum, state.total_messages as u32) {
|
||||
} else if !id_to_imap.is_empty() {
|
||||
for (id, imap_id) in id_to_imap {
|
||||
if sequence.contains(imap_id.seqnum, total_messages as u32) {
|
||||
ids.insert(*id, *imap_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use super::{AssertResult, ImapConnection, Type};
|
||||
use imap_proto::ResponseType;
|
||||
|
||||
pub async fn test(_imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
pub async fn test(imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
println!("Running COPY/MOVE tests...");
|
||||
|
||||
// Check status
|
||||
@@ -151,4 +151,27 @@ pub async fn test(_imap: &mut ImapConnection, imap_check: &mut ImapConnection) {
|
||||
.assert_contains("\"Burrata al Tartufo\" (UIDNEXT 5 MESSAGES 0 UNSEEN 0 SIZE 0)")
|
||||
.assert_contains("\"Scamorza Affumicata\" (UIDNEXT 9 MESSAGES 4 UNSEEN 4 SIZE 5851)")
|
||||
.assert_contains("\"INBOX\" (UIDNEXT 11 MESSAGES 10 UNSEEN 10 SIZE 12193)");
|
||||
|
||||
imap_check.send("SELECT \"Burrata al Tartufo\"").await;
|
||||
imap_check.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
|
||||
imap.send("SELECT \"Scamorza Affumicata\"").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
imap.send("UID MOVE 5 \"Burrata al Tartufo\"").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("COPYUID");
|
||||
|
||||
imap_check.send("UID FETCH 1:* (UID)").await;
|
||||
imap_check
|
||||
.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("UID 5");
|
||||
|
||||
imap.send("SELECT \"Burrata al Tartufo\"").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok).await;
|
||||
imap.send("UID MOVE 5 \"Scamorza Affumicata\"").await;
|
||||
imap.assert_read(Type::Tagged, ResponseType::Ok)
|
||||
.await
|
||||
.assert_contains("COPYUID");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user