From eb1a8578d7d43cd798401aadc0274bd82d62be0c Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sat, 18 Jan 2025 22:59:02 +0100 Subject: [PATCH] Replace this internal_props stuff with CommonPropertiesExtension --- Cargo.lock | 23 ----- .../methods/report/calendar_multiget.rs | 7 +- .../calendar/methods/report/calendar_query.rs | 7 +- .../methods/report/sync_collection.rs | 7 +- crates/caldav/src/calendar/resource.rs | 33 +++---- crates/caldav/src/calendar_object/resource.rs | 37 +++++--- crates/caldav/src/calendar_set/mod.rs | 24 ++++- crates/caldav/src/principal/mod.rs | 39 ++++++-- crates/carddav/src/address_object/resource.rs | 37 +++++--- .../methods/report/addressbook_multiget.rs | 7 +- .../methods/report/sync_collection.rs | 7 +- crates/carddav/src/addressbook/resource.rs | 27 +++--- crates/carddav/src/principal/mod.rs | 34 +++++-- crates/dav/Cargo.toml | 1 - crates/dav/src/extensions/common.rs | 70 +++++++++++++++ crates/dav/src/extensions/mod.rs | 2 + crates/dav/src/resource/methods/propfind.rs | 7 +- crates/dav/src/resource/mod.rs | 89 +------------------ crates/dav/src/resources/root.rs | 38 ++------ 19 files changed, 263 insertions(+), 233 deletions(-) create mode 100644 crates/dav/src/extensions/common.rs diff --git a/Cargo.lock b/Cargo.lock index 97792df..af66503 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2825,7 +2825,6 @@ dependencies = [ "rustical_store", "rustical_xml", "serde", - "strum", "thiserror 2.0.11", "tokio", "tracing", @@ -3384,28 +3383,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - [[package]] name = "subtle" version = "2.6.1" diff --git a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs index e3d48fb..dad0c08 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_multiget.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_multiget.rs @@ -1,5 +1,5 @@ use crate::{ - calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, + calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, Error, }; use actix_web::{ @@ -8,7 +8,7 @@ use actix_web::{ HttpRequest, }; use rustical_dav::{ - resource::{CommonPropertiesProp, EitherProp, Resource}, + resource::Resource, xml::{multistatus::ResponseElement, MultistatusElement, PropElement, PropfindType}, }; use rustical_store::{auth::User, CalendarObject, CalendarStore}; @@ -60,8 +60,7 @@ pub async fn handle_calendar_multiget( principal: &str, cal_id: &str, cal_store: &C, -) -> Result, String>, Error> -{ +) -> Result, Error> { let (objects, not_found) = get_objects_calendar_multiget(&cal_multiget, req.path(), principal, cal_id, cal_store) .await?; diff --git a/crates/caldav/src/calendar/methods/report/calendar_query.rs b/crates/caldav/src/calendar/methods/report/calendar_query.rs index 75d767e..2b29666 100644 --- a/crates/caldav/src/calendar/methods/report/calendar_query.rs +++ b/crates/caldav/src/calendar/methods/report/calendar_query.rs @@ -2,14 +2,14 @@ use std::ops::Deref; use actix_web::HttpRequest; use rustical_dav::{ - resource::{CommonPropertiesProp, EitherProp, Resource}, + resource::Resource, xml::{MultistatusElement, PropElement, PropfindType}, }; use rustical_store::{auth::User, calendar::UtcDateTime, CalendarObject, CalendarStore}; use rustical_xml::XmlDeserialize; use crate::{ - calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, + calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, Error, }; @@ -185,8 +185,7 @@ pub async fn handle_calendar_query( principal: &str, cal_id: &str, cal_store: &C, -) -> Result, String>, Error> -{ +) -> Result, Error> { let objects = get_objects_calendar_query(&cal_query, principal, cal_id, cal_store).await?; let props = match cal_query.prop { diff --git a/crates/caldav/src/calendar/methods/report/sync_collection.rs b/crates/caldav/src/calendar/methods/report/sync_collection.rs index 94fad45..763024e 100644 --- a/crates/caldav/src/calendar/methods/report/sync_collection.rs +++ b/crates/caldav/src/calendar/methods/report/sync_collection.rs @@ -1,6 +1,6 @@ use actix_web::{http::StatusCode, HttpRequest}; use rustical_dav::{ - resource::{CommonPropertiesProp, EitherProp, Resource}, + resource::Resource, xml::{ multistatus::ResponseElement, sync_collection::SyncCollectionRequest, MultistatusElement, PropElement, PropfindType, @@ -13,7 +13,7 @@ use rustical_store::{ }; use crate::{ - calendar_object::resource::{CalendarObjectProp, CalendarObjectResource}, + calendar_object::resource::{CalendarObjectPropWrapper, CalendarObjectResource}, Error, }; @@ -24,8 +24,7 @@ pub async fn handle_sync_collection( principal: &str, cal_id: &str, cal_store: &C, -) -> Result, String>, Error> -{ +) -> Result, Error> { let props = match sync_collection.prop { PropfindType::Allprop => { vec!["allprop".to_owned()] diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index 5f277aa..c9222f9 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -11,7 +11,8 @@ use actix_web::web; use async_trait::async_trait; use derive_more::derive::{From, Into}; use rustical_dav::extensions::{ - DavPushExtension, DavPushExtensionProp, SyncTokenExtension, SyncTokenExtensionProp, + CommonPropertiesExtension, CommonPropertiesProp, DavPushExtension, DavPushExtensionProp, + SyncTokenExtension, SyncTokenExtensionProp, }; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{Resource, ResourceService}; @@ -66,6 +67,7 @@ pub enum CalendarPropWrapper { Calendar(CalendarProp), SyncToken(SyncTokenExtensionProp), DavPush(DavPushExtensionProp), + Common(CommonPropertiesProp), } #[derive(Clone, Debug, From, Into)] @@ -117,8 +119,8 @@ impl Resource for CalendarResource { fn get_prop( &self, - _rmap: &ResourceMap, - _user: &User, + rmap: &ResourceMap, + user: &User, prop: &Self::PropName, ) -> Result { Ok(match prop { @@ -160,18 +162,19 @@ impl Resource for CalendarResource { CalendarPropName::SupportedReportSet => { CalendarProp::SupportedReportSet(SupportedReportSet::default()) } - // CalendarPropName::SyncToken => CalendarProp::SyncToken(self.get_synctoken()), - // CalendarPropName::Getctag => CalendarProp::Getctag(self.get_synctoken()), CalendarPropName::Source => CalendarProp::Source( self.cal.subscription_url.to_owned().map(HrefElement::from), ), }), CalendarPropWrapperName::SyncToken(prop) => { - CalendarPropWrapper::SyncToken(::get_prop(self, prop)?) + CalendarPropWrapper::SyncToken(SyncTokenExtension::get_prop(self, prop)?) } CalendarPropWrapperName::DavPush(prop) => { - CalendarPropWrapper::DavPush(::get_prop(self, prop)?) + CalendarPropWrapper::DavPush(DavPushExtension::get_prop(self, prop)?) } + CalendarPropWrapperName::Common(prop) => CalendarPropWrapper::Common( + CommonPropertiesExtension::get_prop(self, rmap, user, prop)?, + ), }) } @@ -227,10 +230,9 @@ impl Resource for CalendarResource { // Converting between a calendar subscription calendar and a normal one would be weird CalendarProp::Source(_) => Err(rustical_dav::Error::PropReadOnly), }, - CalendarPropWrapper::SyncToken(prop) => { - ::set_prop(self, prop) - } - CalendarPropWrapper::DavPush(prop) => ::set_prop(self, prop), + CalendarPropWrapper::SyncToken(prop) => SyncTokenExtension::set_prop(self, prop), + CalendarPropWrapper::DavPush(prop) => DavPushExtension::set_prop(self, prop), + CalendarPropWrapper::Common(prop) => CommonPropertiesExtension::set_prop(self, prop), } } @@ -275,11 +277,10 @@ impl Resource for CalendarResource { // Converting a calendar subscription calendar into a normal one would be weird CalendarPropName::Source => Err(rustical_dav::Error::PropReadOnly), }, - CalendarPropWrapperName::SyncToken(prop) => { - ::remove_prop(self, prop) - } - CalendarPropWrapperName::DavPush(prop) => { - ::remove_prop(self, prop) + CalendarPropWrapperName::SyncToken(prop) => SyncTokenExtension::remove_prop(self, prop), + CalendarPropWrapperName::DavPush(prop) => DavPushExtension::remove_prop(self, prop), + CalendarPropWrapperName::Common(prop) => { + CommonPropertiesExtension::remove_prop(self, prop) } } } diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index c6cb54d..d7aed71 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -4,6 +4,7 @@ use actix_web::dev::ResourceMap; use async_trait::async_trait; use derive_more::derive::{From, Into}; use rustical_dav::{ + extensions::{CommonPropertiesExtension, CommonPropertiesProp}, privileges::UserPrivilegeSet, resource::{Resource, ResourceService}, xml::Resourcetype, @@ -37,6 +38,13 @@ pub enum CalendarObjectProp { CalendarData(String), } +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)] +#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)] +pub enum CalendarObjectPropWrapper { + CalendarObject(CalendarObjectProp), + Common(CommonPropertiesProp), +} + #[derive(Clone, From, Into)] pub struct CalendarObjectResource { pub object: CalendarObject, @@ -44,8 +52,8 @@ pub struct CalendarObjectResource { } impl Resource for CalendarObjectResource { - type PropName = CalendarObjectPropName; - type Prop = CalendarObjectProp; + type PropName = CalendarObjectPropWrapperName; + type Prop = CalendarObjectPropWrapper; type Error = Error; type PrincipalResource = PrincipalResource; @@ -55,18 +63,27 @@ impl Resource for CalendarObjectResource { fn get_prop( &self, - _rmap: &ResourceMap, - _user: &User, + rmap: &ResourceMap, + user: &User, prop: &Self::PropName, ) -> Result { Ok(match prop { - CalendarObjectPropName::Getetag => CalendarObjectProp::Getetag(self.object.get_etag()), - CalendarObjectPropName::CalendarData => { - CalendarObjectProp::CalendarData(self.object.get_ics().to_owned()) - } - CalendarObjectPropName::Getcontenttype => { - CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8") + CalendarObjectPropWrapperName::CalendarObject(prop) => { + CalendarObjectPropWrapper::CalendarObject(match prop { + CalendarObjectPropName::Getetag => { + CalendarObjectProp::Getetag(self.object.get_etag()) + } + CalendarObjectPropName::CalendarData => { + CalendarObjectProp::CalendarData(self.object.get_ics().to_owned()) + } + CalendarObjectPropName::Getcontenttype => { + CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8") + } + }) } + CalendarObjectPropWrapperName::Common(prop) => CalendarObjectPropWrapper::Common( + CommonPropertiesExtension::get_prop(self, rmap, user, prop)?, + ), }) } diff --git a/crates/caldav/src/calendar_set/mod.rs b/crates/caldav/src/calendar_set/mod.rs index 893dd86..cb8049b 100644 --- a/crates/caldav/src/calendar_set/mod.rs +++ b/crates/caldav/src/calendar_set/mod.rs @@ -3,6 +3,7 @@ use crate::principal::PrincipalResource; use crate::Error; use actix_web::dev::ResourceMap; use async_trait::async_trait; +use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{NamedRoute, Resource, ResourceService}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; @@ -24,9 +25,16 @@ pub enum PrincipalProp { PrincipalUrl(HrefElement), } +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)] +#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)] +pub enum PrincipalPropWrapper { + Principal(PrincipalProp), + Common(CommonPropertiesProp), +} + impl Resource for CalendarSetResource { - type PropName = PrincipalPropName; - type Prop = PrincipalProp; + type PropName = PrincipalPropWrapperName; + type Prop = PrincipalPropWrapper; type Error = Error; type PrincipalResource = PrincipalResource; @@ -40,7 +48,7 @@ impl Resource for CalendarSetResource { fn get_prop( &self, rmap: &ResourceMap, - _user: &User, + user: &User, prop: &Self::PropName, ) -> Result { let principal_href = HrefElement::new( @@ -48,7 +56,15 @@ impl Resource for CalendarSetResource { ); Ok(match prop { - PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_href), + PrincipalPropWrapperName::Principal(prop) => { + PrincipalPropWrapper::Principal(match prop { + PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_href), + }) + } + + PrincipalPropWrapperName::Common(prop) => PrincipalPropWrapper::Common( + ::get_prop(self, rmap, user, prop)?, + ), }) } diff --git a/crates/caldav/src/principal/mod.rs b/crates/caldav/src/principal/mod.rs index 5621ab4..81b5793 100644 --- a/crates/caldav/src/principal/mod.rs +++ b/crates/caldav/src/principal/mod.rs @@ -2,6 +2,7 @@ use crate::calendar_set::CalendarSetResource; use crate::Error; use actix_web::dev::ResourceMap; use async_trait::async_trait; +use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{NamedRoute, Resource, ResourceService}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; @@ -38,6 +39,13 @@ pub enum PrincipalProp { CalendarHomeSet(CalendarHomeSet), } +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)] +#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)] +pub enum PrincipalPropWrapper { + Principal(PrincipalProp), + Common(CommonPropertiesProp), +} + impl PrincipalResource { pub fn get_principal_url(rmap: &ResourceMap, principal: &str) -> String { Self::get_url(rmap, vec![principal]).unwrap() @@ -51,8 +59,8 @@ impl NamedRoute for PrincipalResource { } impl Resource for PrincipalResource { - type PropName = PrincipalPropName; - type Prop = PrincipalProp; + type PropName = PrincipalPropWrapperName; + type Prop = PrincipalPropWrapper; type Error = Error; type PrincipalResource = PrincipalResource; @@ -66,7 +74,7 @@ impl Resource for PrincipalResource { fn get_prop( &self, rmap: &ResourceMap, - _user: &User, + user: &User, prop: &Self::PropName, ) -> Result { let principal_url = Self::get_url(rmap, vec![&self.principal]).unwrap(); @@ -78,13 +86,26 @@ impl Resource for PrincipalResource { ); Ok(match prop { - PrincipalPropName::CalendarUserType => PrincipalProp::CalendarUserType("INDIVIDUAL"), - PrincipalPropName::Displayname => PrincipalProp::Displayname(self.principal.to_owned()), - PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_url.into()), - PrincipalPropName::CalendarHomeSet => PrincipalProp::CalendarHomeSet(home_set), - PrincipalPropName::CalendarUserAddressSet => { - PrincipalProp::CalendarUserAddressSet(principal_url.into()) + PrincipalPropWrapperName::Principal(prop) => { + PrincipalPropWrapper::Principal(match prop { + PrincipalPropName::CalendarUserType => { + PrincipalProp::CalendarUserType("INDIVIDUAL") + } + PrincipalPropName::Displayname => { + PrincipalProp::Displayname(self.principal.to_owned()) + } + PrincipalPropName::PrincipalUrl => { + PrincipalProp::PrincipalUrl(principal_url.into()) + } + PrincipalPropName::CalendarHomeSet => PrincipalProp::CalendarHomeSet(home_set), + PrincipalPropName::CalendarUserAddressSet => { + PrincipalProp::CalendarUserAddressSet(principal_url.into()) + } + }) } + PrincipalPropWrapperName::Common(prop) => PrincipalPropWrapper::Common( + ::get_prop(self, rmap, user, prop)?, + ), }) } diff --git a/crates/carddav/src/address_object/resource.rs b/crates/carddav/src/address_object/resource.rs index 957256a..631b808 100644 --- a/crates/carddav/src/address_object/resource.rs +++ b/crates/carddav/src/address_object/resource.rs @@ -3,6 +3,7 @@ use actix_web::dev::ResourceMap; use async_trait::async_trait; use derive_more::derive::{Constructor, From, Into}; use rustical_dav::{ + extensions::{CommonPropertiesExtension, CommonPropertiesProp}, privileges::UserPrivilegeSet, resource::{Resource, ResourceService}, xml::Resourcetype, @@ -33,6 +34,13 @@ pub enum AddressObjectProp { AddressData(String), } +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)] +#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)] +pub enum AddressObjectPropWrapper { + AddressObject(AddressObjectProp), + Common(CommonPropertiesProp), +} + #[derive(Clone, From, Into)] pub struct AddressObjectResource { pub object: AddressObject, @@ -40,8 +48,8 @@ pub struct AddressObjectResource { } impl Resource for AddressObjectResource { - type PropName = AddressObjectPropName; - type Prop = AddressObjectProp; + type PropName = AddressObjectPropWrapperName; + type Prop = AddressObjectPropWrapper; type Error = Error; type PrincipalResource = PrincipalResource; @@ -51,18 +59,27 @@ impl Resource for AddressObjectResource { fn get_prop( &self, - _rmap: &ResourceMap, - _user: &User, + rmap: &ResourceMap, + user: &User, prop: &Self::PropName, ) -> Result { Ok(match prop { - AddressObjectPropName::Getetag => AddressObjectProp::Getetag(self.object.get_etag()), - AddressObjectPropName::AddressData => { - AddressObjectProp::AddressData(self.object.get_vcf().to_owned()) - } - AddressObjectPropName::Getcontenttype => { - AddressObjectProp::Getcontenttype("text/vcard;charset=utf-8") + AddressObjectPropWrapperName::AddressObject(prop) => { + AddressObjectPropWrapper::AddressObject(match prop { + AddressObjectPropName::Getetag => { + AddressObjectProp::Getetag(self.object.get_etag()) + } + AddressObjectPropName::AddressData => { + AddressObjectProp::AddressData(self.object.get_vcf().to_owned()) + } + AddressObjectPropName::Getcontenttype => { + AddressObjectProp::Getcontenttype("text/vcard;charset=utf-8") + } + }) } + AddressObjectPropWrapperName::Common(prop) => AddressObjectPropWrapper::Common( + CommonPropertiesExtension::get_prop(self, rmap, user, prop)?, + ), }) } diff --git a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs index d80c287..1a2293f 100644 --- a/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs +++ b/crates/carddav/src/addressbook/methods/report/addressbook_multiget.rs @@ -1,5 +1,5 @@ use crate::{ - address_object::resource::{AddressObjectProp, AddressObjectResource}, + address_object::resource::{AddressObjectPropWrapper, AddressObjectResource}, Error, }; use actix_web::{ @@ -8,7 +8,7 @@ use actix_web::{ HttpRequest, }; use rustical_dav::{ - resource::{CommonPropertiesProp, EitherProp, Resource}, + resource::Resource, xml::{multistatus::ResponseElement, MultistatusElement, PropElement, PropfindType}, }; use rustical_store::{auth::User, AddressObject, AddressbookStore}; @@ -60,8 +60,7 @@ pub async fn handle_addressbook_multiget( principal: &str, cal_id: &str, addr_store: &AS, -) -> Result, String>, Error> -{ +) -> Result, Error> { let (objects, not_found) = get_objects_addressbook_multiget(&addr_multiget, req.path(), principal, cal_id, addr_store) .await?; diff --git a/crates/carddav/src/addressbook/methods/report/sync_collection.rs b/crates/carddav/src/addressbook/methods/report/sync_collection.rs index 0ea645c..95f461a 100644 --- a/crates/carddav/src/addressbook/methods/report/sync_collection.rs +++ b/crates/carddav/src/addressbook/methods/report/sync_collection.rs @@ -1,10 +1,10 @@ use crate::{ - address_object::resource::{AddressObjectProp, AddressObjectResource}, + address_object::resource::{AddressObjectPropWrapper, AddressObjectResource}, Error, }; use actix_web::{http::StatusCode, HttpRequest}; use rustical_dav::{ - resource::{CommonPropertiesProp, EitherProp, Resource}, + resource::Resource, xml::{ multistatus::ResponseElement, sync_collection::SyncCollectionRequest, MultistatusElement, PropElement, PropfindType, @@ -23,8 +23,7 @@ pub async fn handle_sync_collection( principal: &str, addressbook_id: &str, addr_store: &AS, -) -> Result, String>, Error> -{ +) -> Result, Error> { let props = match sync_collection.prop { PropfindType::Allprop => { vec!["allprop".to_owned()] diff --git a/crates/carddav/src/addressbook/resource.rs b/crates/carddav/src/addressbook/resource.rs index 4a6829c..8a94fb1 100644 --- a/crates/carddav/src/addressbook/resource.rs +++ b/crates/carddav/src/addressbook/resource.rs @@ -11,7 +11,8 @@ use actix_web::web; use async_trait::async_trait; use derive_more::derive::{From, Into}; use rustical_dav::extensions::{ - DavPushExtension, DavPushExtensionProp, SyncTokenExtension, SyncTokenExtensionProp, + CommonPropertiesExtension, CommonPropertiesProp, DavPushExtension, DavPushExtensionProp, + SyncTokenExtension, SyncTokenExtensionProp, }; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{Resource, ResourceService}; @@ -63,6 +64,7 @@ pub enum AddressbookPropWrapper { Addressbook(AddressbookProp), SyncToken(SyncTokenExtensionProp), DavPush(DavPushExtensionProp), + Common(CommonPropertiesProp), } #[derive(Clone, Debug, From, Into)] @@ -95,8 +97,8 @@ impl Resource for AddressbookResource { fn get_prop( &self, - _rmap: &ResourceMap, - _user: &User, + rmap: &ResourceMap, + user: &User, prop: &Self::PropName, ) -> Result { Ok(match prop { @@ -129,6 +131,9 @@ impl Resource for AddressbookResource { AddressbookPropWrapperName::DavPush(prop) => { AddressbookPropWrapper::DavPush(::get_prop(self, prop)?) } + AddressbookPropWrapperName::Common(prop) => AddressbookPropWrapper::Common( + CommonPropertiesExtension::get_prop(self, rmap, user, prop)?, + ), }) } @@ -148,12 +153,9 @@ impl Resource for AddressbookResource { AddressbookProp::SupportedReportSet(_) => Err(rustical_dav::Error::PropReadOnly), AddressbookProp::SupportedAddressData(_) => Err(rustical_dav::Error::PropReadOnly), }, - AddressbookPropWrapper::SyncToken(prop) => { - ::set_prop(self, prop) - } - AddressbookPropWrapper::DavPush(prop) => { - ::set_prop(self, prop) - } + AddressbookPropWrapper::SyncToken(prop) => SyncTokenExtension::set_prop(self, prop), + AddressbookPropWrapper::DavPush(prop) => DavPushExtension::set_prop(self, prop), + AddressbookPropWrapper::Common(prop) => CommonPropertiesExtension::set_prop(self, prop), } } @@ -174,10 +176,11 @@ impl Resource for AddressbookResource { AddressbookPropName::SupportedAddressData => Err(rustical_dav::Error::PropReadOnly), }, AddressbookPropWrapperName::SyncToken(prop) => { - ::remove_prop(self, prop) + SyncTokenExtension::remove_prop(self, prop) } - AddressbookPropWrapperName::DavPush(prop) => { - ::remove_prop(self, prop) + AddressbookPropWrapperName::DavPush(prop) => DavPushExtension::remove_prop(self, prop), + AddressbookPropWrapperName::Common(prop) => { + CommonPropertiesExtension::remove_prop(self, prop) } } } diff --git a/crates/carddav/src/principal/mod.rs b/crates/carddav/src/principal/mod.rs index f3adf3f..1ba29c4 100644 --- a/crates/carddav/src/principal/mod.rs +++ b/crates/carddav/src/principal/mod.rs @@ -2,6 +2,7 @@ use crate::addressbook::resource::AddressbookResource; use crate::Error; use actix_web::dev::ResourceMap; use async_trait::async_trait; +use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{NamedRoute, Resource, ResourceService}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; @@ -43,6 +44,13 @@ pub enum PrincipalProp { PrincipalAddress(Option), } +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)] +#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)] +pub enum PrincipalPropWrapper { + Principal(PrincipalProp), + Common(CommonPropertiesProp), +} + impl PrincipalResource { pub fn get_principal_url(rmap: &ResourceMap, principal: &str) -> String { Self::get_url(rmap, vec![principal]).unwrap() @@ -56,8 +64,8 @@ impl NamedRoute for PrincipalResource { } impl Resource for PrincipalResource { - type PropName = PrincipalPropName; - type Prop = PrincipalProp; + type PropName = PrincipalPropWrapperName; + type Prop = PrincipalPropWrapper; type Error = Error; type PrincipalResource = PrincipalResource; @@ -71,18 +79,28 @@ impl Resource for PrincipalResource { fn get_prop( &self, rmap: &ResourceMap, - _user: &User, + user: &User, prop: &Self::PropName, ) -> Result { let principal_href = HrefElement::new(Self::get_principal_url(rmap, &self.principal)); Ok(match prop { - PrincipalPropName::Displayname => PrincipalProp::Displayname(self.principal.to_owned()), - PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_href), - PrincipalPropName::AddressbookHomeSet => { - PrincipalProp::AddressbookHomeSet(principal_href) + PrincipalPropWrapperName::Principal(prop) => { + PrincipalPropWrapper::Principal(match prop { + PrincipalPropName::Displayname => { + PrincipalProp::Displayname(self.principal.to_owned()) + } + PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_href), + PrincipalPropName::AddressbookHomeSet => { + PrincipalProp::AddressbookHomeSet(principal_href) + } + PrincipalPropName::PrincipalAddress => PrincipalProp::PrincipalAddress(None), + }) } - PrincipalPropName::PrincipalAddress => PrincipalProp::PrincipalAddress(None), + + PrincipalPropWrapperName::Common(prop) => PrincipalPropWrapper::Common( + CommonPropertiesExtension::get_prop(self, rmap, user, prop)?, + ), }) } diff --git a/crates/dav/Cargo.toml b/crates/dav/Cargo.toml index 17dc258..ed382cd 100644 --- a/crates/dav/Cargo.toml +++ b/crates/dav/Cargo.toml @@ -14,7 +14,6 @@ futures-util = { workspace = true } quick-xml = { workspace = true } rustical_store = { workspace = true } serde = { workspace = true } -strum = { workspace = true } thiserror = { workspace = true } itertools = { workspace = true } log = { workspace = true } diff --git a/crates/dav/src/extensions/common.rs b/crates/dav/src/extensions/common.rs new file mode 100644 index 0000000..c30f415 --- /dev/null +++ b/crates/dav/src/extensions/common.rs @@ -0,0 +1,70 @@ +use crate::{ + privileges::UserPrivilegeSet, + resource::{NamedRoute, Resource}, + xml::{HrefElement, Resourcetype}, +}; +use actix_web::dev::ResourceMap; +use rustical_store::auth::User; +use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; + +#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)] +#[xml(unit_variants_ident = "CommonPropertiesPropName")] +pub enum CommonPropertiesProp { + // WebDAV (RFC 2518) + #[xml(skip_deserializing)] + #[xml(ns = "crate::namespace::NS_DAV")] + Resourcetype(Resourcetype), + + // WebDAV Current Principal Extension (RFC 5397) + #[xml(ns = "crate::namespace::NS_DAV")] + CurrentUserPrincipal(HrefElement), + + // WebDAV Access Control Protocol (RFC 3477) + #[xml(skip_deserializing)] + #[xml(ns = "crate::namespace::NS_DAV")] + CurrentUserPrivilegeSet(UserPrivilegeSet), + #[xml(ns = "crate::namespace::NS_DAV")] + Owner(Option), +} + +pub trait CommonPropertiesExtension: Resource { + fn get_prop( + &self, + rmap: &ResourceMap, + user: &User, + prop: &CommonPropertiesPropName, + ) -> Result::Error> { + Ok(match prop { + CommonPropertiesPropName::Resourcetype => { + CommonPropertiesProp::Resourcetype(self.get_resourcetype()) + } + CommonPropertiesPropName::CurrentUserPrincipal => { + CommonPropertiesProp::CurrentUserPrincipal( + Self::PrincipalResource::get_url(rmap, [&user.id]) + .unwrap() + .into(), + ) + } + CommonPropertiesPropName::CurrentUserPrivilegeSet => { + CommonPropertiesProp::CurrentUserPrivilegeSet(self.get_user_privileges(user)?) + } + CommonPropertiesPropName::Owner => { + CommonPropertiesProp::Owner(self.get_owner().map(|owner| { + Self::PrincipalResource::get_url(rmap, [owner]) + .unwrap() + .into() + })) + } + }) + } + + fn set_prop(&self, _prop: CommonPropertiesProp) -> Result<(), crate::Error> { + Err(crate::Error::PropReadOnly) + } + + fn remove_prop(&self, _prop: &CommonPropertiesPropName) -> Result<(), crate::Error> { + Err(crate::Error::PropReadOnly) + } +} + +impl CommonPropertiesExtension for T {} diff --git a/crates/dav/src/extensions/mod.rs b/crates/dav/src/extensions/mod.rs index 3813280..b711782 100644 --- a/crates/dav/src/extensions/mod.rs +++ b/crates/dav/src/extensions/mod.rs @@ -1,5 +1,7 @@ +mod common; mod davpush; mod synctoken; +pub use common::*; pub use davpush::*; pub use synctoken::*; diff --git a/crates/dav/src/resource/methods/propfind.rs b/crates/dav/src/resource/methods/propfind.rs index 47fd520..1a54ae2 100644 --- a/crates/dav/src/resource/methods/propfind.rs +++ b/crates/dav/src/resource/methods/propfind.rs @@ -1,7 +1,5 @@ use crate::depth_header::Depth; use crate::privileges::UserPrivilege; -use crate::resource::CommonPropertiesProp; -use crate::resource::EitherProp; use crate::resource::Resource; use crate::resource::ResourceService; use crate::xml::MultistatusElement; @@ -28,10 +26,7 @@ pub(crate) async fn route_propfind( root_span: RootSpan, resource_service: Data, ) -> Result< - MultistatusElement< - EitherProp<::Prop, CommonPropertiesProp>, - EitherProp<::Prop, CommonPropertiesProp>, - >, + MultistatusElement<::Prop, ::Prop>, R::Error, > { let resource = resource_service.get_resource(&path).await?; diff --git a/crates/dav/src/resource/mod.rs b/crates/dav/src/resource/mod.rs index f5f5a84..02ff5c0 100644 --- a/crates/dav/src/resource/mod.rs +++ b/crates/dav/src/resource/mod.rs @@ -1,7 +1,7 @@ use crate::privileges::UserPrivilegeSet; use crate::xml::multistatus::{PropTagWrapper, PropstatElement, PropstatWrapper}; +use crate::xml::Resourcetype; use crate::xml::{multistatus::ResponseElement, TagList}; -use crate::xml::{HrefElement, Resourcetype}; use crate::Error; use actix_web::dev::ResourceMap; use actix_web::{http::StatusCode, ResponseError}; @@ -11,7 +11,6 @@ pub use resource_service::ResourceService; use rustical_store::auth::User; use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize}; use std::str::FromStr; -use strum::EnumString; mod methods; mod resource_service; @@ -24,41 +23,6 @@ impl ResourceProp for T {} pub trait ResourcePropName: FromStr {} impl ResourcePropName for T {} -#[derive(XmlDeserialize, XmlSerialize, PartialEq, EnumVariants)] -pub enum CommonPropertiesProp { - // WebDAV (RFC 2518) - #[xml(skip_deserializing)] - #[xml(ns = "crate::namespace::NS_DAV")] - Resourcetype(Resourcetype), - - // WebDAV Current Principal Extension (RFC 5397) - #[xml(ns = "crate::namespace::NS_DAV")] - CurrentUserPrincipal(HrefElement), - - // WebDAV Access Control Protocol (RFC 3477) - #[xml(skip_deserializing)] - #[xml(ns = "crate::namespace::NS_DAV")] - CurrentUserPrivilegeSet(UserPrivilegeSet), - #[xml(ns = "crate::namespace::NS_DAV")] - Owner(Option), -} - -#[derive(XmlSerialize)] -#[xml(untagged)] -pub enum EitherProp { - Left(Left), - Right(Right), -} - -#[derive(EnumString, Clone)] -#[strum(serialize_all = "kebab-case")] -pub enum CommonPropertiesPropName { - Resourcetype, - CurrentUserPrincipal, - CurrentUserPrivilegeSet, - Owner, -} - pub trait Resource: Clone + 'static { type PropName: ResourcePropName + From @@ -70,41 +34,7 @@ pub trait Resource: Clone + 'static { fn get_resourcetype(&self) -> Resourcetype; fn list_props() -> Vec<(Option>, &'static str)> { - [ - Self::Prop::variant_names(), - CommonPropertiesProp::variant_names(), - ] - .concat() - } - - fn get_internal_prop( - &self, - rmap: &ResourceMap, - user: &User, - prop: &CommonPropertiesPropName, - ) -> Result { - Ok(match prop { - CommonPropertiesPropName::Resourcetype => { - CommonPropertiesProp::Resourcetype(self.get_resourcetype()) - } - CommonPropertiesPropName::CurrentUserPrincipal => { - CommonPropertiesProp::CurrentUserPrincipal( - Self::PrincipalResource::get_url(rmap, [&user.id]) - .unwrap() - .into(), - ) - } - CommonPropertiesPropName::CurrentUserPrivilegeSet => { - CommonPropertiesProp::CurrentUserPrivilegeSet(self.get_user_privileges(user)?) - } - CommonPropertiesPropName::Owner => { - CommonPropertiesProp::Owner(self.get_owner().map(|owner| { - Self::PrincipalResource::get_url(rmap, [owner]) - .unwrap() - .into() - })) - } - }) + Self::Prop::variant_names() } fn get_prop( @@ -134,7 +64,7 @@ pub trait Resource: Clone + 'static { props: &[&str], user: &User, rmap: &ResourceMap, - ) -> Result>, Self::Error> { + ) -> Result, Self::Error> { let mut props = props.to_vec(); if props.contains(&"propname") { @@ -174,30 +104,19 @@ pub trait Resource: Clone + 'static { } let mut valid_props = vec![]; - let mut internal_props = vec![]; let mut invalid_props = vec![]; for prop in props { if let Ok(valid_prop) = Self::PropName::from_str(prop) { valid_props.push(valid_prop); - } else if let Ok(internal_prop) = CommonPropertiesPropName::from_str(prop) { - internal_props.push(internal_prop); } else { invalid_props.push(prop.to_string()) } } - let internal_prop_responses: Vec<_> = internal_props - .into_iter() - .map(|prop| self.get_internal_prop(rmap, user, &prop)) - .map_ok(EitherProp::Right) - .collect::>()?; - - let mut prop_responses = valid_props + let prop_responses = valid_props .into_iter() .map(|prop| self.get_prop(rmap, user, &prop)) - .map_ok(EitherProp::Left) .collect::, Self::Error>>()?; - prop_responses.extend(internal_prop_responses); let mut propstats = vec![PropstatWrapper::Normal(PropstatElement { status: StatusCode::OK, diff --git a/crates/dav/src/resources/root.rs b/crates/dav/src/resources/root.rs index 9079b19..ebef506 100644 --- a/crates/dav/src/resources/root.rs +++ b/crates/dav/src/resources/root.rs @@ -1,14 +1,13 @@ +use crate::extensions::{ + CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName, +}; use crate::privileges::UserPrivilegeSet; use crate::resource::{NamedRoute, Resource, ResourceService}; use crate::xml::{Resourcetype, ResourcetypeInner}; use actix_web::dev::ResourceMap; use async_trait::async_trait; -use quick_xml::name::Namespace; use rustical_store::auth::User; -use rustical_xml::{EnumVariants, XmlDeserialize, XmlSerialize}; -use serde::Serialize; use std::marker::PhantomData; -use strum::{EnumString, IntoStaticStr}; #[derive(Clone)] pub struct RootResource(PhantomData); @@ -19,28 +18,9 @@ impl Default for RootResource { } } -#[derive(EnumString, Clone, IntoStaticStr)] -#[strum(serialize_all = "kebab-case")] -pub enum RootResourcePropName {} - -impl From for (Option>, &'static str) { - fn from(_value: RootResourcePropName) -> Self { - (None, "unreachable") - } -} - -#[derive(XmlDeserialize, XmlSerialize, Serialize, Clone, PartialEq, EnumVariants)] -pub enum RootResourceProp {} - -impl From for RootResourcePropName { - fn from(_value: RootResourceProp) -> Self { - unreachable!() - } -} - impl Resource for RootResource { - type PropName = RootResourcePropName; - type Prop = RootResourceProp; + type PropName = CommonPropertiesPropName; + type Prop = CommonPropertiesProp; type Error = PR::Error; type PrincipalResource = PR; @@ -53,11 +33,11 @@ impl Resource for RootResource { fn get_prop( &self, - _rmap: &ResourceMap, - _user: &User, - _prop: &Self::PropName, + rmap: &ResourceMap, + user: &User, + prop: &Self::PropName, ) -> Result { - unreachable!("we shouldn't end up here") + CommonPropertiesExtension::get_prop(self, rmap, user, prop) } fn get_user_privileges(&self, _user: &User) -> Result {