From 5a694aa8f52b0dbb1d2b0b7f621d8460193b31bd Mon Sep 17 00:00:00 2001 From: link2xt Date: Tue, 11 Mar 2025 05:16:35 +0000 Subject: [PATCH] feat: protect the Date header We do not try to delete resent messages anymore. Previously resent messages were distinguised by having duplicate Message-ID, but future Date, but now we need to download the message before we even see the Date. We now move the message to the destination folder but do not fetch it. It may not be a good idea to delete the duplicate in multi-device setups anyway, because the device which has a message may delete the duplicate of a message the other device missed. To avoid triggering IMAP move loop described in the comments we now only move the messages from INBOX and Spam folders. --- python/tests/test_1_online.py | 43 ++++++++++++----- src/config.rs | 3 -- src/context.rs | 3 -- src/download.rs | 26 ++--------- src/imap.rs | 69 +++++----------------------- src/imap/imap_tests.rs | 4 +- src/imap/scan_folders.rs | 2 +- src/imap/session.rs | 5 ++ src/message.rs | 15 +++--- src/mimefactory.rs | 28 ++++++++++- src/reaction.rs | 2 +- src/receive_imf.rs | 56 ++++------------------ src/receive_imf/receive_imf_tests.rs | 2 +- src/scheduler.rs | 2 +- src/sync.rs | 4 +- 15 files changed, 105 insertions(+), 159 deletions(-) diff --git a/python/tests/test_1_online.py b/python/tests/test_1_online.py index 9ae861eb9a..7f7e44c6ec 100644 --- a/python/tests/test_1_online.py +++ b/python/tests/test_1_online.py @@ -334,7 +334,7 @@ def test_move_works(acfactory): def test_move_avoids_loop(acfactory): - """Test that the message is only moved once. + """Test that the message is only moved from INBOX to DeltaChat. This is to avoid busy loop if moved message reappears in the Inbox or some scanned folder later. @@ -345,6 +345,14 @@ def test_move_avoids_loop(acfactory): ac1 = acfactory.new_online_configuring_account() ac2 = acfactory.new_online_configuring_account(mvbox_move=True) acfactory.bring_accounts_online() + + # Create INBOX.DeltaChat folder and make sure + # it is detected by full folder scan. + ac2.direct_imap.create_folder("INBOX.DeltaChat") + ac2.stop_io() + ac2.start_io() + ac2._evtracker.get_info_contains("Found folders:") # Wait until the end of folder scan. + ac1_chat = acfactory.get_accepted_chat(ac1, ac2) ac1_chat.send_text("Message 1") @@ -352,20 +360,28 @@ def test_move_avoids_loop(acfactory): ac2_msg1 = ac2._evtracker.wait_next_incoming_message() assert ac2_msg1.text == "Message 1" - # Move the message to the INBOX again. + # Move the message to the INBOX.DeltaChat again. + # We assume that test server uses "." as the delimiter. ac2.direct_imap.select_folder("DeltaChat") - ac2.direct_imap.conn.move(["*"], "INBOX") + ac2.direct_imap.conn.move(["*"], "INBOX.DeltaChat") ac1_chat.send_text("Message 2") ac2_msg2 = ac2._evtracker.wait_next_incoming_message() assert ac2_msg2.text == "Message 2" - # Check that Message 1 is still in the INBOX folder + # Stop and start I/O to trigger folder scan. + ac2.stop_io() + ac2.start_io() + ac2._evtracker.get_info_contains("Found folders:") # Wait until the end of folder scan. + + # Check that Message 1 is still in the INBOX.DeltaChat folder # and Message 2 is in the DeltaChat folder. ac2.direct_imap.select_folder("INBOX") - assert len(ac2.direct_imap.get_all_messages()) == 1 + assert len(ac2.direct_imap.get_all_messages()) == 0 ac2.direct_imap.select_folder("DeltaChat") assert len(ac2.direct_imap.get_all_messages()) == 1 + ac2.direct_imap.select_folder("INBOX.DeltaChat") + assert len(ac2.direct_imap.get_all_messages()) == 1 def test_move_works_on_self_sent(acfactory): @@ -476,14 +492,19 @@ def test_resend_message(acfactory, lp): lp.sec("ac2: receive message") msg_in = ac2._evtracker.wait_next_incoming_message() assert msg_in.text == "message" - chat2 = msg_in.chat - chat2_msg_cnt = len(chat2.get_messages()) lp.sec("ac1: resend message") ac1.resend_messages([msg_in]) - lp.sec("ac2: check that message is deleted") - ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED") + lp.sec("ac1: send another message") + chat1.send_text("another message") + + lp.sec("ac2: receive another message") + msg_in = ac2._evtracker.wait_next_incoming_message() + assert msg_in.text == "another message" + chat2 = msg_in.chat + chat2_msg_cnt = len(chat2.get_messages()) + assert len(chat2.get_messages()) == chat2_msg_cnt @@ -1785,8 +1806,8 @@ def test_group_quote(acfactory, lp): ( "xyz", True, - "DeltaChat", - ), # ...emails are found in a random folder and moved to DeltaChat + "xyz", + ), # ...emails are found in a random folder and downloaded without moving ( "Spam", False, diff --git a/src/config.rs b/src/config.rs index 7bfebd868e..835653553a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -738,9 +738,6 @@ impl Context { _ => Default::default(), }; self.set_config_internal(key, value).await?; - if key == Config::SentboxWatch { - self.last_full_folder_scan.lock().await.take(); - } Ok(()) } diff --git a/src/context.rs b/src/context.rs index 0272a74c06..4a7c04c38f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -262,8 +262,6 @@ pub struct InnerContext { /// IMAP METADATA. pub(crate) metadata: RwLock>, - pub(crate) last_full_folder_scan: Mutex>, - /// ID for this `Context` in the current process. /// /// This allows for multiple `Context`s open in a single process where each context can @@ -469,7 +467,6 @@ impl Context { server_id: RwLock::new(None), metadata: RwLock::new(None), creation_time: tools::Time::now(), - last_full_folder_scan: Mutex::new(None), last_error: parking_lot::RwLock::new("".to_string()), migration_error: parking_lot::RwLock::new(None), debug_logging: std::sync::RwLock::new(None), diff --git a/src/download.rs b/src/download.rs index 57086a43dc..3bd0d0c8da 100644 --- a/src/download.rs +++ b/src/download.rs @@ -162,25 +162,18 @@ pub(crate) async fn download_msg( |row| { let server_uid: u32 = row.get(0)?; let server_folder: String = row.get(1)?; - let uidvalidity: u32 = row.get(2)?; - Ok((server_uid, server_folder, uidvalidity)) + Ok((server_uid, server_folder)) }, ) .await?; - let Some((server_uid, server_folder, uidvalidity)) = row else { + let Some((server_uid, server_folder)) = row else { // No IMAP record found, we don't know the UID and folder. return Err(anyhow!("Call download_full() again to try over.")); }; session - .fetch_single_msg( - context, - &server_folder, - uidvalidity, - server_uid, - msg.rfc724_mid.clone(), - ) + .fetch_single_msg(context, &server_folder, server_uid, msg.rfc724_mid.clone()) .await?; Ok(()) } @@ -194,7 +187,6 @@ impl Session { &mut self, context: &Context, folder: &str, - uidvalidity: u32, uid: u32, rfc724_mid: String, ) -> Result<()> { @@ -214,16 +206,8 @@ impl Session { let mut uid_message_ids: BTreeMap = BTreeMap::new(); uid_message_ids.insert(uid, rfc724_mid); let (sender, receiver) = async_channel::unbounded(); - self.fetch_many_msgs( - context, - folder, - uidvalidity, - vec![uid], - &uid_message_ids, - false, - sender, - ) - .await?; + self.fetch_many_msgs(context, folder, vec![uid], &uid_message_ids, false, sender) + .await?; if receiver.recv().await.is_err() { bail!("Failed to fetch UID {uid}"); } diff --git a/src/imap.rs b/src/imap.rs index 81f62721e4..3c7aacab04 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -581,52 +581,26 @@ impl Imap { // Determine the target folder where the message should be moved to. // - // If we have seen the message on the IMAP server before, do not move it. + // We only move the messages from the INBOX folder. // This is required to avoid infinite MOVE loop on IMAP servers // that alias `DeltaChat` folder to other names. // For example, some Dovecot servers alias `DeltaChat` folder to `INBOX.DeltaChat`. - // In this case Delta Chat configured with `DeltaChat` as the destination folder - // would detect messages in the `INBOX.DeltaChat` folder - // and try to move them to the `DeltaChat` folder. - // Such move to the same folder results in the messages - // getting a new UID, so the messages will be detected as new + // In this case moving from `INBOX.DeltaChat` to `DeltaChat` + // results in the messages getting a new UID, + // so the messages will be detected as new // in the `INBOX.DeltaChat` folder again. let _target; let target = if let Some(message_id) = &message_id { let msg_info = message::rfc724_mid_exists_ex(context, message_id, "deleted=1").await?; - let delete = if let Some((_, _, true)) = msg_info { + let delete = if let Some((_, true)) = msg_info { info!(context, "Deleting locally deleted message {message_id}."); true - } else if let Some((_, ts_sent_old, _)) = msg_info { - let is_chat_msg = headers.get_header_value(HeaderDef::ChatVersion).is_some(); - let ts_sent = headers - .get_header_value(HeaderDef::Date) - .and_then(|v| mailparse::dateparse(&v).ok()) - .unwrap_or_default(); - let is_dup = is_dup_msg(is_chat_msg, ts_sent, ts_sent_old); - if is_dup { - info!(context, "Deleting duplicate message {message_id}."); - } - is_dup } else { false }; if delete { &delete_target - } else if context - .sql - .exists( - "SELECT COUNT (*) FROM imap WHERE rfc724_mid=?", - (message_id,), - ) - .await? - { - info!( - context, - "Not moving the message {} that we have seen before.", &message_id - ); - folder } else { _target = target_folder(context, folder, folder_meaning, &headers).await?; &_target @@ -728,7 +702,6 @@ impl Imap { .fetch_many_msgs( context, folder, - uid_validity, uids_fetch_in_batch.split_off(0), &uid_message_ids, fetch_partially, @@ -1337,12 +1310,10 @@ impl Session { /// /// If the message is incorrect or there is a failure to write a message to the database, /// it is skipped and the error is logged. - #[expect(clippy::too_many_arguments)] pub(crate) async fn fetch_many_msgs( &mut self, context: &Context, folder: &str, - uidvalidity: u32, request_uids: Vec, uid_message_ids: &BTreeMap, fetch_partially: bool, @@ -1466,18 +1437,7 @@ impl Session { context, "Passing message UID {} to receive_imf().", request_uid ); - match receive_imf_inner( - context, - folder, - uidvalidity, - request_uid, - rfc724_mid, - body, - is_seen, - partial, - ) - .await - { + match receive_imf_inner(context, rfc724_mid, body, is_seen, partial).await { Ok(received_msg) => { received_msgs_channel .send((request_uid, received_msg)) @@ -1994,7 +1954,9 @@ pub async fn target_folder_cfg( if folder_meaning == FolderMeaning::Spam { spam_target_folder_cfg(context, headers).await - } else if needs_move_to_mvbox(context, headers).await? { + } else if folder_meaning == FolderMeaning::Inbox + && needs_move_to_mvbox(context, headers).await? + { Ok(Some(Config::ConfiguredMvboxFolder)) } else { Ok(None) @@ -2163,7 +2125,9 @@ fn get_folder_meaning_by_name(folder_name: &str) -> FolderMeaning { ]; let lower = folder_name.to_lowercase(); - if SENT_NAMES.iter().any(|s| s.to_lowercase() == lower) { + if lower == "inbox" { + FolderMeaning::Inbox + } else if SENT_NAMES.iter().any(|s| s.to_lowercase() == lower) { FolderMeaning::Sent } else if SPAM_NAMES.iter().any(|s| s.to_lowercase() == lower) { FolderMeaning::Spam @@ -2322,15 +2286,6 @@ pub(crate) async fn prefetch_should_download( Ok(should_download) } -/// Returns whether a message is a duplicate (resent message). -pub(crate) fn is_dup_msg(is_chat_msg: bool, ts_sent: i64, ts_sent_old: i64) -> bool { - // If the existing message has timestamp_sent == 0, that means we don't know its actual sent - // timestamp, so don't delete the new message. E.g. outgoing messages have zero timestamp_sent - // because they are stored to the db before sending. Also consider as duplicates only messages - // with greater timestamp to avoid deleting both messages in a multi-device setting. - is_chat_msg && ts_sent_old != 0 && ts_sent > ts_sent_old -} - /// Marks messages in `msgs` table as seen, searching for them by UID. /// /// Returns updated chat ID if any message was marked as seen. diff --git a/src/imap/imap_tests.rs b/src/imap/imap_tests.rs index 60637045de..1aa746ea99 100644 --- a/src/imap/imap_tests.rs +++ b/src/imap/imap_tests.rs @@ -186,7 +186,7 @@ const COMBINATIONS_ACCEPTED_CHAT: &[(&str, bool, bool, &str)] = &[ ("Sent", false, false, "Sent"), ("Sent", false, true, "Sent"), ("Sent", true, false, "Sent"), - ("Sent", true, true, "DeltaChat"), + ("Sent", true, true, "Sent"), ("Spam", false, false, "INBOX"), // Move classical emails in accepted chats from Spam to Inbox, not 100% sure on this, we could also just never move non-chat-msgs ("Spam", false, true, "INBOX"), ("Spam", true, false, "INBOX"), // Move classical emails in accepted chats from Spam to Inbox, not 100% sure on this, we could also just never move non-chat-msgs @@ -202,7 +202,7 @@ const COMBINATIONS_REQUEST: &[(&str, bool, bool, &str)] = &[ ("Sent", false, false, "Sent"), ("Sent", false, true, "Sent"), ("Sent", true, false, "Sent"), - ("Sent", true, true, "DeltaChat"), + ("Sent", true, true, "Sent"), ("Spam", false, false, "Spam"), ("Spam", false, true, "INBOX"), ("Spam", true, false, "Spam"), diff --git a/src/imap/scan_folders.rs b/src/imap/scan_folders.rs index 860c27568d..ba6ecec092 100644 --- a/src/imap/scan_folders.rs +++ b/src/imap/scan_folders.rs @@ -18,7 +18,7 @@ impl Imap { ) -> Result { // First of all, debounce to once per minute: { - let mut last_scan = context.last_full_folder_scan.lock().await; + let mut last_scan = session.last_full_folder_scan.lock().await; if let Some(last_scan) = *last_scan { let elapsed_secs = time_elapsed(&last_scan).as_secs(); let debounce_secs = context diff --git a/src/imap/session.rs b/src/imap/session.rs index eb73b4a35b..3d3241b632 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -5,9 +5,11 @@ use anyhow::{Context as _, Result}; use async_imap::Session as ImapSession; use async_imap::types::Mailbox; use futures::TryStreamExt; +use tokio::sync::Mutex; use crate::imap::capabilities::Capabilities; use crate::net::session::SessionStream; +use crate::tools; /// Prefetch: /// - Message-ID to check if we already have the message. @@ -40,6 +42,8 @@ pub(crate) struct Session { pub selected_folder_needs_expunge: bool, + pub(crate) last_full_folder_scan: Mutex>, + /// True if currently selected folder has new messages. /// /// Should be false if no folder is currently selected. @@ -71,6 +75,7 @@ impl Session { selected_folder: None, selected_mailbox: None, selected_folder_needs_expunge: false, + last_full_folder_scan: Mutex::new(None), new_mail: false, } } diff --git a/src/message.rs b/src/message.rs index 04fbad610a..f0e7e27a32 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1312,7 +1312,7 @@ impl Message { /// `References` header is not taken into account. pub async fn parent(&self, context: &Context) -> Result> { if let Some(in_reply_to) = &self.in_reply_to { - if let Some((msg_id, _ts_sent)) = rfc724_mid_exists(context, in_reply_to).await? { + if let Some(msg_id) = rfc724_mid_exists(context, in_reply_to).await? { let msg = Message::load_from_db_optional(context, msg_id).await?; return Ok(msg); } @@ -2121,13 +2121,13 @@ pub async fn estimate_deletion_cnt( pub(crate) async fn rfc724_mid_exists( context: &Context, rfc724_mid: &str, -) -> Result> { +) -> Result> { Ok(rfc724_mid_exists_ex(context, rfc724_mid, "1") .await? - .map(|(id, ts_sent, _)| (id, ts_sent))) + .map(|(id, _)| id)) } -/// Returns [MsgId] and "sent" timestamp of the most recent message with given `rfc724_mid` +/// Returns [MsgId] of the most recent message with given `rfc724_mid` /// (Message-ID header) and bool `expr` result if such messages exists in the db. /// /// * `expr`: SQL expression additionally passed into `SELECT`. Evaluated to `true` iff it is true @@ -2136,7 +2136,7 @@ pub(crate) async fn rfc724_mid_exists_ex( context: &Context, rfc724_mid: &str, expr: &str, -) -> Result> { +) -> Result> { let rfc724_mid = rfc724_mid.trim_start_matches('<').trim_end_matches('>'); if rfc724_mid.is_empty() { warn!(context, "Empty rfc724_mid passed to rfc724_mid_exists"); @@ -2154,9 +2154,8 @@ pub(crate) async fn rfc724_mid_exists_ex( (rfc724_mid,), |row| { let msg_id: MsgId = row.get(0)?; - let timestamp_sent: i64 = row.get(1)?; let expr_res: bool = row.get(2)?; - Ok((msg_id, timestamp_sent, expr_res)) + Ok((msg_id, expr_res)) }, ) .await?; @@ -2176,7 +2175,7 @@ pub(crate) async fn get_by_rfc724_mids( ) -> Result> { let mut latest = None; for id in mids.iter().rev() { - let Some((msg_id, _)) = rfc724_mid_exists(context, id).await? else { + let Some(msg_id) = rfc724_mid_exists(context, id).await? else { continue; }; let Some(msg) = Message::load_from_db_optional(context, msg_id).await? else { diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 0d971bf243..25f5bec38a 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -1005,6 +1005,31 @@ impl MimeFactory { } else { unprotected_headers.push(header.clone()); } + } else if is_encrypted && header_name == "date" { + protected_headers.push(header.clone()); + + // Coarse-grained date goes to unprotected header. + // + // We cannot just send "Thu, 01 Jan 1970 00:00:00 +0000" + // or omit the header because GMX then fails with + // + // host mx00.emig.gmx.net[212.227.15.9] said: + // 554-Transaction failed + // 554-Reject due to policy restrictions. + // 554 For explanation visit https://postmaster.gmx.net/en/case?... + // (in reply to end of DATA command) + // + // and the explanation page says + // "The time information deviates too much from the actual time". + let coarse_grained_timestamp = self.timestamp - (self.timestamp % 1000000); + let unprotected_date = + chrono::DateTime::::from_timestamp(coarse_grained_timestamp, 0) + .unwrap() + .to_rfc2822(); + unprotected_headers.push(( + "Date", + mail_builder::headers::raw::Raw::new(unprotected_date).into(), + )); } else if is_encrypted { protected_headers.push(header.clone()); @@ -1015,8 +1040,7 @@ impl MimeFactory { mail_builder::headers::raw::Raw::new("[...]").into(), )); } - "date" - | "in-reply-to" + "in-reply-to" | "references" | "auto-submitted" | "chat-version" diff --git a/src/reaction.rs b/src/reaction.rs index 6b90e0947f..cdd7b48128 100644 --- a/src/reaction.rs +++ b/src/reaction.rs @@ -278,7 +278,7 @@ pub(crate) async fn set_msg_reaction( reaction: Reaction, is_incoming_fresh: bool, ) -> Result<()> { - if let Some((msg_id, _)) = rfc724_mid_exists(context, in_reply_to).await? { + if let Some(msg_id) = rfc724_mid_exists(context, in_reply_to).await? { set_msg_id_reaction(context, msg_id, chat_id, contact_id, timestamp, &reaction).await?; if is_incoming_fresh diff --git a/src/receive_imf.rs b/src/receive_imf.rs index 03375862f4..11755ed31a 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -19,7 +19,7 @@ use crate::chat::{ }; use crate::config::Config; use crate::constants::{self, Blocked, Chattype, DC_CHAT_ID_TRASH, EDITED_PREFIX, ShowEmails}; -use crate::contact::{Contact, ContactId, Origin, mark_contact_id_as_verified}; +use crate::contact::{self, Contact, ContactId, Origin, mark_contact_id_as_verified}; use crate::context::Context; use crate::debug_logging::maybe_set_logging_xdc_inner; use crate::download::DownloadState; @@ -46,7 +46,6 @@ use crate::stock_str; use crate::sync::Sync::*; use crate::tools::{self, buf_compress, remove_subject_prefix}; use crate::{chatlist_events, ensure_and_debug_assert, ensure_and_debug_assert_eq, location}; -use crate::{contact, imap}; /// This is the struct that is returned after receiving one email (aka MIME message). /// @@ -155,8 +154,8 @@ pub async fn receive_imf( seen: bool, ) -> Result> { let mail = mailparse::parse_mail(imf_raw).context("can't parse mail")?; - let rfc724_mid = - imap::prefetch_get_message_id(&mail.headers).unwrap_or_else(imap::create_message_id); + let rfc724_mid = crate::imap::prefetch_get_message_id(&mail.headers) + .unwrap_or_else(crate::imap::create_message_id); if let Some(download_limit) = context.download_limit().await? { let download_limit: usize = download_limit.try_into()?; if imf_raw.len() > download_limit { @@ -188,17 +187,7 @@ pub(crate) async fn receive_imf_from_inbox( seen: bool, is_partial_download: Option, ) -> Result> { - receive_imf_inner( - context, - "INBOX", - 0, - 0, - rfc724_mid, - imf_raw, - seen, - is_partial_download, - ) - .await + receive_imf_inner(context, rfc724_mid, imf_raw, seen, is_partial_download).await } /// Inserts a tombstone into `msgs` table @@ -497,12 +486,8 @@ async fn get_to_and_past_contact_ids( /// If `is_partial_download` is set, it contains the full message size in bytes. /// Do not confuse that with `replace_msg_id` that will be set when the full message is loaded /// later. -#[expect(clippy::too_many_arguments)] pub(crate) async fn receive_imf_inner( context: &Context, - folder: &str, - uidvalidity: u32, - uid: u32, rfc724_mid: &str, imf_raw: &[u8], seen: bool, @@ -550,7 +535,7 @@ pub(crate) async fn receive_imf_inner( // check, if the mail is already in our database. // make sure, this check is done eg. before securejoin-processing. let (replace_msg_id, replace_chat_id); - if let Some((old_msg_id, _)) = message::rfc724_mid_exists(context, rfc724_mid).await? { + if let Some(old_msg_id) = message::rfc724_mid_exists(context, rfc724_mid).await? { if is_partial_download.is_some() { // Should never happen, see imap::prefetch_should_download(), but still. info!( @@ -574,27 +559,8 @@ pub(crate) async fn receive_imf_inner( } else { replace_msg_id = if rfc724_mid_orig == rfc724_mid { None - } else if let Some((old_msg_id, old_ts_sent)) = - message::rfc724_mid_exists(context, rfc724_mid_orig).await? - { - if imap::is_dup_msg( - mime_parser.has_chat_version(), - mime_parser.timestamp_sent, - old_ts_sent, - ) { - info!(context, "Deleting duplicate message {rfc724_mid_orig}."); - let target = context.get_delete_msgs_target().await?; - context - .sql - .execute( - "UPDATE imap SET target=? WHERE folder=? AND uidvalidity=? AND uid=?", - (target, folder, uidvalidity, uid), - ) - .await?; - } - Some(old_msg_id) } else { - None + message::rfc724_mid_exists(context, rfc724_mid_orig).await? }; replace_chat_id = None; } @@ -1969,7 +1935,7 @@ async fn add_parts( if let Some(node_addr) = mime_parser.get_header(HeaderDef::IrohNodeAddr) { match mime_parser.get_header(HeaderDef::InReplyTo) { Some(in_reply_to) => match rfc724_mid_exists(context, in_reply_to).await? { - Some((instance_id, _ts_sent)) => { + Some(instance_id) => { if let Err(err) = add_gossip_peer_from_header(context, instance_id, node_addr).await { @@ -2259,7 +2225,7 @@ async fn handle_edit_delete( from_id: ContactId, ) -> Result<()> { if let Some(rfc724_mid) = mime_parser.get_header(HeaderDef::ChatEdit) { - if let Some((original_msg_id, _)) = rfc724_mid_exists(context, rfc724_mid).await? { + if let Some(original_msg_id) = rfc724_mid_exists(context, rfc724_mid).await? { if let Some(mut original_msg) = Message::load_from_db_optional(context, original_msg_id).await? { @@ -2300,9 +2266,7 @@ async fn handle_edit_delete( let rfc724_mid_vec: Vec<&str> = rfc724_mid_list.split_whitespace().collect(); for rfc724_mid in rfc724_mid_vec { - if let Some((msg_id, _)) = - message::rfc724_mid_exists(context, rfc724_mid).await? - { + if let Some(msg_id) = message::rfc724_mid_exists(context, rfc724_mid).await? { if let Some(msg) = Message::load_from_db_optional(context, msg_id).await? { if msg.from_id == from_id { message::delete_msg_locally(context, &msg).await?; @@ -3688,7 +3652,7 @@ async fn get_previous_message( ) -> Result> { if let Some(field) = mime_parser.get_header(HeaderDef::References) { if let Some(rfc724mid) = parse_message_ids(field).last() { - if let Some((msg_id, _)) = rfc724_mid_exists(context, rfc724mid).await? { + if let Some(msg_id) = rfc724_mid_exists(context, rfc724mid).await? { return Message::load_from_db_optional(context, msg_id).await; } } diff --git a/src/receive_imf/receive_imf_tests.rs b/src/receive_imf/receive_imf_tests.rs index 7f777742a8..325180d11b 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -1759,7 +1759,7 @@ async fn check_alias_reply(from_dc: bool, chat_request: bool, group_request: boo .await .unwrap(); - let (msg_id, _) = rfc724_mid_exists(&claire, "non-dc-1@example.org") + let msg_id = rfc724_mid_exists(&claire, "non-dc-1@example.org") .await .unwrap() .unwrap(); diff --git a/src/scheduler.rs b/src/scheduler.rs index 36c0d84485..a9fe3fc6b8 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -616,7 +616,7 @@ async fn fetch_idle( .await .context("delete_expired_imap_messages")?; } else if folder_config == Config::ConfiguredInboxFolder { - ctx.last_full_folder_scan.lock().await.take(); + session.last_full_folder_scan.lock().await.take(); } // Scan additional folders only after finishing fetching the watched folder. diff --git a/src/sync.rs b/src/sync.rs index 90e302f06b..5bd20e1dc1 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -298,7 +298,7 @@ impl Context { } async fn save_message(&self, src_rfc724_mid: &str, dest_rfc724_mid: &String) -> Result<()> { - if let Some((src_msg_id, _)) = message::rfc724_mid_exists(self, src_rfc724_mid).await? { + if let Some(src_msg_id) = message::rfc724_mid_exists(self, src_rfc724_mid).await? { chat::save_copy_in_self_talk(self, src_msg_id, dest_rfc724_mid).await?; } Ok(()) @@ -308,7 +308,7 @@ impl Context { let mut modified_chat_ids = HashSet::new(); let mut msg_ids = Vec::new(); for rfc724_mid in msgs { - if let Some((msg_id, _)) = message::rfc724_mid_exists(self, rfc724_mid).await? { + if let Some(msg_id) = message::rfc724_mid_exists(self, rfc724_mid).await? { if let Some(msg) = Message::load_from_db_optional(self, msg_id).await? { message::delete_msg_locally(self, &msg).await?; msg_ids.push(msg.id);