Files
inbuxa-server/crates/email/src/sieve/delete.rs
T
jcoffey-dev 7b97efbb7f Legal holds, step 3: deleted items in a held account are kept
Every way of deleting mail (JMAP, IMAP EXPUNGE, POP3, mailbox removal,
Trash emptying) and Sieve scripts, events, contacts and files now asks
how the account's deletions are kept: a hold keeps them with no expiry
(archivedUntil 9999-12-31), even with undelete off; otherwise undelete's
period applies as before (LH-4).

A hold's date range decides by the item's own date (LH-3). Mail is noted
as held at deletion and settled when it's archived, once its received
date is known; outside the range it gets undelete's deadline or isn't
kept. Events go by their start, with a day's slack for time zones;
recurring events, contacts, files and scripts are held whole.

A groupware item's note now stays until its archive succeeds, and a
failure retries the task instead of being logged and lost (LH-5).
2026-09-27 19:33:07 -07:00

97 lines
3.1 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*
* Modified by Coffey Labs in 2026 for INBUXA.
*/
use super::SieveScript;
use common::{Server, auth::AccessToken, storage::index::ObjectIndexBuilder};
use store::write::BatchBuilder;
use store::{
ValueKey,
write::{AlignedBytes, Archive},
};
use trc::AddContext;
use types::{collection::Collection, field::SieveField};
pub trait SieveScriptDelete: Sync + Send {
fn sieve_script_delete(
&self,
account_id: u32,
document_id: u32,
access_token: &AccessToken,
batch: &mut BatchBuilder,
) -> impl Future<Output = trc::Result<bool>> + Send;
}
impl SieveScriptDelete for Server {
async fn sieve_script_delete(
&self,
account_id: u32,
document_id: u32,
access_token: &AccessToken,
batch: &mut BatchBuilder,
) -> trc::Result<bool> {
// Fetch record
if let Some(obj_) = self
.store()
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
account_id,
Collection::SieveScript,
document_id,
))
.await?
{
// inbuxa: UD-1, LH-4: a deleted script is kept, when archiving
// is on or a hold covers the account (whole: scripts have no date)
let keeping = self.keeping(account_id).await?;
let now = store::write::now();
if let Some(until) = keeping.until(now, keeping.is_held()) {
let retention = until.saturating_sub(now);
let script = obj_
.deserialize::<SieveScript>()
.caused_by(trc::location!())?;
let content = self
.blob_store()
.get_blob(script.blob_hash.as_slice(), 0..usize::MAX)
.await?
.map(|bytes| String::from_utf8_lossy(&bytes).into_owned())
.unwrap_or_default();
inbuxa_features::undelete::groupware::archive_sieve(
&self.core.storage.data,
self.registry(),
account_id,
&script.name,
content,
script.blob_hash.clone(),
retention,
)
.await?;
}
// Delete record
batch
.with_account_id(account_id)
.with_collection(Collection::SieveScript)
.with_document(document_id)
.clear(SieveField::Ids)
.custom(
ObjectIndexBuilder::<_, ()>::new()
.with_current(
obj_.to_unarchived::<SieveScript>()
.caused_by(trc::location!())?,
)
.with_changed_by(access_token.account_tenant_ids()),
)
.caused_by(trc::location!())?
.commit_point();
Ok(true)
} else {
Ok(false)
}
}
}