diff --git a/crates/caldav/src/lib.rs b/crates/caldav/src/lib.rs index 93aa7ea..99989a7 100644 --- a/crates/caldav/src/lib.rs +++ b/crates/caldav/src/lib.rs @@ -5,9 +5,9 @@ use actix_web::web::{self, Data}; use calendar::resource::CalendarResourceService; use calendar_object::resource::CalendarObjectResourceService; use futures_util::FutureExt; -use principal::PrincipalResourceService; -use root::RootResourceService; +use principal::{PrincipalResource, PrincipalResourceService}; use rustical_dav::resource::ResourceService; +use rustical_dav::resources::RootResourceService; use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider}; use rustical_store::CalendarStore; use std::sync::Arc; @@ -16,7 +16,6 @@ pub mod calendar; pub mod calendar_object; pub mod error; pub mod principal; -pub mod root; pub use error::Error; @@ -55,7 +54,7 @@ pub fn configure_dav( }) }) .app_data(Data::from(store.clone())) - .service(RootResourceService::actix_resource()) + .service(RootResourceService::::actix_resource()) .service( web::scope("/user").service( web::scope("/{principal}") diff --git a/crates/caldav/src/root/mod.rs b/crates/caldav/src/root/mod.rs deleted file mode 100644 index b18001f..0000000 --- a/crates/caldav/src/root/mod.rs +++ /dev/null @@ -1,110 +0,0 @@ -use crate::principal::PrincipalResource; -use crate::Error; -use actix_web::dev::ResourceMap; -use actix_web::HttpRequest; -use async_trait::async_trait; -use derive_more::derive::{From, TryInto}; -use rustical_dav::extension::BoxedExtension; -use rustical_dav::extensions::{ - CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName, -}; -use rustical_dav::privileges::UserPrivilegeSet; -use rustical_dav::resource::{InvalidProperty, Resource, ResourceService}; -use rustical_store::auth::User; -use serde::{Deserialize, Serialize}; -use strum::{EnumString, VariantNames}; - -#[derive(EnumString, VariantNames, Clone, From, TryInto)] -#[strum(serialize_all = "kebab-case")] -pub enum RootPropName { - Resourcetype, - #[from] - #[try_into] - #[strum(disabled)] - ExtCommonProperties(CommonPropertiesPropName), -} - -#[derive(Deserialize, Serialize, Default, Debug)] -#[serde(rename_all = "kebab-case")] -pub struct Resourcetype { - collection: (), -} - -#[derive(Deserialize, Serialize, From, TryInto)] -#[serde(rename_all = "kebab-case")] -pub enum RootProp { - // WebDAV (RFC 2518) - Resourcetype(Resourcetype), - - #[serde(skip_deserializing, untagged)] - #[from] - #[try_into] - ExtCommonProperties(CommonPropertiesProp), - - #[serde(untagged)] - Invalid, -} - -impl InvalidProperty for RootProp { - fn invalid_property(&self) -> bool { - matches!(self, Self::Invalid) - } -} - -#[derive(Clone)] -pub struct RootResource; - -impl Resource for RootResource { - type PropName = RootPropName; - type Prop = RootProp; - type Error = Error; - type ResourceType = Resourcetype; - - fn list_extensions() -> Vec> { - vec![BoxedExtension::from_ext(CommonPropertiesExtension::< - PrincipalResource, - >::default())] - } - - fn get_prop( - &self, - _rmap: &ResourceMap, - _user: &User, - prop: &Self::PropName, - ) -> Result { - Ok(match prop { - RootPropName::Resourcetype => RootProp::Resourcetype(Resourcetype::default()), - _ => panic!("we shouldn't end up here"), - }) - } - - #[inline] - fn resource_name() -> &'static str { - "caldav_root" - } - - fn get_user_privileges(&self, _user: &User) -> Result { - Ok(UserPrivilegeSet::all()) - } -} - -pub struct RootResourceService; - -#[async_trait(?Send)] -impl ResourceService for RootResourceService { - type PathComponents = (); - type MemberType = RootResource; - type Resource = RootResource; - type Error = Error; - - async fn new( - _req: &HttpRequest, - _path_components: Self::PathComponents, - ) -> Result { - Ok(Self) - } - - async fn get_resource(&self) -> Result { - Ok(RootResource) - } -} diff --git a/crates/carddav/src/lib.rs b/crates/carddav/src/lib.rs index bc750bd..b243ed9 100644 --- a/crates/carddav/src/lib.rs +++ b/crates/carddav/src/lib.rs @@ -10,9 +10,9 @@ use address_object::resource::AddressObjectResourceService; use addressbook::resource::AddressbookResourceService; pub use error::Error; use futures_util::FutureExt; -use principal::PrincipalResourceService; -use root::RootResourceService; +use principal::{PrincipalResource, PrincipalResourceService}; use rustical_dav::resource::ResourceService; +use rustical_dav::resources::RootResourceService; use rustical_store::{ auth::{AuthenticationMiddleware, AuthenticationProvider}, AddressbookStore, @@ -23,7 +23,6 @@ pub mod address_object; pub mod addressbook; pub mod error; pub mod principal; -pub mod root; pub fn configure_well_known(cfg: &mut web::ServiceConfig, carddav_root: String) { cfg.service(web::redirect("/carddav", carddav_root).permanent()); @@ -60,7 +59,7 @@ pub fn configure_dav( }) }) .app_data(Data::from(store.clone())) - .service(RootResourceService::actix_resource()) + .service(RootResourceService::::actix_resource()) .service( web::scope("/user").service( web::scope("/{principal}") diff --git a/crates/carddav/src/root/mod.rs b/crates/carddav/src/root/mod.rs deleted file mode 100644 index 0d513e2..0000000 --- a/crates/carddav/src/root/mod.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::principal::PrincipalResource; -use crate::Error; -use actix_web::dev::ResourceMap; -use actix_web::HttpRequest; -use async_trait::async_trait; -use derive_more::{From, TryInto}; -use rustical_dav::extension::BoxedExtension; -use rustical_dav::extensions::{ - CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName, -}; -use rustical_dav::privileges::UserPrivilegeSet; -use rustical_dav::resource::{InvalidProperty, Resource, ResourceService}; -use rustical_store::auth::User; -use serde::{Deserialize, Serialize}; -use strum::{EnumString, VariantNames}; - -#[derive(EnumString, VariantNames, Clone, From, TryInto)] -#[strum(serialize_all = "kebab-case")] -pub enum RootPropName { - #[from] - #[try_into] - #[strum(disabled)] - ExtCommonProperties(CommonPropertiesPropName), -} - -#[derive(Deserialize, Serialize, Default, Debug)] -#[serde(rename_all = "kebab-case")] -pub struct Resourcetype { - collection: (), -} - -#[derive(Deserialize, Serialize, From, TryInto)] -#[serde(rename_all = "kebab-case")] -pub enum RootProp { - #[serde(skip_deserializing, untagged)] - #[from] - #[try_into] - ExtCommonProperties(CommonPropertiesProp), - - #[serde(untagged)] - Invalid, -} - -impl InvalidProperty for RootProp { - fn invalid_property(&self) -> bool { - matches!(self, Self::Invalid) - } -} - -#[derive(Clone)] -pub struct RootResource; - -impl Resource for RootResource { - type PropName = RootPropName; - type Prop = RootProp; - type Error = Error; - type ResourceType = Resourcetype; - - fn list_extensions() -> Vec> { - vec![BoxedExtension::from_ext(CommonPropertiesExtension::< - PrincipalResource, - >::default())] - } - - fn get_prop( - &self, - _rmap: &ResourceMap, - _user: &User, - _prop: &Self::PropName, - ) -> Result { - panic!("we shouldn't end up here") - } - - #[inline] - fn resource_name() -> &'static str { - "carddav_root" - } - - fn get_user_privileges(&self, _user: &User) -> Result { - Ok(UserPrivilegeSet::all()) - } -} - -pub struct RootResourceService; - -#[async_trait(?Send)] -impl ResourceService for RootResourceService { - type PathComponents = (); - type MemberType = PrincipalResource; - type Resource = RootResource; - type Error = Error; - - async fn new( - _req: &HttpRequest, - _path_components: Self::PathComponents, - ) -> Result { - Ok(Self) - } - - async fn get_resource(&self) -> Result { - Ok(RootResource) - } -} diff --git a/crates/dav/src/lib.rs b/crates/dav/src/lib.rs index 8b2b771..38ee9c0 100644 --- a/crates/dav/src/lib.rs +++ b/crates/dav/src/lib.rs @@ -6,6 +6,7 @@ pub mod methods; pub mod namespace; pub mod privileges; pub mod resource; +pub mod resources; pub mod xml; pub use error::Error; diff --git a/crates/dav/src/resource.rs b/crates/dav/src/resource.rs index 48f72c5..3f79a8f 100644 --- a/crates/dav/src/resource.rs +++ b/crates/dav/src/resource.rs @@ -26,7 +26,7 @@ impl Deserialize<'de>> ResourceProp for T {} pub trait ResourcePropName: FromStr + VariantNames {} impl ResourcePropName for T {} -pub trait Resource: Clone { +pub trait Resource: Clone + 'static { type PropName: ResourcePropName; type Prop: ResourceProp; type Error: ResponseError + From; @@ -132,6 +132,14 @@ pub trait Resource: Clone { } } + let mut prop_responses = Vec::new(); + + for extension in Self::list_extensions() { + let (ext_invalid_props, ext_responses) = extension.propfind(self, props, user, rmap)?; + props = ext_invalid_props; + prop_responses.extend(ext_responses); + } + let (valid_props, invalid_props): (Vec>, Vec>) = props .into_iter() .map(|prop| { @@ -143,19 +151,14 @@ pub trait Resource: Clone { }) .unzip(); let valid_props: Vec = valid_props.into_iter().flatten().collect(); - let mut invalid_props: Vec<&str> = invalid_props.into_iter().flatten().collect(); + let invalid_props: Vec<&str> = invalid_props.into_iter().flatten().collect(); - let mut prop_responses = valid_props - .into_iter() - .map(|prop| self.get_prop(rmap, user, &prop)) - .collect::, Self::Error>>()?; - - for extension in Self::list_extensions() { - let (ext_invalid_props, ext_responses) = - extension.propfind(self, invalid_props, user, rmap)?; - invalid_props = ext_invalid_props; - prop_responses.extend(ext_responses); - } + prop_responses.extend( + valid_props + .into_iter() + .map(|prop| self.get_prop(rmap, user, &prop)) + .collect::, Self::Error>>()?, + ); let mut propstats = vec![PropstatWrapper::Normal(PropstatElement { status: format!("HTTP/1.1 {}", StatusCode::OK), diff --git a/crates/dav/src/resources/mod.rs b/crates/dav/src/resources/mod.rs new file mode 100644 index 0000000..985820b --- /dev/null +++ b/crates/dav/src/resources/mod.rs @@ -0,0 +1,3 @@ +pub mod root; + +pub use root::{RootResource, RootResourceService}; diff --git a/crates/dav/src/resources/root.rs b/crates/dav/src/resources/root.rs new file mode 100644 index 0000000..d4be1cc --- /dev/null +++ b/crates/dav/src/resources/root.rs @@ -0,0 +1,82 @@ +use std::any::type_name; +use std::marker::PhantomData; + +use crate::extension::BoxedExtension; +use crate::extensions::{ + CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName, +}; +use crate::privileges::UserPrivilegeSet; +use crate::resource::{Resource, ResourceService}; +use actix_web::dev::ResourceMap; +use actix_web::HttpRequest; +use async_trait::async_trait; +use rustical_store::auth::User; +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Default, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct Resourcetype { + collection: (), +} + +#[derive(Clone)] +pub struct RootResource(PhantomData); + +impl Default for RootResource { + fn default() -> Self { + Self(Default::default()) + } +} + +impl Resource for RootResource { + type PropName = CommonPropertiesPropName; + type Prop = CommonPropertiesProp>; + type Error = PR::Error; + type ResourceType = Resourcetype; + + fn list_extensions() -> Vec> { + vec![BoxedExtension::from_ext( + CommonPropertiesExtension::::default(), + )] + } + + fn get_prop( + &self, + _rmap: &ResourceMap, + _user: &User, + _prop: &Self::PropName, + ) -> Result { + panic!("we shouldn't end up here") + } + + #[inline] + fn resource_name() -> &'static str { + type_name::() + } + + fn get_user_privileges(&self, _user: &User) -> Result { + Ok(UserPrivilegeSet::all()) + } +} + +#[derive(Default)] +pub struct RootResourceService(PhantomData); + +#[async_trait(?Send)] +impl ResourceService for RootResourceService { + type PathComponents = (); + type MemberType = PR; + type Resource = RootResource; + type Error = PR::Error; + + async fn new( + _req: &HttpRequest, + _path_components: Self::PathComponents, + ) -> Result { + Ok(Self(Default::default())) + } + + async fn get_resource(&self) -> Result { + Ok(RootResource::::default()) + } +}