aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--aero-ical/src/lib.rs2
-rw-r--r--aero-ical/src/prune.rs63
-rw-r--r--aero-proto/src/dav/controller.rs1
-rw-r--r--aero-proto/src/dav/resource.rs53
-rw-r--r--aerogramme/tests/behavior.rs50
-rw-r--r--aerogramme/tests/common/constants.rs28
6 files changed, 160 insertions, 37 deletions
diff --git a/aero-ical/src/lib.rs b/aero-ical/src/lib.rs
index 696010a..3f6f633 100644
--- a/aero-ical/src/lib.rs
+++ b/aero-ical/src/lib.rs
@@ -4,5 +4,5 @@
/// the goal will be to rewrite it in the end so it better
/// integrates into Aerogramme
pub mod parser;
-pub mod query;
pub mod prune;
+pub mod query;
diff --git a/aero-ical/src/prune.rs b/aero-ical/src/prune.rs
index d04f700..3eb50ca 100644
--- a/aero-ical/src/prune.rs
+++ b/aero-ical/src/prune.rs
@@ -1,40 +1,55 @@
-use icalendar::parser::{Component, Property};
use aero_dav::caltypes as cal;
+use icalendar::parser::{Component, Property};
pub fn component<'a>(src: &'a Component<'a>, prune: &cal::Comp) -> Option<Component<'a>> {
if src.name.as_str() != prune.name.as_str() {
- return None
+ return None;
}
let name = src.name.clone();
let properties = match &prune.prop_kind {
- None => vec![],
- Some(cal::PropKind::AllProp) => src.properties.clone(),
- Some(cal::PropKind::Prop(l)) => src.properties.iter().filter_map(|prop| {
- let sel_filt = match l.iter().find(|filt| filt.name.0.as_str() == prop.name.as_str()) {
- Some(v) => v,
- None => return None
- };
+ Some(cal::PropKind::AllProp) | None => src.properties.clone(),
+ Some(cal::PropKind::Prop(l)) => src
+ .properties
+ .iter()
+ .filter_map(|prop| {
+ let sel_filt = match l
+ .iter()
+ .find(|filt| filt.name.0.as_str() == prop.name.as_str())
+ {
+ Some(v) => v,
+ None => return None,
+ };
- match sel_filt.novalue {
- None | Some(false) => Some(prop.clone()),
- Some(true) => Some(Property {
- name: prop.name.clone(),
- params: prop.params.clone(),
- val: "".into()
- }),
- }
- }).collect::<Vec<_>>(),
+ match sel_filt.novalue {
+ None | Some(false) => Some(prop.clone()),
+ Some(true) => Some(Property {
+ name: prop.name.clone(),
+ params: prop.params.clone(),
+ val: "".into(),
+ }),
+ }
+ })
+ .collect::<Vec<_>>(),
};
let components = match &prune.comp_kind {
- None => vec![],
- Some(cal::CompKind::AllComp) => src.components.clone(),
- Some(cal::CompKind::Comp(many_inner_prune)) => src.components.iter().filter_map(|src_component| {
- many_inner_prune.iter().find_map(|inner_prune| component(src_component, inner_prune))
- }).collect::<Vec<_>>(),
+ Some(cal::CompKind::AllComp) | None => src.components.clone(),
+ Some(cal::CompKind::Comp(many_inner_prune)) => src
+ .components
+ .iter()
+ .filter_map(|src_component| {
+ many_inner_prune
+ .iter()
+ .find_map(|inner_prune| component(src_component, inner_prune))
+ })
+ .collect::<Vec<_>>(),
};
- Some(Component { name, properties, components })
+ Some(Component {
+ name,
+ properties,
+ components,
+ })
}
diff --git a/aero-proto/src/dav/controller.rs b/aero-proto/src/dav/controller.rs
index abf6a97..eeb6d43 100644
--- a/aero-proto/src/dav/controller.rs
+++ b/aero-proto/src/dav/controller.rs
@@ -333,7 +333,6 @@ impl<'a> Path<'a> {
}
}
-//@FIXME move somewhere else
//@FIXME naive implementation, must be refactored later
use futures::stream::Stream;
fn apply_filter<'a>(
diff --git a/aero-proto/src/dav/resource.rs b/aero-proto/src/dav/resource.rs
index d65ce38..04bae4f 100644
--- a/aero-proto/src/dav/resource.rs
+++ b/aero-proto/src/dav/resource.rs
@@ -565,17 +565,49 @@ impl DavNode for EventNode {
dav::Property::GetEtag(etag)
}
dav::PropertyRequest::Extension(all::PropertyRequest::Cal(
- cal::PropertyRequest::CalendarData(_req),
- )) => {
+ cal::PropertyRequest::CalendarData(req),
+ )) => {
let ics = String::from_utf8(
this.col.get(this.blob_id).await.or(Err(n.clone()))?,
- )
- .or(Err(n.clone()))?;
+ )
+ .or(Err(n.clone()))?;
+
+ let new_ics = match &req.comp {
+ None => ics,
+ Some(prune_comp) => {
+ // parse content
+ let ics = match icalendar::parser::read_calendar(&ics) {
+ Ok(v) => v,
+ Err(e) => {
+ tracing::warn!(err=?e, "Unable to parse ICS in calendar-query");
+ return Err(n.clone())
+ }
+ };
+
+ // build a fake vcal component for caldav compat
+ let fake_vcal_component = icalendar::parser::Component {
+ name: cal::Component::VCalendar.as_str().into(),
+ properties: ics.properties,
+ components: ics.components,
+ };
+
+ // rebuild component
+ let new_comp = match aero_ical::prune::component(&fake_vcal_component, prune_comp) {
+ Some(v) => v,
+ None => return Err(n.clone()),
+ };
+
+ // reserialize
+ format!("{}", icalendar::parser::Calendar { properties: new_comp.properties, components: new_comp.components })
+ },
+ };
+
+
dav::Property::Extension(all::Property::Cal(
cal::Property::CalendarData(cal::CalendarDataPayload {
mime: None,
- payload: ics,
+ payload: new_ics,
}),
))
}
@@ -634,14 +666,15 @@ impl DavNode for EventNode {
// so we load everything in memory
let calendar = self.col.clone();
let blob_id = self.blob_id.clone();
- let r = async move {
- let content = calendar
+ let calblob = async move {
+ let raw_ics = calendar
.get(blob_id)
.await
- .or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted)));
- Ok(hyper::body::Bytes::from(content?))
+ .or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted)))?;
+
+ Ok(hyper::body::Bytes::from(raw_ics))
};
- futures::stream::once(Box::pin(r)).boxed()
+ futures::stream::once(Box::pin(calblob)).boxed()
}
fn content_type(&self) -> &str {
diff --git a/aerogramme/tests/behavior.rs b/aerogramme/tests/behavior.rs
index 7b93d51..c88583f 100644
--- a/aerogramme/tests/behavior.rs
+++ b/aerogramme/tests/behavior.rs
@@ -956,7 +956,55 @@ fn rfc4791_webdav_caldav() {
);
// --- REPORT calendar-query, with calendar-data tx ---
- //@FIXME add support for calendar-data...
+ let cal_query = r#"<?xml version="1.0" encoding="utf-8" ?>
+ <C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
+ <D:prop>
+ <D:getetag/>
+ <C:calendar-data>
+ <C:comp name="VCALENDAR">
+ <C:prop name="VERSION"/>
+ <C:comp name="VEVENT">
+ <C:prop name="UID"/>
+ <C:prop name="DTSTART"/>
+ <C:prop name="DTEND"/>
+ <C:prop name="DURATION"/>
+ <C:prop name="RRULE"/>
+ <C:prop name="RDATE"/>
+ <C:prop name="EXRULE"/>
+ <C:prop name="EXDATE"/>
+ <C:prop name="RECURRENCE-ID"/>
+ </C:comp>
+ <C:comp name="VTIMEZONE"/>
+ </C:comp>
+ </C:calendar-data>
+ </D:prop>
+ <C:filter>
+ <C:comp-filter name="VCALENDAR">
+ <C:comp-filter name="VEVENT">
+ <C:time-range start="20060104T000000Z" end="20060105T000000Z"/>
+ </C:comp-filter>
+ </C:comp-filter>
+ </C:filter>
+ </C:calendar-query>"#;
+
+ let resp = http
+ .request(
+ reqwest::Method::from_bytes(b"REPORT")?,
+ "http://localhost:8087/alice/calendar/Personal/",
+ )
+ .body(cal_query)
+ .send()?;
+ assert_eq!(resp.status(), 207);
+ let multistatus = dav_deserialize::<dav::Multistatus<All>>(&resp.text()?);
+ assert_eq!(multistatus.responses.len(), 1);
+ check_cal(
+ &multistatus,
+ (
+ "/alice/calendar/Personal/rfc3.ics",
+ Some(obj3_etag.to_str().expect("etag header convertible to str")),
+ Some(ICAL_RFC3_STRIPPED),
+ ),
+ );
Ok(())
})
diff --git a/aerogramme/tests/common/constants.rs b/aerogramme/tests/common/constants.rs
index c04bae0..16daec6 100644
--- a/aerogramme/tests/common/constants.rs
+++ b/aerogramme/tests/common/constants.rs
@@ -125,6 +125,34 @@ END:VEVENT
END:VCALENDAR
";
+pub static ICAL_RFC3_STRIPPED: &[u8] = b"BEGIN:VCALENDAR\r
+VERSION:2.0\r
+BEGIN:VTIMEZONE\r
+LAST-MODIFIED:20040110T032845Z\r
+TZID:US/Eastern\r
+BEGIN:DAYLIGHT\r
+DTSTART:20000404T020000\r
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\r
+TZNAME:EDT\r
+TZOFFSETFROM:-0500\r
+TZOFFSETTO:-0400\r
+END:DAYLIGHT\r
+BEGIN:STANDARD\r
+DTSTART:20001026T020000\r
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r
+TZNAME:EST\r
+TZOFFSETFROM:-0400\r
+TZOFFSETTO:-0500\r
+END:STANDARD\r
+END:VTIMEZONE\r
+BEGIN:VEVENT\r
+DTSTART;TZID=US/Eastern:20060104T100000\r
+DURATION:PT1H\r
+UID:DC6C50A017428C5216A2F1CD@example.com\r
+END:VEVENT\r
+END:VCALENDAR\r
+";
+
pub static ICAL_RFC4: &[u8] = br#"BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN