From bb816ff398b7ad0ac01ad7745e2a29962c95434d Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 18 Apr 2026 16:20:21 +0200 Subject: [PATCH] fix: do not sort prefetched messages by INTERNALDATE Messages are iterated over in fetch_new_msg_batch() and largest_uid_fetched variable is updated there assuming that messages come in the order of increasing UID. If UIDs are not increasing, it is possible that largest_uid_fetched will be updated even though smaller UID is not fetched yet and the message will be lost. INTERNALDATE sorting was introduced to deal with email providers such as Gmail that keep INTERNALDATE but not the UID order when moving the messages. Since we don't move the messages anymore after commit 04c0e7da16d334079330bcc15492b9a1d33494e4, there is no need for ordering by INTERNALDATE. --- deltachat-rpc-client/tests/test_folders.py | 53 +--------------------- src/imap/session.rs | 8 ++-- 2 files changed, 5 insertions(+), 56 deletions(-) diff --git a/deltachat-rpc-client/tests/test_folders.py b/deltachat-rpc-client/tests/test_folders.py index 621cd5729..85cd8e110 100644 --- a/deltachat-rpc-client/tests/test_folders.py +++ b/deltachat-rpc-client/tests/test_folders.py @@ -1,59 +1,8 @@ -import logging import re -import time from imap_tools import AND, U -from deltachat_rpc_client import Contact, EventType, Message - - -def test_reactions_for_a_reordering_move(acfactory, direct_imap): - """When a batch of messages is moved from Inbox to another folder with a single MOVE command, - their UIDs may be reordered (e.g. Gmail is known for that) which led to that messages were - processed by receive_imf in the wrong order, and, particularly, reactions were processed before - messages they refer to and thus dropped. - """ - (ac1,) = acfactory.get_online_accounts(1) - - addr, password = acfactory.get_credentials() - ac2 = acfactory.get_unconfigured_account() - ac2.add_or_update_transport({"addr": addr, "password": password}) - assert ac2.is_configured() - - ac2.bring_online() - chat1 = acfactory.get_accepted_chat(ac1, ac2) - ac2.stop_io() - - logging.info("sending message + reaction from ac1 to ac2") - msg1 = chat1.send_text("hi") - msg1.wait_until_delivered() - # It's is sad, but messages must differ in their INTERNALDATEs to be processed in the correct - # order by DC, and most (if not all) mail servers provide only seconds precision. - time.sleep(1.1) - react_str = "\N{THUMBS UP SIGN}" - msg1.send_reaction(react_str).wait_until_delivered() - - logging.info("moving messages to ac2's movebox folder in the reverse order") - ac2_direct_imap = direct_imap(ac2) - ac2_direct_imap.create_folder("Movebox") - ac2_direct_imap.connect() - for uid in sorted([m.uid for m in ac2_direct_imap.get_all_messages()], reverse=True): - ac2_direct_imap.conn.move(uid, "Movebox") - - logging.info("moving messages back") - ac2_direct_imap.select_folder("Movebox") - for uid in sorted([m.uid for m in ac2_direct_imap.get_all_messages()]): - ac2_direct_imap.conn.move(uid, "INBOX") - - logging.info("receiving messages by ac2") - ac2.start_io() - msg2 = Message(ac2, ac2.wait_for_reactions_changed().msg_id) - assert msg2.get_snapshot().text == msg1.get_snapshot().text - reactions = msg2.get_reactions() - contacts = [Contact(ac2, int(i)) for i in reactions.reactions_by_contact] - assert len(contacts) == 1 - assert contacts[0].get_snapshot().address == ac1.get_config("addr") - assert list(reactions.reactions_by_contact.values())[0] == [react_str] +from deltachat_rpc_client import EventType def test_moved_markseen(acfactory, direct_imap, log): diff --git a/src/imap/session.rs b/src/imap/session.rs index 05e3c6d3d..8d4e7e087 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -16,7 +16,7 @@ use crate::net::session::SessionStream; /// - Autocrypt-Setup-Message to check if a message is an autocrypt setup message, /// not necessarily sent by Delta Chat. /// - Chat-Is-Post-Message to skip it in background fetch or when it is > `DownloadLimit`. -const PREFETCH_FLAGS: &str = "(UID INTERNALDATE RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\ +const PREFETCH_FLAGS: &str = "(UID RFC822.SIZE BODY.PEEK[HEADER.FIELDS (\ MESSAGE-ID \ DATE \ X-MICROSOFT-ORIGINAL-MESSAGE-ID \ @@ -124,7 +124,7 @@ impl Session { } /// Prefetch `n_uids` messages starting from `uid_next`. Returns a list of fetch results in the - /// order of ascending delivery time to the server (INTERNALDATE). + /// order of ascending UIDs. #[expect(clippy::arithmetic_side_effects)] pub(crate) async fn prefetch( &mut self, @@ -142,10 +142,10 @@ impl Session { let mut msgs = BTreeMap::new(); while let Some(msg) = list.try_next().await? { if let Some(msg_uid) = msg.uid { - msgs.insert((msg.internal_date(), msg_uid), msg); + msgs.insert(msg_uid, msg); } } - Ok(msgs.into_iter().map(|((_, uid), msg)| (uid, msg)).collect()) + Ok(Vec::from_iter(msgs)) } }