mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 07:02:24 +00:00
Refactoring: Lots of fixes still necessary to get it into a working state
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1748,6 +1748,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"itertools",
|
"itertools",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
|
"roxmltree",
|
||||||
"rustical_auth",
|
"rustical_auth",
|
||||||
"serde",
|
"serde",
|
||||||
"strum",
|
"strum",
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ use actix_web::web::{self, Data};
|
|||||||
use actix_web::{guard, HttpResponse, Responder};
|
use actix_web::{guard, HttpResponse, Responder};
|
||||||
use resources::calendar::CalendarResource;
|
use resources::calendar::CalendarResource;
|
||||||
use resources::event::EventResource;
|
use resources::event::EventResource;
|
||||||
use resources::principal::PrincipalCalendarsResource;
|
use resources::principal::PrincipalResource;
|
||||||
use resources::root::RootResource;
|
use resources::root::RootResource;
|
||||||
use routes::propfind::route_propfind;
|
|
||||||
use routes::{calendar, event};
|
use routes::{calendar, event};
|
||||||
use rustical_auth::CheckAuthentication;
|
use rustical_auth::CheckAuthentication;
|
||||||
use rustical_dav::error::Error;
|
use rustical_dav::error::Error;
|
||||||
|
use rustical_dav::propfind::{handle_propfind, ServicePrefix};
|
||||||
use rustical_store::calendar::CalendarStore;
|
use rustical_store::calendar::CalendarStore;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -18,7 +18,6 @@ pub mod resources;
|
|||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
pub struct CalDavContext<C: CalendarStore + ?Sized> {
|
pub struct CalDavContext<C: CalendarStore + ?Sized> {
|
||||||
pub prefix: String,
|
|
||||||
pub store: Arc<RwLock<C>>,
|
pub store: Arc<RwLock<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,14 +31,14 @@ pub fn configure_dav<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
|||||||
auth: Arc<A>,
|
auth: Arc<A>,
|
||||||
store: Arc<RwLock<C>>,
|
store: Arc<RwLock<C>>,
|
||||||
) {
|
) {
|
||||||
let propfind_method = || Method::from_str("PROPFIND").unwrap();
|
let propfind_method = || web::method(Method::from_str("PROPFIND").unwrap());
|
||||||
let report_method = || Method::from_str("REPORT").unwrap();
|
let report_method = || web::method(Method::from_str("REPORT").unwrap());
|
||||||
let mkcol_method = || Method::from_str("MKCOL").unwrap();
|
let mkcol_method = || web::method(Method::from_str("MKCOL").unwrap());
|
||||||
|
|
||||||
cfg.app_data(Data::new(CalDavContext {
|
cfg.app_data(Data::new(CalDavContext {
|
||||||
prefix,
|
|
||||||
store: store.clone(),
|
store: store.clone(),
|
||||||
}))
|
}))
|
||||||
|
.app_data(Data::new(ServicePrefix(prefix)))
|
||||||
.app_data(Data::from(store.clone()))
|
.app_data(Data::from(store.clone()))
|
||||||
.app_data(Data::from(auth))
|
.app_data(Data::from(auth))
|
||||||
.service(
|
.service(
|
||||||
@@ -48,25 +47,25 @@ pub fn configure_dav<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
|||||||
.guard(guard::Method(Method::OPTIONS))
|
.guard(guard::Method(Method::OPTIONS))
|
||||||
.to(options_handler),
|
.to(options_handler),
|
||||||
)
|
)
|
||||||
|
.service(web::resource("").route(propfind_method().to(handle_propfind::<A, RootResource>)))
|
||||||
.service(
|
.service(
|
||||||
web::resource("").route(web::method(propfind_method()).to(route_propfind::<
|
web::resource("/{principal}")
|
||||||
A,
|
.route(propfind_method().to(handle_propfind::<A, PrincipalResource<C>>)),
|
||||||
RootResource,
|
|
||||||
C,
|
|
||||||
>)),
|
|
||||||
)
|
)
|
||||||
.service(web::resource("/{principal}").route(
|
// .service(DavResourceService::<PrincipalResource>::new("/{principal}"))
|
||||||
web::method(propfind_method()).to(route_propfind::<A, PrincipalCalendarsResource<C>, C>),
|
|
||||||
))
|
|
||||||
.service(
|
.service(
|
||||||
web::resource("/{principal}/{calendar}")
|
web::resource("/{principal}/{calendar}")
|
||||||
.route(web::method(report_method()).to(calendar::route_report_calendar::<A, C>))
|
.route(report_method().to(calendar::route_report_calendar::<A, C>))
|
||||||
.route(web::method(propfind_method()).to(route_propfind::<A, CalendarResource<C>, C>))
|
// .route(web::method(propfind_method()).to(route_propfind::<A, CalendarResource<C>, C>))
|
||||||
.route(web::method(mkcol_method()).to(calendar::route_mkcol_calendar::<A, C>)),
|
.route(propfind_method().to(handle_propfind::<A, CalendarResource<C>>))
|
||||||
|
.route(mkcol_method().to(calendar::route_mkcol_calendar::<A, C>))
|
||||||
|
.route(web::method(Method::DELETE).to(calendar::delete_calendar::<A, C>)),
|
||||||
)
|
)
|
||||||
|
// .service(web::resource("/{principal}/{calendar}").route(route))
|
||||||
.service(
|
.service(
|
||||||
web::resource("/{principal}/{calendar}/{event}")
|
web::resource("/{principal}/{calendar}/{event}")
|
||||||
.route(web::method(propfind_method()).to(route_propfind::<A, EventResource<C>, C>))
|
// .route(web::method(propfind_method()).to(route_propfind::<A, EventResource<C>, C>))
|
||||||
|
.route(propfind_method().to(handle_propfind::<A, EventResource<C>>))
|
||||||
.route(web::method(Method::DELETE).to(event::delete_event::<A, C>))
|
.route(web::method(Method::DELETE).to(event::delete_event::<A, C>))
|
||||||
.route(web::method(Method::GET).to(event::get_event::<A, C>))
|
.route(web::method(Method::GET).to(event::get_event::<A, C>))
|
||||||
.route(web::method(Method::PUT).to(event::put_event::<A, C>)),
|
.route(web::method(Method::PUT).to(event::put_event::<A, C>)),
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ use actix_web::{web::Data, HttpRequest};
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rustical_auth::AuthInfo;
|
use rustical_auth::AuthInfo;
|
||||||
|
use rustical_dav::dav_resource::{Resource, ResourceService};
|
||||||
use rustical_dav::error::Error;
|
use rustical_dav::error::Error;
|
||||||
use rustical_dav::{
|
use rustical_dav::xml_snippets::{HrefElement, TextNode};
|
||||||
resource::Resource,
|
|
||||||
xml_snippets::{HrefElement, TextNode},
|
|
||||||
};
|
|
||||||
use rustical_store::calendar::{Calendar, CalendarStore};
|
use rustical_store::calendar::{Calendar, CalendarStore};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -15,10 +13,10 @@ use tokio::sync::RwLock;
|
|||||||
|
|
||||||
pub struct CalendarResource<C: CalendarStore + ?Sized> {
|
pub struct CalendarResource<C: CalendarStore + ?Sized> {
|
||||||
pub cal_store: Arc<RwLock<C>>,
|
pub cal_store: Arc<RwLock<C>>,
|
||||||
pub calendar: Calendar,
|
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub prefix: String,
|
pub prefix: String,
|
||||||
pub principal: String,
|
pub principal: String,
|
||||||
|
pub calendar_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -159,51 +157,17 @@ pub enum CalendarPropResponse {
|
|||||||
CurrentUserPrivilegeSet(UserPrivilegeSet),
|
CurrentUserPrivilegeSet(UserPrivilegeSet),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
pub struct CalendarFile {
|
||||||
impl<C: CalendarStore + ?Sized> Resource for CalendarResource<C> {
|
pub calendar: Calendar,
|
||||||
type MemberType = Self;
|
pub principal: String,
|
||||||
type UriComponents = (String, String); // principal, calendar_id
|
pub prefix: String,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for CalendarFile {
|
||||||
type PropType = CalendarProp;
|
type PropType = CalendarProp;
|
||||||
type PropResponse = CalendarPropResponse;
|
type PropResponse = CalendarPropResponse;
|
||||||
|
|
||||||
async fn acquire_from_request(
|
|
||||||
req: HttpRequest,
|
|
||||||
_auth_info: AuthInfo,
|
|
||||||
uri_components: Self::UriComponents,
|
|
||||||
prefix: String,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let cal_store = req
|
|
||||||
.app_data::<Data<RwLock<C>>>()
|
|
||||||
.ok_or(anyhow!("no calendar store in app_data!"))?
|
|
||||||
.clone()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
let (principal, cid) = uri_components;
|
|
||||||
// TODO: fix errors
|
|
||||||
let calendar = cal_store
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.get_calendar(&cid)
|
|
||||||
.await
|
|
||||||
.map_err(|_e| Error::NotFound)?;
|
|
||||||
Ok(Self {
|
|
||||||
cal_store,
|
|
||||||
calendar,
|
|
||||||
path: req.path().to_string(),
|
|
||||||
prefix,
|
|
||||||
principal,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_path(&self) -> &str {
|
|
||||||
&self.path
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(&self) -> Result<Vec<Self::MemberType>> {
|
|
||||||
// As of now the calendar resource has no members
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
||||||
match prop {
|
match prop {
|
||||||
CalendarProp::Resourcetype => {
|
CalendarProp::Resourcetype => {
|
||||||
@@ -246,4 +210,61 @@ impl<C: CalendarStore + ?Sized> Resource for CalendarResource<C> {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_path(&self) -> &str {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<C: CalendarStore + ?Sized> ResourceService for CalendarResource<C> {
|
||||||
|
type MemberType = CalendarFile;
|
||||||
|
type PathComponents = (String, String); // principal, calendar_id
|
||||||
|
type File = CalendarFile;
|
||||||
|
|
||||||
|
async fn get_file(&self) -> Result<Self::File> {
|
||||||
|
let calendar = self
|
||||||
|
.cal_store
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.get_calendar(&self.calendar_id)
|
||||||
|
.await
|
||||||
|
.map_err(|_e| Error::NotFound)?;
|
||||||
|
Ok(CalendarFile {
|
||||||
|
calendar,
|
||||||
|
prefix: self.prefix.to_owned(),
|
||||||
|
principal: self.principal.to_owned(),
|
||||||
|
path: self.path.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
_auth_info: AuthInfo,
|
||||||
|
_path_components: Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>> {
|
||||||
|
// As of now the calendar resource has no members
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
req: HttpRequest,
|
||||||
|
auth_info: AuthInfo,
|
||||||
|
path_components: Self::PathComponents,
|
||||||
|
prefix: String,
|
||||||
|
) -> Result<Self, rustical_dav::error::Error> {
|
||||||
|
let cal_store = req
|
||||||
|
.app_data::<Data<RwLock<C>>>()
|
||||||
|
.ok_or(anyhow!("no calendar store in app_data!"))?
|
||||||
|
.clone()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
prefix,
|
||||||
|
path: req.path().to_owned(),
|
||||||
|
principal: auth_info.user_id,
|
||||||
|
calendar_id: path_components.1,
|
||||||
|
cal_store,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use actix_web::{web::Data, HttpRequest};
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rustical_auth::AuthInfo;
|
use rustical_auth::AuthInfo;
|
||||||
use rustical_dav::error::Error;
|
use rustical_dav::dav_resource::Resource;
|
||||||
use rustical_dav::{resource::Resource, xml_snippets::TextNode};
|
use rustical_dav::xml_snippets::TextNode;
|
||||||
|
use rustical_dav::{dav_resource::ResourceService, error::Error};
|
||||||
use rustical_store::calendar::CalendarStore;
|
use rustical_store::calendar::CalendarStore;
|
||||||
use rustical_store::event::Event;
|
use rustical_store::event::Event;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -14,7 +15,8 @@ use tokio::sync::RwLock;
|
|||||||
pub struct EventResource<C: CalendarStore + ?Sized> {
|
pub struct EventResource<C: CalendarStore + ?Sized> {
|
||||||
pub cal_store: Arc<RwLock<C>>,
|
pub cal_store: Arc<RwLock<C>>,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub event: Event,
|
pub cid: String,
|
||||||
|
pub uid: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(EnumString, Debug, VariantNames, IntoStaticStr, Clone)]
|
#[derive(EnumString, Debug, VariantNames, IntoStaticStr, Clone)]
|
||||||
@@ -33,42 +35,16 @@ pub enum PrincipalPropResponse {
|
|||||||
Getcontenttype(TextNode),
|
Getcontenttype(TextNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
pub struct EventFile {
|
||||||
impl<C: CalendarStore + ?Sized> Resource for EventResource<C> {
|
pub event: Event,
|
||||||
type UriComponents = (String, String, String); // principal, calendar, event
|
}
|
||||||
type MemberType = Self;
|
|
||||||
|
impl Resource for EventFile {
|
||||||
type PropType = EventProp;
|
type PropType = EventProp;
|
||||||
type PropResponse = PrincipalPropResponse;
|
type PropResponse = PrincipalPropResponse;
|
||||||
|
|
||||||
fn get_path(&self) -> &str {
|
fn get_path(&self) -> &str {
|
||||||
&self.path
|
"asd"
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(&self) -> Result<Vec<Self::MemberType>> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn acquire_from_request(
|
|
||||||
req: HttpRequest,
|
|
||||||
_auth_info: AuthInfo,
|
|
||||||
uri_components: Self::UriComponents,
|
|
||||||
_prefix: String,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let (_principal, cid, uid) = uri_components;
|
|
||||||
|
|
||||||
let cal_store = req
|
|
||||||
.app_data::<Data<RwLock<C>>>()
|
|
||||||
.ok_or(anyhow!("no calendar store in app_data!"))?
|
|
||||||
.clone()
|
|
||||||
.into_inner();
|
|
||||||
|
|
||||||
let event = cal_store.read().await.get_event(&cid, &uid).await?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
cal_store,
|
|
||||||
event,
|
|
||||||
path: req.path().to_string(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
||||||
@@ -77,7 +53,7 @@ impl<C: CalendarStore + ?Sized> Resource for EventResource<C> {
|
|||||||
self.event.get_etag(),
|
self.event.get_etag(),
|
||||||
)))),
|
)))),
|
||||||
EventProp::CalendarData => Ok(PrincipalPropResponse::CalendarData(TextNode(Some(
|
EventProp::CalendarData => Ok(PrincipalPropResponse::CalendarData(TextNode(Some(
|
||||||
self.event.get_ics(),
|
self.event.get_ics().to_owned(),
|
||||||
)))),
|
)))),
|
||||||
EventProp::Getcontenttype => Ok(PrincipalPropResponse::Getcontenttype(TextNode(Some(
|
EventProp::Getcontenttype => Ok(PrincipalPropResponse::Getcontenttype(TextNode(Some(
|
||||||
"text/calendar;charset=utf-8".to_owned(),
|
"text/calendar;charset=utf-8".to_owned(),
|
||||||
@@ -85,3 +61,52 @@ impl<C: CalendarStore + ?Sized> Resource for EventResource<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<C: CalendarStore + ?Sized> ResourceService for EventResource<C> {
|
||||||
|
type PathComponents = (String, String, String); // principal, calendar, event
|
||||||
|
type File = EventFile;
|
||||||
|
type MemberType = EventFile;
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
_auth_info: AuthInfo,
|
||||||
|
_path_components: Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
req: HttpRequest,
|
||||||
|
_auth_info: AuthInfo,
|
||||||
|
path_components: Self::PathComponents,
|
||||||
|
_prefix: String,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let (_principal, cid, uid) = path_components;
|
||||||
|
|
||||||
|
let cal_store = req
|
||||||
|
.app_data::<Data<RwLock<C>>>()
|
||||||
|
.ok_or(anyhow!("no calendar store in app_data!"))?
|
||||||
|
.clone()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
// let event = cal_store.read().await.get_event(&cid, &uid).await?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
cal_store,
|
||||||
|
cid,
|
||||||
|
uid,
|
||||||
|
path: req.path().to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_file(&self) -> Result<Self::File> {
|
||||||
|
let event = self
|
||||||
|
.cal_store
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.get_event(&self.cid, &self.uid)
|
||||||
|
.await?;
|
||||||
|
Ok(EventFile { event })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
use actix_web::{web::Data, HttpRequest};
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use actix_web::web::Data;
|
||||||
|
use actix_web::HttpRequest;
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rustical_auth::AuthInfo;
|
use rustical_auth::AuthInfo;
|
||||||
use rustical_dav::error::Error;
|
use rustical_dav::dav_resource::{Resource, ResourceService};
|
||||||
use rustical_dav::{resource::Resource, xml_snippets::HrefElement};
|
use rustical_dav::xml_snippets::HrefElement;
|
||||||
use rustical_store::calendar::CalendarStore;
|
use rustical_store::calendar::CalendarStore;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::sync::Arc;
|
|
||||||
use strum::{EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumString, IntoStaticStr, VariantNames};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use super::calendar::CalendarResource;
|
use super::calendar::CalendarFile;
|
||||||
|
|
||||||
pub struct PrincipalCalendarsResource<C: CalendarStore + ?Sized> {
|
pub struct PrincipalResource<C: CalendarStore + ?Sized> {
|
||||||
prefix: String,
|
prefix: String,
|
||||||
principal: String,
|
principal: String,
|
||||||
path: String,
|
path: String,
|
||||||
cal_store: Arc<RwLock<C>>,
|
cal_store: Arc<RwLock<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PrincipalFile {
|
||||||
|
prefix: String,
|
||||||
|
principal: String,
|
||||||
|
path: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Default)]
|
#[derive(Serialize, Default)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct Resourcetype {
|
pub struct Resourcetype {
|
||||||
@@ -51,55 +59,10 @@ pub enum PrincipalProp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl<C: CalendarStore + ?Sized> Resource for PrincipalCalendarsResource<C> {
|
impl Resource for PrincipalFile {
|
||||||
type UriComponents = ();
|
|
||||||
type MemberType = CalendarResource<C>;
|
|
||||||
type PropType = PrincipalProp;
|
type PropType = PrincipalProp;
|
||||||
type PropResponse = PrincipalPropResponse;
|
type PropResponse = PrincipalPropResponse;
|
||||||
|
|
||||||
fn get_path(&self) -> &str {
|
|
||||||
&self.path
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(&self) -> Result<Vec<Self::MemberType>> {
|
|
||||||
let calendars = self
|
|
||||||
.cal_store
|
|
||||||
.read()
|
|
||||||
.await
|
|
||||||
.get_calendars(&self.principal)
|
|
||||||
.await?;
|
|
||||||
let mut out = Vec::new();
|
|
||||||
for calendar in calendars {
|
|
||||||
let path = format!("{}/{}", &self.path, &calendar.id);
|
|
||||||
out.push(CalendarResource {
|
|
||||||
cal_store: self.cal_store.clone(),
|
|
||||||
calendar,
|
|
||||||
path,
|
|
||||||
prefix: self.prefix.clone(),
|
|
||||||
principal: self.principal.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn acquire_from_request(
|
|
||||||
req: HttpRequest,
|
|
||||||
auth_info: AuthInfo,
|
|
||||||
_uri_components: Self::UriComponents,
|
|
||||||
prefix: String,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let cal_store = req
|
|
||||||
.app_data::<Data<RwLock<C>>>()
|
|
||||||
.ok_or(anyhow!("no calendar store in app_data!"))?
|
|
||||||
.clone()
|
|
||||||
.into_inner();
|
|
||||||
Ok(Self {
|
|
||||||
cal_store,
|
|
||||||
prefix,
|
|
||||||
principal: auth_info.user_id,
|
|
||||||
path: req.path().to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
||||||
match prop {
|
match prop {
|
||||||
PrincipalProp::Resourcetype => {
|
PrincipalProp::Resourcetype => {
|
||||||
@@ -121,4 +84,65 @@ impl<C: CalendarStore + ?Sized> Resource for PrincipalCalendarsResource<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_path(&self) -> &str {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<C: CalendarStore + ?Sized> ResourceService for PrincipalResource<C> {
|
||||||
|
type PathComponents = (String,);
|
||||||
|
type MemberType = CalendarFile;
|
||||||
|
type File = PrincipalFile;
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
req: HttpRequest,
|
||||||
|
auth_info: AuthInfo,
|
||||||
|
_path_components: Self::PathComponents,
|
||||||
|
prefix: String,
|
||||||
|
) -> Result<Self, rustical_dav::error::Error> {
|
||||||
|
let cal_store = req
|
||||||
|
.app_data::<Data<RwLock<C>>>()
|
||||||
|
.ok_or(anyhow!("no calendar store in app_data!"))?
|
||||||
|
.clone()
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
cal_store,
|
||||||
|
path: req.path().to_owned(),
|
||||||
|
principal: auth_info.user_id,
|
||||||
|
prefix,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_file(&self) -> Result<Self::File> {
|
||||||
|
Ok(PrincipalFile {
|
||||||
|
principal: self.principal.to_owned(),
|
||||||
|
prefix: self.prefix.to_owned(),
|
||||||
|
path: self.path.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
_auth_info: AuthInfo,
|
||||||
|
_path_components: Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>> {
|
||||||
|
let calendars = self
|
||||||
|
.cal_store
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.get_calendars(&self.principal)
|
||||||
|
.await?;
|
||||||
|
Ok(calendars
|
||||||
|
.into_iter()
|
||||||
|
.map(|cal| CalendarFile {
|
||||||
|
calendar: cal,
|
||||||
|
principal: self.principal.to_owned(),
|
||||||
|
prefix: self.prefix.to_owned(),
|
||||||
|
path: self.path.to_owned(),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use actix_web::HttpRequest;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rustical_auth::AuthInfo;
|
use rustical_auth::AuthInfo;
|
||||||
|
use rustical_dav::dav_resource::{Resource, ResourceService};
|
||||||
use rustical_dav::error::Error;
|
use rustical_dav::error::Error;
|
||||||
use rustical_dav::{resource::Resource, xml_snippets::HrefElement};
|
use rustical_dav::xml_snippets::HrefElement;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use strum::{EnumString, IntoStaticStr, VariantNames};
|
use strum::{EnumString, IntoStaticStr, VariantNames};
|
||||||
|
|
||||||
@@ -33,34 +34,16 @@ pub enum RootPropResponse {
|
|||||||
CurrentUserPrincipal(HrefElement),
|
CurrentUserPrincipal(HrefElement),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
pub struct RootFile {
|
||||||
impl Resource for RootResource {
|
pub prefix: String,
|
||||||
type UriComponents = ();
|
pub principal: String,
|
||||||
type MemberType = Self;
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resource for RootFile {
|
||||||
type PropType = RootProp;
|
type PropType = RootProp;
|
||||||
type PropResponse = RootPropResponse;
|
type PropResponse = RootPropResponse;
|
||||||
|
|
||||||
fn get_path(&self) -> &str {
|
|
||||||
&self.path
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_members(&self) -> Result<Vec<Self::MemberType>> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn acquire_from_request(
|
|
||||||
req: HttpRequest,
|
|
||||||
auth_info: AuthInfo,
|
|
||||||
_uri_components: Self::UriComponents,
|
|
||||||
prefix: String,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
Ok(Self {
|
|
||||||
prefix,
|
|
||||||
principal: auth_info.user_id,
|
|
||||||
path: req.path().to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
||||||
match prop {
|
match prop {
|
||||||
RootProp::Resourcetype => Ok(RootPropResponse::Resourcetype(Resourcetype::default())),
|
RootProp::Resourcetype => Ok(RootPropResponse::Resourcetype(Resourcetype::default())),
|
||||||
@@ -69,4 +52,44 @@ impl Resource for RootResource {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_path(&self) -> &str {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl ResourceService for RootResource {
|
||||||
|
type PathComponents = ();
|
||||||
|
type MemberType = RootFile;
|
||||||
|
type File = RootFile;
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
_auth_info: AuthInfo,
|
||||||
|
_path_components: Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
req: HttpRequest,
|
||||||
|
auth_info: AuthInfo,
|
||||||
|
_path_components: Self::PathComponents,
|
||||||
|
prefix: String,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
prefix,
|
||||||
|
principal: auth_info.user_id,
|
||||||
|
path: req.path().to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_file(&self) -> Result<Self::File> {
|
||||||
|
Ok(RootFile {
|
||||||
|
path: self.path.to_owned(),
|
||||||
|
principal: self.principal.to_owned(),
|
||||||
|
prefix: self.prefix.to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::resources::event::EventResource;
|
use crate::resources::event::EventFile;
|
||||||
use crate::CalDavContext;
|
use crate::CalDavContext;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use actix_web::http::header::ContentType;
|
use actix_web::http::header::ContentType;
|
||||||
@@ -7,8 +7,8 @@ use actix_web::{HttpRequest, HttpResponse};
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use roxmltree::{Node, NodeType};
|
use roxmltree::{Node, NodeType};
|
||||||
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
||||||
|
use rustical_dav::dav_resource::HandlePropfind;
|
||||||
use rustical_dav::namespace::Namespace;
|
use rustical_dav::namespace::Namespace;
|
||||||
use rustical_dav::resource::HandlePropfind;
|
|
||||||
use rustical_dav::xml_snippets::generate_multistatus;
|
use rustical_dav::xml_snippets::generate_multistatus;
|
||||||
use rustical_store::calendar::{Calendar, CalendarStore};
|
use rustical_store::calendar::{Calendar, CalendarStore};
|
||||||
use rustical_store::event::Event;
|
use rustical_store::event::Event;
|
||||||
@@ -36,9 +36,9 @@ async fn _parse_filter(filter_node: &Node<'_, '_>) {
|
|||||||
|
|
||||||
async fn handle_report_calendar_query<C: CalendarStore + ?Sized>(
|
async fn handle_report_calendar_query<C: CalendarStore + ?Sized>(
|
||||||
query_node: Node<'_, '_>,
|
query_node: Node<'_, '_>,
|
||||||
request: HttpRequest,
|
_request: HttpRequest,
|
||||||
events: Vec<Event>,
|
events: Vec<Event>,
|
||||||
cal_store: Arc<RwLock<C>>,
|
_cal_store: Arc<RwLock<C>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let prop_node = query_node
|
let prop_node = query_node
|
||||||
.children()
|
.children()
|
||||||
@@ -50,22 +50,25 @@ async fn handle_report_calendar_query<C: CalendarStore + ?Sized>(
|
|||||||
.map(|node| node.tag_name().name())
|
.map(|node| node.tag_name().name())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let event_resources: Vec<_> = events
|
let event_files: Vec<_> = events
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|event| {
|
.map(|event| {
|
||||||
let path = format!("{}/{}", request.path(), event.get_uid());
|
// TODO: fix
|
||||||
EventResource {
|
// let path = format!("{}/{}", request.path(), event.get_uid());
|
||||||
cal_store: cal_store.clone(),
|
EventFile {
|
||||||
path: path.clone(),
|
event, // cal_store: cal_store.clone(),
|
||||||
event: event.clone(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let event_results: Result<Vec<_>, _> = event_resources
|
let mut event_responses = Vec::new();
|
||||||
.iter()
|
for event_file in event_files {
|
||||||
.map(|ev| ev.propfind(props.clone()))
|
event_responses.push(event_file.propfind(props.clone()).await?);
|
||||||
.collect();
|
}
|
||||||
let event_responses = event_results?;
|
// let event_results: Result<Vec<_>, _> = event_files
|
||||||
|
// .iter()
|
||||||
|
// .map(|ev| ev.propfind(props.clone()))
|
||||||
|
// .collect();
|
||||||
|
// let event_responses = event_results?;
|
||||||
|
|
||||||
let output = generate_multistatus(vec![Namespace::Dav, Namespace::CalDAV], |writer| {
|
let output = generate_multistatus(vec![Namespace::Dav, Namespace::CalDAV], |writer| {
|
||||||
for result in event_responses {
|
for result in event_responses {
|
||||||
@@ -178,3 +181,16 @@ pub async fn route_mkcol_calendar<A: CheckAuthentication, C: CalendarStore + ?Si
|
|||||||
|
|
||||||
Ok(HttpResponse::Created().body(""))
|
Ok(HttpResponse::Created().body(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete_calendar<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
||||||
|
context: Data<CalDavContext<C>>,
|
||||||
|
path: Path<(String, String)>,
|
||||||
|
auth: AuthInfoExtractor<A>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
let _user = auth.inner.user_id;
|
||||||
|
// TODO: verify whether user is authorized
|
||||||
|
let (_principal, cid) = path.into_inner();
|
||||||
|
context.store.write().await.delete_calendar(&cid).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().body(""))
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ pub async fn get_event<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
|||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.insert_header(("ETag", event.get_etag()))
|
.insert_header(("ETag", event.get_etag()))
|
||||||
.body(event.get_ics()))
|
.body(event.get_ics().to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn put_event<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
pub async fn put_event<A: CheckAuthentication, C: CalendarStore + ?Sized>(
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
pub mod calendar;
|
pub mod calendar;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod propfind;
|
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ serde = { version = "1.0.197", features = ["derive"] }
|
|||||||
strum = "0.26"
|
strum = "0.26"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
roxmltree = "0.19"
|
||||||
|
|||||||
131
crates/dav/src/dav_resource.rs
Normal file
131
crates/dav/src/dav_resource.rs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
use crate::{error::Error, xml_snippets::TagList};
|
||||||
|
use actix_web::{http::StatusCode, HttpRequest};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rustical_auth::AuthInfo;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use strum::VariantNames;
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
pub trait Resource {
|
||||||
|
type PropType: FromStr + VariantNames + Into<&'static str> + Clone;
|
||||||
|
type PropResponse: Serialize;
|
||||||
|
|
||||||
|
fn list_dead_props() -> &'static [&'static str] {
|
||||||
|
Self::PropType::VARIANTS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse>;
|
||||||
|
|
||||||
|
fn get_path(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A resource is identified by a URI and has properties
|
||||||
|
// A resource can also be a collection
|
||||||
|
// A resource cannot be none, only Methods like PROPFIND, GET, REPORT, etc. can be exposed
|
||||||
|
// A resource exists
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
pub trait ResourceService: Sized {
|
||||||
|
type MemberType: Resource;
|
||||||
|
type PathComponents: Sized + Clone; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
|
||||||
|
type File: Resource;
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
req: HttpRequest,
|
||||||
|
auth_info: AuthInfo,
|
||||||
|
path_components: Self::PathComponents,
|
||||||
|
prefix: String,
|
||||||
|
) -> Result<Self, Error>;
|
||||||
|
|
||||||
|
async fn get_file(&self) -> Result<Self::File>;
|
||||||
|
|
||||||
|
async fn get_members(
|
||||||
|
&self,
|
||||||
|
auth_info: AuthInfo,
|
||||||
|
path_components: Self::PathComponents,
|
||||||
|
) -> Result<Vec<Self::MemberType>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct PropWrapper<T: Serialize> {
|
||||||
|
#[serde(rename = "$value")]
|
||||||
|
prop: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
struct PropstatElement<T: Serialize> {
|
||||||
|
prop: T,
|
||||||
|
status: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct PropstatResponseElement<T1: Serialize, T2: Serialize> {
|
||||||
|
href: String,
|
||||||
|
propstat: Vec<PropstatType<T1, T2>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum PropstatType<T1: Serialize, T2: Serialize> {
|
||||||
|
Normal(PropstatElement<T1>),
|
||||||
|
NotFound(PropstatElement<T2>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
pub trait HandlePropfind {
|
||||||
|
async fn propfind(&self, props: Vec<&str>) -> Result<impl Serialize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<R: Resource> HandlePropfind for R {
|
||||||
|
async fn propfind(
|
||||||
|
&self,
|
||||||
|
props: Vec<&str>,
|
||||||
|
) -> Result<PropstatResponseElement<PropWrapper<Vec<R::PropResponse>>, TagList>> {
|
||||||
|
let mut props = props.into_iter().unique().collect_vec();
|
||||||
|
if props.contains(&"allprops") {
|
||||||
|
if props.len() != 1 {
|
||||||
|
// allprops MUST be the only queried prop per spec
|
||||||
|
return Err(anyhow!("allprops MUST be the only queried prop"));
|
||||||
|
}
|
||||||
|
props = R::list_dead_props().into();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut invalid_props = Vec::<&str>::new();
|
||||||
|
let mut prop_responses = Vec::new();
|
||||||
|
for prop in props {
|
||||||
|
if let Ok(valid_prop) = R::PropType::from_str(prop) {
|
||||||
|
match self.get_prop(valid_prop.clone()) {
|
||||||
|
Ok(response) => {
|
||||||
|
prop_responses.push(response);
|
||||||
|
}
|
||||||
|
Err(_) => invalid_props.push(prop),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
invalid_props.push(prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut propstats = Vec::new();
|
||||||
|
propstats.push(PropstatType::Normal(PropstatElement {
|
||||||
|
status: format!("HTTP/1.1 {}", StatusCode::OK),
|
||||||
|
prop: PropWrapper {
|
||||||
|
prop: prop_responses,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
if !invalid_props.is_empty() {
|
||||||
|
propstats.push(PropstatType::NotFound(PropstatElement {
|
||||||
|
status: format!("HTTP/1.1 {}", StatusCode::NOT_FOUND),
|
||||||
|
prop: TagList(invalid_props.iter().map(|&s| s.to_owned()).collect()),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Ok(PropstatResponseElement {
|
||||||
|
href: self.get_path().to_owned(),
|
||||||
|
propstat: propstats,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
pub mod dav_resource;
|
||||||
pub mod depth_extractor;
|
pub mod depth_extractor;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod namespace;
|
pub mod namespace;
|
||||||
|
pub mod propfind;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
pub mod xml_snippets;
|
pub mod xml_snippets;
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
use crate::CalDavContext;
|
use crate::dav_resource::HandlePropfind;
|
||||||
|
use crate::dav_resource::ResourceService;
|
||||||
|
use crate::depth_extractor::Depth;
|
||||||
|
use crate::namespace::Namespace;
|
||||||
|
use crate::xml_snippets::generate_multistatus;
|
||||||
use actix_web::http::header::ContentType;
|
use actix_web::http::header::ContentType;
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::web::{Data, Path};
|
use actix_web::web::{Data, Path};
|
||||||
use actix_web::{HttpRequest, HttpResponse};
|
use actix_web::{HttpRequest, HttpResponse};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
||||||
use rustical_dav::depth_extractor::Depth;
|
|
||||||
use rustical_dav::namespace::Namespace;
|
|
||||||
use rustical_dav::resource::{HandlePropfind, Resource};
|
|
||||||
use rustical_dav::xml_snippets::generate_multistatus;
|
|
||||||
use rustical_store::calendar::CalendarStore;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
// This is not the final place for this struct
|
||||||
|
pub struct ServicePrefix(pub String);
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("invalid propfind request: {0}")]
|
#[error("invalid propfind request: {0}")]
|
||||||
@@ -67,33 +69,36 @@ pub fn parse_propfind(body: &str) -> Result<Vec<&str>, Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn route_propfind<A: CheckAuthentication, R: Resource, C: CalendarStore + ?Sized>(
|
pub async fn handle_propfind<
|
||||||
path: Path<R::UriComponents>,
|
A: CheckAuthentication,
|
||||||
|
R: ResourceService + ?Sized,
|
||||||
|
// C: CalendarStore + ?Sized,
|
||||||
|
>(
|
||||||
|
path: Path<R::PathComponents>,
|
||||||
body: String,
|
body: String,
|
||||||
req: HttpRequest,
|
req: HttpRequest,
|
||||||
context: Data<CalDavContext<C>>,
|
prefix: Data<ServicePrefix>,
|
||||||
auth: AuthInfoExtractor<A>,
|
auth: AuthInfoExtractor<A>,
|
||||||
depth: Depth,
|
depth: Depth,
|
||||||
) -> Result<HttpResponse, rustical_dav::error::Error> {
|
) -> Result<HttpResponse, crate::error::Error> {
|
||||||
// TODO: fix errors
|
// TODO: fix errors
|
||||||
let props = parse_propfind(&body).map_err(|_e| anyhow!("propfing parsing error"))?;
|
let props = parse_propfind(&body).map_err(|_e| anyhow!("propfind parsing error"))?;
|
||||||
let auth_info = auth.inner;
|
let auth_info = auth.inner;
|
||||||
|
let prefix = prefix.0.to_owned();
|
||||||
|
let path_components = path.into_inner();
|
||||||
|
|
||||||
let resource = R::acquire_from_request(
|
let resource_service = R::new(req, auth_info.clone(), path_components.clone(), prefix).await?;
|
||||||
req,
|
|
||||||
auth_info,
|
|
||||||
path.into_inner(),
|
|
||||||
context.prefix.to_string(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let response = resource.propfind(props.clone())?;
|
let resource = resource_service.get_file().await?;
|
||||||
let members = resource.get_members().await?;
|
let response = resource.propfind(props.clone()).await?;
|
||||||
let mut member_responses = Vec::new();
|
let mut member_responses = Vec::new();
|
||||||
|
|
||||||
if depth != Depth::Zero {
|
if depth != Depth::Zero {
|
||||||
for member in &members {
|
for member in resource_service
|
||||||
member_responses.push(member.propfind(props.clone())?);
|
.get_members(auth_info, path_components)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
member_responses.push(member.propfind(props.clone()).await?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,8 +13,8 @@ use strum::VariantNames;
|
|||||||
// A resource cannot be none, only Methods like PROPFIND, GET, REPORT, etc. can be exposed
|
// A resource cannot be none, only Methods like PROPFIND, GET, REPORT, etc. can be exposed
|
||||||
// A resource exists
|
// A resource exists
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait Resource: Sized {
|
pub trait OldResource: Sized {
|
||||||
type MemberType: Resource;
|
type MemberType: OldResource;
|
||||||
type UriComponents: Sized; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
|
type UriComponents: Sized; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
|
||||||
type PropType: FromStr + VariantNames + Into<&'static str> + Clone;
|
type PropType: FromStr + VariantNames + Into<&'static str> + Clone;
|
||||||
type PropResponse: Serialize;
|
type PropResponse: Serialize;
|
||||||
@@ -66,7 +66,7 @@ pub trait HandlePropfind {
|
|||||||
fn propfind(&self, props: Vec<&str>) -> Result<impl Serialize>;
|
fn propfind(&self, props: Vec<&str>) -> Result<impl Serialize>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Resource> HandlePropfind for R {
|
impl<R: OldResource> HandlePropfind for R {
|
||||||
fn propfind(&self, props: Vec<&str>) -> Result<impl Serialize> {
|
fn propfind(&self, props: Vec<&str>) -> Result<impl Serialize> {
|
||||||
let mut props = props.into_iter().unique().collect_vec();
|
let mut props = props.into_iter().unique().collect_vec();
|
||||||
if props.contains(&"allprops") {
|
if props.contains(&"allprops") {
|
||||||
|
|||||||
Reference in New Issue
Block a user