diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e814060..41a7d01f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If - JMAP for File Storage: `FileNode/get` returns a stale state string. - Make `SieveSystemInterpreter.defaultReturnPath` and `MtaQueueQuota.match` optional expressions. - Rate limiter panics when periods under 1 second are used. +- CalDAV/CardDAV: Calendar events, contacts, calendars and address books deleted via JMAP do not write a vanished tombstone. ## [0.16.7] - 2026-05-28 diff --git a/crates/common/src/storage/dav.rs b/crates/common/src/storage/dav.rs index 6e90002b..9b98bcbf 100644 --- a/crates/common/src/storage/dav.rs +++ b/crates/common/src/storage/dav.rs @@ -161,6 +161,21 @@ impl DavResources { } } + pub fn format_resource_paths_by_id( + &self, + document_id: u32, + ) -> impl Iterator + '_ { + self.paths + .iter() + .filter(move |path| self.resources[path.resource_idx].document_id == document_id) + .map(move |path| { + self.format_resource(DavResourcePath { + path, + resource: &self.resources[path.resource_idx], + }) + }) + } + pub fn format_collection(&self, name: &str) -> String { format!("{}{name}/", self.base_path) } diff --git a/crates/jmap/src/addressbook/set.rs b/crates/jmap/src/addressbook/set.rs index e3a344c1..ff163741 100644 --- a/crates/jmap/src/addressbook/set.rs +++ b/crates/jmap/src/addressbook/set.rs @@ -285,12 +285,15 @@ impl AddressBookSet for Server { destroy_parents.insert(document_id); // Delete record + let delete_path = cache + .container_resource_path_by_id(document_id) + .map(|resource| cache.format_resource(resource)); DestroyArchive(address_book) .delete( access_token.account_tenant_ids(), account_id, document_id, - None, + delete_path, &mut batch, ) .caused_by(trc::location!())?; diff --git a/crates/jmap/src/calendar/set.rs b/crates/jmap/src/calendar/set.rs index a8e16751..0c27bea0 100644 --- a/crates/jmap/src/calendar/set.rs +++ b/crates/jmap/src/calendar/set.rs @@ -282,12 +282,15 @@ impl CalendarSet for Server { destroy_parents.insert(document_id); // Delete record + let delete_path = cache + .container_resource_path_by_id(document_id) + .map(|resource| cache.format_resource(resource)); DestroyArchive(calendar) .delete( access_token.account_tenant_ids(), account_id, document_id, - None, + delete_path, &mut batch, ) .caused_by(trc::location!())?; diff --git a/crates/jmap/src/calendar_event/set.rs b/crates/jmap/src/calendar_event/set.rs index 012d66f5..e95cef12 100644 --- a/crates/jmap/src/calendar_event/set.rs +++ b/crates/jmap/src/calendar_event/set.rs @@ -50,7 +50,7 @@ use trc::AddContext; use types::{ acl::Acl, blob::BlobId, - collection::{Collection, SyncCollection}, + collection::{Collection, SyncCollection, VanishedCollection}, id::Id, }; @@ -494,6 +494,10 @@ impl CalendarEventSet for Server { ) .caused_by(trc::location!())?; + for path in cache.format_resource_paths_by_id(document_id) { + batch.log_vanished_item(VanishedCollection::Calendar, path); + } + response.destroyed.push(id); } diff --git a/crates/jmap/src/contact/set.rs b/crates/jmap/src/contact/set.rs index 0910272d..c88540d1 100644 --- a/crates/jmap/src/contact/set.rs +++ b/crates/jmap/src/contact/set.rs @@ -30,7 +30,7 @@ use trc::AddContext; use types::{ acl::Acl, blob::BlobId, - collection::{Collection, SyncCollection}, + collection::{Collection, SyncCollection, VanishedCollection}, id::Id, }; @@ -339,6 +339,10 @@ impl ContactCardSet for Server { ) .caused_by(trc::location!())?; + for path in cache.format_resource_paths_by_id(document_id) { + batch.log_vanished_item(VanishedCollection::AddressBook, path); + } + response.destroyed.push(id); }