From 118c10d8e4800f2fbeca450cdbb080a9d81d9541 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 | 23 ++-------- src/imap.rs | 68 +++++----------------------- 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, 104 insertions(+), 156 deletions(-) diff --git a/python/tests/test_1_online.py b/python/tests/test_1_online.py index 2a29a54fae..32d0121560 100644 --- a/python/tests/test_1_online.py +++ b/python/tests/test_1_online.py @@ -333,7 +333,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. @@ -344,6 +344,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") @@ -351,20 +359,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): @@ -471,14 +487,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 @@ -1770,8 +1791,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 b5e66ea273..5bf7e2e508 100644 --- a/src/config.rs +++ b/src/config.rs @@ -735,9 +735,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 2d1ba7655b..2222284d26 100644 --- a/src/context.rs +++ b/src/context.rs @@ -261,8 +261,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 @@ -455,7 +453,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 b56d974fcd..66330cbe62 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,14 +206,7 @@ impl Session { let mut uid_message_ids: BTreeMap = BTreeMap::new(); uid_message_ids.insert(uid, rfc724_mid); let (last_uid, _received) = self - .fetch_many_msgs( - context, - folder, - uidvalidity, - vec![uid], - &uid_message_ids, - false, - ) + .fetch_many_msgs(context, folder, vec![uid], &uid_message_ids, false) .await?; if last_uid.is_none() { bail!("Failed to fetch UID {uid}"); diff --git a/src/imap.rs b/src/imap.rs index 5119c61d1e..4e15b11508 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 @@ -707,7 +681,6 @@ impl Imap { .fetch_many_msgs( context, folder, - uid_validity, uids_fetch_in_batch.split_off(0), &uid_message_ids, fetch_partially, @@ -1305,7 +1278,6 @@ impl Session { &mut self, context: &Context, folder: &str, - uidvalidity: u32, request_uids: Vec, uid_message_ids: &BTreeMap, fetch_partially: bool, @@ -1433,18 +1405,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) => { if let Some(m) = received_msg { received_msgs.push(m); @@ -1952,7 +1913,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) @@ -2121,7 +2084,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 @@ -2280,15 +2245,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 c2f5678b59..27a3087963 100644 --- a/src/imap/imap_tests.rs +++ b/src/imap/imap_tests.rs @@ -180,7 +180,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 @@ -196,7 +196,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 4f2348af52..5802fc2725 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 74ad7aa757..bd939d4164 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -5,9 +5,11 @@ use anyhow::{Context as _, Result}; use async_imap::types::Mailbox; use async_imap::Session as ImapSession; 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 91351dd6ff..5e4bab0567 100644 --- a/src/message.rs +++ b/src/message.rs @@ -1321,7 +1321,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); } @@ -2141,13 +2141,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 @@ -2156,7 +2156,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"); @@ -2174,9 +2174,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?; @@ -2196,7 +2195,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 d301b2a53f..d1c90818ca 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -980,6 +980,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()); @@ -990,8 +1015,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 a612eb862e..f173c5b169 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 e0264d6056..d4d7998da1 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -17,7 +17,7 @@ use regex::Regex; use crate::chat::{self, Chat, ChatId, ChatIdBlocked, ProtectionStatus}; use crate::config::Config; use crate::constants::{Blocked, Chattype, ShowEmails, DC_CHAT_ID_TRASH, EDITED_PREFIX}; -use crate::contact::{mark_contact_id_as_verified, Contact, ContactId, Origin}; +use crate::contact::{self, mark_contact_id_as_verified, Contact, ContactId, Origin}; use crate::context::Context; use crate::debug_logging::maybe_set_logging_xdc_inner; use crate::download::DownloadState; @@ -43,7 +43,6 @@ use crate::stock_str; use crate::sync::Sync::*; use crate::tools::{self, buf_compress, remove_subject_prefix}; use crate::{chatlist_events, location}; -use crate::{contact, imap}; /// This is the struct that is returned after receiving one email (aka MIME message). /// @@ -152,8 +151,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 { @@ -185,17 +184,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 @@ -464,12 +453,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, @@ -517,7 +502,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!( @@ -541,27 +526,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; } @@ -1915,7 +1881,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 { @@ -2199,7 +2165,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? { @@ -2240,9 +2206,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?; @@ -3514,7 +3478,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 1555edf33d..6da4508577 100644 --- a/src/receive_imf/receive_imf_tests.rs +++ b/src/receive_imf/receive_imf_tests.rs @@ -1752,7 +1752,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 9da056f7f2..7bac3a603a 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -609,7 +609,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 e23f10ccec..95b77bd892 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);