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).
This commit is contained in:
holger krekel
2026-09-17 16:28:37 +00:00
parent 8014c2af2f
commit 57b2c000e6
4 changed files with 15 additions and 15 deletions

View File

@@ -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;

View File

@@ -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<bool> {
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

View File

@@ -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);

View File

@@ -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")?;