Add per-user CardDAV properties + fix per-user CalDAV properties (fixes #2058)

This commit is contained in:
mdecimus
2025-10-02 10:07:22 +02:00
parent d0aef5d5a2
commit 98deb17482
13 changed files with 187 additions and 129 deletions

View File

@@ -100,7 +100,7 @@ impl CalendarMkColRequestHandler for Server {
let mut prop_stat = PropStatBuilder::default();
is_mkcalendar = mkcol.is_mkcalendar;
if !self.apply_calendar_properties(
account_id,
access_token,
&mut calendar,
false,
mkcol.props,

View File

@@ -47,7 +47,7 @@ pub(crate) trait CalendarPropPatchRequestHandler: Sync + Send {
fn apply_calendar_properties(
&self,
account_id: u32,
access_token: &AccessToken,
calendar: &mut Calendar,
is_update: bool,
properties: Vec<DavPropertyValue>,
@@ -149,7 +149,7 @@ impl CalendarPropPatchRequestHandler for Server {
// Remove properties
if !request.set_first && !request.remove.is_empty() {
remove_calendar_properties(
account_id,
access_token,
&mut new_calendar,
std::mem::take(&mut request.remove),
&mut items,
@@ -158,7 +158,7 @@ impl CalendarPropPatchRequestHandler for Server {
// Set properties
is_success = self.apply_calendar_properties(
account_id,
access_token,
&mut new_calendar,
true,
request.set,
@@ -168,7 +168,7 @@ impl CalendarPropPatchRequestHandler for Server {
// Remove properties
if is_success && !request.remove.is_empty() {
remove_calendar_properties(
account_id,
access_token,
&mut new_calendar,
request.remove,
&mut items,
@@ -238,7 +238,7 @@ impl CalendarPropPatchRequestHandler for Server {
fn apply_calendar_properties(
&self,
account_id: u32,
access_token: &AccessToken,
calendar: &mut Calendar,
is_update: bool,
properties: Vec<DavPropertyValue>,
@@ -250,7 +250,7 @@ impl CalendarPropPatchRequestHandler for Server {
match (&property.property, property.value) {
(DavProperty::WebDav(WebDavProperty::DisplayName), DavValue::String(name)) => {
if name.len() <= self.core.groupware.live_property_size {
calendar.preferences_mut(account_id).name = name;
calendar.preferences_mut(access_token).name = name;
items.insert_ok(property.property);
} else {
items.insert_error_with_description(
@@ -266,7 +266,7 @@ impl CalendarPropPatchRequestHandler for Server {
DavValue::String(name),
) => {
if name.len() <= self.core.groupware.live_property_size {
calendar.preferences_mut(account_id).description = Some(name);
calendar.preferences_mut(access_token).description = Some(name);
items.insert_ok(property.property);
} else {
items.insert_error_with_description(
@@ -298,13 +298,14 @@ impl CalendarPropPatchRequestHandler for Server {
);
has_errors = true;
} else {
calendar.preferences_mut(account_id).time_zone = Timezone::Custom(ical);
calendar.preferences_mut(access_token).time_zone = Timezone::Custom(ical);
items.insert_ok(property.property);
}
}
(DavProperty::CalDav(CalDavProperty::TimezoneId), DavValue::String(tz_id)) => {
if let Ok(tz) = Tz::from_str(&tz_id) {
calendar.preferences_mut(account_id).time_zone = Timezone::IANA(tz.as_id());
calendar.preferences_mut(access_token).time_zone =
Timezone::IANA(tz.as_id());
items.insert_ok(property.property);
} else {
items.insert_precondition_failed_with_description(
@@ -472,7 +473,7 @@ fn remove_event_properties(
}
fn remove_calendar_properties(
account_id: u32,
access_token: &AccessToken,
calendar: &mut Calendar,
properties: Vec<DavProperty>,
items: &mut PropStatBuilder,
@@ -480,12 +481,12 @@ fn remove_calendar_properties(
for property in properties {
match &property {
DavProperty::CalDav(CalDavProperty::CalendarDescription) => {
calendar.preferences_mut(account_id).description = None;
calendar.preferences_mut(access_token).description = None;
items.insert_with_status(property, StatusCode::NO_CONTENT);
}
DavProperty::CalDav(CalDavProperty::CalendarTimezone)
| DavProperty::CalDav(CalDavProperty::TimezoneId) => {
calendar.preferences_mut(account_id).time_zone = Timezone::Default;
calendar.preferences_mut(access_token).time_zone = Timezone::Default;
items.insert_with_status(property, StatusCode::NO_CONTENT);
}
DavProperty::DeadProperty(dead) => {

View File

@@ -18,7 +18,7 @@ use dav_proto::{Depth, RequestHeaders};
use groupware::{
DestroyArchive,
cache::GroupwareCache,
contact::{AddressBook, ContactCard},
contact::{AddressBook, AddressBookPreferences, ContactCard},
};
use http_proto::HttpResponse;
use hyper::StatusCode;
@@ -759,10 +759,17 @@ async fn copy_container(
.caused_by(trc::location!())?;
}
let preference = book.preferences.into_iter().next().unwrap();
book.name = new_name.to_string();
book.subscribers.clear();
book.acls.clear();
book.is_default = false;
book.preferences = vec![AddressBookPreferences {
account_id: to_account_id,
name: preference.name,
description: preference.description,
sort_order: 0,
is_default: false,
}];
let is_overwrite = to_document_id.is_some();
let to_document_id = if let Some(to_document_id) = to_document_id {

View File

@@ -89,7 +89,13 @@ impl CardMkColRequestHandler for Server {
let mut return_prop_stat = None;
if let Some(mkcol) = request {
let mut prop_stat = PropStatBuilder::default();
if !self.apply_addressbook_properties(&mut book, false, mkcol.props, &mut prop_stat) {
if !self.apply_addressbook_properties(
access_token,
&mut book,
false,
mkcol.props,
&mut prop_stat,
) {
return Ok(HttpResponse::new(StatusCode::FORBIDDEN).with_xml_body(
MkColResponse::new(prop_stat.build())
.with_namespace(Namespace::CardDav)

View File

@@ -45,6 +45,7 @@ pub(crate) trait CardPropPatchRequestHandler: Sync + Send {
fn apply_addressbook_properties(
&self,
access_token: &AccessToken,
address_book: &mut AddressBook,
is_update: bool,
properties: Vec<DavPropertyValue>,
@@ -146,6 +147,7 @@ impl CardPropPatchRequestHandler for Server {
// Remove properties
if !request.set_first && !request.remove.is_empty() {
remove_addressbook_properties(
access_token,
&mut new_book,
std::mem::take(&mut request.remove),
&mut items,
@@ -153,12 +155,22 @@ impl CardPropPatchRequestHandler for Server {
}
// Set properties
is_success =
self.apply_addressbook_properties(&mut new_book, true, request.set, &mut items);
is_success = self.apply_addressbook_properties(
access_token,
&mut new_book,
true,
request.set,
&mut items,
);
// Remove properties
if is_success && !request.remove.is_empty() {
remove_addressbook_properties(&mut new_book, request.remove, &mut items);
remove_addressbook_properties(
access_token,
&mut new_book,
request.remove,
&mut items,
);
}
if is_success {
@@ -224,6 +236,7 @@ impl CardPropPatchRequestHandler for Server {
fn apply_addressbook_properties(
&self,
access_token: &AccessToken,
address_book: &mut AddressBook,
is_update: bool,
properties: Vec<DavPropertyValue>,
@@ -235,7 +248,7 @@ impl CardPropPatchRequestHandler for Server {
match (&property.property, property.value) {
(DavProperty::WebDav(WebDavProperty::DisplayName), DavValue::String(name)) => {
if name.len() <= self.core.groupware.live_property_size {
address_book.display_name = Some(name);
address_book.preferences_mut(access_token).name = name;
items.insert_ok(property.property);
} else {
items.insert_error_with_description(
@@ -251,7 +264,7 @@ impl CardPropPatchRequestHandler for Server {
DavValue::String(name),
) => {
if name.len() <= self.core.groupware.live_property_size {
address_book.description = Some(name);
address_book.preferences_mut(access_token).description = Some(name);
items.insert_ok(property.property);
} else {
items.insert_error_with_description(
@@ -418,6 +431,7 @@ fn remove_card_properties(
}
fn remove_addressbook_properties(
access_token: &AccessToken,
book: &mut AddressBook,
properties: Vec<DavProperty>,
items: &mut PropStatBuilder,
@@ -425,13 +439,13 @@ fn remove_addressbook_properties(
for property in properties {
match &property {
DavProperty::CardDav(CardDavProperty::AddressbookDescription) => {
book.description = None;
book.preferences_mut(access_token).description = None;
items.insert_with_status(property, StatusCode::NO_CONTENT);
}
DavProperty::WebDav(WebDavProperty::DisplayName) => {
/*DavProperty::WebDav(WebDavProperty::DisplayName) => {
book.display_name = None;
items.insert_with_status(property, StatusCode::NO_CONTENT);
}
}*/
DavProperty::DeadProperty(dead) => {
book.dead_properties.remove_element(dead);
items.insert_with_status(property, StatusCode::NO_CONTENT);

View File

@@ -8,6 +8,7 @@ use calcard::{
icalendar::{ICalendarComponentType, ICalendarParameterName, ICalendarProperty},
vcard::{VCardParameterName, VCardVersion},
};
use common::auth::AccessToken;
use dav_proto::{
Depth, RequestHeaders, Return,
schema::{
@@ -418,13 +419,15 @@ impl<'x> ArchivedResource<'x> {
}
}
pub fn display_name(&self, account_id: u32) -> Option<&str> {
pub fn display_name(&self, access_token: &AccessToken) -> Option<&str> {
match self {
ArchivedResource::Calendar(archive) => {
Some(archive.inner.preferences(account_id).name.as_str())
Some(archive.inner.preferences(access_token).name.as_str())
}
ArchivedResource::CalendarEvent(archive) => archive.inner.display_name.as_deref(),
ArchivedResource::AddressBook(archive) => archive.inner.display_name.as_deref(),
ArchivedResource::AddressBook(archive) => {
Some(archive.inner.preferences(access_token).name.as_str())
}
ArchivedResource::ContactCard(archive) => archive.inner.display_name.as_deref(),
ArchivedResource::FileNode(archive) => archive.inner.display_name.as_deref(),
ArchivedResource::CalendarScheduling(_)

View File

@@ -408,7 +408,6 @@ impl PropFindRequestHandler for Server {
PropFind::Prop(items) => items.clone(),
};
let view_as_id = access_token.primary_id();
let is_scheduling = collection_container == Collection::CalendarScheduling;
'outer: for item in paths {
let account_id = item.account_id;
@@ -491,7 +490,7 @@ impl PropFindRequestHandler for Server {
));
}
WebDavProperty::DisplayName => {
if let Some(name) = archive.display_name(view_as_id) {
if let Some(name) = archive.display_name(access_token) {
fields.push(DavPropertyValue::new(
property.clone(),
DavValue::String(name.to_string()),
@@ -777,11 +776,17 @@ impl PropFindRequestHandler for Server {
(
CardDavProperty::AddressbookDescription,
ArchivedResource::AddressBook(book),
) if book.inner.description.is_some() => {
fields.push(DavPropertyValue::new(
property.clone(),
book.inner.description.as_ref().unwrap().to_string(),
));
) => {
if let Some(desc) =
book.inner.preferences(access_token).description.as_deref()
{
fields.push(DavPropertyValue::new(
property.clone(),
desc.to_string(),
));
} else {
fields_not_found.push(DavPropertyValue::empty(property.clone()));
}
}
(
CardDavProperty::SupportedAddressData,
@@ -844,7 +849,7 @@ impl PropFindRequestHandler for Server {
) => {
if let Some(desc) = calendar
.inner
.preferences(account_id)
.preferences(access_token)
.description
.as_deref()
{
@@ -861,7 +866,7 @@ impl PropFindRequestHandler for Server {
ArchivedResource::Calendar(calendar),
) => {
if let ArchivedTimezone::Custom(tz) =
&calendar.inner.preferences(account_id).time_zone
&calendar.inner.preferences(access_token).time_zone
{
fields.push(DavPropertyValue::new(
property.clone(),
@@ -873,7 +878,7 @@ impl PropFindRequestHandler for Server {
}
(CalDavProperty::TimezoneId, ArchivedResource::Calendar(calendar)) => {
if let ArchivedTimezone::IANA(tz) =
&calendar.inner.preferences(account_id).time_zone
&calendar.inner.preferences(access_token).time_zone
{
fields.push(DavPropertyValue::new(
property.clone(),