From 020bcaef8b6f0eeba1635007835c60fdf49a6e1f Mon Sep 17 00:00:00 2001 From: mdecimus Date: Tue, 24 Jun 2025 10:41:02 +0200 Subject: [PATCH] Update account name when refreshing DAV caches (closes ##1694) --- crates/groupware/src/cache/mod.rs | 151 +++++++++++++++++++----------- crates/groupware/src/lib.rs | 14 ++- 2 files changed, 110 insertions(+), 55 deletions(-) diff --git a/crates/groupware/src/cache/mod.rs b/crates/groupware/src/cache/mod.rs index fc25d307..a17bcf3f 100644 --- a/crates/groupware/src/cache/mod.rs +++ b/crates/groupware/src/cache/mod.rs @@ -5,6 +5,7 @@ */ use crate::{ + DavResourceName, RFC_3986, cache::calcard::{build_scheduling_resources, path_from_scheduling, resource_from_scheduling}, calendar::{Calendar, CalendarEvent, CalendarPreferences}, contact::{AddressBook, ContactCard}, @@ -16,6 +17,7 @@ use calcard::{ resource_from_calendar, resource_from_card, resource_from_event, }; use common::{CacheSwap, DavResource, DavResources, Server, auth::AccessToken}; +use directory::backend::internal::manage::ManageDirectory; use file::{build_file_resources, build_nested_hierarchy, resource_from_file}; use jmap_proto::types::collection::{Collection, SyncCollection}; use std::{sync::Arc, time::Instant}; @@ -174,62 +176,41 @@ impl GroupwareCache for Server { return Ok(cache); } + // Build base path + let base_path = if access_token.primary_id() == account_id { + format!( + "{}/{}/", + DavResourceName::from(collection).base_path(), + percent_encoding::utf8_percent_encode(&access_token.name, RFC_3986) + ) + } else { + let name = self + .store() + .get_principal_name(account_id) + .await + .caused_by(trc::location!())? + .unwrap_or_else(|| format!("_{account_id}")); + format!( + "{}/{}/", + DavResourceName::from(collection).base_path(), + percent_encoding::utf8_percent_encode(&name, RFC_3986) + ) + }; + let num_changes = changes.changes.len(); let cache = if !matches!(collection, SyncCollection::CalendarScheduling) { let mut updated_resources = AHashMap::with_capacity(8); let has_no_children = collection == SyncCollection::FileNode; - for change in changes.changes { - match change { - Change::InsertItem(id) | Change::UpdateItem(id) => { - let document_id = id as u32; - if let Some(archive) = self - .get_archive(account_id, collection.collection(false), document_id) - .await - .caused_by(trc::location!())? - { - updated_resources.insert( - (has_no_children, document_id), - Some(resource_from_archive( - archive, - document_id, - collection, - false, - )?), - ); - } else { - updated_resources.insert((has_no_children, document_id), None); - } - } - Change::DeleteItem(id) => { - updated_resources.insert((has_no_children, id as u32), None); - } - Change::InsertContainer(id) | Change::UpdateContainer(id) => { - let document_id = id as u32; - if let Some(archive) = self - .get_archive(account_id, collection.collection(true), document_id) - .await - .caused_by(trc::location!())? - { - updated_resources.insert( - (true, document_id), - Some(resource_from_archive( - archive, - document_id, - collection, - true, - )?), - ); - } else { - updated_resources.insert((true, document_id), None); - } - } - Change::DeleteContainer(id) => { - updated_resources.insert((true, id as u32), None); - } - Change::UpdateContainerProperty(_) => (), - } - } + process_changes( + self, + account_id, + collection, + has_no_children, + &mut updated_resources, + changes.changes, + ) + .await?; let mut rebuild_hierarchy = false; let mut resources = Vec::with_capacity(cache.resources.len()); @@ -260,7 +241,7 @@ impl GroupwareCache for Server { if rebuild_hierarchy { let mut cache = DavResources { - base_path: cache.base_path.clone(), + base_path, paths: Default::default(), resources, item_change_id: changes.item_change_id.unwrap_or(cache.item_change_id), @@ -280,7 +261,7 @@ impl GroupwareCache for Server { cache } else { DavResources { - base_path: cache.base_path.clone(), + base_path, paths: cache.paths.clone(), resources, item_change_id: changes.item_change_id.unwrap_or(cache.item_change_id), @@ -323,7 +304,7 @@ impl GroupwareCache for Server { } DavResources { - base_path: cache.base_path.clone(), + base_path, paths, resources, item_change_id: changes.item_change_id.unwrap_or(cache.item_change_id), @@ -440,6 +421,68 @@ impl GroupwareCache for Server { } } +async fn process_changes( + server: &Server, + account_id: u32, + collection: SyncCollection, + has_no_children: bool, + updated_resources: &mut AHashMap<(bool, u32), Option>, + changes: Vec, +) -> trc::Result<()> { + for change in changes { + match change { + Change::InsertItem(id) | Change::UpdateItem(id) => { + let document_id = id as u32; + if let Some(archive) = server + .get_archive(account_id, collection.collection(false), document_id) + .await + .caused_by(trc::location!())? + { + updated_resources.insert( + (has_no_children, document_id), + Some(resource_from_archive( + archive, + document_id, + collection, + false, + )?), + ); + } else { + updated_resources.insert((has_no_children, document_id), None); + } + } + Change::DeleteItem(id) => { + updated_resources.insert((has_no_children, id as u32), None); + } + Change::InsertContainer(id) | Change::UpdateContainer(id) => { + let document_id = id as u32; + if let Some(archive) = server + .get_archive(account_id, collection.collection(true), document_id) + .await + .caused_by(trc::location!())? + { + updated_resources.insert( + (true, document_id), + Some(resource_from_archive( + archive, + document_id, + collection, + true, + )?), + ); + } else { + updated_resources.insert((true, document_id), None); + } + } + Change::DeleteContainer(id) => { + updated_resources.insert((true, id as u32), None); + } + Change::UpdateContainerProperty(_) => (), + } + } + Ok(()) +} + async fn full_cache_build( server: &Server, account_id: u32, diff --git a/crates/groupware/src/lib.rs b/crates/groupware/src/lib.rs index 4f3a368c..602251cc 100644 --- a/crates/groupware/src/lib.rs +++ b/crates/groupware/src/lib.rs @@ -6,7 +6,7 @@ use calcard::common::timezone::Tz; use common::DavResources; -use jmap_proto::types::collection::Collection; +use jmap_proto::types::collection::{Collection, SyncCollection}; use percent_encoding::{AsciiSet, CONTROLS}; pub mod cache; @@ -124,6 +124,18 @@ impl From for DavResourceName { } } +impl From for DavResourceName { + fn from(value: SyncCollection) -> Self { + match value { + SyncCollection::AddressBook => DavResourceName::Card, + SyncCollection::Calendar => DavResourceName::Cal, + SyncCollection::FileNode => DavResourceName::File, + SyncCollection::CalendarScheduling => DavResourceName::Scheduling, + _ => unreachable!(), + } + } +} + pub trait DavCalendarResource { fn calendar_default_tz(&self, calendar_id: u32) -> Option; }