From 5bf3517acfea3694fbe586e69a0e02b94c61eb1b Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Mon, 18 Mar 2024 22:56:49 +0100 Subject: [PATCH] Pass thunderbird autodetect... --- aero-dav/src/caldecoder.rs | 9 +++++++++ aero-dav/src/calencoder.rs | 8 ++++++++ aero-dav/src/caltypes.rs | 26 ++++++++++++++++++++++++++ aero-proto/src/dav.rs | 21 ++++++++++++++------- 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/aero-dav/src/caldecoder.rs b/aero-dav/src/caldecoder.rs index dbc6e18..b124154 100644 --- a/aero-dav/src/caldecoder.rs +++ b/aero-dav/src/caldecoder.rs @@ -162,6 +162,11 @@ impl QRead for Violation { impl QRead for Property { async fn qread(xml: &mut Reader) -> Result { + if xml.maybe_open_start(CAL_URN, "calendar-home-set").await?.is_some() { + let href = xml.find().await?; + xml.close().await?; + return Ok(Property::CalendarHomeSet(href)) + } if xml.maybe_open_start(CAL_URN, "calendar-description").await?.is_some() { let lang = xml.prev_attr("xml:lang"); let text = xml.tag_string().await?; @@ -231,6 +236,10 @@ impl QRead for Property { impl QRead for PropertyRequest { async fn qread(xml: &mut Reader) -> Result { + if xml.maybe_open(CAL_URN, "calendar-home-set").await?.is_some() { + xml.close().await?; + return Ok(Self::CalendarHomeSet) + } if xml.maybe_open(CAL_URN, "calendar-description").await?.is_some() { xml.close().await?; return Ok(Self::CalendarDescription) diff --git a/aero-dav/src/calencoder.rs b/aero-dav/src/calencoder.rs index 5323229..d4e79dc 100644 --- a/aero-dav/src/calencoder.rs +++ b/aero-dav/src/calencoder.rs @@ -88,6 +88,7 @@ impl QWrite for PropertyRequest { }; match self { + Self::CalendarHomeSet => atom("calendar-home-set").await, Self::CalendarDescription => atom("calendar-description").await, Self::CalendarTimezone => atom("calendar-timezone").await, Self::SupportedCalendarComponentSet => atom("supported-calendar-component-set").await, @@ -105,6 +106,13 @@ impl QWrite for PropertyRequest { impl QWrite for Property { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { match self { + Self::CalendarHomeSet(href) => { + let start = xml.create_cal_element("calendar-home-set"); + let end = start.to_end(); + xml.q.write_event_async(Event::Start(start.clone())).await?; + href.qwrite(xml).await?; + xml.q.write_event_async(Event::End(end)).await + } Self::CalendarDescription { lang, text } => { let mut start = xml.create_cal_element("calendar-description"); if let Some(the_lang) = lang { diff --git a/aero-dav/src/caltypes.rs b/aero-dav/src/caltypes.rs index 5ac50e6..aa056d4 100644 --- a/aero-dav/src/caltypes.rs +++ b/aero-dav/src/caltypes.rs @@ -116,6 +116,7 @@ pub enum ResourceType { /// Check the matching Property object for documentation #[derive(Debug, PartialEq, Clone)] pub enum PropertyRequest { + CalendarHomeSet, CalendarDescription, CalendarTimezone, SupportedCalendarComponentSet, @@ -131,6 +132,31 @@ pub enum PropertyRequest { #[derive(Debug, PartialEq, Clone)] pub enum Property { + /// Name: calendar-home-set + /// + /// Namespace: urn:ietf:params:xml:ns:caldav + /// + /// Purpose: Identifies the URL of any WebDAV collections that contain + /// calendar collections owned by the associated principal resource. + /// + /// Conformance: This property SHOULD be defined on a principal + /// resource. If defined, it MAY be protected and SHOULD NOT be + /// returned by a PROPFIND DAV:allprop request (as defined in Section + /// 12.14.1 of [RFC2518]). + /// + /// Description: The CALDAV:calendar-home-set property is meant to allow + /// users to easily find the calendar collections owned by the + /// principal. Typically, users will group all the calendar + /// collections that they own under a common collection. This + /// property specifies the URL of collections that are either calendar + /// collections or ordinary collections that have child or descendant + /// calendar collections owned by the principal. + /// + /// Definition: + /// + /// + CalendarHomeSet(dav::Href), + /// Name: calendar-description /// /// Namespace: urn:ietf:params:xml:ns:caldav diff --git a/aero-proto/src/dav.rs b/aero-proto/src/dav.rs index b964760..c8e534e 100644 --- a/aero-proto/src/dav.rs +++ b/aero-proto/src/dav.rs @@ -212,7 +212,7 @@ async fn router(user: std::sync::Arc, req: Request) -> Result { tracing::warn!("HEAD+GET not correctly implemented"); return Ok(Response::builder() - .status(200) + .status(404) .body(text_body(""))?) }, "PROPFIND" => propfind(user, req, node).await, @@ -259,6 +259,7 @@ const ALLPROP: [dav::PropertyRequest; 10] = [ async fn propfind(user: std::sync::Arc, req: Request, node: Box) -> Result>> { let depth = depth(&req); + let status = hyper::StatusCode::from_u16(207)?; // A client may choose not to submit a request body. An empty PROPFIND // request body MUST be treated as if it were an 'allprop' request. @@ -268,7 +269,7 @@ async fn propfind(user: std::sync::Arc, req: Request, node: Box< tracing::debug!(recv=?propfind, "inferred propfind request"); if matches!(propfind, dav::PropFind::PropName) { - return serialize(node.multistatus_name(&user, depth)); + return serialize(status, node.multistatus_name(&user, depth)); } let propname = match propfind { @@ -281,7 +282,7 @@ async fn propfind(user: std::sync::Arc, req: Request, node: Box< dav::PropFind::Prop(inner) => inner, }; - serialize(node.multistatus_val(&user, propname, depth)) + serialize(status, node.multistatus_val(&user, propname, depth)) } // ---- HTTP DAV Binding @@ -308,7 +309,7 @@ fn text_body(txt: &'static str) -> BoxBody { BoxBody::new(Full::new(Bytes::from(txt)).map_err(|e| match e {})) } -fn serialize(elem: T) -> Result>> { +fn serialize(status_ok: hyper::StatusCode, elem: T) -> Result>> { let (tx, rx) = tokio::sync::mpsc::channel::(1); // Build the writer @@ -318,7 +319,7 @@ fn serialize(elem: T) -> Result (), Err(e) => tracing::error!(err=?e, "unable to write XML declaration "), @@ -336,7 +337,8 @@ fn serialize(elem: T) -> Result) -> Vec> { @@ -517,6 +520,8 @@ impl DavNode for HomeNode { dav::PropertyRequest::DisplayName => dav::AnyProperty::Value(dav::Property::DisplayName(format!("{} home", user.username))), dav::PropertyRequest::ResourceType => dav::AnyProperty::Value(dav::Property::ResourceType(vec![dav::ResourceType::Collection])), dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())), + dav::PropertyRequest::Extension(cal::PropertyRequest::CalendarHomeSet) => + dav::AnyProperty::Value(dav::Property::Extension(cal::Property::CalendarHomeSet(dav::Href(CalendarListNode{}.path(user))))), v => dav::AnyProperty::Request(v), }).collect() } @@ -604,7 +609,9 @@ impl DavNode for CalendarNode { dav::ResourceType::Collection, dav::ResourceType::Extension(cal::ResourceType::Calendar), ])), - dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())), + //dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("httpd/unix-directory".into())), + //@FIXME seems wrong but seems to be what Thunderbird expects... + dav::PropertyRequest::GetContentType => dav::AnyProperty::Value(dav::Property::GetContentType("text/calendar".into())), v => dav::AnyProperty::Request(v), }).collect() }