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).
This commit is contained in:
2026-09-27 19:33:07 -07:00
parent 318783f444
commit 7b97efbb7f
11 changed files with 350 additions and 42 deletions
+10 -1
View File
@@ -10,7 +10,7 @@
//! there are few holds.
use crate::Server;
use inbuxa_features::hold::{self, Hold, Member};
use inbuxa_features::hold::{self, Hold, Keeping, Member};
impl Server {
/// The active holds covering `account_id`, through its own name, its
@@ -36,6 +36,15 @@ impl Server {
hold::covering(self.store(), &member).await
}
/// How `account_id`'s deleted items are kept: its holds' ranges and the
/// undelete period in force now (LH-4, UD-6a).
pub async fn keeping(&self, account_id: u32) -> trc::Result<Keeping> {
let retention = inbuxa_features::undelete::settings::retention(self.registry())
.await?
.items;
Ok(Keeping::new(retention, &self.holds_on(account_id).await?))
}
/// Whether any active hold covers `account_id` at all.
pub async fn is_held(&self, account_id: u32) -> trc::Result<bool> {
Ok(!self.holds_on(account_id).await?.is_empty())