mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 10:32:19 +00:00
lots of changes
This commit is contained in:
@@ -37,14 +37,16 @@ pub struct SupportedCalendarComponentSet {
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct CalendarData {
|
||||
#[serde(rename = "@content-type")]
|
||||
content_type: &'static str,
|
||||
#[serde(rename = "@version")]
|
||||
version: &'static str,
|
||||
}
|
||||
|
||||
impl Default for CalendarData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
content_type: "text/calendar;charset=utf-8",
|
||||
content_type: "text/calendar",
|
||||
version: "2.0",
|
||||
}
|
||||
}
|
||||
@@ -121,13 +123,13 @@ pub enum CalendarProp {
|
||||
CurrentUserPrincipal,
|
||||
Owner,
|
||||
Displayname,
|
||||
#[strum(props(tagname = "IC:calendar-color"))]
|
||||
// #[strum(props(tagname = "IC:calendar-color"))]
|
||||
CalendarColor,
|
||||
#[strum(props(tagname = "C:calendar-description"))]
|
||||
// #[strum(props(tagname = "C:calendar-description"))]
|
||||
CalendarDescription,
|
||||
#[strum(props(tagname = "C:supported-calendar-component-set"))]
|
||||
// #[strum(props(tagname = "C:supported-calendar-component-set"))]
|
||||
SupportedCalendarComponentSet,
|
||||
#[strum(props(tagname = "C:supported-calendar-data"))]
|
||||
// #[strum(props(tagname = "C:supported-calendar-data"))]
|
||||
SupportedCalendarData,
|
||||
Getcontenttype,
|
||||
CurrentUserPrivilegeSet,
|
||||
@@ -141,9 +143,19 @@ pub enum CalendarPropResponse {
|
||||
CurrentUserPrincipal(HrefElement),
|
||||
Owner(HrefElement),
|
||||
Displayname(TextNode),
|
||||
#[serde(rename = "IC:calendar-color", alias = "calendar-color")]
|
||||
CalendarColor(TextNode),
|
||||
#[serde(rename = "C:calendar-description", alias = "calendar-description")]
|
||||
CalendarDescription(TextNode),
|
||||
#[serde(
|
||||
rename = "C:supported-calendar-component-set",
|
||||
alias = "supported-calendar-component-set"
|
||||
)]
|
||||
SupportedCalendarComponentSet(SupportedCalendarComponentSet),
|
||||
#[serde(
|
||||
rename = "C:supported-calendar-data",
|
||||
alias = "supported-calendar-data"
|
||||
)]
|
||||
SupportedCalendarData(SupportedCalendarData),
|
||||
Getcontenttype(TextNode),
|
||||
MaxResourceSize(TextNode),
|
||||
|
||||
@@ -29,7 +29,7 @@ pub struct Resourcetype {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum RootPropResponse {
|
||||
Resourcetype(Resourcetype),
|
||||
CurrentUser(HrefElement),
|
||||
CurrentUserPrincipal(HrefElement),
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
@@ -63,9 +63,9 @@ impl Resource for RootResource {
|
||||
fn get_prop(&self, prop: Self::PropType) -> Result<Self::PropResponse> {
|
||||
match prop {
|
||||
RootProp::Resourcetype => Ok(RootPropResponse::Resourcetype(Resourcetype::default())),
|
||||
RootProp::CurrentUserPrincipal => Ok(RootPropResponse::CurrentUser(HrefElement::new(
|
||||
format!("{}/{}/", self.prefix, self.principal),
|
||||
))),
|
||||
RootProp::CurrentUserPrincipal => Ok(RootPropResponse::CurrentUserPrincipal(
|
||||
HrefElement::new(format!("{}/{}/", self.prefix, self.principal)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use actix_web::http::header::ContentType;
|
||||
use actix_web::web::{Data, Path};
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use anyhow::Result;
|
||||
use quick_xml::events::BytesText;
|
||||
use roxmltree::{Node, NodeType};
|
||||
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
|
||||
use rustical_dav::namespace::Namespace;
|
||||
@@ -51,22 +50,28 @@ async fn handle_report_calendar_query<C: CalendarStore + ?Sized>(
|
||||
.map(|node| node.tag_name().name())
|
||||
.collect();
|
||||
|
||||
let mut event_results = Vec::new();
|
||||
for event in events {
|
||||
let path = format!("{}/{}", request.path(), event.get_uid());
|
||||
let event_resource = EventResource {
|
||||
cal_store: cal_store.clone(),
|
||||
path: path.clone(),
|
||||
event,
|
||||
};
|
||||
event_results.push(event_resource.propfind(props.clone())?);
|
||||
}
|
||||
let event_resources: Vec<_> = events
|
||||
.iter()
|
||||
.map(|event| {
|
||||
let path = format!("{}/{}", request.path(), event.get_uid());
|
||||
EventResource {
|
||||
cal_store: cal_store.clone(),
|
||||
path: path.clone(),
|
||||
event: event.clone(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let event_results: Result<Vec<_>, _> = event_resources
|
||||
.iter()
|
||||
.map(|ev| ev.propfind(props.clone()))
|
||||
.collect();
|
||||
let event_responses = event_results?;
|
||||
|
||||
let output = generate_multistatus(vec![Namespace::Dav, Namespace::CalDAV], |writer| {
|
||||
for result in event_results {
|
||||
writer.write_event(quick_xml::events::Event::Text(BytesText::from_escaped(
|
||||
result,
|
||||
)))?;
|
||||
for result in event_responses {
|
||||
writer
|
||||
.write_serializable("response", &result)
|
||||
.map_err(|_e| quick_xml::Error::TextNotFound)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -90,8 +95,6 @@ pub async fn route_report_calendar<A: CheckAuthentication, C: CalendarStore + ?S
|
||||
let query_node = doc.root_element();
|
||||
let events = context.store.read().await.get_events(&cid).await.unwrap();
|
||||
|
||||
dbg!(&body);
|
||||
|
||||
// TODO: implement filtering
|
||||
match query_node.tag_name().name() {
|
||||
"calendar-query" => {}
|
||||
|
||||
@@ -4,13 +4,13 @@ use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Data, Path};
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use anyhow::Result;
|
||||
use quick_xml::events::BytesText;
|
||||
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 thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -76,7 +76,6 @@ pub async fn route_propfind<A: CheckAuthentication, R: Resource, C: CalendarStor
|
||||
depth: Depth,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let props = parse_propfind(&body)?;
|
||||
// let req_path = req.path().to_string();
|
||||
let auth_info = auth.inner;
|
||||
|
||||
let resource = R::acquire_from_request(
|
||||
@@ -87,21 +86,26 @@ pub async fn route_propfind<A: CheckAuthentication, R: Resource, C: CalendarStor
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut responses = vec![resource.propfind(props.clone())?];
|
||||
let response = resource.propfind(props.clone())?;
|
||||
let members = resource.get_members().await?;
|
||||
let mut member_responses = Vec::new();
|
||||
|
||||
if depth != Depth::Zero {
|
||||
for member in resource.get_members().await? {
|
||||
responses.push(member.propfind(props.clone())?);
|
||||
for member in &members {
|
||||
member_responses.push(member.propfind(props.clone())?);
|
||||
}
|
||||
}
|
||||
|
||||
let output = generate_multistatus(
|
||||
vec![Namespace::Dav, Namespace::CalDAV, Namespace::ICal],
|
||||
|writer| {
|
||||
for response in responses {
|
||||
writer.write_event(quick_xml::events::Event::Text(BytesText::from_escaped(
|
||||
response,
|
||||
)))?;
|
||||
writer
|
||||
.write_serializable("response", &response)
|
||||
.map_err(|_e| quick_xml::Error::TextNotFound)?;
|
||||
for response in member_responses {
|
||||
writer
|
||||
.write_serializable("response", &response)
|
||||
.map_err(|_e| quick_xml::Error::TextNotFound)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@@ -111,3 +115,17 @@ pub async fn route_propfind<A: CheckAuthentication, R: Resource, C: CalendarStor
|
||||
.content_type(ContentType::xml())
|
||||
.body(output))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MultistatusElement<T1: Serialize, T2: Serialize> {
|
||||
#[serde(rename = "$value")]
|
||||
responses: Vec<T1>,
|
||||
#[serde(rename = "$value")]
|
||||
member_responses: Vec<T2>,
|
||||
#[serde(rename = "@xmlns")]
|
||||
ns_dav: &'static str,
|
||||
#[serde(rename = "@xmlns:C")]
|
||||
ns_caldav: &'static str,
|
||||
#[serde(rename = "@xmlns:IC")]
|
||||
ns_ical: &'static str,
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ futures-util = "0.3"
|
||||
quick-xml = "0.31"
|
||||
rustical_auth = { path = "../auth/" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
strum = "0.26.2"
|
||||
strum = "0.26"
|
||||
itertools = "0.12"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod depth_extractor;
|
||||
pub mod namespace;
|
||||
pub mod resource;
|
||||
pub mod tagname;
|
||||
pub mod xml_snippets;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use actix_web::{http::StatusCode, HttpRequest};
|
||||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use quick_xml::Writer;
|
||||
use itertools::Itertools;
|
||||
use rustical_auth::AuthInfo;
|
||||
use serde::Serialize;
|
||||
use std::str::FromStr;
|
||||
@@ -51,18 +51,25 @@ struct PropstatElement<T: Serialize> {
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
struct PropstatResponseElement<T: Serialize> {
|
||||
struct PropstatResponseElement<T1: Serialize, T2: Serialize> {
|
||||
href: String,
|
||||
propstat: PropstatElement<T>,
|
||||
propstat: Vec<PropstatType<T1, T2>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum PropstatType<T1: Serialize, T2: Serialize> {
|
||||
Normal(PropstatElement<T1>),
|
||||
NotFound(PropstatElement<T2>),
|
||||
}
|
||||
|
||||
pub trait HandlePropfind {
|
||||
fn propfind(&self, props: Vec<&str>) -> Result<String>;
|
||||
fn propfind(&self, props: Vec<&str>) -> Result<impl Serialize>;
|
||||
}
|
||||
|
||||
impl<R: Resource> HandlePropfind for R {
|
||||
fn propfind(&self, props: Vec<&str>) -> Result<String> {
|
||||
let mut props = props;
|
||||
fn propfind(&self, props: Vec<&str>) -> Result<impl Serialize> {
|
||||
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
|
||||
@@ -71,9 +78,6 @@ impl<R: Resource> HandlePropfind for R {
|
||||
props = R::list_dead_props().into();
|
||||
}
|
||||
|
||||
let mut output_buffer = Vec::new();
|
||||
let mut writer = Writer::new_with_indent(&mut output_buffer, b' ', 2);
|
||||
|
||||
let mut invalid_props = Vec::<&str>::new();
|
||||
let mut prop_responses = Vec::new();
|
||||
for prop in props {
|
||||
@@ -89,31 +93,22 @@ impl<R: Resource> HandlePropfind for R {
|
||||
}
|
||||
}
|
||||
|
||||
writer.write_serializable(
|
||||
"response",
|
||||
&PropstatResponseElement {
|
||||
href: self.get_path().to_owned(),
|
||||
propstat: PropstatElement {
|
||||
status: format!("HTTP/1.1 {}", StatusCode::OK),
|
||||
prop: PropWrapper {
|
||||
prop: prop_responses,
|
||||
},
|
||||
},
|
||||
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() {
|
||||
// TODO: proper error reporting
|
||||
writer.write_serializable(
|
||||
"response",
|
||||
&PropstatResponseElement {
|
||||
href: self.get_path().to_owned(),
|
||||
propstat: PropstatElement {
|
||||
status: format!("HTTP/1.1 {}", StatusCode::NOT_FOUND),
|
||||
prop: TagList(invalid_props.iter().map(|&s| s.to_owned()).collect()),
|
||||
},
|
||||
},
|
||||
)?;
|
||||
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(std::str::from_utf8(&output_buffer)?.to_string())
|
||||
Ok(PropstatResponseElement {
|
||||
href: self.get_path().to_owned(),
|
||||
propstat: propstats,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
use strum::EnumProperty;
|
||||
|
||||
pub trait TagName {
|
||||
fn tagname(self) -> &'static str;
|
||||
}
|
||||
|
||||
impl<P: EnumProperty + Into<&'static str>> TagName for P {
|
||||
fn tagname(self) -> &'static str {
|
||||
self.get_str("tagname").unwrap_or(self.into())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user