From 57b2c000e66fcf0ca2c0db86860770a782dd60d8 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Thu, 17 Sep 2026 16:28:37 +0000 Subject: [PATCH] feat: base server-side message deletion on force_encryption don't use `is_chatmail` for determining whether to delete messages on the server: whether a downloaded message may be dropped from the relay depends on whether another device still needs it (BccSelf) and on whether plaintext mail is allowed (ForceEncryption). --- src/download.rs | 4 +--- src/ephemeral.rs | 10 +++++----- src/ephemeral/ephemeral_tests.rs | 14 ++++++++------ src/imap.rs | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/download.rs b/src/download.rs index 7cdd3a995..e154fbaaa 100644 --- a/src/download.rs +++ b/src/download.rs @@ -6,7 +6,6 @@ use anyhow::{Result, anyhow, bail, ensure}; use deltachat_derive::{FromSql, ToSql}; use serde::{Deserialize, Serialize}; -use crate::config::Config; use crate::context::Context; use crate::imap::session::Session; use crate::log::warn; @@ -164,8 +163,7 @@ pub(crate) async fn download_msg( .fetch_single_msg(context, &server_folder, server_uid, rfc724_mid) .await?; - let bcc_self = context.get_config_bool(Config::BccSelf).await?; - if ephemeral::should_delete_all_downloaded_messages(bcc_self, session.is_chatmail()) { + if ephemeral::should_delete_all_downloaded_messages(context).await? { // Now that the message was downloaded, it likely needs to be deleted; // trigger a re-check by interrupting the inbox folder. // This is mainly needed to make the tests pass; diff --git a/src/ephemeral.rs b/src/ephemeral.rs index d5b9a388a..8bf197f24 100644 --- a/src/ephemeral.rs +++ b/src/ephemeral.rs @@ -654,12 +654,11 @@ pub(crate) async fn ephemeral_loop(context: &Context, interrupt_receiver: Receiv pub(crate) async fn delete_expired_imap_messages( context: &Context, transport_id: u32, - is_chatmail: bool, ) -> Result<()> { let now = time(); let bcc_self = context.get_config_bool(Config::BccSelf).await?; - if should_delete_all_downloaded_messages(bcc_self, is_chatmail) { + if should_delete_all_downloaded_messages(context).await? { // This is the only device using this relay. // Mark all downloaded messages for deletion, because they are not needed anymore. // @@ -708,7 +707,7 @@ pub(crate) async fn delete_expired_imap_messages( ) .await?; } else { - // Single device. + // Single device, but unencrypted messages are allowed. // Delete all expired and encrypted messages. context .sql @@ -736,8 +735,9 @@ pub(crate) async fn delete_expired_imap_messages( Ok(()) } -pub(crate) fn should_delete_all_downloaded_messages(bcc_self: bool, is_chatmail: bool) -> bool { - !bcc_self && is_chatmail +pub(crate) async fn should_delete_all_downloaded_messages(context: &Context) -> Result { + Ok(!context.get_config_bool(Config::BccSelf).await? + && context.get_config_bool(Config::ForceEncryption).await?) } /// Start ephemeral timers for seen messages if they are not started diff --git a/src/ephemeral/ephemeral_tests.rs b/src/ephemeral/ephemeral_tests.rs index 110dd0aca..dae560c56 100644 --- a/src/ephemeral/ephemeral_tests.rs +++ b/src/ephemeral/ephemeral_tests.rs @@ -509,7 +509,7 @@ async fn test_delete_expired_imap_messages() -> Result<()> { .await?; } - for (is_chatmail, other_transport, bcc_self) in [ + for (force_encryption, other_transport, bcc_self) in [ (false, false, false), (false, false, true), (false, true, false), @@ -520,10 +520,12 @@ async fn test_delete_expired_imap_messages() -> Result<()> { (true, true, true), ] { println!( - "Testing combination is_chatmail={is_chatmail}, other_transport={other_transport}, bcc_self={bcc_self}" + "Testing combination force_encryption={force_encryption}, other_transport={other_transport}, bcc_self={bcc_self}" ); t.set_config_bool(Config::BccSelf, bcc_self).await?; + t.set_config_bool(Config::ForceEncryption, force_encryption) + .await?; delete_expired_imap_messages( &t, @@ -532,7 +534,6 @@ async fn test_delete_expired_imap_messages() -> Result<()> { } else { transport_id }, - is_chatmail, ) .await?; @@ -548,7 +549,7 @@ async fn test_delete_expired_imap_messages() -> Result<()> { assert_eq!(is_deleted(&t, "no_expire@localhost").await?, !bcc_self); assert_eq!( is_deleted(&t, "no_expire_unencrypted@localhost").await?, - is_chatmail && !bcc_self + force_encryption && !bcc_self ); assert_eq!(is_deleted(&t, "future@localhost").await?, !bcc_self); assert_eq!(is_deleted(&t, "expired_post@localhost").await?, true); @@ -563,9 +564,10 @@ async fn test_delete_expired_imap_messages() -> Result<()> { reset_targets(&t).await; } - // With BccSelf=true, non-expired messages are kept even if `is_chatmail` is true + // With BccSelf=true, non-expired messages are kept even if `force_encryption` is true t.set_config_bool(Config::BccSelf, true).await?; - delete_expired_imap_messages(&t, transport_id, true).await?; + t.set_config_bool(Config::ForceEncryption, true).await?; + delete_expired_imap_messages(&t, transport_id).await?; assert_eq!(is_deleted(&t, "expired@localhost").await?, true); assert_eq!(is_deleted(&t, "no_expire@localhost").await?, false); assert_eq!(is_deleted(&t, "done_pre@localhost").await?, false); diff --git a/src/imap.rs b/src/imap.rs index 1c34e5d76..a86696fe5 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -452,7 +452,7 @@ impl Imap { // Mark expired messages for deletion. Note that `delete_expired_imap_messages` is // not well optimized and should not be called before fetching. - delete_expired_imap_messages(context, session.transport_id(), session.is_chatmail()) + delete_expired_imap_messages(context, session.transport_id()) .await .context("delete_expired_imap_messages")?;