mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 21:42:34 +00:00
Move properties into separate files
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
use crate::calendar::CalendarResourceService;
|
||||||
use crate::calendar::prop::SupportedCalendarComponentSet;
|
use crate::calendar::prop::SupportedCalendarComponentSet;
|
||||||
use crate::calendar::resource::CalendarResourceService;
|
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, State};
|
||||||
use axum::response::{IntoResponse, Response};
|
use axum::response::{IntoResponse, Response};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
|
use crate::{Error, calendar_object::CalendarObjectPropWrapperName};
|
||||||
use rustical_dav::xml::PropfindType;
|
use rustical_dav::xml::PropfindType;
|
||||||
use rustical_ical::CalendarObject;
|
use rustical_ical::CalendarObject;
|
||||||
use rustical_store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
|
use crate::{Error, calendar_object::CalendarObjectPropWrapperName};
|
||||||
use rustical_dav::xml::PropfindType;
|
use rustical_dav::xml::PropfindType;
|
||||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||||
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};
|
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
CalDavPrincipalUri, Error,
|
CalDavPrincipalUri, Error,
|
||||||
calendar::resource::CalendarResourceService,
|
calendar::CalendarResourceService,
|
||||||
calendar_object::resource::{
|
calendar_object::{
|
||||||
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
|
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, resource::CalendarObjectResource,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
@@ -147,7 +147,7 @@ pub async fn route_report_calendar<C: CalendarStore, S: SubscriptionStore>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::calendar_object::resource::{CalendarData, CalendarObjectPropName, ExpandElement};
|
use crate::calendar_object::{CalendarData, CalendarObjectPropName, ExpandElement};
|
||||||
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
|
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
|
||||||
use rustical_dav::xml::PropElement;
|
use rustical_dav::xml::PropElement;
|
||||||
use rustical_ical::UtcDateTime;
|
use rustical_ical::UtcDateTime;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
calendar_object::resource::{
|
calendar_object::{
|
||||||
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
|
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, resource::CalendarObjectResource,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
pub mod methods;
|
pub mod methods;
|
||||||
pub mod prop;
|
pub mod prop;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
|
mod service;
|
||||||
|
|
||||||
|
pub use service::CalendarResourceService;
|
||||||
|
|||||||
@@ -1,32 +1,20 @@
|
|||||||
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet};
|
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet};
|
||||||
use crate::calendar::methods::mkcalendar::route_mkcalendar;
|
use crate::Error;
|
||||||
use crate::calendar::methods::report::route_report_calendar;
|
|
||||||
use crate::calendar_object::resource::{CalendarObjectResource, CalendarObjectResourceService};
|
|
||||||
use crate::{CalDavPrincipalUri, Error};
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use axum::Router;
|
|
||||||
use axum::extract::Request;
|
|
||||||
use axum::handler::Handler;
|
|
||||||
use axum::response::Response;
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use derive_more::derive::{From, Into};
|
use derive_more::derive::{From, Into};
|
||||||
use futures_util::future::BoxFuture;
|
|
||||||
use rustical_dav::extensions::{
|
use rustical_dav::extensions::{
|
||||||
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
|
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
|
||||||
};
|
};
|
||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
use rustical_dav::privileges::UserPrivilegeSet;
|
||||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_dav_push::DavPushExtension;
|
use rustical_dav_push::DavPushExtension;
|
||||||
use rustical_ical::CalDateTime;
|
use rustical_ical::CalDateTime;
|
||||||
|
use rustical_store::Calendar;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
|
||||||
use rustical_xml::{EnumVariants, PropName};
|
use rustical_xml::{EnumVariants, PropName};
|
||||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||||
use std::convert::Infallible;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
|
||||||
use tower::Service;
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
#[xml(unit_variants_ident = "CalendarPropName")]
|
#[xml(unit_variants_ident = "CalendarPropName")]
|
||||||
@@ -317,113 +305,3 @@ impl Resource for CalendarResource {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CalendarResourceService<C: CalendarStore, S: SubscriptionStore> {
|
|
||||||
pub(crate) cal_store: Arc<C>,
|
|
||||||
pub(crate) sub_store: Arc<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarResourceService<C, S> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
cal_store: self.cal_store.clone(),
|
|
||||||
sub_store: self.sub_store.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> CalendarResourceService<C, S> {
|
|
||||||
pub fn new(cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
|
||||||
Self {
|
|
||||||
cal_store,
|
|
||||||
sub_store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourceService<C, S> {
|
|
||||||
type MemberType = CalendarObjectResource;
|
|
||||||
type PathComponents = (String, String); // principal, calendar_id
|
|
||||||
type Resource = CalendarResource;
|
|
||||||
type Error = Error;
|
|
||||||
type Principal = User;
|
|
||||||
type PrincipalUri = CalDavPrincipalUri;
|
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
|
||||||
|
|
||||||
async fn get_resource(
|
|
||||||
&self,
|
|
||||||
(principal, cal_id): &Self::PathComponents,
|
|
||||||
) -> Result<Self::Resource, Error> {
|
|
||||||
let calendar = self.cal_store.get_calendar(principal, cal_id).await?;
|
|
||||||
Ok(CalendarResource {
|
|
||||||
cal: calendar,
|
|
||||||
read_only: self.cal_store.is_read_only(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(
|
|
||||||
&self,
|
|
||||||
(principal, cal_id): &Self::PathComponents,
|
|
||||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
|
||||||
Ok(self
|
|
||||||
.cal_store
|
|
||||||
.get_objects(principal, cal_id)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.map(|object| CalendarObjectResource {
|
|
||||||
object,
|
|
||||||
principal: principal.to_owned(),
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn save_resource(
|
|
||||||
&self,
|
|
||||||
(principal, cal_id): &Self::PathComponents,
|
|
||||||
file: Self::Resource,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.cal_store
|
|
||||||
.update_calendar(principal.to_owned(), cal_id.to_owned(), file.into())
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_resource(
|
|
||||||
&self,
|
|
||||||
(principal, cal_id): &Self::PathComponents,
|
|
||||||
use_trashbin: bool,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.cal_store
|
|
||||||
.delete_calendar(principal, cal_id, use_trashbin)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
|
||||||
Router::new()
|
|
||||||
.nest(
|
|
||||||
"/{object_id}",
|
|
||||||
CalendarObjectResourceService::new(self.cal_store.clone()).axum_router(),
|
|
||||||
)
|
|
||||||
.route_service("/", self.axum_service())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> {
|
|
||||||
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(route_report_calendar::<C, S>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mkcalendar() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>>
|
|
||||||
{
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(route_mkcalendar::<C, S>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
128
crates/caldav/src/calendar/service.rs
Normal file
128
crates/caldav/src/calendar/service.rs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
use crate::calendar::methods::mkcalendar::route_mkcalendar;
|
||||||
|
use crate::calendar::methods::report::route_report_calendar;
|
||||||
|
use crate::calendar::resource::CalendarResource;
|
||||||
|
use crate::calendar_object::CalendarObjectResourceService;
|
||||||
|
use crate::calendar_object::resource::CalendarObjectResource;
|
||||||
|
use crate::{CalDavPrincipalUri, Error};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::Router;
|
||||||
|
use axum::extract::Request;
|
||||||
|
use axum::handler::Handler;
|
||||||
|
use axum::response::Response;
|
||||||
|
use futures_util::future::BoxFuture;
|
||||||
|
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||||
|
use rustical_store::auth::User;
|
||||||
|
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||||
|
use std::convert::Infallible;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tower::Service;
|
||||||
|
|
||||||
|
pub struct CalendarResourceService<C: CalendarStore, S: SubscriptionStore> {
|
||||||
|
pub(crate) cal_store: Arc<C>,
|
||||||
|
pub(crate) sub_store: Arc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarResourceService<C, S> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
cal_store: self.cal_store.clone(),
|
||||||
|
sub_store: self.sub_store.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> CalendarResourceService<C, S> {
|
||||||
|
pub fn new(cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
||||||
|
Self {
|
||||||
|
cal_store,
|
||||||
|
sub_store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourceService<C, S> {
|
||||||
|
type MemberType = CalendarObjectResource;
|
||||||
|
type PathComponents = (String, String); // principal, calendar_id
|
||||||
|
type Resource = CalendarResource;
|
||||||
|
type Error = Error;
|
||||||
|
type Principal = User;
|
||||||
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
|
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||||
|
|
||||||
|
async fn get_resource(
|
||||||
|
&self,
|
||||||
|
(principal, cal_id): &Self::PathComponents,
|
||||||
|
) -> Result<Self::Resource, Error> {
|
||||||
|
let calendar = self.cal_store.get_calendar(principal, cal_id).await?;
|
||||||
|
Ok(CalendarResource {
|
||||||
|
cal: calendar,
|
||||||
|
read_only: self.cal_store.is_read_only(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
(principal, cal_id): &Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||||
|
Ok(self
|
||||||
|
.cal_store
|
||||||
|
.get_objects(principal, cal_id)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|object| CalendarObjectResource {
|
||||||
|
object,
|
||||||
|
principal: principal.to_owned(),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save_resource(
|
||||||
|
&self,
|
||||||
|
(principal, cal_id): &Self::PathComponents,
|
||||||
|
file: Self::Resource,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.cal_store
|
||||||
|
.update_calendar(principal.to_owned(), cal_id.to_owned(), file.into())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_resource(
|
||||||
|
&self,
|
||||||
|
(principal, cal_id): &Self::PathComponents,
|
||||||
|
use_trashbin: bool,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.cal_store
|
||||||
|
.delete_calendar(principal, cal_id, use_trashbin)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||||
|
Router::new()
|
||||||
|
.nest(
|
||||||
|
"/{object_id}",
|
||||||
|
CalendarObjectResourceService::new(self.cal_store.clone()).axum_router(),
|
||||||
|
)
|
||||||
|
.route_service("/", self.axum_service())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> {
|
||||||
|
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(route_report_calendar::<C, S>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mkcalendar() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>>
|
||||||
|
{
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(route_mkcalendar::<C, S>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
use super::resource::CalendarObjectPathComponents;
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::calendar_object::resource::CalendarObjectResourceService;
|
use crate::calendar_object::{CalendarObjectPathComponents, CalendarObjectResourceService};
|
||||||
use crate::error::Precondition;
|
use crate::error::Precondition;
|
||||||
use axum::body::Body;
|
use axum::body::Body;
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, State};
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
pub mod methods;
|
pub mod methods;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
|
mod service;
|
||||||
|
pub use service::*;
|
||||||
|
mod prop;
|
||||||
|
pub use prop::*;
|
||||||
|
|||||||
45
crates/caldav/src/calendar_object/prop.rs
Normal file
45
crates/caldav/src/calendar_object/prop.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use rustical_dav::extensions::CommonPropertiesProp;
|
||||||
|
use rustical_ical::UtcDateTime;
|
||||||
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "CalendarObjectPropName")]
|
||||||
|
pub enum CalendarObjectProp {
|
||||||
|
// WebDAV (RFC 2518)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
|
Getetag(String),
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
||||||
|
Getcontenttype(&'static str),
|
||||||
|
|
||||||
|
// CalDAV (RFC 4791)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
#[xml(prop = "CalendarData")]
|
||||||
|
CalendarData(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
|
||||||
|
pub enum CalendarObjectPropWrapper {
|
||||||
|
CalendarObject(CalendarObjectProp),
|
||||||
|
Common(CommonPropertiesProp),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) struct ExpandElement {
|
||||||
|
#[xml(ty = "attr")]
|
||||||
|
pub(crate) start: UtcDateTime,
|
||||||
|
#[xml(ty = "attr")]
|
||||||
|
pub(crate) end: UtcDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
|
||||||
|
pub struct CalendarData {
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) comp: Option<()>,
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) expand: Option<ExpandElement>,
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) limit_recurrence_set: Option<()>,
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
pub(crate) limit_freebusy_set: Option<()>,
|
||||||
|
}
|
||||||
@@ -1,66 +1,14 @@
|
|||||||
// use super::methods::{get_event, put_event};
|
use super::prop::*;
|
||||||
use crate::{
|
use crate::Error;
|
||||||
CalDavPrincipalUri, Error,
|
|
||||||
calendar_object::methods::{get_event, put_event},
|
|
||||||
};
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use axum::{extract::Request, handler::Handler, response::Response};
|
|
||||||
use derive_more::derive::{From, Into};
|
use derive_more::derive::{From, Into};
|
||||||
use futures_util::future::BoxFuture;
|
|
||||||
use rustical_dav::{
|
use rustical_dav::{
|
||||||
extensions::{CommonPropertiesExtension, CommonPropertiesProp},
|
extensions::CommonPropertiesExtension,
|
||||||
privileges::UserPrivilegeSet,
|
privileges::UserPrivilegeSet,
|
||||||
resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService},
|
resource::{PrincipalUri, Resource, ResourceName},
|
||||||
xml::Resourcetype,
|
xml::Resourcetype,
|
||||||
};
|
};
|
||||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
use rustical_ical::CalendarObject;
|
||||||
use rustical_store::{CalendarStore, auth::User};
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
|
||||||
use serde::{Deserialize, Deserializer};
|
|
||||||
use std::{convert::Infallible, sync::Arc};
|
|
||||||
use tower::Service;
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub(crate) struct ExpandElement {
|
|
||||||
#[xml(ty = "attr")]
|
|
||||||
pub(crate) start: UtcDateTime,
|
|
||||||
#[xml(ty = "attr")]
|
|
||||||
pub(crate) end: UtcDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
|
|
||||||
pub struct CalendarData {
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
pub(crate) comp: Option<()>,
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
pub(crate) expand: Option<ExpandElement>,
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
pub(crate) limit_recurrence_set: Option<()>,
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
pub(crate) limit_freebusy_set: Option<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "CalendarObjectPropName")]
|
|
||||||
pub enum CalendarObjectProp {
|
|
||||||
// WebDAV (RFC 2518)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
|
||||||
Getetag(String),
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
|
||||||
Getcontenttype(&'static str),
|
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
#[xml(prop = "CalendarData")]
|
|
||||||
CalendarData(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
|
|
||||||
pub enum CalendarObjectPropWrapper {
|
|
||||||
CalendarObject(CalendarObjectProp),
|
|
||||||
Common(CommonPropertiesProp),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, From, Into)]
|
#[derive(Clone, From, Into)]
|
||||||
pub struct CalendarObjectResource {
|
pub struct CalendarObjectResource {
|
||||||
@@ -130,101 +78,3 @@ impl Resource for CalendarObjectResource {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_ics_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let name: String = Deserialize::deserialize(deserializer)?;
|
|
||||||
if let Some(object_id) = name.strip_suffix(".ics") {
|
|
||||||
Ok(object_id.to_owned())
|
|
||||||
} else {
|
|
||||||
Err(serde::de::Error::custom("Missing .ics extension"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct CalendarObjectPathComponents {
|
|
||||||
pub principal: String,
|
|
||||||
pub calendar_id: String,
|
|
||||||
#[serde(deserialize_with = "deserialize_ics_name")]
|
|
||||||
pub object_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CalendarObjectResourceService<C: CalendarStore> {
|
|
||||||
pub(crate) cal_store: Arc<C>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore> Clone for CalendarObjectResourceService<C> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
cal_store: self.cal_store.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore> CalendarObjectResourceService<C> {
|
|
||||||
pub fn new(cal_store: Arc<C>) -> Self {
|
|
||||||
Self { cal_store }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
|
||||||
type PathComponents = CalendarObjectPathComponents;
|
|
||||||
type Resource = CalendarObjectResource;
|
|
||||||
type MemberType = CalendarObjectResource;
|
|
||||||
type Error = Error;
|
|
||||||
type Principal = User;
|
|
||||||
type PrincipalUri = CalDavPrincipalUri;
|
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
|
||||||
|
|
||||||
async fn get_resource(
|
|
||||||
&self,
|
|
||||||
CalendarObjectPathComponents {
|
|
||||||
principal,
|
|
||||||
calendar_id,
|
|
||||||
object_id,
|
|
||||||
}: &Self::PathComponents,
|
|
||||||
) -> Result<Self::Resource, Self::Error> {
|
|
||||||
let object = self
|
|
||||||
.cal_store
|
|
||||||
.get_object(principal, calendar_id, object_id)
|
|
||||||
.await?;
|
|
||||||
Ok(CalendarObjectResource {
|
|
||||||
object,
|
|
||||||
principal: principal.to_owned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_resource(
|
|
||||||
&self,
|
|
||||||
CalendarObjectPathComponents {
|
|
||||||
principal,
|
|
||||||
calendar_id,
|
|
||||||
object_id,
|
|
||||||
}: &Self::PathComponents,
|
|
||||||
use_trashbin: bool,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.cal_store
|
|
||||||
.delete_object(principal, calendar_id, object_id, use_trashbin)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore> AxumMethods for CalendarObjectResourceService<C> {
|
|
||||||
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(get_event::<C>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(put_event::<C>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
113
crates/caldav/src/calendar_object/service.rs
Normal file
113
crates/caldav/src/calendar_object/service.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
use crate::{
|
||||||
|
CalDavPrincipalUri, Error,
|
||||||
|
calendar_object::{
|
||||||
|
methods::{get_event, put_event},
|
||||||
|
resource::CalendarObjectResource,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::{extract::Request, handler::Handler, response::Response};
|
||||||
|
use futures_util::future::BoxFuture;
|
||||||
|
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||||
|
use rustical_store::{CalendarStore, auth::User};
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
use std::{convert::Infallible, sync::Arc};
|
||||||
|
use tower::Service;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct CalendarObjectPathComponents {
|
||||||
|
pub principal: String,
|
||||||
|
pub calendar_id: String,
|
||||||
|
#[serde(deserialize_with = "deserialize_ics_name")]
|
||||||
|
pub object_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CalendarObjectResourceService<C: CalendarStore> {
|
||||||
|
pub(crate) cal_store: Arc<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore> Clone for CalendarObjectResourceService<C> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
cal_store: self.cal_store.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore> CalendarObjectResourceService<C> {
|
||||||
|
pub fn new(cal_store: Arc<C>) -> Self {
|
||||||
|
Self { cal_store }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
||||||
|
type PathComponents = CalendarObjectPathComponents;
|
||||||
|
type Resource = CalendarObjectResource;
|
||||||
|
type MemberType = CalendarObjectResource;
|
||||||
|
type Error = Error;
|
||||||
|
type Principal = User;
|
||||||
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
|
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||||
|
|
||||||
|
async fn get_resource(
|
||||||
|
&self,
|
||||||
|
CalendarObjectPathComponents {
|
||||||
|
principal,
|
||||||
|
calendar_id,
|
||||||
|
object_id,
|
||||||
|
}: &Self::PathComponents,
|
||||||
|
) -> Result<Self::Resource, Self::Error> {
|
||||||
|
let object = self
|
||||||
|
.cal_store
|
||||||
|
.get_object(principal, calendar_id, object_id)
|
||||||
|
.await?;
|
||||||
|
Ok(CalendarObjectResource {
|
||||||
|
object,
|
||||||
|
principal: principal.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_resource(
|
||||||
|
&self,
|
||||||
|
CalendarObjectPathComponents {
|
||||||
|
principal,
|
||||||
|
calendar_id,
|
||||||
|
object_id,
|
||||||
|
}: &Self::PathComponents,
|
||||||
|
use_trashbin: bool,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.cal_store
|
||||||
|
.delete_object(principal, calendar_id, object_id, use_trashbin)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore> AxumMethods for CalendarObjectResourceService<C> {
|
||||||
|
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(get_event::<C>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(put_event::<C>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_ics_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let name: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
if let Some(object_id) = name.strip_suffix(".ics") {
|
||||||
|
Ok(object_id.to_owned())
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::custom("Missing .ics extension"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
use crate::calendar::resource::{CalendarResource, CalendarResourceService};
|
use crate::Error;
|
||||||
use crate::{CalDavPrincipalUri, Error};
|
use rustical_dav::extensions::CommonPropertiesExtension;
|
||||||
use async_trait::async_trait;
|
|
||||||
use axum::Router;
|
|
||||||
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
|
||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
use rustical_dav::privileges::UserPrivilegeSet;
|
||||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
|
||||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
mod service;
|
||||||
use std::sync::Arc;
|
pub use service::*;
|
||||||
|
mod prop;
|
||||||
|
pub use prop::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CalendarSetResource {
|
pub struct CalendarSetResource {
|
||||||
@@ -24,12 +23,6 @@ impl ResourceName for CalendarSetResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
|
||||||
pub enum PrincipalPropWrapper {
|
|
||||||
Common(CommonPropertiesProp),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resource for CalendarSetResource {
|
impl Resource for CalendarSetResource {
|
||||||
type Prop = PrincipalPropWrapper;
|
type Prop = PrincipalPropWrapper;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
@@ -67,77 +60,3 @@ impl Resource for CalendarSetResource {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CalendarSetResourceService<C: CalendarStore, S: SubscriptionStore> {
|
|
||||||
name: &'static str,
|
|
||||||
cal_store: Arc<C>,
|
|
||||||
sub_store: Arc<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarSetResourceService<C, S> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
name: self.name,
|
|
||||||
cal_store: self.cal_store.clone(),
|
|
||||||
sub_store: self.sub_store.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> CalendarSetResourceService<C, S> {
|
|
||||||
pub fn new(name: &'static str, cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
|
||||||
Self {
|
|
||||||
name,
|
|
||||||
cal_store,
|
|
||||||
sub_store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetResourceService<C, S> {
|
|
||||||
type PathComponents = (String,);
|
|
||||||
type MemberType = CalendarResource;
|
|
||||||
type Resource = CalendarSetResource;
|
|
||||||
type Error = Error;
|
|
||||||
type Principal = User;
|
|
||||||
type PrincipalUri = CalDavPrincipalUri;
|
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
|
|
||||||
|
|
||||||
async fn get_resource(
|
|
||||||
&self,
|
|
||||||
(principal,): &Self::PathComponents,
|
|
||||||
) -> Result<Self::Resource, Self::Error> {
|
|
||||||
Ok(CalendarSetResource {
|
|
||||||
principal: principal.to_owned(),
|
|
||||||
read_only: self.cal_store.is_read_only(),
|
|
||||||
name: self.name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(
|
|
||||||
&self,
|
|
||||||
(principal,): &Self::PathComponents,
|
|
||||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
|
||||||
let calendars = self.cal_store.get_calendars(principal).await?;
|
|
||||||
Ok(calendars
|
|
||||||
.into_iter()
|
|
||||||
.map(|cal| CalendarResource {
|
|
||||||
cal,
|
|
||||||
read_only: self.cal_store.is_read_only(),
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
|
||||||
Router::new()
|
|
||||||
.nest(
|
|
||||||
"/{calendar_id}",
|
|
||||||
CalendarResourceService::new(self.cal_store.clone(), self.sub_store.clone())
|
|
||||||
.axum_router(),
|
|
||||||
)
|
|
||||||
.route_service("/", self.axum_service())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {}
|
|
||||||
|
|||||||
8
crates/caldav/src/calendar_set/prop.rs
Normal file
8
crates/caldav/src/calendar_set/prop.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
use rustical_dav::extensions::CommonPropertiesProp;
|
||||||
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||||
|
pub enum PrincipalPropWrapper {
|
||||||
|
Common(CommonPropertiesProp),
|
||||||
|
}
|
||||||
84
crates/caldav/src/calendar_set/service.rs
Normal file
84
crates/caldav/src/calendar_set/service.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
use crate::calendar::CalendarResourceService;
|
||||||
|
use crate::calendar::resource::CalendarResource;
|
||||||
|
use crate::calendar_set::CalendarSetResource;
|
||||||
|
use crate::{CalDavPrincipalUri, Error};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::Router;
|
||||||
|
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||||
|
use rustical_store::auth::User;
|
||||||
|
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct CalendarSetResourceService<C: CalendarStore, S: SubscriptionStore> {
|
||||||
|
name: &'static str,
|
||||||
|
cal_store: Arc<C>,
|
||||||
|
sub_store: Arc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarSetResourceService<C, S> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
name: self.name,
|
||||||
|
cal_store: self.cal_store.clone(),
|
||||||
|
sub_store: self.sub_store.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> CalendarSetResourceService<C, S> {
|
||||||
|
pub fn new(name: &'static str, cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
cal_store,
|
||||||
|
sub_store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetResourceService<C, S> {
|
||||||
|
type PathComponents = (String,);
|
||||||
|
type MemberType = CalendarResource;
|
||||||
|
type Resource = CalendarSetResource;
|
||||||
|
type Error = Error;
|
||||||
|
type Principal = User;
|
||||||
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
|
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
|
||||||
|
|
||||||
|
async fn get_resource(
|
||||||
|
&self,
|
||||||
|
(principal,): &Self::PathComponents,
|
||||||
|
) -> Result<Self::Resource, Self::Error> {
|
||||||
|
Ok(CalendarSetResource {
|
||||||
|
principal: principal.to_owned(),
|
||||||
|
read_only: self.cal_store.is_read_only(),
|
||||||
|
name: self.name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
(principal,): &Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||||
|
let calendars = self.cal_store.get_calendars(principal).await?;
|
||||||
|
Ok(calendars
|
||||||
|
.into_iter()
|
||||||
|
.map(|cal| CalendarResource {
|
||||||
|
cal,
|
||||||
|
read_only: self.cal_store.is_read_only(),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||||
|
Router::new()
|
||||||
|
.nest(
|
||||||
|
"/{calendar_id}",
|
||||||
|
CalendarResourceService::new(self.cal_store.clone(), self.sub_store.clone())
|
||||||
|
.axum_router(),
|
||||||
|
)
|
||||||
|
.route_service("/", self.axum_service())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {}
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
use crate::calendar_set::{CalendarSetResource, CalendarSetResourceService};
|
use crate::Error;
|
||||||
use crate::{CalDavPrincipalUri, Error};
|
use rustical_dav::extensions::CommonPropertiesExtension;
|
||||||
use async_trait::async_trait;
|
|
||||||
use axum::Router;
|
|
||||||
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
|
||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
use rustical_dav::privileges::UserPrivilegeSet;
|
||||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::user::PrincipalType;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::auth::{AuthenticationProvider, User};
|
|
||||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
mod service;
|
||||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
pub use service::*;
|
||||||
use std::sync::Arc;
|
mod prop;
|
||||||
|
pub use prop::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PrincipalResource {
|
pub struct PrincipalResource {
|
||||||
@@ -24,37 +22,6 @@ impl ResourceName for PrincipalResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
|
||||||
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
|
||||||
pub enum PrincipalProp {
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
|
||||||
Displayname(String),
|
|
||||||
|
|
||||||
// Scheduling Extensions to CalDAV (RFC 6638)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)]
|
|
||||||
CalendarUserType(PrincipalType),
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
CalendarUserAddressSet(HrefElement),
|
|
||||||
|
|
||||||
// WebDAV Access Control (RFC 3744)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
|
|
||||||
PrincipalUrl(HrefElement),
|
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
|
||||||
CalendarHomeSet(CalendarHomeSet),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
|
||||||
pub enum PrincipalPropWrapper {
|
|
||||||
Principal(PrincipalProp),
|
|
||||||
Common(CommonPropertiesProp),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resource for PrincipalResource {
|
impl Resource for PrincipalResource {
|
||||||
type Prop = PrincipalPropWrapper;
|
type Prop = PrincipalPropWrapper;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
@@ -124,104 +91,3 @@ impl Resource for PrincipalResource {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PrincipalResourceService<
|
|
||||||
AP: AuthenticationProvider,
|
|
||||||
S: SubscriptionStore,
|
|
||||||
CS: CalendarStore,
|
|
||||||
BS: CalendarStore,
|
|
||||||
> {
|
|
||||||
pub(crate) auth_provider: Arc<AP>,
|
|
||||||
pub(crate) sub_store: Arc<S>,
|
|
||||||
pub(crate) cal_store: Arc<CS>,
|
|
||||||
pub(crate) birthday_store: Arc<BS>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore> Clone
|
|
||||||
for PrincipalResourceService<AP, S, CS, BS>
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
auth_provider: self.auth_provider.clone(),
|
|
||||||
sub_store: self.sub_store.clone(),
|
|
||||||
cal_store: self.cal_store.clone(),
|
|
||||||
birthday_store: self.birthday_store.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
|
||||||
ResourceService for PrincipalResourceService<AP, S, CS, BS>
|
|
||||||
{
|
|
||||||
type PathComponents = (String,);
|
|
||||||
type MemberType = CalendarSetResource;
|
|
||||||
type Resource = PrincipalResource;
|
|
||||||
type Error = Error;
|
|
||||||
type Principal = User;
|
|
||||||
type PrincipalUri = CalDavPrincipalUri;
|
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
|
||||||
|
|
||||||
async fn get_resource(
|
|
||||||
&self,
|
|
||||||
(principal,): &Self::PathComponents,
|
|
||||||
) -> Result<Self::Resource, Self::Error> {
|
|
||||||
let user = self
|
|
||||||
.auth_provider
|
|
||||||
.get_principal(principal)
|
|
||||||
.await?
|
|
||||||
.ok_or(crate::Error::NotFound)?;
|
|
||||||
Ok(PrincipalResource {
|
|
||||||
principal: user,
|
|
||||||
home_set: &["calendar", "birthdays"],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(
|
|
||||||
&self,
|
|
||||||
(principal,): &Self::PathComponents,
|
|
||||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
|
||||||
Ok(vec![
|
|
||||||
CalendarSetResource {
|
|
||||||
name: "calendar",
|
|
||||||
principal: principal.to_owned(),
|
|
||||||
read_only: false,
|
|
||||||
},
|
|
||||||
CalendarSetResource {
|
|
||||||
name: "birthdays",
|
|
||||||
principal: principal.to_owned(),
|
|
||||||
read_only: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
|
||||||
Router::new()
|
|
||||||
.nest(
|
|
||||||
"/calendar",
|
|
||||||
CalendarSetResourceService::new(
|
|
||||||
"calendar",
|
|
||||||
self.cal_store.clone(),
|
|
||||||
self.sub_store.clone(),
|
|
||||||
)
|
|
||||||
.axum_router(),
|
|
||||||
)
|
|
||||||
.nest(
|
|
||||||
"/birthdays",
|
|
||||||
CalendarSetResourceService::new(
|
|
||||||
"birthdays",
|
|
||||||
self.birthday_store.clone(),
|
|
||||||
self.sub_store.clone(),
|
|
||||||
)
|
|
||||||
.axum_router(),
|
|
||||||
)
|
|
||||||
.route_service("/", self.axum_service())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
|
||||||
AxumMethods for PrincipalResourceService<AP, S, CS, BS>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
34
crates/caldav/src/principal/prop.rs
Normal file
34
crates/caldav/src/principal/prop.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use rustical_dav::{extensions::CommonPropertiesProp, xml::HrefElement};
|
||||||
|
use rustical_store::auth::user::PrincipalType;
|
||||||
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||||
|
pub enum PrincipalProp {
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
|
Displayname(String),
|
||||||
|
|
||||||
|
// Scheduling Extensions to CalDAV (RFC 6638)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)]
|
||||||
|
CalendarUserType(PrincipalType),
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
CalendarUserAddressSet(HrefElement),
|
||||||
|
|
||||||
|
// WebDAV Access Control (RFC 3744)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
|
||||||
|
PrincipalUrl(HrefElement),
|
||||||
|
|
||||||
|
// CalDAV (RFC 4791)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||||
|
CalendarHomeSet(CalendarHomeSet),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||||
|
pub enum PrincipalPropWrapper {
|
||||||
|
Principal(PrincipalProp),
|
||||||
|
Common(CommonPropertiesProp),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||||
|
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] pub(super) Vec<HrefElement>);
|
||||||
110
crates/caldav/src/principal/service.rs
Normal file
110
crates/caldav/src/principal/service.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use crate::calendar_set::{CalendarSetResource, CalendarSetResourceService};
|
||||||
|
use crate::principal::PrincipalResource;
|
||||||
|
use crate::{CalDavPrincipalUri, Error};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::Router;
|
||||||
|
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||||
|
use rustical_store::auth::{AuthenticationProvider, User};
|
||||||
|
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PrincipalResourceService<
|
||||||
|
AP: AuthenticationProvider,
|
||||||
|
S: SubscriptionStore,
|
||||||
|
CS: CalendarStore,
|
||||||
|
BS: CalendarStore,
|
||||||
|
> {
|
||||||
|
pub(crate) auth_provider: Arc<AP>,
|
||||||
|
pub(crate) sub_store: Arc<S>,
|
||||||
|
pub(crate) cal_store: Arc<CS>,
|
||||||
|
pub(crate) birthday_store: Arc<BS>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore> Clone
|
||||||
|
for PrincipalResourceService<AP, S, CS, BS>
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
auth_provider: self.auth_provider.clone(),
|
||||||
|
sub_store: self.sub_store.clone(),
|
||||||
|
cal_store: self.cal_store.clone(),
|
||||||
|
birthday_store: self.birthday_store.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
||||||
|
ResourceService for PrincipalResourceService<AP, S, CS, BS>
|
||||||
|
{
|
||||||
|
type PathComponents = (String,);
|
||||||
|
type MemberType = CalendarSetResource;
|
||||||
|
type Resource = PrincipalResource;
|
||||||
|
type Error = Error;
|
||||||
|
type Principal = User;
|
||||||
|
type PrincipalUri = CalDavPrincipalUri;
|
||||||
|
|
||||||
|
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||||
|
|
||||||
|
async fn get_resource(
|
||||||
|
&self,
|
||||||
|
(principal,): &Self::PathComponents,
|
||||||
|
) -> Result<Self::Resource, Self::Error> {
|
||||||
|
let user = self
|
||||||
|
.auth_provider
|
||||||
|
.get_principal(principal)
|
||||||
|
.await?
|
||||||
|
.ok_or(crate::Error::NotFound)?;
|
||||||
|
Ok(PrincipalResource {
|
||||||
|
principal: user,
|
||||||
|
home_set: &["calendar", "birthdays"],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
(principal,): &Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||||
|
Ok(vec![
|
||||||
|
CalendarSetResource {
|
||||||
|
name: "calendar",
|
||||||
|
principal: principal.to_owned(),
|
||||||
|
read_only: false,
|
||||||
|
},
|
||||||
|
CalendarSetResource {
|
||||||
|
name: "birthdays",
|
||||||
|
principal: principal.to_owned(),
|
||||||
|
read_only: true,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||||
|
Router::new()
|
||||||
|
.nest(
|
||||||
|
"/calendar",
|
||||||
|
CalendarSetResourceService::new(
|
||||||
|
"calendar",
|
||||||
|
self.cal_store.clone(),
|
||||||
|
self.sub_store.clone(),
|
||||||
|
)
|
||||||
|
.axum_router(),
|
||||||
|
)
|
||||||
|
.nest(
|
||||||
|
"/birthdays",
|
||||||
|
CalendarSetResourceService::new(
|
||||||
|
"birthdays",
|
||||||
|
self.birthday_store.clone(),
|
||||||
|
self.sub_store.clone(),
|
||||||
|
)
|
||||||
|
.axum_router(),
|
||||||
|
)
|
||||||
|
.route_service("/", self.axum_service())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
||||||
|
AxumMethods for PrincipalResourceService<AP, S, CS, BS>
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::str::FromStr;
|
use super::AddressObjectPathComponents;
|
||||||
|
use super::AddressObjectResourceService;
|
||||||
use super::resource::AddressObjectPathComponents;
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::address_object::resource::AddressObjectResourceService;
|
|
||||||
use crate::addressbook::resource::AddressbookResource;
|
use crate::addressbook::resource::AddressbookResource;
|
||||||
use axum::body::Body;
|
use axum::body::Body;
|
||||||
use axum::extract::{Path, State};
|
use axum::extract::{Path, State};
|
||||||
@@ -15,6 +13,7 @@ use rustical_dav::resource::Resource;
|
|||||||
use rustical_ical::AddressObject;
|
use rustical_ical::AddressObject;
|
||||||
use rustical_store::AddressbookStore;
|
use rustical_store::AddressbookStore;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
|
use std::str::FromStr;
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
#[instrument(skip(addr_store))]
|
#[instrument(skip(addr_store))]
|
||||||
|
|||||||
@@ -1,2 +1,6 @@
|
|||||||
pub mod methods;
|
pub mod methods;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
|
mod service;
|
||||||
|
pub use service::*;
|
||||||
|
mod prop;
|
||||||
|
pub use prop::*;
|
||||||
|
|||||||
23
crates/carddav/src/address_object/prop.rs
Normal file
23
crates/carddav/src/address_object/prop.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use rustical_dav::extensions::CommonPropertiesProp;
|
||||||
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "AddressObjectPropName")]
|
||||||
|
pub enum AddressObjectProp {
|
||||||
|
// WebDAV (RFC 2518)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
|
Getetag(String),
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
||||||
|
Getcontenttype(&'static str),
|
||||||
|
|
||||||
|
// CalDAV (RFC 4791)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||||
|
AddressData(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
|
||||||
|
pub enum AddressObjectPropWrapper {
|
||||||
|
AddressObject(AddressObjectProp),
|
||||||
|
Common(CommonPropertiesProp),
|
||||||
|
}
|
||||||
@@ -1,56 +1,19 @@
|
|||||||
use crate::{CardDavPrincipalUri, Error};
|
use crate::{
|
||||||
use async_trait::async_trait;
|
Error,
|
||||||
use axum::{extract::Request, handler::Handler, response::Response};
|
address_object::{
|
||||||
use derive_more::derive::{Constructor, From, Into};
|
AddressObjectProp, AddressObjectPropName, AddressObjectPropWrapper,
|
||||||
use futures_util::future::BoxFuture;
|
AddressObjectPropWrapperName,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use derive_more::derive::{From, Into};
|
||||||
use rustical_dav::{
|
use rustical_dav::{
|
||||||
extensions::{CommonPropertiesExtension, CommonPropertiesProp},
|
extensions::CommonPropertiesExtension,
|
||||||
privileges::UserPrivilegeSet,
|
privileges::UserPrivilegeSet,
|
||||||
resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService},
|
resource::{PrincipalUri, Resource, ResourceName},
|
||||||
xml::Resourcetype,
|
xml::Resourcetype,
|
||||||
};
|
};
|
||||||
use rustical_ical::AddressObject;
|
use rustical_ical::AddressObject;
|
||||||
use rustical_store::{AddressbookStore, auth::User};
|
use rustical_store::auth::User;
|
||||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
|
||||||
use serde::{Deserialize, Deserializer};
|
|
||||||
use std::{convert::Infallible, sync::Arc};
|
|
||||||
use tower::Service;
|
|
||||||
|
|
||||||
use super::methods::{get_object, put_object};
|
|
||||||
|
|
||||||
#[derive(Constructor)]
|
|
||||||
pub struct AddressObjectResourceService<AS: AddressbookStore> {
|
|
||||||
pub(crate) addr_store: Arc<AS>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AS: AddressbookStore> Clone for AddressObjectResourceService<AS> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
addr_store: self.addr_store.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "AddressObjectPropName")]
|
|
||||||
pub enum AddressObjectProp {
|
|
||||||
// WebDAV (RFC 2518)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
|
||||||
Getetag(String),
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
|
||||||
Getcontenttype(&'static str),
|
|
||||||
|
|
||||||
// CalDAV (RFC 4791)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
|
||||||
AddressData(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
|
|
||||||
pub enum AddressObjectPropWrapper {
|
|
||||||
AddressObject(AddressObjectProp),
|
|
||||||
Common(CommonPropertiesProp),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, From, Into)]
|
#[derive(Clone, From, Into)]
|
||||||
pub struct AddressObjectResource {
|
pub struct AddressObjectResource {
|
||||||
@@ -113,84 +76,3 @@ impl Resource for AddressObjectResource {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_vcf_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let name: String = Deserialize::deserialize(deserializer)?;
|
|
||||||
if let Some(object_id) = name.strip_suffix(".vcf") {
|
|
||||||
Ok(object_id.to_owned())
|
|
||||||
} else {
|
|
||||||
Err(serde::de::Error::custom("Missing .vcf extension"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct AddressObjectPathComponents {
|
|
||||||
pub principal: String,
|
|
||||||
pub addressbook_id: String,
|
|
||||||
#[serde(deserialize_with = "deserialize_vcf_name")]
|
|
||||||
pub object_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS> {
|
|
||||||
type PathComponents = AddressObjectPathComponents;
|
|
||||||
type Resource = AddressObjectResource;
|
|
||||||
type MemberType = AddressObjectResource;
|
|
||||||
type Error = Error;
|
|
||||||
type Principal = User;
|
|
||||||
type PrincipalUri = CardDavPrincipalUri;
|
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
|
||||||
|
|
||||||
async fn get_resource(
|
|
||||||
&self,
|
|
||||||
AddressObjectPathComponents {
|
|
||||||
principal,
|
|
||||||
addressbook_id,
|
|
||||||
object_id,
|
|
||||||
}: &Self::PathComponents,
|
|
||||||
) -> Result<Self::Resource, Self::Error> {
|
|
||||||
let object = self
|
|
||||||
.addr_store
|
|
||||||
.get_object(principal, addressbook_id, object_id, false)
|
|
||||||
.await?;
|
|
||||||
Ok(AddressObjectResource {
|
|
||||||
object,
|
|
||||||
principal: principal.to_owned(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_resource(
|
|
||||||
&self,
|
|
||||||
AddressObjectPathComponents {
|
|
||||||
principal,
|
|
||||||
addressbook_id,
|
|
||||||
object_id,
|
|
||||||
}: &Self::PathComponents,
|
|
||||||
use_trashbin: bool,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.addr_store
|
|
||||||
.delete_object(principal, addressbook_id, object_id, use_trashbin)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AS: AddressbookStore> AxumMethods for AddressObjectResourceService<AS> {
|
|
||||||
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(get_object::<AS>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(put_object::<AS>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
105
crates/carddav/src/address_object/service.rs
Normal file
105
crates/carddav/src/address_object/service.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use super::methods::{get_object, put_object};
|
||||||
|
use crate::{CardDavPrincipalUri, Error, address_object::resource::AddressObjectResource};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::{extract::Request, handler::Handler, response::Response};
|
||||||
|
use derive_more::derive::Constructor;
|
||||||
|
use futures_util::future::BoxFuture;
|
||||||
|
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||||
|
use rustical_store::{AddressbookStore, auth::User};
|
||||||
|
use serde::{Deserialize, Deserializer};
|
||||||
|
use std::{convert::Infallible, sync::Arc};
|
||||||
|
use tower::Service;
|
||||||
|
|
||||||
|
#[derive(Constructor)]
|
||||||
|
pub struct AddressObjectResourceService<AS: AddressbookStore> {
|
||||||
|
pub(crate) addr_store: Arc<AS>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AS: AddressbookStore> Clone for AddressObjectResourceService<AS> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
addr_store: self.addr_store.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct AddressObjectPathComponents {
|
||||||
|
pub principal: String,
|
||||||
|
pub addressbook_id: String,
|
||||||
|
#[serde(deserialize_with = "deserialize_vcf_name")]
|
||||||
|
pub object_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS> {
|
||||||
|
type PathComponents = AddressObjectPathComponents;
|
||||||
|
type Resource = AddressObjectResource;
|
||||||
|
type MemberType = AddressObjectResource;
|
||||||
|
type Error = Error;
|
||||||
|
type Principal = User;
|
||||||
|
type PrincipalUri = CardDavPrincipalUri;
|
||||||
|
|
||||||
|
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||||
|
|
||||||
|
async fn get_resource(
|
||||||
|
&self,
|
||||||
|
AddressObjectPathComponents {
|
||||||
|
principal,
|
||||||
|
addressbook_id,
|
||||||
|
object_id,
|
||||||
|
}: &Self::PathComponents,
|
||||||
|
) -> Result<Self::Resource, Self::Error> {
|
||||||
|
let object = self
|
||||||
|
.addr_store
|
||||||
|
.get_object(principal, addressbook_id, object_id, false)
|
||||||
|
.await?;
|
||||||
|
Ok(AddressObjectResource {
|
||||||
|
object,
|
||||||
|
principal: principal.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_resource(
|
||||||
|
&self,
|
||||||
|
AddressObjectPathComponents {
|
||||||
|
principal,
|
||||||
|
addressbook_id,
|
||||||
|
object_id,
|
||||||
|
}: &Self::PathComponents,
|
||||||
|
use_trashbin: bool,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.addr_store
|
||||||
|
.delete_object(principal, addressbook_id, object_id, use_trashbin)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AS: AddressbookStore> AxumMethods for AddressObjectResourceService<AS> {
|
||||||
|
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(get_object::<AS>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(put_object::<AS>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_vcf_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let name: String = Deserialize::deserialize(deserializer)?;
|
||||||
|
if let Some(object_id) = name.strip_suffix(".vcf") {
|
||||||
|
Ok(object_id.to_owned())
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::custom("Missing .vcf extension"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Error, addressbook::resource::AddressbookResourceService};
|
use crate::{Error, addressbook::AddressbookResourceService};
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
address_object::resource::{
|
address_object::{
|
||||||
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
|
AddressObjectPropWrapper, AddressObjectPropWrapperName, resource::AddressObjectResource,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
CardDavPrincipalUri, Error, address_object::resource::AddressObjectPropWrapperName,
|
CardDavPrincipalUri, Error, address_object::AddressObjectPropWrapperName,
|
||||||
addressbook::resource::AddressbookResourceService,
|
addressbook::AddressbookResourceService,
|
||||||
};
|
};
|
||||||
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
|
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
|
||||||
use axum::{
|
use axum::{
|
||||||
@@ -81,7 +81,7 @@ pub async fn route_report_addressbook<AS: AddressbookStore, S: SubscriptionStore
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::address_object::resource::AddressObjectPropName;
|
use crate::address_object::AddressObjectPropName;
|
||||||
use rustical_dav::xml::{PropElement, sync_collection::SyncLevel};
|
use rustical_dav::xml::{PropElement, sync_collection::SyncLevel};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
Error,
|
Error,
|
||||||
address_object::resource::{
|
address_object::{
|
||||||
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
|
AddressObjectPropWrapper, AddressObjectPropWrapperName, resource::AddressObjectResource,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
pub mod methods;
|
pub mod methods;
|
||||||
pub mod prop;
|
pub mod prop;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
|
mod service;
|
||||||
|
pub use service::*;
|
||||||
|
|||||||
@@ -1,4 +1,33 @@
|
|||||||
use rustical_xml::XmlSerialize;
|
use rustical_dav::extensions::{CommonPropertiesProp, SyncTokenExtensionProp};
|
||||||
|
use rustical_dav_push::DavPushExtensionProp;
|
||||||
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "AddressbookPropName")]
|
||||||
|
pub enum AddressbookProp {
|
||||||
|
// WebDAV (RFC 2518)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
|
Displayname(Option<String>),
|
||||||
|
|
||||||
|
// CardDAV (RFC 6352)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||||
|
AddressbookDescription(Option<String>),
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
||||||
|
SupportedAddressData(SupportedAddressData),
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
||||||
|
SupportedReportSet(SupportedReportSet),
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
|
MaxResourceSize(i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
|
||||||
|
pub enum AddressbookPropWrapper {
|
||||||
|
Addressbook(AddressbookProp),
|
||||||
|
SyncToken(SyncTokenExtensionProp),
|
||||||
|
DavPush(DavPushExtensionProp),
|
||||||
|
Common(CommonPropertiesProp),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||||
pub struct AddressDataType {
|
pub struct AddressDataType {
|
||||||
|
|||||||
@@ -1,78 +1,16 @@
|
|||||||
use super::methods::mkcol::route_mkcol;
|
|
||||||
use super::methods::report::route_report_addressbook;
|
|
||||||
use super::prop::{SupportedAddressData, SupportedReportSet};
|
use super::prop::{SupportedAddressData, SupportedReportSet};
|
||||||
use crate::address_object::resource::{AddressObjectResource, AddressObjectResourceService};
|
use crate::Error;
|
||||||
use crate::{CardDavPrincipalUri, Error};
|
use crate::addressbook::prop::{
|
||||||
use async_trait::async_trait;
|
AddressbookProp, AddressbookPropName, AddressbookPropWrapper, AddressbookPropWrapperName,
|
||||||
use axum::Router;
|
|
||||||
use axum::extract::Request;
|
|
||||||
use axum::handler::Handler;
|
|
||||||
use axum::response::Response;
|
|
||||||
use derive_more::derive::{From, Into};
|
|
||||||
use futures_util::future::BoxFuture;
|
|
||||||
use rustical_dav::extensions::{
|
|
||||||
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
|
|
||||||
};
|
};
|
||||||
|
use derive_more::derive::{From, Into};
|
||||||
|
use rustical_dav::extensions::{CommonPropertiesExtension, SyncTokenExtension};
|
||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
use rustical_dav::privileges::UserPrivilegeSet;
|
||||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||||
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
use rustical_dav_push::DavPushExtension;
|
||||||
|
use rustical_store::Addressbook;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
|
||||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
|
||||||
use std::convert::Infallible;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use tower::Service;
|
|
||||||
|
|
||||||
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
|
|
||||||
pub(crate) addr_store: Arc<AS>,
|
|
||||||
pub(crate) sub_store: Arc<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S> {
|
|
||||||
pub fn new(addr_store: Arc<A>, sub_store: Arc<S>) -> Self {
|
|
||||||
Self {
|
|
||||||
addr_store,
|
|
||||||
sub_store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: AddressbookStore, S: SubscriptionStore> Clone for AddressbookResourceService<A, S> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
addr_store: self.addr_store.clone(),
|
|
||||||
sub_store: self.sub_store.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "AddressbookPropName")]
|
|
||||||
pub enum AddressbookProp {
|
|
||||||
// WebDAV (RFC 2518)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
|
||||||
Displayname(Option<String>),
|
|
||||||
|
|
||||||
// CardDAV (RFC 6352)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
|
||||||
AddressbookDescription(Option<String>),
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
|
||||||
SupportedAddressData(SupportedAddressData),
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
|
||||||
SupportedReportSet(SupportedReportSet),
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
|
||||||
MaxResourceSize(i64),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
|
|
||||||
pub enum AddressbookPropWrapper {
|
|
||||||
Addressbook(AddressbookProp),
|
|
||||||
SyncToken(SyncTokenExtensionProp),
|
|
||||||
DavPush(DavPushExtensionProp),
|
|
||||||
Common(CommonPropertiesProp),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, From, Into)]
|
#[derive(Clone, Debug, From, Into)]
|
||||||
pub struct AddressbookResource(pub(crate) Addressbook);
|
pub struct AddressbookResource(pub(crate) Addressbook);
|
||||||
@@ -205,92 +143,3 @@ impl Resource for AddressbookResource {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
|
|
||||||
for AddressbookResourceService<AS, S>
|
|
||||||
{
|
|
||||||
type MemberType = AddressObjectResource;
|
|
||||||
type PathComponents = (String, String); // principal, addressbook_id
|
|
||||||
type Resource = AddressbookResource;
|
|
||||||
type Error = Error;
|
|
||||||
type Principal = User;
|
|
||||||
type PrincipalUri = CardDavPrincipalUri;
|
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
|
||||||
|
|
||||||
async fn get_resource(
|
|
||||||
&self,
|
|
||||||
(principal, addressbook_id): &Self::PathComponents,
|
|
||||||
) -> Result<Self::Resource, Error> {
|
|
||||||
let addressbook = self
|
|
||||||
.addr_store
|
|
||||||
.get_addressbook(principal, addressbook_id, false)
|
|
||||||
.await
|
|
||||||
.map_err(|_e| Error::NotFound)?;
|
|
||||||
Ok(addressbook.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(
|
|
||||||
&self,
|
|
||||||
(principal, addressbook_id): &Self::PathComponents,
|
|
||||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
|
||||||
Ok(self
|
|
||||||
.addr_store
|
|
||||||
.get_objects(principal, addressbook_id)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.map(|object| AddressObjectResource {
|
|
||||||
object,
|
|
||||||
principal: principal.to_owned(),
|
|
||||||
})
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn save_resource(
|
|
||||||
&self,
|
|
||||||
(principal, addressbook_id): &Self::PathComponents,
|
|
||||||
file: Self::Resource,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.addr_store
|
|
||||||
.update_addressbook(principal.to_owned(), addressbook_id.to_owned(), file.into())
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete_resource(
|
|
||||||
&self,
|
|
||||||
(principal, addressbook_id): &Self::PathComponents,
|
|
||||||
use_trashbin: bool,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
self.addr_store
|
|
||||||
.delete_addressbook(principal, addressbook_id, use_trashbin)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
|
||||||
Router::new()
|
|
||||||
.nest(
|
|
||||||
"/{object_id}",
|
|
||||||
AddressObjectResourceService::new(self.addr_store.clone()).axum_router(),
|
|
||||||
)
|
|
||||||
.route_service("/", self.axum_service())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> {
|
|
||||||
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(route_report_addressbook::<AS, S>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mkcol() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
|
||||||
Some(|state, req| {
|
|
||||||
let mut service = Handler::with_state(route_mkcol::<AS, S>, state);
|
|
||||||
Box::pin(Service::call(&mut service, req))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
130
crates/carddav/src/addressbook/service.rs
Normal file
130
crates/carddav/src/addressbook/service.rs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
use super::methods::mkcol::route_mkcol;
|
||||||
|
use super::methods::report::route_report_addressbook;
|
||||||
|
use crate::address_object::AddressObjectResourceService;
|
||||||
|
use crate::address_object::resource::AddressObjectResource;
|
||||||
|
use crate::addressbook::resource::AddressbookResource;
|
||||||
|
use crate::{CardDavPrincipalUri, Error};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::Router;
|
||||||
|
use axum::extract::Request;
|
||||||
|
use axum::handler::Handler;
|
||||||
|
use axum::response::Response;
|
||||||
|
use futures_util::future::BoxFuture;
|
||||||
|
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||||
|
use rustical_store::auth::User;
|
||||||
|
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||||
|
use std::convert::Infallible;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tower::Service;
|
||||||
|
|
||||||
|
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
|
||||||
|
pub(crate) addr_store: Arc<AS>,
|
||||||
|
pub(crate) sub_store: Arc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S> {
|
||||||
|
pub fn new(addr_store: Arc<A>, sub_store: Arc<S>) -> Self {
|
||||||
|
Self {
|
||||||
|
addr_store,
|
||||||
|
sub_store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: AddressbookStore, S: SubscriptionStore> Clone for AddressbookResourceService<A, S> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
addr_store: self.addr_store.clone(),
|
||||||
|
sub_store: self.sub_store.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
|
||||||
|
for AddressbookResourceService<AS, S>
|
||||||
|
{
|
||||||
|
type MemberType = AddressObjectResource;
|
||||||
|
type PathComponents = (String, String); // principal, addressbook_id
|
||||||
|
type Resource = AddressbookResource;
|
||||||
|
type Error = Error;
|
||||||
|
type Principal = User;
|
||||||
|
type PrincipalUri = CardDavPrincipalUri;
|
||||||
|
|
||||||
|
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||||
|
|
||||||
|
async fn get_resource(
|
||||||
|
&self,
|
||||||
|
(principal, addressbook_id): &Self::PathComponents,
|
||||||
|
) -> Result<Self::Resource, Error> {
|
||||||
|
let addressbook = self
|
||||||
|
.addr_store
|
||||||
|
.get_addressbook(principal, addressbook_id, false)
|
||||||
|
.await
|
||||||
|
.map_err(|_e| Error::NotFound)?;
|
||||||
|
Ok(addressbook.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
(principal, addressbook_id): &Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||||
|
Ok(self
|
||||||
|
.addr_store
|
||||||
|
.get_objects(principal, addressbook_id)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|object| AddressObjectResource {
|
||||||
|
object,
|
||||||
|
principal: principal.to_owned(),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn save_resource(
|
||||||
|
&self,
|
||||||
|
(principal, addressbook_id): &Self::PathComponents,
|
||||||
|
file: Self::Resource,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.addr_store
|
||||||
|
.update_addressbook(principal.to_owned(), addressbook_id.to_owned(), file.into())
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_resource(
|
||||||
|
&self,
|
||||||
|
(principal, addressbook_id): &Self::PathComponents,
|
||||||
|
use_trashbin: bool,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.addr_store
|
||||||
|
.delete_addressbook(principal, addressbook_id, use_trashbin)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
||||||
|
Router::new()
|
||||||
|
.nest(
|
||||||
|
"/{object_id}",
|
||||||
|
AddressObjectResourceService::new(self.addr_store.clone()).axum_router(),
|
||||||
|
)
|
||||||
|
.route_service("/", self.axum_service())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> {
|
||||||
|
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(route_report_addressbook::<AS, S>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mkcol() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||||
|
Some(|state, req| {
|
||||||
|
let mut service = Handler::with_state(route_mkcol::<AS, S>, state);
|
||||||
|
Box::pin(Service::call(&mut service, req))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +1,14 @@
|
|||||||
use crate::addressbook::resource::{AddressbookResource, AddressbookResourceService};
|
use crate::Error;
|
||||||
use crate::{CardDavPrincipalUri, Error};
|
use rustical_dav::extensions::CommonPropertiesExtension;
|
||||||
use async_trait::async_trait;
|
|
||||||
use axum::Router;
|
|
||||||
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
|
||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
use rustical_dav::privileges::UserPrivilegeSet;
|
||||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::{AuthenticationProvider, User};
|
use rustical_store::auth::User;
|
||||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
|
||||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub struct PrincipalResourceService<
|
mod service;
|
||||||
A: AddressbookStore,
|
pub use service::*;
|
||||||
AP: AuthenticationProvider,
|
mod prop;
|
||||||
S: SubscriptionStore,
|
pub use prop::*;
|
||||||
> {
|
|
||||||
addr_store: Arc<A>,
|
|
||||||
auth_provider: Arc<AP>,
|
|
||||||
sub_store: Arc<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Clone
|
|
||||||
for PrincipalResourceService<A, AP, S>
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
addr_store: self.addr_store.clone(),
|
|
||||||
auth_provider: self.auth_provider.clone(),
|
|
||||||
sub_store: self.sub_store.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore>
|
|
||||||
PrincipalResourceService<A, AP, S>
|
|
||||||
{
|
|
||||||
pub fn new(addr_store: Arc<A>, auth_provider: Arc<AP>, sub_store: Arc<S>) -> Self {
|
|
||||||
Self {
|
|
||||||
addr_store,
|
|
||||||
auth_provider,
|
|
||||||
sub_store,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PrincipalResource {
|
pub struct PrincipalResource {
|
||||||
@@ -56,34 +21,6 @@ impl ResourceName for PrincipalResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
|
||||||
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
|
||||||
pub enum PrincipalProp {
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
|
||||||
Displayname(String),
|
|
||||||
|
|
||||||
// WebDAV Access Control (RFC 3744)
|
|
||||||
#[xml(rename = b"principal-URL")]
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
|
||||||
PrincipalUrl(HrefElement),
|
|
||||||
|
|
||||||
// CardDAV (RFC 6352)
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
|
||||||
AddressbookHomeSet(AddressbookHomeSet),
|
|
||||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
|
||||||
PrincipalAddress(Option<HrefElement>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
|
||||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
|
||||||
pub enum PrincipalPropWrapper {
|
|
||||||
Principal(PrincipalProp),
|
|
||||||
Common(CommonPropertiesProp),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Resource for PrincipalResource {
|
impl Resource for PrincipalResource {
|
||||||
type Prop = PrincipalPropWrapper;
|
type Prop = PrincipalPropWrapper;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
@@ -145,55 +82,3 @@ impl Resource for PrincipalResource {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> ResourceService
|
|
||||||
for PrincipalResourceService<A, AP, S>
|
|
||||||
{
|
|
||||||
type PathComponents = (String,);
|
|
||||||
type MemberType = AddressbookResource;
|
|
||||||
type Resource = PrincipalResource;
|
|
||||||
type Error = Error;
|
|
||||||
type Principal = User;
|
|
||||||
type PrincipalUri = CardDavPrincipalUri;
|
|
||||||
|
|
||||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
|
||||||
|
|
||||||
async fn get_resource(
|
|
||||||
&self,
|
|
||||||
(principal,): &Self::PathComponents,
|
|
||||||
) -> Result<Self::Resource, Self::Error> {
|
|
||||||
let user = self
|
|
||||||
.auth_provider
|
|
||||||
.get_principal(principal)
|
|
||||||
.await?
|
|
||||||
.ok_or(crate::Error::NotFound)?;
|
|
||||||
Ok(PrincipalResource { principal: user })
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(
|
|
||||||
&self,
|
|
||||||
(principal,): &Self::PathComponents,
|
|
||||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
|
||||||
let addressbooks = self.addr_store.get_addressbooks(principal).await?;
|
|
||||||
Ok(addressbooks
|
|
||||||
.into_iter()
|
|
||||||
.map(AddressbookResource::from)
|
|
||||||
.collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
|
||||||
Router::new()
|
|
||||||
.nest(
|
|
||||||
"/{addressbook_id}",
|
|
||||||
AddressbookResourceService::new(self.addr_store.clone(), self.sub_store.clone())
|
|
||||||
.axum_router(),
|
|
||||||
)
|
|
||||||
.route_service("/", self.axum_service())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods
|
|
||||||
for PrincipalResourceService<A, AP, S>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
30
crates/carddav/src/principal/prop.rs
Normal file
30
crates/carddav/src/principal/prop.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use rustical_dav::{extensions::CommonPropertiesProp, xml::HrefElement};
|
||||||
|
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||||
|
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] pub(super) Vec<HrefElement>);
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||||
|
pub enum PrincipalProp {
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
|
Displayname(String),
|
||||||
|
|
||||||
|
// WebDAV Access Control (RFC 3744)
|
||||||
|
#[xml(rename = b"principal-URL")]
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||||
|
PrincipalUrl(HrefElement),
|
||||||
|
|
||||||
|
// CardDAV (RFC 6352)
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||||
|
AddressbookHomeSet(AddressbookHomeSet),
|
||||||
|
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||||
|
PrincipalAddress(Option<HrefElement>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||||
|
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||||
|
pub enum PrincipalPropWrapper {
|
||||||
|
Principal(PrincipalProp),
|
||||||
|
Common(CommonPropertiesProp),
|
||||||
|
}
|
||||||
96
crates/carddav/src/principal/service.rs
Normal file
96
crates/carddav/src/principal/service.rs
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
use crate::addressbook::AddressbookResourceService;
|
||||||
|
use crate::addressbook::resource::AddressbookResource;
|
||||||
|
use crate::principal::PrincipalResource;
|
||||||
|
use crate::{CardDavPrincipalUri, Error};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use axum::Router;
|
||||||
|
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||||
|
use rustical_store::auth::{AuthenticationProvider, User};
|
||||||
|
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct PrincipalResourceService<
|
||||||
|
A: AddressbookStore,
|
||||||
|
AP: AuthenticationProvider,
|
||||||
|
S: SubscriptionStore,
|
||||||
|
> {
|
||||||
|
addr_store: Arc<A>,
|
||||||
|
auth_provider: Arc<AP>,
|
||||||
|
sub_store: Arc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Clone
|
||||||
|
for PrincipalResourceService<A, AP, S>
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
addr_store: self.addr_store.clone(),
|
||||||
|
auth_provider: self.auth_provider.clone(),
|
||||||
|
sub_store: self.sub_store.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore>
|
||||||
|
PrincipalResourceService<A, AP, S>
|
||||||
|
{
|
||||||
|
pub fn new(addr_store: Arc<A>, auth_provider: Arc<AP>, sub_store: Arc<S>) -> Self {
|
||||||
|
Self {
|
||||||
|
addr_store,
|
||||||
|
auth_provider,
|
||||||
|
sub_store,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> ResourceService
|
||||||
|
for PrincipalResourceService<A, AP, S>
|
||||||
|
{
|
||||||
|
type PathComponents = (String,);
|
||||||
|
type MemberType = AddressbookResource;
|
||||||
|
type Resource = PrincipalResource;
|
||||||
|
type Error = Error;
|
||||||
|
type Principal = User;
|
||||||
|
type PrincipalUri = CardDavPrincipalUri;
|
||||||
|
|
||||||
|
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||||
|
|
||||||
|
async fn get_resource(
|
||||||
|
&self,
|
||||||
|
(principal,): &Self::PathComponents,
|
||||||
|
) -> Result<Self::Resource, Self::Error> {
|
||||||
|
let user = self
|
||||||
|
.auth_provider
|
||||||
|
.get_principal(principal)
|
||||||
|
.await?
|
||||||
|
.ok_or(crate::Error::NotFound)?;
|
||||||
|
Ok(PrincipalResource { principal: user })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
(principal,): &Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||||
|
let addressbooks = self.addr_store.get_addressbooks(principal).await?;
|
||||||
|
Ok(addressbooks
|
||||||
|
.into_iter()
|
||||||
|
.map(AddressbookResource::from)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
||||||
|
Router::new()
|
||||||
|
.nest(
|
||||||
|
"/{addressbook_id}",
|
||||||
|
AddressbookResourceService::new(self.addr_store.clone(), self.sub_store.clone())
|
||||||
|
.axum_router(),
|
||||||
|
)
|
||||||
|
.route_service("/", self.axum_service())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods
|
||||||
|
for PrincipalResourceService<A, AP, S>
|
||||||
|
{
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user