aboutsummaryrefslogtreecommitdiff
path: root/src/dav
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2024-03-08 08:17:03 +0100
committerQuentin Dufour <quentin@deuxfleurs.fr>2024-03-08 08:17:03 +0100
commit1a43ce5ac7033c148f64a033f2b1d335e95e11d5 (patch)
tree60b234604170fe207248458a9c4cdd3f4b7c36f2 /src/dav
parentbb9cb386b65834c44cae86bd100f800883022062 (diff)
downloadaerogramme-1a43ce5ac7033c148f64a033f2b1d335e95e11d5.tar.gz
aerogramme-1a43ce5ac7033c148f64a033f2b1d335e95e11d5.zip
WIP refactor
Diffstat (limited to 'src/dav')
-rw-r--r--src/dav/acltypes.rs4
-rw-r--r--src/dav/caldecoder.rs33
-rw-r--r--src/dav/calencoder.rs886
-rw-r--r--src/dav/caltypes.rs1440
-rw-r--r--src/dav/decoder.rs948
-rw-r--r--src/dav/encoder.rs1117
-rw-r--r--src/dav/error.rs42
-rw-r--r--src/dav/mod.rs167
-rw-r--r--src/dav/realization.rs42
-rw-r--r--src/dav/types.rs950
-rw-r--r--src/dav/versioningtypes.rs3
-rw-r--r--src/dav/xml.rs273
12 files changed, 0 insertions, 5905 deletions
diff --git a/src/dav/acltypes.rs b/src/dav/acltypes.rs
deleted file mode 100644
index f356813..0000000
--- a/src/dav/acltypes.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-//@FIXME required for a full DAV implementation
-// See section 6. of the CalDAV RFC
-// It seems mainly required for free-busy that I will not implement now.
-// It can also be used for discovering main calendar, not sure it is used.
diff --git a/src/dav/caldecoder.rs b/src/dav/caldecoder.rs
deleted file mode 100644
index 5f40c4b..0000000
--- a/src/dav/caldecoder.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use super::types as dav;
-use super::caltypes::*;
-use super::xml;
-use super::error;
-
-// ---- ROOT ELEMENTS ---
-
-// ---- EXTENSIONS ---
-impl xml::QRead<Violation> for Violation {
- async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
- unreachable!();
- }
-}
-
-impl xml::QRead<Property> for Property {
- async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
- unreachable!();
- }
-}
-
-impl xml::QRead<PropertyRequest> for PropertyRequest {
- async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
- unreachable!();
- }
-}
-
-impl xml::QRead<ResourceType> for ResourceType {
- async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
- unreachable!();
- }
-}
-
-// ---- INNER XML ----
diff --git a/src/dav/calencoder.rs b/src/dav/calencoder.rs
deleted file mode 100644
index 58b88c7..0000000
--- a/src/dav/calencoder.rs
+++ /dev/null
@@ -1,886 +0,0 @@
-use quick_xml::Error as QError;
-use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText};
-use quick_xml::name::PrefixDeclaration;
-use tokio::io::AsyncWrite;
-
-use super::caltypes::*;
-use super::xml::{Node, QWrite, IWrite, Writer};
-use super::types::Extension;
-
-const ICAL_DATETIME_FMT: &str = "%Y%m%dT%H%M%SZ";
-
-// ==================== Calendar Types Serialization =========================
-
-// -------------------- MKCALENDAR METHOD ------------------------------------
-impl<E: Extension> QWrite for MkCalendar<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_cal_element("mkcalendar");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension, N: Node<N>> QWrite for MkCalendarResponse<E,N> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_cal_element("mkcalendar-response");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for propstat in self.0.iter() {
- propstat.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-// ----------------------- REPORT METHOD -------------------------------------
-
-impl<E: Extension> QWrite for CalendarQuery<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_cal_element("calendar-query");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- if let Some(selector) = &self.selector {
- selector.qwrite(xml).await?;
- }
- self.filter.qwrite(xml).await?;
- if let Some(tz) = &self.timezone {
- tz.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension> QWrite for CalendarMultiget<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_cal_element("calendar-multiget");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- if let Some(selector) = &self.selector {
- selector.qwrite(xml).await?;
- }
- for href in self.href.iter() {
- href.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for FreeBusyQuery {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_cal_element("free-busy-query");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-// -------------------------- DAV::prop --------------------------------------
-impl QWrite for PropertyRequest {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut atom = async |c| {
- let empty_tag = xml.create_cal_element(c);
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- };
-
- match self {
- Self::CalendarDescription => atom("calendar-description").await,
- Self::CalendarTimezone => atom("calendar-timezone").await,
- Self::SupportedCalendarComponentSet => atom("supported-calendar-component-set").await,
- Self::SupportedCalendarData => atom("supported-calendar-data").await,
- Self::MaxResourceSize => atom("max-resource-size").await,
- Self::MinDateTime => atom("min-date-time").await,
- Self::MaxDateTime => atom("max-date-time").await,
- Self::MaxInstances => atom("max-instances").await,
- Self::MaxAttendeesPerInstance => atom("max-attendees-per-instance").await,
- Self::SupportedCollationSet => atom("supported-collation-set").await,
- Self::CalendarData(req) => req.qwrite(xml).await,
- }
- }
-}
-impl QWrite for Property {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::CalendarDescription { lang, text } => {
- let mut start = xml.create_cal_element("calendar-description");
- if let Some(the_lang) = lang {
- start.push_attribute(("xml:lang", the_lang.as_str()));
- }
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(text))).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::CalendarTimezone(payload) => {
- let start = xml.create_cal_element("calendar-timezone");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(payload))).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::SupportedCalendarComponentSet(many_comp) => {
- let start = xml.create_cal_element("supported-calendar-component-set");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for comp in many_comp.iter() {
- comp.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::SupportedCalendarData(many_mime) => {
- let start = xml.create_cal_element("supported-calendar-data");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for mime in many_mime.iter() {
- mime.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::MaxResourceSize(bytes) => {
- let start = xml.create_cal_element("max-resource-size");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(bytes.to_string().as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::MinDateTime(dt) => {
- let start = xml.create_cal_element("min-date-time");
- let end = start.to_end();
-
- let dtstr = format!("{}", dt.format(ICAL_DATETIME_FMT));
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(dtstr.as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::MaxDateTime(dt) => {
- let start = xml.create_cal_element("max-date-time");
- let end = start.to_end();
-
- let dtstr = format!("{}", dt.format(ICAL_DATETIME_FMT));
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(dtstr.as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::MaxInstances(count) => {
- let start = xml.create_cal_element("max-instances");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(count.to_string().as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::MaxAttendeesPerInstance(count) => {
- let start = xml.create_cal_element("max-attendees-per-instance");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(count.to_string().as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::SupportedCollationSet(many_collations) => {
- let start = xml.create_cal_element("supported-collation-set");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for collation in many_collations.iter() {
- collation.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::CalendarData(inner) => inner.qwrite(xml).await,
- }
- }
-}
-
-// ---------------------- DAV::resourcetype ----------------------------------
-impl QWrite for ResourceType {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::Calendar => {
- let empty_tag = xml.create_dav_element("calendar");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- }
- }
-}
-
-// --------------------------- DAV::error ------------------------------------
-impl QWrite for Violation {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut atom = async |c| {
- let empty_tag = xml.create_cal_element(c);
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- };
-
- match self {
- //@FIXME
- // DAV elements, should not be here but in RFC3744 on ACLs
- // (we do not use atom as this error is in the DAV namespace, not the caldav one)
- Self::NeedPrivileges => {
- let empty_tag = xml.create_dav_element("need-privileges");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
-
- // Regular CalDAV errors
- Self::ResourceMustBeNull => atom("resource-must-be-null").await,
- Self::CalendarCollectionLocationOk => atom("calendar-collection-location-ok").await,
- Self::ValidCalendarData => atom("valid-calendar-data").await,
- Self::InitializeCalendarCollection => atom("initialize-calendar-collection").await,
- Self::SupportedCalendarData => atom("supported-calendar-data").await,
- Self::ValidCalendarObjectResource => atom("valid-calendar-object-resource").await,
- Self::SupportedCalendarComponent => atom("supported-calendar-component").await,
- Self::NoUidConflict(href) => {
- let start = xml.create_cal_element("no-uid-conflict");
- 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::MaxResourceSize => atom("max-resource-size").await,
- Self::MinDateTime => atom("min-date-time").await,
- Self::MaxDateTime => atom("max-date-time").await,
- Self::MaxInstances => atom("max-instances").await,
- Self::MaxAttendeesPerInstance => atom("max-attendees-per-instance").await,
- Self::ValidFilter => atom("valid-filter").await,
- Self::SupportedFilter { comp, prop, param } => {
- let start = xml.create_cal_element("supported-filter");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for comp_item in comp.iter() {
- comp_item.qwrite(xml).await?;
- }
- for prop_item in prop.iter() {
- prop_item.qwrite(xml).await?;
- }
- for param_item in param.iter() {
- param_item.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- },
- Self::NumberOfMatchesWithinLimits => atom("number-of-matches-within-limits").await,
- }
- }
-}
-
-
-// ---------------------------- Inner XML ------------------------------------
-impl QWrite for SupportedCollation {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_cal_element("supported-collation");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
-
- }
-}
-
-impl QWrite for Collation {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let col = match self {
- Self::AsciiCaseMap => "i;ascii-casemap",
- Self::Octet => "i;octet",
- Self::Unknown(v) => v.as_str(),
- };
-
- xml.q.write_event_async(Event::Text(BytesText::new(col))).await
- }
-}
-
-impl QWrite for CalendarDataPayload {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("calendar-data");
- if let Some(mime) = &self.mime {
- start.push_attribute(("content-type", mime.content_type.as_str()));
- start.push_attribute(("version", mime.version.as_str()));
- }
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(self.payload.as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for CalendarDataRequest {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("calendar-data");
- if let Some(mime) = &self.mime {
- start.push_attribute(("content-type", mime.content_type.as_str()));
- start.push_attribute(("version", mime.version.as_str()));
- }
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- if let Some(comp) = &self.comp {
- comp.qwrite(xml).await?;
- }
- if let Some(recurrence) = &self.recurrence {
- recurrence.qwrite(xml).await?;
- }
- if let Some(freebusy) = &self.limit_freebusy_set {
- freebusy.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for CalendarDataEmpty {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut empty = xml.create_cal_element("calendar-data");
- if let Some(mime) = &self.0 {
- empty.push_attribute(("content-type", mime.content_type.as_str()));
- empty.push_attribute(("version", mime.version.as_str()));
- }
- xml.q.write_event_async(Event::Empty(empty)).await
- }
-}
-
-impl QWrite for Comp {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("comp");
- start.push_attribute(("name", self.name.as_str()));
- match &self.additional_rules {
- None => xml.q.write_event_async(Event::Empty(start)).await,
- Some(rules) => {
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- rules.prop_kind.qwrite(xml).await?;
- rules.comp_kind.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- },
- }
- }
-}
-
-impl QWrite for CompSupport {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut empty = xml.create_cal_element("comp");
- empty.push_attribute(("name", self.0.as_str()));
- xml.q.write_event_async(Event::Empty(empty)).await
- }
-}
-
-impl QWrite for CompKind {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::AllComp => {
- let empty_tag = xml.create_cal_element("allcomp");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- Self::Comp(many_comp) => {
- for comp in many_comp.iter() {
- // Required: recursion in an async fn requires boxing
- // rustc --explain E0733
- Box::pin(comp.qwrite(xml)).await?;
- }
- Ok(())
- }
- }
- }
-}
-
-impl QWrite for PropKind {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::AllProp => {
- let empty_tag = xml.create_cal_element("allprop");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- Self::Prop(many_prop) => {
- for prop in many_prop.iter() {
- prop.qwrite(xml).await?;
- }
- Ok(())
- }
- }
- }
-}
-
-impl QWrite for CalProp {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut empty = xml.create_cal_element("prop");
- empty.push_attribute(("name", self.name.0.as_str()));
- match self.novalue {
- None => (),
- Some(true) => empty.push_attribute(("novalue", "yes")),
- Some(false) => empty.push_attribute(("novalue", "no")),
- }
- xml.q.write_event_async(Event::Empty(empty)).await
- }
-}
-
-impl QWrite for RecurrenceModifier {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::Expand(exp) => exp.qwrite(xml).await,
- Self::LimitRecurrenceSet(lrs) => lrs.qwrite(xml).await,
- }
- }
-}
-
-impl QWrite for Expand {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut empty = xml.create_cal_element("expand");
- empty.push_attribute(("start", format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str()));
- empty.push_attribute(("end", format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str()));
- xml.q.write_event_async(Event::Empty(empty)).await
- }
-}
-
-impl QWrite for LimitRecurrenceSet {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut empty = xml.create_cal_element("limit-recurrence-set");
- empty.push_attribute(("start", format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str()));
- empty.push_attribute(("end", format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str()));
- xml.q.write_event_async(Event::Empty(empty)).await
- }
-}
-
-impl QWrite for LimitFreebusySet {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut empty = xml.create_cal_element("limit-freebusy-set");
- empty.push_attribute(("start", format!("{}", self.0.format(ICAL_DATETIME_FMT)).as_str()));
- empty.push_attribute(("end", format!("{}", self.1.format(ICAL_DATETIME_FMT)).as_str()));
- xml.q.write_event_async(Event::Empty(empty)).await
- }
-}
-
-impl<E: Extension> QWrite for CalendarSelector<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::AllProp => {
- let empty_tag = xml.create_dav_element("allprop");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- Self::PropName => {
- let empty_tag = xml.create_dav_element("propname");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- Self::Prop(prop) => prop.qwrite(xml).await,
- }
- }
-}
-
-impl QWrite for CompFilter {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("comp-filter");
- start.push_attribute(("name", self.name.as_str()));
-
- match &self.additional_rules {
- None => xml.q.write_event_async(Event::Empty(start)).await,
- Some(rules) => {
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- rules.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
- }
- }
-}
-
-impl QWrite for CompFilterRules {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::IsNotDefined => {
- let empty_tag = xml.create_dav_element("is-not-defined");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- Self::Matches(cfm) => cfm.qwrite(xml).await,
- }
- }
-}
-
-impl QWrite for CompFilterMatch {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- if let Some(time_range) = &self.time_range {
- time_range.qwrite(xml).await?;
- }
-
- for prop_item in self.prop_filter.iter() {
- prop_item.qwrite(xml).await?;
- }
- for comp_item in self.comp_filter.iter() {
- // Required: recursion in an async fn requires boxing
- // rustc --explain E0733
- Box::pin(comp_item.qwrite(xml)).await?;
- }
- Ok(())
- }
-}
-
-impl QWrite for PropFilter {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("prop-filter");
- start.push_attribute(("name", self.name.as_str()));
-
- match &self.additional_rules {
- None => xml.q.write_event_async(Event::Empty(start.clone())).await,
- Some(rules) => {
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- rules.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
- }
- }
-}
-
-impl QWrite for PropFilterRules {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::IsNotDefined => {
- let empty_tag = xml.create_dav_element("is-not-defined");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- Self::Match(prop_match) => prop_match.qwrite(xml).await,
- }
- }
-}
-
-impl QWrite for PropFilterMatch {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- if let Some(time_range) = &self.time_range {
- time_range.qwrite(xml).await?;
- }
- if let Some(time_or_text) = &self.time_or_text {
- time_or_text.qwrite(xml).await?;
- }
- for param_item in self.param_filter.iter() {
- param_item.qwrite(xml).await?;
- }
- Ok(())
- }
-}
-
-impl QWrite for TimeOrText {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::Time(time) => time.qwrite(xml).await,
- Self::Text(txt) => txt.qwrite(xml).await,
- }
- }
-}
-
-impl QWrite for TextMatch {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("text-match");
- if let Some(collation) = &self.collation {
- start.push_attribute(("collation", collation.as_str()));
- }
- match self.negate_condition {
- None => (),
- Some(true) => start.push_attribute(("negate-condition", "yes")),
- Some(false) => start.push_attribute(("negate-condition", "no")),
- }
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(self.text.as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for ParamFilter {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("param-filter");
- start.push_attribute(("name", self.name.as_str()));
-
- match &self.additional_rules {
- None => xml.q.write_event_async(Event::Empty(start)).await,
- Some(rules) => {
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- rules.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
- }
- }
-}
-
-impl QWrite for ParamFilterMatch {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::IsNotDefined => {
- let empty_tag = xml.create_dav_element("is-not-defined");
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- },
- Self::Match(tm) => tm.qwrite(xml).await,
- }
- }
-}
-
-impl QWrite for TimeZone {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("timezone");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(self.0.as_str()))).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for Filter {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut start = xml.create_cal_element("filter");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for TimeRange {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut empty = xml.create_cal_element("time-range");
- match self {
- Self::OnlyStart(start) => empty.push_attribute(("start", format!("{}", start.format(ICAL_DATETIME_FMT)).as_str())),
- Self::OnlyEnd(end) => empty.push_attribute(("end", format!("{}", end.format(ICAL_DATETIME_FMT)).as_str())),
- Self::FullRange(start, end) => {
- empty.push_attribute(("start", format!("{}", start.format(ICAL_DATETIME_FMT)).as_str()));
- empty.push_attribute(("end", format!("{}", end.format(ICAL_DATETIME_FMT)).as_str()));
- }
- }
- xml.q.write_event_async(Event::Empty(empty)).await
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::dav::types as dav;
- use crate::dav::realization::Calendar;
- use tokio::io::AsyncWriteExt;
- use chrono::{Utc,TimeZone,DateTime};
-
- async fn serialize(elem: &impl QWrite) -> String {
- let mut buffer = Vec::new();
- let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
- let q = quick_xml::writer::Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
- let ns_to_apply = vec![
- ("xmlns:D".into(), "DAV:".into()),
- ("xmlns:C".into(), "urn:ietf:params:xml:ns:caldav".into()),
- ];
- let mut writer = Writer { q, ns_to_apply };
-
- elem.qwrite(&mut writer).await.expect("xml serialization");
- tokio_buffer.flush().await.expect("tokio buffer flush");
- let got = std::str::from_utf8(buffer.as_slice()).unwrap();
-
- return got.into()
- }
-
- #[tokio::test]
- async fn basic_violation() {
- let got = serialize(
- &dav::Error::<Calendar>(vec![
- dav::Violation::Extension(Violation::ResourceMustBeNull),
- ])
- ).await;
-
- let expected = r#"<D:error xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
- <C:resource-must-be-null/>
-</D:error>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_calendar_query1_req() {
- let got = serialize(
- &CalendarQuery::<Calendar> {
- selector: Some(CalendarSelector::Prop(dav::PropName(vec![
- dav::PropertyRequest::GetEtag,
- dav::PropertyRequest::Extension(PropertyRequest::CalendarData(CalendarDataRequest {
- mime: None,
- comp: Some(Comp {
- name: Component::VCalendar,
- additional_rules: Some(CompInner {
- prop_kind: PropKind::Prop(vec![
- CalProp {
- name: ComponentProperty("VERSION".into()),
- novalue: None,
- }
- ]),
- comp_kind: CompKind::Comp(vec![
- Comp {
- name: Component::VEvent,
- additional_rules: Some(CompInner {
- prop_kind: PropKind::Prop(vec![
- CalProp { name: ComponentProperty("SUMMARY".into()), novalue: None },
- CalProp { name: ComponentProperty("UID".into()), novalue: None },
- CalProp { name: ComponentProperty("DTSTART".into()), novalue: None },
- CalProp { name: ComponentProperty("DTEND".into()), novalue: None },
- CalProp { name: ComponentProperty("DURATION".into()), novalue: None },
- CalProp { name: ComponentProperty("RRULE".into()), novalue: None },
- CalProp { name: ComponentProperty("RDATE".into()), novalue: None },
- CalProp { name: ComponentProperty("EXRULE".into()), novalue: None },
- CalProp { name: ComponentProperty("EXDATE".into()), novalue: None },
- CalProp { name: ComponentProperty("RECURRENCE-ID".into()), novalue: None },
- ]),
- comp_kind: CompKind::Comp(vec![]),
- }),
- },
- Comp {
- name: Component::VTimeZone,
- additional_rules: None,
- }
- ]),
- }),
- }),
- recurrence: None,
- limit_freebusy_set: None,
- })),
- ]))),
- filter: Filter(CompFilter {
- name: Component::VCalendar,
- additional_rules: Some(CompFilterRules::Matches(CompFilterMatch {
- time_range: None,
- prop_filter: vec![],
- comp_filter: vec![
- CompFilter {
- name: Component::VEvent,
- additional_rules: Some(CompFilterRules::Matches(CompFilterMatch {
- time_range: Some(TimeRange::FullRange(
- Utc.with_ymd_and_hms(2006,1,4,0,0,0).unwrap(),
- Utc.with_ymd_and_hms(2006,1,5,0,0,0).unwrap(),
- )),
- prop_filter: vec![],
- comp_filter: vec![],
- })),
- },
- ],
- })),
- }),
- timezone: None,
- }
- ).await;
-
- let expected = r#"<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="SUMMARY"/>
- <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>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_calendar_query1_res() {
- let got = serialize(
- &dav::Multistatus::<Calendar,dav::PropValue<Calendar>> {
- responses: vec![
- dav::Response {
- status_or_propstat: dav::StatusOrPropstat::PropStat(
- dav::Href("http://cal.example.com/bernard/work/abcd2.ics".into()),
- vec![dav::PropStat {
- prop: dav::PropValue(vec![
- dav::Property::GetEtag("\"fffff-abcd2\"".into()),
- dav::Property::Extension(Property::CalendarData(CalendarDataPayload {
- mime: None,
- payload: "PLACEHOLDER".into()
- })),
- ]),
- status: dav::Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }]
- ),
- location: None,
- error: None,
- responsedescription: None,
- },
- dav::Response {
- status_or_propstat: dav::StatusOrPropstat::PropStat(
- dav::Href("http://cal.example.com/bernard/work/abcd3.ics".into()),
- vec![dav::PropStat {
- prop: dav::PropValue(vec![
- dav::Property::GetEtag("\"fffff-abcd3\"".into()),
- dav::Property::Extension(Property::CalendarData(CalendarDataPayload{
- mime: None,
- payload: "PLACEHOLDER".into(),
- })),
- ]),
- status: dav::Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }]
- ),
- location: None,
- error: None,
- responsedescription: None,
- },
- ],
- responsedescription: None,
- },
- ).await;
-
- let expected = r#"<D:multistatus xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
- <D:response>
- <D:href>http://cal.example.com/bernard/work/abcd2.ics</D:href>
- <D:propstat>
- <D:prop>
- <D:getetag>&quot;fffff-abcd2&quot;</D:getetag>
- <C:calendar-data>PLACEHOLDER</C:calendar-data>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
- <D:response>
- <D:href>http://cal.example.com/bernard/work/abcd3.ics</D:href>
- <D:propstat>
- <D:prop>
- <D:getetag>&quot;fffff-abcd3&quot;</D:getetag>
- <C:calendar-data>PLACEHOLDER</C:calendar-data>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
-</D:multistatus>"#;
-
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-}
diff --git a/src/dav/caltypes.rs b/src/dav/caltypes.rs
deleted file mode 100644
index befecef..0000000
--- a/src/dav/caltypes.rs
+++ /dev/null
@@ -1,1440 +0,0 @@
-#![allow(dead_code)]
-
-use chrono::{DateTime,Utc};
-use super::types as dav;
-use super::xml;
-
-//@FIXME ACL (rfc3744) is missing, required
-//@FIXME Versioning (rfc3253) is missing, required
-//@FIXME WebDAV sync (rfc6578) is missing, optional
-// For reference, SabreDAV guide gives high-level & real-world overview:
-// https://sabre.io/dav/building-a-caldav-client/
-// For reference, non-official extensions documented by SabreDAV:
-// https://github.com/apple/ccs-calendarserver/tree/master/doc/Extensions
-
-
-// ----- Root elements -----
-
-// --- (MKCALENDAR PART) ---
-
-/// If a request body is included, it MUST be a CALDAV:mkcalendar XML
-/// element. Instruction processing MUST occur in the order
-/// instructions are received (i.e., from top to bottom).
-/// Instructions MUST either all be executed or none executed. Thus,
-/// if any error occurs during processing, all executed instructions
-/// MUST be undone and a proper error result returned. Instruction
-/// processing details can be found in the definition of the DAV:set
-/// instruction in Section 12.13.2 of [RFC2518].
-///
-/// <!ELEMENT mkcalendar (DAV:set)>
-#[derive(Debug, PartialEq)]
-pub struct MkCalendar<E: dav::Extension>(pub dav::Set<E>);
-
-
-/// If a response body for a successful request is included, it MUST
-/// be a CALDAV:mkcalendar-response XML element.
-///
-/// <!ELEMENT mkcalendar-response ANY>
-///
-/// ----
-///
-/// ANY is not satisfying, so looking at RFC5689
-/// https://www.rfc-editor.org/rfc/rfc5689.html#section-5.2
-///
-/// Definition:
-///
-/// <!ELEMENT mkcol-response (propstat+)>
-#[derive(Debug, PartialEq)]
-pub struct MkCalendarResponse<E: dav::Extension, N: xml::Node<N>>(pub Vec<dav::PropStat<E,N>>);
-
-// --- (REPORT PART) ---
-
-/// Name: calendar-query
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Defines a report for querying calendar object resources.
-///
-/// Description: See Section 7.8.
-///
-/// Definition:
-///
-/// <!ELEMENT calendar-query ((DAV:allprop |
-/// DAV:propname |
-/// DAV:prop)?, filter, timezone?)>
-#[derive(Debug, PartialEq)]
-pub struct CalendarQuery<E: dav::Extension> {
- pub selector: Option<CalendarSelector<E>>,
- pub filter: Filter,
- pub timezone: Option<TimeZone>,
-}
-
-/// Name: calendar-multiget
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: CalDAV report used to retrieve specific calendar object
-/// resources.
-///
-/// Description: See Section 7.9.
-///
-/// Definition:
-///
-/// <!ELEMENT calendar-multiget ((DAV:allprop |
-/// DAV:propname |
-/// DAV:prop)?, DAV:href+)>
-#[derive(Debug, PartialEq)]
-pub struct CalendarMultiget<E: dav::Extension> {
- pub selector: Option<CalendarSelector<E>>,
- pub href: Vec<dav::Href>,
-}
-
-/// Name: free-busy-query
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: CalDAV report used to generate a VFREEBUSY to determine
-/// busy time over a specific time range.
-///
-/// Description: See Section 7.10.
-///
-/// Definition:
-/// <!ELEMENT free-busy-query (time-range)>
-#[derive(Debug, PartialEq)]
-pub struct FreeBusyQuery(pub TimeRange);
-
-// ----- Hooks -----
-#[derive(Debug, PartialEq)]
-pub enum ResourceType {
- Calendar,
-}
-
-/// Check the matching Property object for documentation
-#[derive(Debug, PartialEq)]
-pub enum PropertyRequest {
- CalendarDescription,
- CalendarTimezone,
- SupportedCalendarComponentSet,
- SupportedCalendarData,
- MaxResourceSize,
- MinDateTime,
- MaxDateTime,
- MaxInstances,
- MaxAttendeesPerInstance,
- SupportedCollationSet,
- CalendarData(CalendarDataRequest),
-}
-
-#[derive(Debug, PartialEq)]
-pub enum Property {
- /// Name: calendar-description
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Provides a human-readable description of the calendar
- /// collection.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. 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]). An xml:lang attribute indicating the human
- /// language of the description SHOULD be set for this property by
- /// clients or through server provisioning. Servers MUST return any
- /// xml:lang attribute if set for the property.
- ///
- /// Description: If present, the property contains a description of the
- /// calendar collection that is suitable for presentation to a user.
- /// If not present, the client should assume no description for the
- /// calendar collection.
- ///
- /// Definition:
- ///
- /// <!ELEMENT calendar-description (#PCDATA)>
- /// PCDATA value: string
- ///
- /// Example:
- ///
- /// <C:calendar-description xml:lang="fr-CA"
- /// xmlns:C="urn:ietf:params:xml:ns:caldav"
- /// >Calendrier de Mathilde Desruisseaux</C:calendar-description>
- CalendarDescription {
- lang: Option<String>,
- text: String,
- },
-
- /// 5.2.2. CALDAV:calendar-timezone Property
- ///
- /// Name: calendar-timezone
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Specifies a time zone on a calendar collection.
- ///
- /// Conformance: This property SHOULD be defined on all calendar
- /// collections. If defined, it SHOULD NOT be returned by a PROPFIND
- /// DAV:allprop request (as defined in Section 12.14.1 of [RFC2518]).
- ///
- /// Description: The CALDAV:calendar-timezone property is used to
- /// specify the time zone the server should rely on to resolve "date"
- /// values and "date with local time" values (i.e., floating time) to
- /// "date with UTC time" values. The server will require this
- /// information to determine if a calendar component scheduled with
- /// "date" values or "date with local time" values overlaps a CALDAV:
- /// time-range specified in a CALDAV:calendar-query REPORT. The
- /// server will also require this information to compute the proper
- /// FREEBUSY time period as "date with UTC time" in the VFREEBUSY
- /// component returned in a response to a CALDAV:free-busy-query
- /// REPORT request that takes into account calendar components
- /// scheduled with "date" values or "date with local time" values. In
- /// the absence of this property, the server MAY rely on the time zone
- /// of their choice.
- ///
- /// Note: The iCalendar data embedded within the CALDAV:calendar-
- /// timezone XML element MUST follow the standard XML character data
- /// encoding rules, including use of &lt;, &gt;, &amp; etc. entity
- /// encoding or the use of a <![CDATA[ ... ]]> construct. In the
- /// later case, the iCalendar data cannot contain the character
- /// sequence "]]>", which is the end delimiter for the CDATA section.
- ///
- /// Definition:
- ///
- /// <!ELEMENT calendar-timezone (#PCDATA)>
- /// PCDATA value: an iCalendar object with exactly one VTIMEZONE component.
- ///
- /// Example:
- ///
- /// <C:calendar-timezone
- /// xmlns:C="urn:ietf:params:xml:ns:caldav">BEGIN:VCALENDAR
- /// PRODID:-//Example Corp.//CalDAV Client//EN
- /// VERSION:2.0
- /// BEGIN:VTIMEZONE
- /// TZID:US-Eastern
- /// LAST-MODIFIED:19870101T000000Z
- /// BEGIN:STANDARD
- /// DTSTART:19671029T020000
- /// RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
- /// TZOFFSETFROM:-0400
- /// TZOFFSETTO:-0500
- /// TZNAME:Eastern Standard Time (US &amp; Canada)
- /// END:STANDARD
- /// BEGIN:DAYLIGHT
- /// DTSTART:19870405T020000
- /// RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
- /// TZOFFSETFROM:-0500
- /// TZOFFSETTO:-0400
- /// TZNAME:Eastern Daylight Time (US &amp; Canada)
- /// END:DAYLIGHT
- /// END:VTIMEZONE
- /// END:VCALENDAR
- /// </C:calendar-timezone>
- //@FIXME we might want to put a buffer here or an iCal parsed object
- CalendarTimezone(String),
-
- /// Name: supported-calendar-component-set
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Specifies the calendar component types (e.g., VEVENT,
- /// VTODO, etc.) that calendar object resources can contain in the
- /// calendar collection.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. If defined, it MUST 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:supported-calendar-component-set property is
- /// used to specify restrictions on the calendar component types that
- /// calendar object resources may contain in a calendar collection.
- /// Any attempt by the client to store calendar object resources with
- /// component types not listed in this property, if it exists, MUST
- /// result in an error, with the CALDAV:supported-calendar-component
- /// precondition (Section 5.3.2.1) being violated. Since this
- /// property is protected, it cannot be changed by clients using a
- /// PROPPATCH request. However, clients can initialize the value of
- /// this property when creating a new calendar collection with
- /// MKCALENDAR. The empty-element tag <C:comp name="VTIMEZONE"/> MUST
- /// only be specified if support for calendar object resources that
- /// only contain VTIMEZONE components is provided or desired. Support
- /// for VTIMEZONE components in calendar object resources that contain
- /// VEVENT or VTODO components is always assumed. In the absence of
- /// this property, the server MUST accept all component types, and the
- /// client can assume that all component types are accepted.
- ///
- /// Definition:
- ///
- /// <!ELEMENT supported-calendar-component-set (comp+)>
- ///
- /// Example:
- ///
- /// <C:supported-calendar-component-set
- /// xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// <C:comp name="VEVENT"/>
- /// <C:comp name="VTODO"/>
- /// </C:supported-calendar-component-set>
- SupportedCalendarComponentSet(Vec<CompSupport>),
-
- /// Name: supported-calendar-data
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Specifies what media types are allowed for calendar object
- /// resources in a calendar collection.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. If defined, it MUST 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:supported-calendar-data property is used to
- /// specify the media type supported for the calendar object resources
- /// contained in a given calendar collection (e.g., iCalendar version
- /// 2.0). Any attempt by the client to store calendar object
- /// resources with a media type not listed in this property MUST
- /// result in an error, with the CALDAV:supported-calendar-data
- /// precondition (Section 5.3.2.1) being violated. In the absence of
- /// this property, the server MUST only accept data with the media
- /// type "text/calendar" and iCalendar version 2.0, and clients can
- /// assume that the server will only accept this data.
- ///
- /// Definition:
- ///
- /// <!ELEMENT supported-calendar-data (calendar-data+)>
- ///
- /// Example:
- ///
- /// <C:supported-calendar-data
- /// xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// <C:calendar-data content-type="text/calendar" version="2.0"/>
- /// </C:supported-calendar-data>
- ///
- /// -----
- ///
- /// <!ELEMENT calendar-data EMPTY>
- ///
- /// when nested in the CALDAV:supported-calendar-data property
- /// to specify a supported media type for calendar object
- /// resources;
- SupportedCalendarData(Vec<CalendarDataEmpty>),
-
- /// Name: max-resource-size
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Provides a numeric value indicating the maximum size of a
- /// resource in octets that the server is willing to accept when a
- /// calendar object resource is stored in a calendar collection.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. If defined, it MUST 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:max-resource-size is used to specify a
- /// numeric value that represents the maximum size in octets that the
- /// server is willing to accept when a calendar object resource is
- /// stored in a calendar collection. Any attempt to store a calendar
- /// object resource exceeding this size MUST result in an error, with
- /// the CALDAV:max-resource-size precondition (Section 5.3.2.1) being
- /// violated. In the absence of this property, the client can assume
- /// that the server will allow storing a resource of any reasonable
- /// size.
- ///
- /// Definition:
- ///
- /// <!ELEMENT max-resource-size (#PCDATA)>
- /// PCDATA value: a numeric value (positive integer)
- ///
- /// Example:
- ///
- /// <C:max-resource-size xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// 102400
- /// </C:max-resource-size>
- MaxResourceSize(u64),
-
- /// CALDAV:min-date-time Property
- ///
- /// Name: min-date-time
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Provides a DATE-TIME value indicating the earliest date and
- /// time (in UTC) that the server is willing to accept for any DATE or
- /// DATE-TIME value in a calendar object resource stored in a calendar
- /// collection.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. If defined, it MUST 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:min-date-time is used to specify an
- /// iCalendar DATE-TIME value in UTC that indicates the earliest
- /// inclusive date that the server is willing to accept for any
- /// explicit DATE or DATE-TIME value in a calendar object resource
- /// stored in a calendar collection. Any attempt to store a calendar
- /// object resource using a DATE or DATE-TIME value earlier than this
- /// value MUST result in an error, with the CALDAV:min-date-time
- /// precondition (Section 5.3.2.1) being violated. Note that servers
- /// MUST accept recurring components that specify instances beyond
- /// this limit, provided none of those instances have been overridden.
- /// In that case, the server MAY simply ignore those instances outside
- /// of the acceptable range when processing reports on the calendar
- /// object resource. In the absence of this property, the client can
- /// assume any valid iCalendar date may be used at least up to the
- /// CALDAV:max-date-time value, if that is defined.
- ///
- /// Definition:
- ///
- /// <!ELEMENT min-date-time (#PCDATA)>
- /// PCDATA value: an iCalendar format DATE-TIME value in UTC
- ///
- /// Example:
- ///
- /// <C:min-date-time xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// 19000101T000000Z
- /// </C:min-date-time>
- MinDateTime(DateTime<Utc>),
-
- /// CALDAV:max-date-time Property
- ///
- /// Name: max-date-time
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Provides a DATE-TIME value indicating the latest date and
- /// time (in UTC) that the server is willing to accept for any DATE or
- /// DATE-TIME value in a calendar object resource stored in a calendar
- /// collection.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. If defined, it MUST 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:max-date-time is used to specify an
- /// iCalendar DATE-TIME value in UTC that indicates the inclusive
- /// latest date that the server is willing to accept for any date or
- /// time value in a calendar object resource stored in a calendar
- /// collection. Any attempt to store a calendar object resource using
- /// a DATE or DATE-TIME value later than this value MUST result in an
- /// error, with the CALDAV:max-date-time precondition
- /// (Section 5.3.2.1) being violated. Note that servers MUST accept
- /// recurring components that specify instances beyond this limit,
- /// provided none of those instances have been overridden. In that
- /// case, the server MAY simply ignore those instances outside of the
- /// acceptable range when processing reports on the calendar object
- /// resource. In the absence of this property, the client can assume
- /// any valid iCalendar date may be used at least down to the CALDAV:
- /// min-date-time value, if that is defined.
- ///
- /// Definition:
- ///
- /// <!ELEMENT max-date-time (#PCDATA)>
- /// PCDATA value: an iCalendar format DATE-TIME value in UTC
- ///
- /// Example:
- ///
- /// <C:max-date-time xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// 20491231T235959Z
- /// </C:max-date-time>
- MaxDateTime(DateTime<Utc>),
-
- /// CALDAV:max-instances Property
- ///
- /// Name: max-instances
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Provides a numeric value indicating the maximum number of
- /// recurrence instances that a calendar object resource stored in a
- /// calendar collection can generate.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. If defined, it MUST 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:max-instances is used to specify a numeric
- /// value that indicates the maximum number of recurrence instances
- /// that a calendar object resource stored in a calendar collection
- /// can generate. Any attempt to store a calendar object resource
- /// with a recurrence pattern that generates more instances than this
- /// value MUST result in an error, with the CALDAV:max-instances
- /// precondition (Section 5.3.2.1) being violated. In the absence of
- /// this property, the client can assume that the server has no limits
- /// on the number of recurrence instances it can handle or expand.
- ///
- /// Definition:
- ///
- /// <!ELEMENT max-instances (#PCDATA)>
- /// PCDATA value: a numeric value (integer greater than zero)
- ///
- /// Example:
- ///
- /// <C:max-instances xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// 100
- /// </C:max-instances>
- MaxInstances(u64),
-
- /// CALDAV:max-attendees-per-instance Property
- ///
- /// Name: max-attendees-per-instance
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Provides a numeric value indicating the maximum number of
- /// ATTENDEE properties in any instance of a calendar object resource
- /// stored in a calendar collection.
- ///
- /// Conformance: This property MAY be defined on any calendar
- /// collection. If defined, it MUST 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:max-attendees-per-instance is used to
- /// specify a numeric value that indicates the maximum number of
- /// iCalendar ATTENDEE properties on any one instance of a calendar
- /// object resource stored in a calendar collection. Any attempt to
- /// store a calendar object resource with more ATTENDEE properties per
- /// instance than this value MUST result in an error, with the CALDAV:
- /// max-attendees-per-instance precondition (Section 5.3.2.1) being
- /// violated. In the absence of this property, the client can assume
- /// that the server can handle any number of ATTENDEE properties in a
- /// calendar component.
- ///
- /// Definition:
- ///
- /// <!ELEMENT max-attendees-per-instance (#PCDATA)>
- /// PCDATA value: a numeric value (integer greater than zero)
- ///
- /// Example:
- ///
- /// <C:max-attendees-per-instance
- /// xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// 25
- /// </C:max-attendees-per-instance>
- MaxAttendeesPerInstance(u64),
-
- /// Name: supported-collation-set
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Identifies the set of collations supported by the server
- /// for text matching operations.
- ///
- /// Conformance: This property MUST be defined on any resource that
- /// supports a report that does text matching. If defined, it MUST 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:supported-collation-set property contains
- /// zero or more CALDAV:supported-collation elements, which specify
- /// the collection identifiers of the collations supported by the
- /// server.
- ///
- /// Definition:
- ///
- /// <!ELEMENT supported-collation-set (supported-collation*)>
- /// <!ELEMENT supported-collation (#PCDATA)>
- ///
- /// Example:
- ///
- /// <C:supported-collation-set
- /// xmlns:C="urn:ietf:params:xml:ns:caldav">
- /// <C:supported-collation>i;ascii-casemap</C:supported-collation>
- /// <C:supported-collation>i;octet</C:supported-collation>
- /// </C:supported-collation-set>
- SupportedCollationSet(Vec<SupportedCollation>),
-
- /// Name: calendar-data
- ///
- /// Namespace: urn:ietf:params:xml:ns:caldav
- ///
- /// Purpose: Specified one of the following:
- ///
- /// 1. A supported media type for calendar object resources when
- /// nested in the CALDAV:supported-calendar-data property;
- ///
- /// 2. The parts of a calendar object resource should be returned by
- /// a calendaring report;
- ///
- /// 3. The content of a calendar object resource in a response to a
- /// calendaring report.
- ///
- /// Description: When nested in the CALDAV:supported-calendar-data
- /// property, the CALDAV:calendar-data XML element specifies a media
- /// type supported by the CalDAV server for calendar object resources.
- ///
- /// When used in a calendaring REPORT request, the CALDAV:calendar-
- /// data XML element specifies which parts of calendar object
- /// resources need to be returned in the response. If the CALDAV:
- /// calendar-data XML element doesn't contain any CALDAV:comp element,
- /// calendar object resources will be returned in their entirety.
- ///
- /// Finally, when used in a calendaring REPORT response, the CALDAV:
- /// calendar-data XML element specifies the content of a calendar
- /// object resource. Given that XML parsers normalize the two-
- /// character sequence CRLF (US-ASCII decimal 13 and US-ASCII decimal
- /// 10) to a single LF character (US-ASCII decimal 10), the CR
- /// character (US-ASCII decimal 13) MAY be omitted in calendar object
- /// resources specified in the CALDAV:calendar-data XML element.
- /// Furthermore, calendar object resources specified in the CALDAV:
- /// calendar-data XML element MAY be invalid per their media type
- /// specification if the CALDAV:calendar-data XML element part of the
- /// calendaring REPORT request did not specify required properties
- /// (e.g., UID, DTSTAMP, etc.), or specified a CALDAV:prop XML element
- /// with the "novalue" attribute set to "yes".
- ///
- /// Note: The CALDAV:calendar-data XML element is specified in requests
- /// and responses inside the DAV:prop XML element as if it were a
- /// WebDAV property. However, the CALDAV:calendar-data XML element is
- /// not a WebDAV property and, as such, is not returned in PROPFIND
- /// responses, nor used in PROPPATCH requests.
- ///
- /// Note: The iCalendar data embedded within the CALDAV:calendar-data
- /// XML element MUST follow the standard XML character data encoding
- /// rules, including use of &lt;, &gt;, &amp; etc. entity encoding or
- /// the use of a <![CDATA[ ... ]]> construct. In the later case, the
- /// iCalendar data cannot contain the character sequence "]]>", which
- /// is the end delimiter for the CDATA section.
- CalendarData(CalendarDataPayload),
-}
-
-#[derive(Debug, PartialEq)]
-pub enum Violation {
- /// (DAV:resource-must-be-null): A resource MUST NOT exist at the
- /// Request-URI;
- ResourceMustBeNull,
-
- /// (CALDAV:calendar-collection-location-ok): The Request-URI MUST
- /// identify a location where a calendar collection can be created;
- CalendarCollectionLocationOk,
-
- /// (CALDAV:valid-calendar-data): The time zone specified in CALDAV:
- /// calendar-timezone property MUST be a valid iCalendar object
- /// containing a single valid VTIMEZONE component.
- ValidCalendarData,
-
- ///@FIXME should not be here but in RFC3744
- /// !!! ERRATA 1002 !!!
- /// (DAV:need-privileges): The DAV:bind privilege MUST be granted to
- /// the current user on the parent collection of the Request-URI.
- NeedPrivileges,
-
- /// (CALDAV:initialize-calendar-collection): A new calendar collection
- /// exists at the Request-URI. The DAV:resourcetype of the calendar
- /// collection MUST contain both DAV:collection and CALDAV:calendar
- /// XML elements.
- InitializeCalendarCollection,
-
- /// (CALDAV:supported-calendar-data): The resource submitted in the
- /// PUT request, or targeted by a COPY or MOVE request, MUST be a
- /// supported media type (i.e., iCalendar) for calendar object
- /// resources;
- SupportedCalendarData,
-
- /// (CALDAV:valid-calendar-object-resource): The resource submitted in
- /// the PUT request, or targeted by a COPY or MOVE request, MUST obey
- /// all restrictions specified in Section 4.1 (e.g., calendar object
- /// resources MUST NOT contain more than one type of calendar
- /// component, calendar object resources MUST NOT specify the
- /// iCalendar METHOD property, etc.);
- ValidCalendarObjectResource,
-
- /// (CALDAV:supported-calendar-component): The resource submitted in
- /// the PUT request, or targeted by a COPY or MOVE request, MUST
- /// contain a type of calendar component that is supported in the
- /// targeted calendar collection;
- SupportedCalendarComponent,
-
- /// (CALDAV:no-uid-conflict): The resource submitted in the PUT
- /// request, or targeted by a COPY or MOVE request, MUST NOT specify
- /// an iCalendar UID property value already in use in the targeted
- /// calendar collection or overwrite an existing calendar object
- /// resource with one that has a different UID property value.
- /// Servers SHOULD report the URL of the resource that is already
- /// making use of the same UID property value in the DAV:href element;
- ///
- /// <!ELEMENT no-uid-conflict (DAV:href)>
- NoUidConflict(dav::Href),
-
- /// (CALDAV:max-resource-size): The resource submitted in the PUT
- /// request, or targeted by a COPY or MOVE request, MUST have an octet
- /// size less than or equal to the value of the CALDAV:max-resource-
- /// size property value (Section 5.2.5) on the calendar collection
- /// where the resource will be stored;
- MaxResourceSize,
-
- /// (CALDAV:min-date-time): The resource submitted in the PUT request,
- /// or targeted by a COPY or MOVE request, MUST have all of its
- /// iCalendar DATE or DATE-TIME property values (for each recurring
- /// instance) greater than or equal to the value of the CALDAV:min-
- /// date-time property value (Section 5.2.6) on the calendar
- /// collection where the resource will be stored;
- MinDateTime,
-
- /// (CALDAV:max-date-time): The resource submitted in the PUT request,
- /// or targeted by a COPY or MOVE request, MUST have all of its
- /// iCalendar DATE or DATE-TIME property values (for each recurring
- /// instance) less than the value of the CALDAV:max-date-time property
- /// value (Section 5.2.7) on the calendar collection where the
- /// resource will be stored;
- MaxDateTime,
-
- /// (CALDAV:max-instances): The resource submitted in the PUT request,
- /// or targeted by a COPY or MOVE request, MUST generate a number of
- /// recurring instances less than or equal to the value of the CALDAV:
- /// max-instances property value (Section 5.2.8) on the calendar
- /// collection where the resource will be stored;
- MaxInstances,
-
- /// (CALDAV:max-attendees-per-instance): The resource submitted in the
- /// PUT request, or targeted by a COPY or MOVE request, MUST have a
- /// number of ATTENDEE properties on any one instance less than or
- /// equal to the value of the CALDAV:max-attendees-per-instance
- /// property value (Section 5.2.9) on the calendar collection where
- /// the resource will be stored;
- MaxAttendeesPerInstance,
-
- /// (CALDAV:valid-filter): The CALDAV:filter XML element (see
- /// Section 9.7) specified in the REPORT request MUST be valid. For
- /// instance, a CALDAV:filter cannot nest a <C:comp name="VEVENT">
- /// element in a <C:comp name="VTODO"> element, and a CALDAV:filter
- /// cannot nest a <C:time-range start="..." end="..."> element in a
- /// <C:prop name="SUMMARY"> element.
- ValidFilter,
-
- /// (CALDAV:supported-filter): The CALDAV:comp-filter (see
- /// Section 9.7.1), CALDAV:prop-filter (see Section 9.7.2), and
- /// CALDAV:param-filter (see Section 9.7.3) XML elements used in the
- /// CALDAV:filter XML element (see Section 9.7) in the REPORT request
- /// only make reference to components, properties, and parameters for
- /// which queries are supported by the server, i.e., if the CALDAV:
- /// filter element attempts to reference an unsupported component,
- /// property, or parameter, this precondition is violated. Servers
- /// SHOULD report the CALDAV:comp-filter, CALDAV:prop-filter, or
- /// CALDAV:param-filter for which it does not provide support.
- ///
- /// <!ELEMENT supported-filter (comp-filter*,
- /// prop-filter*,
- /// param-filter*)>
- SupportedFilter {
- comp: Vec<CompFilter>,
- prop: Vec<PropFilter>,
- param: Vec<ParamFilter>,
- },
-
- /// (DAV:number-of-matches-within-limits): The number of matching
- /// calendar object resources must fall within server-specific,
- /// predefined limits. For example, this condition might be triggered
- /// if a search specification would cause the return of an extremely
- /// large number of responses.
- NumberOfMatchesWithinLimits,
-}
-
-// -------- Inner XML elements ---------
-
-/// Some of the reports defined in this section do text matches of
-/// character strings provided by the client and are compared to stored
-/// calendar data. Since iCalendar data is, by default, encoded in the
-/// UTF-8 charset and may include characters outside the US-ASCII charset
-/// range in some property and parameter values, there is a need to
-/// ensure that text matching follows well-defined rules.
-///
-/// To deal with this, this specification makes use of the IANA Collation
-/// Registry defined in [RFC4790] to specify collations that may be used
-/// to carry out the text comparison operations with a well-defined rule.
-///
-/// The comparisons used in CalDAV are all "substring" matches, as per
-/// [RFC4790], Section 4.2. Collations supported by the server MUST
-/// support "substring" match operations.
-///
-/// CalDAV servers are REQUIRED to support the "i;ascii-casemap" and
-/// "i;octet" collations, as described in [RFC4790], and MAY support
-/// other collations.
-///
-/// Servers MUST advertise the set of collations that they support via
-/// the CALDAV:supported-collation-set property defined on any resource
-/// that supports reports that use collations.
-///
-/// Clients MUST only use collations from the list advertised by the
-/// server.
-///
-/// In the absence of a collation explicitly specified by the client, or
-/// if the client specifies the "default" collation identifier (as
-/// defined in [RFC4790], Section 3.1), the server MUST default to using
-/// "i;ascii-casemap" as the collation.
-///
-/// Wildcards (as defined in [RFC4790], Section 3.2) MUST NOT be used in
-/// the collation identifier.
-///
-/// If the client chooses a collation not supported by the server, the
-/// server MUST respond with a CALDAV:supported-collation precondition
-/// error response.
-#[derive(Debug, PartialEq)]
-pub struct SupportedCollation(pub Collation);
-
-/// <!ELEMENT calendar-data (#PCDATA)>
-/// PCDATA value: iCalendar object
-///
-/// when nested in the DAV:prop XML element in a calendaring
-/// REPORT response to specify the content of a returned
-/// calendar object resource.
-#[derive(Debug, PartialEq)]
-pub struct CalendarDataPayload {
- pub mime: Option<CalendarDataSupport>,
- pub payload: String,
-}
-
-/// <!ELEMENT calendar-data (comp?,
-/// (expand | limit-recurrence-set)?,
-/// limit-freebusy-set?)>
-///
-/// when nested in the DAV:prop XML element in a calendaring
-/// REPORT request to specify which parts of calendar object
-/// resources should be returned in the response;
-#[derive(Debug, PartialEq)]
-pub struct CalendarDataRequest {
- pub mime: Option<CalendarDataSupport>,
- pub comp: Option<Comp>,
- pub recurrence: Option<RecurrenceModifier>,
- pub limit_freebusy_set: Option<LimitFreebusySet>,
-}
-
-/// calendar-data specialization for Property
-///
-/// <!ELEMENT calendar-data EMPTY>
-///
-/// when nested in the CALDAV:supported-calendar-data property
-/// to specify a supported media type for calendar object
-/// resources;
-#[derive(Debug, PartialEq)]
-pub struct CalendarDataEmpty(pub Option<CalendarDataSupport>);
-
-/// <!ATTLIST calendar-data content-type CDATA "text/calendar"
-/// version CDATA "2.0">
-/// content-type value: a MIME media type
-/// version value: a version string
-/// attributes can be used on all three variants of the
-/// CALDAV:calendar-data XML element.
-#[derive(Debug, PartialEq)]
-pub struct CalendarDataSupport {
- pub content_type: String,
- pub version: String,
-}
-
-/// Name: comp
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Defines which component types to return.
-///
-/// Description: The name value is a calendar component name (e.g.,
-/// VEVENT).
-///
-/// Definition:
-///
-/// <!ELEMENT comp ((allprop | prop*), (allcomp | comp*))>
-/// <!ATTLIST comp name CDATA #REQUIRED>
-/// name value: a calendar component name
-///
-/// Note: The CALDAV:prop and CALDAV:allprop elements have the same name
-/// as the DAV:prop and DAV:allprop elements defined in [RFC2518].
-/// However, the CALDAV:prop and CALDAV:allprop elements are defined
-/// in the "urn:ietf:params:xml:ns:caldav" namespace instead of the
-/// "DAV:" namespace.
-#[derive(Debug, PartialEq)]
-pub struct Comp {
- pub name: Component,
- pub additional_rules: Option<CompInner>,
-}
-
-#[derive(Debug, PartialEq)]
-pub struct CompInner {
- pub prop_kind: PropKind,
- pub comp_kind: CompKind,
-}
-
-/// For SupportedCalendarComponentSet
-///
-/// Definition:
-///
-/// <!ELEMENT supported-calendar-component-set (comp+)>
-///
-/// Example:
-///
-/// <C:supported-calendar-component-set
-/// xmlns:C="urn:ietf:params:xml:ns:caldav">
-/// <C:comp name="VEVENT"/>
-/// <C:comp name="VTODO"/>
-/// </C:supported-calendar-component-set>
-#[derive(Debug, PartialEq)]
-pub struct CompSupport(pub Component);
-
-/// Name: allcomp
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies that all components shall be returned.
-///
-/// Description: The CALDAV:allcomp XML element can be used when the
-/// client wants all types of components returned by a calendaring
-/// REPORT request.
-///
-/// Definition:
-///
-/// <!ELEMENT allcomp EMPTY>
-#[derive(Debug, PartialEq)]
-pub enum CompKind {
- AllComp,
- Comp(Vec<Comp>),
-}
-
-/// Name: allprop
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies that all properties shall be returned.
-///
-/// Description: The CALDAV:allprop XML element can be used when the
-/// client wants all properties of components returned by a
-/// calendaring REPORT request.
-///
-/// Definition:
-///
-/// <!ELEMENT allprop EMPTY>
-///
-/// Note: The CALDAV:allprop element has the same name as the DAV:
-/// allprop element defined in [RFC2518]. However, the CALDAV:allprop
-/// element is defined in the "urn:ietf:params:xml:ns:caldav"
-/// namespace instead of the "DAV:" namespace.
-#[derive(Debug, PartialEq)]
-pub enum PropKind {
- AllProp,
- Prop(Vec<CalProp>),
-}
-
-/// Name: prop
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Defines which properties to return in the response.
-///
-/// Description: The "name" attribute specifies the name of the calendar
-/// property to return (e.g., ATTENDEE). The "novalue" attribute can
-/// be used by clients to request that the actual value of the
-/// property not be returned (if the "novalue" attribute is set to
-/// "yes"). In that case, the server will return just the iCalendar
-/// property name and any iCalendar parameters and a trailing ":"
-/// without the subsequent value data.
-///
-/// Definition:
-/// <!ELEMENT prop EMPTY>
-/// <!ATTLIST prop name CDATA #REQUIRED novalue (yes | no) "no">
-/// name value: a calendar property name
-/// novalue value: "yes" or "no"
-///
-/// Note: The CALDAV:prop element has the same name as the DAV:prop
-/// element defined in [RFC2518]. However, the CALDAV:prop element is
-/// defined in the "urn:ietf:params:xml:ns:caldav" namespace instead
-/// of the "DAV:" namespace.
-#[derive(Debug, PartialEq)]
-pub struct CalProp {
- pub name: ComponentProperty,
- pub novalue: Option<bool>,
-}
-
-#[derive(Debug, PartialEq)]
-pub enum RecurrenceModifier {
- Expand(Expand),
- LimitRecurrenceSet(LimitRecurrenceSet),
-}
-
-/// Name: expand
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Forces the server to expand recurring components into
-/// individual recurrence instances.
-///
-/// Description: The CALDAV:expand XML element specifies that for a
-/// given calendaring REPORT request, the server MUST expand the
-/// recurrence set into calendar components that define exactly one
-/// recurrence instance, and MUST return only those whose scheduled
-/// time intersect a specified time range.
-///
-/// The "start" attribute specifies the inclusive start of the time
-/// range, and the "end" attribute specifies the non-inclusive end of
-/// the time range. Both attributes are specified as date with UTC
-/// time value. The value of the "end" attribute MUST be greater than
-/// the value of the "start" attribute.
-///
-/// The server MUST use the same logic as defined for CALDAV:time-
-/// range to determine if a recurrence instance intersects the
-/// specified time range.
-///
-/// Recurring components, other than the initial instance, MUST
-/// include a RECURRENCE-ID property indicating which instance they
-/// refer to.
-///
-/// The returned calendar components MUST NOT use recurrence
-/// properties (i.e., EXDATE, EXRULE, RDATE, and RRULE) and MUST NOT
-/// have reference to or include VTIMEZONE components. Date and local
-/// time with reference to time zone information MUST be converted
-/// into date with UTC time.
-///
-/// Definition:
-///
-/// <!ELEMENT expand EMPTY>
-/// <!ATTLIST expand start CDATA #REQUIRED
-/// end CDATA #REQUIRED>
-/// start value: an iCalendar "date with UTC time"
-/// end value: an iCalendar "date with UTC time"
-#[derive(Debug, PartialEq)]
-pub struct Expand(pub DateTime<Utc>, pub DateTime<Utc>);
-
-/// CALDAV:limit-recurrence-set XML Element
-///
-/// Name: limit-recurrence-set
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies a time range to limit the set of "overridden
-/// components" returned by the server.
-///
-/// Description: The CALDAV:limit-recurrence-set XML element specifies
-/// that for a given calendaring REPORT request, the server MUST
-/// return, in addition to the "master component", only the
-/// "overridden components" that impact a specified time range. An
-/// overridden component impacts a time range if its current start and
-/// end times overlap the time range, or if the original start and end
-/// times -- the ones that would have been used if the instance were
-/// not overridden -- overlap the time range.
-///
-/// The "start" attribute specifies the inclusive start of the time
-/// range, and the "end" attribute specifies the non-inclusive end of
-/// the time range. Both attributes are specified as date with UTC
-/// time value. The value of the "end" attribute MUST be greater than
-/// the value of the "start" attribute.
-///
-/// The server MUST use the same logic as defined for CALDAV:time-
-/// range to determine if the current or original scheduled time of an
-/// "overridden" recurrence instance intersects the specified time
-/// range.
-///
-/// Overridden components that have a RANGE parameter on their
-/// RECURRENCE-ID property may specify one or more instances in the
-/// recurrence set, and some of those instances may fall within the
-/// specified time range or may have originally fallen within the
-/// specified time range prior to being overridden. If that is the
-/// case, the overridden component MUST be included in the results, as
-/// it has a direct impact on the interpretation of instances within
-/// the specified time range.
-///
-/// Definition:
-///
-/// <!ELEMENT limit-recurrence-set EMPTY>
-/// <!ATTLIST limit-recurrence-set start CDATA #REQUIRED
-/// end CDATA #REQUIRED>
-/// start value: an iCalendar "date with UTC time"
-/// end value: an iCalendar "date with UTC time"
-#[derive(Debug, PartialEq)]
-pub struct LimitRecurrenceSet(pub DateTime<Utc>, pub DateTime<Utc>);
-
-/// Name: limit-freebusy-set
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies a time range to limit the set of FREEBUSY values
-/// returned by the server.
-///
-/// Description: The CALDAV:limit-freebusy-set XML element specifies
-/// that for a given calendaring REPORT request, the server MUST only
-/// return the FREEBUSY property values of a VFREEBUSY component that
-/// intersects a specified time range.
-///
-/// The "start" attribute specifies the inclusive start of the time
-/// range, and the "end" attribute specifies the non-inclusive end of
-/// the time range. Both attributes are specified as "date with UTC
-/// time" value. The value of the "end" attribute MUST be greater
-/// than the value of the "start" attribute.
-///
-/// The server MUST use the same logic as defined for CALDAV:time-
-/// range to determine if a FREEBUSY property value intersects the
-/// specified time range.
-///
-/// Definition:
-/// <!ELEMENT limit-freebusy-set EMPTY>
-/// <!ATTLIST limit-freebusy-set start CDATA #REQUIRED
-/// end CDATA #REQUIRED>
-/// start value: an iCalendar "date with UTC time"
-/// end value: an iCalendar "date with UTC time"
-#[derive(Debug, PartialEq)]
-pub struct LimitFreebusySet(pub DateTime<Utc>, pub DateTime<Utc>);
-
-/// Used by CalendarQuery & CalendarMultiget
-#[derive(Debug, PartialEq)]
-pub enum CalendarSelector<E: dav::Extension> {
- AllProp,
- PropName,
- Prop(dav::PropName<E>),
-}
-
-/// Name: comp-filter
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies search criteria on calendar components.
-///
-/// Description: The CALDAV:comp-filter XML element specifies a query
-/// targeted at the calendar object (i.e., VCALENDAR) or at a specific
-/// calendar component type (e.g., VEVENT). The scope of the
-/// CALDAV:comp-filter XML element is the calendar object when used as
-/// a child of the CALDAV:filter XML element. The scope of the
-/// CALDAV:comp-filter XML element is the enclosing calendar component
-/// when used as a child of another CALDAV:comp-filter XML element. A
-/// CALDAV:comp-filter is said to match if:
-///
-/// * The CALDAV:comp-filter XML element is empty and the calendar
-/// object or calendar component type specified by the "name"
-/// attribute exists in the current scope;
-///
-/// or:
-///
-/// * The CALDAV:comp-filter XML element contains a CALDAV:is-not-
-/// defined XML element and the calendar object or calendar
-/// component type specified by the "name" attribute does not exist
-/// in the current scope;
-///
-/// or:
-///
-/// * The CALDAV:comp-filter XML element contains a CALDAV:time-range
-/// XML element and at least one recurrence instance in the
-/// targeted calendar component is scheduled to overlap the
-/// specified time range, and all specified CALDAV:prop-filter and
-/// CALDAV:comp-filter child XML elements also match the targeted
-/// calendar component;
-///
-/// or:
-///
-/// * The CALDAV:comp-filter XML element only contains CALDAV:prop-
-/// filter and CALDAV:comp-filter child XML elements that all match
-/// the targeted calendar component.
-///
-/// Definition:
-/// <!ELEMENT comp-filter (is-not-defined | (time-range?,
-/// prop-filter*, comp-filter*))>
-///
-/// <!ATTLIST comp-filter name CDATA #REQUIRED>
-/// name value: a calendar object or calendar component
-/// type (e.g., VEVENT)
-#[derive(Debug, PartialEq)]
-pub struct CompFilter {
- pub name: Component,
- // Option 1 = None, Option 2, 3, 4 = Some
- pub additional_rules: Option<CompFilterRules>,
-}
-#[derive(Debug, PartialEq)]
-pub enum CompFilterRules {
- // Option 2
- IsNotDefined,
- // Options 3 & 4
- Matches(CompFilterMatch),
-}
-#[derive(Debug, PartialEq)]
-pub struct CompFilterMatch {
- pub time_range: Option<TimeRange>,
- pub prop_filter: Vec<PropFilter>,
- pub comp_filter: Vec<CompFilter>,
-}
-
-/// Name: prop-filter
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies search criteria on calendar properties.
-///
-/// Description: The CALDAV:prop-filter XML element specifies a query
-/// targeted at a specific calendar property (e.g., CATEGORIES) in the
-/// scope of the enclosing calendar component. A calendar property is
-/// said to match a CALDAV:prop-filter if:
-///
-/// * The CALDAV:prop-filter XML element is empty and a property of
-/// the type specified by the "name" attribute exists in the
-/// enclosing calendar component;
-///
-/// or:
-///
-/// * The CALDAV:prop-filter XML element contains a CALDAV:is-not-
-/// defined XML element and no property of the type specified by
-/// the "name" attribute exists in the enclosing calendar
-/// component;
-///
-/// or:
-///
-/// * The CALDAV:prop-filter XML element contains a CALDAV:time-range
-/// XML element and the property value overlaps the specified time
-/// range, and all specified CALDAV:param-filter child XML elements
-/// also match the targeted property;
-///
-/// or:
-///
-/// * The CALDAV:prop-filter XML element contains a CALDAV:text-match
-/// XML element and the property value matches it, and all
-/// specified CALDAV:param-filter child XML elements also match the
-/// targeted property;
-///
-/// Definition:
-///
-/// <!ELEMENT prop-filter (is-not-defined |
-/// ((time-range | text-match)?,
-/// param-filter*))>
-///
-/// <!ATTLIST prop-filter name CDATA #REQUIRED>
-/// name value: a calendar property name (e.g., ATTENDEE)
-#[derive(Debug, PartialEq)]
-pub struct PropFilter {
- pub name: Component,
- // None = Option 1, Some() = Option 2, 3 & 4
- pub additional_rules: Option<PropFilterRules>,
-}
-#[derive(Debug, PartialEq)]
-pub enum PropFilterRules {
- // Option 2
- IsNotDefined,
- // Options 3 & 4
- Match(PropFilterMatch),
-}
-#[derive(Debug, PartialEq)]
-pub struct PropFilterMatch {
- pub time_range: Option<TimeRange>,
- pub time_or_text: Option<TimeOrText>,
- pub param_filter: Vec<ParamFilter>,
-}
-#[derive(Debug, PartialEq)]
-pub enum TimeOrText {
- Time(TimeRange),
- Text(TextMatch),
-}
-
-/// Name: text-match
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies a substring match on a property or parameter
-/// value.
-///
-/// Description: The CALDAV:text-match XML element specifies text used
-/// for a substring match against the property or parameter value
-/// specified in a calendaring REPORT request.
-///
-/// The "collation" attribute is used to select the collation that the
-/// server MUST use for character string matching. In the absence of
-/// this attribute, the server MUST use the "i;ascii-casemap"
-/// collation.
-///
-/// The "negate-condition" attribute is used to indicate that this
-/// test returns a match if the text matches when the attribute value
-/// is set to "no", or return a match if the text does not match, if
-/// the attribute value is set to "yes". For example, this can be
-/// used to match components with a STATUS property not set to
-/// CANCELLED.
-///
-/// Definition:
-/// <!ELEMENT text-match (#PCDATA)>
-/// PCDATA value: string
-/// <!ATTLIST text-match collation CDATA "i;ascii-casemap"
-/// negate-condition (yes | no) "no">
-#[derive(Debug, PartialEq)]
-pub struct TextMatch {
- pub collation: Option<Collation>,
- pub negate_condition: Option<bool>,
- pub text: String,
-}
-
-/// Name: param-filter
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Limits the search to specific parameter values.
-///
-/// Description: The CALDAV:param-filter XML element specifies a query
-/// targeted at a specific calendar property parameter (e.g.,
-/// PARTSTAT) in the scope of the calendar property on which it is
-/// defined. A calendar property parameter is said to match a CALDAV:
-/// param-filter if:
-///
-/// * The CALDAV:param-filter XML element is empty and a parameter of
-/// the type specified by the "name" attribute exists on the
-/// calendar property being examined;
-///
-/// or:
-///
-/// * The CALDAV:param-filter XML element contains a CALDAV:is-not-
-/// defined XML element and no parameter of the type specified by
-/// the "name" attribute exists on the calendar property being
-/// examined;
-///
-/// Definition:
-///
-/// <!ELEMENT param-filter (is-not-defined | text-match?)>
-///
-/// <!ATTLIST param-filter name CDATA #REQUIRED>
-/// name value: a property parameter name (e.g., PARTSTAT)
-#[derive(Debug, PartialEq)]
-pub struct ParamFilter {
- pub name: PropertyParameter,
- pub additional_rules: Option<ParamFilterMatch>,
-}
-#[derive(Debug, PartialEq)]
-pub enum ParamFilterMatch {
- IsNotDefined,
- Match(TextMatch),
-}
-
-/// CALDAV:is-not-defined XML Element
-///
-/// Name: is-not-defined
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies that a match should occur if the enclosing
-/// component, property, or parameter does not exist.
-///
-/// Description: The CALDAV:is-not-defined XML element specifies that a
-/// match occurs if the enclosing component, property, or parameter
-/// value specified in a calendaring REPORT request does not exist in
-/// the calendar data being tested.
-///
-/// Definition:
-/// <!ELEMENT is-not-defined EMPTY>
-/* CURRENTLY INLINED */
-
-
-
-/// Name: timezone
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies the time zone component to use when determining
-/// the results of a report.
-///
-/// Description: The CALDAV:timezone XML element specifies that for a
-/// given calendaring REPORT request, the server MUST rely on the
-/// specified VTIMEZONE component instead of the CALDAV:calendar-
-/// timezone property of the calendar collection, in which the
-/// calendar object resource is contained to resolve "date" values and
-/// "date with local time" values (i.e., floating time) to "date with
-/// UTC time" values. The server will require this information to
-/// determine if a calendar component scheduled with "date" values or
-/// "date with local time" values intersects a CALDAV:time-range
-/// specified in a CALDAV:calendar-query REPORT.
-///
-/// Note: The iCalendar data embedded within the CALDAV:timezone XML
-/// element MUST follow the standard XML character data encoding
-/// rules, including use of &lt;, &gt;, &amp; etc. entity encoding or
-/// the use of a <![CDATA[ ... ]]> construct. In the later case, the
-///
-/// iCalendar data cannot contain the character sequence "]]>", which
-/// is the end delimiter for the CDATA section.
-///
-/// Definition:
-///
-/// <!ELEMENT timezone (#PCDATA)>
-/// PCDATA value: an iCalendar object with exactly one VTIMEZONE
-#[derive(Debug, PartialEq)]
-pub struct TimeZone(pub String);
-
-/// Name: filter
-///
-/// Namespace: urn:ietf:params:xml:ns:caldav
-///
-/// Purpose: Specifies a filter to limit the set of calendar components
-/// returned by the server.
-///
-/// Description: The CALDAV:filter XML element specifies the search
-/// filter used to limit the calendar components returned by a
-/// calendaring REPORT request.
-///
-/// Definition:
-/// <!ELEMENT filter (comp-filter)>
-#[derive(Debug, PartialEq)]
-pub struct Filter(pub CompFilter);
-
-/// Name: time-range
-///
-/// Definition:
-///
-/// <!ELEMENT time-range EMPTY>
-/// <!ATTLIST time-range start CDATA #IMPLIED
-/// end CDATA #IMPLIED>
-/// start value: an iCalendar "date with UTC time"
-/// end value: an iCalendar "date with UTC time"
-#[derive(Debug, PartialEq)]
-pub enum TimeRange {
- OnlyStart(DateTime<Utc>),
- OnlyEnd(DateTime<Utc>),
- FullRange(DateTime<Utc>, DateTime<Utc>),
-}
-
-// ----------------------- ENUM ATTRIBUTES ---------------------
-
-/// Known components
-#[derive(Debug, PartialEq)]
-pub enum Component {
- VCalendar,
- VJournal,
- VFreeBusy,
- VEvent,
- VTodo,
- VAlarm,
- VTimeZone,
- Unknown(String),
-}
-impl Component {
- pub fn as_str<'a>(&'a self) -> &'a str {
- match self {
- Self::VCalendar => "VCALENDAR",
- Self::VJournal => "VJOURNAL",
- Self::VFreeBusy => "VFREEBUSY",
- Self::VEvent => "VEVENT",
- Self::VTodo => "VTODO",
- Self::VAlarm => "VALARM",
- Self::VTimeZone => "VTIMEZONE",
- Self::Unknown(c) => c,
- }
- }
-}
-
-/// name="VERSION", name="SUMMARY", etc.
-/// Can be set on different objects: VCalendar, VEvent, etc.
-/// Might be replaced by an enum later
-#[derive(Debug, PartialEq)]
-pub struct ComponentProperty(pub String);
-
-/// like PARSTAT
-#[derive(Debug, PartialEq)]
-pub struct PropertyParameter(pub String);
-impl PropertyParameter {
- pub fn as_str<'a>(&'a self) -> &'a str {
- self.0.as_str()
- }
-}
-
-#[derive(Default,Debug,PartialEq)]
-pub enum Collation {
- #[default]
- AsciiCaseMap,
- Octet,
- Unknown(String),
-}
-impl Collation {
- pub fn as_str<'a>(&'a self) -> &'a str {
- match self {
- Self::AsciiCaseMap => "i;ascii-casemap",
- Self::Octet => "i;octet",
- Self::Unknown(c) => c.as_str(),
- }
- }
-}
diff --git a/src/dav/decoder.rs b/src/dav/decoder.rs
deleted file mode 100644
index aa3c7e5..0000000
--- a/src/dav/decoder.rs
+++ /dev/null
@@ -1,948 +0,0 @@
-use std::borrow::Cow;
-use std::future::Future;
-
-use quick_xml::events::{Event, BytesStart, BytesDecl, BytesText};
-use quick_xml::events::attributes::AttrError;
-use quick_xml::name::{Namespace, QName, PrefixDeclaration, ResolveResult, ResolveResult::*};
-use quick_xml::reader::NsReader;
-use tokio::io::AsyncBufRead;
-
-use super::types::*;
-use super::error::ParsingError;
-use super::xml::{Node, QRead, Reader, IRead, DAV_URN, CAL_URN};
-
-//@TODO (1) Rewrite all objects as Href,
-// where we return Ok(None) instead of trying to find the object at any cost.
-// Add a xml.find<E: Qread>() -> Result<Option<E>, ParsingError> or similar for the cases we
-// really need the object
-// (2) Rewrite QRead and replace Result<Option<_>, _> with Result<_, _>, not found being a possible
-// error.
-// (3) Rewrite vectors with xml.collect<E: QRead>() -> Result<Vec<E>, _>
-// (4) Something for alternatives would be great but no idea yet
-
-// ---- ROOT ----
-
-/// Propfind request
-impl<E: Extension> QRead<PropFind<E>> for PropFind<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "propfind").await?;
- let propfind: PropFind<E> = loop {
- // allprop
- if let Some(_) = xml.maybe_open(DAV_URN, "allprop").await? {
- let includ = xml.maybe_find::<Include<E>>().await?;
- xml.close().await?;
- break PropFind::AllProp(includ)
- }
-
- // propname
- if let Some(_) = xml.maybe_open(DAV_URN, "propname").await? {
- xml.close().await?;
- break PropFind::PropName
- }
-
- // prop
- let (mut maybe_prop, mut dirty) = (None, false);
- xml.maybe_read::<PropName<E>>(&mut maybe_prop, &mut dirty).await?;
- if let Some(prop) = maybe_prop {
- break PropFind::Prop(prop)
- }
-
- // not found, skipping
- xml.skip().await?;
- };
- xml.close().await?;
-
- Ok(propfind)
- }
-}
-
-/// PROPPATCH request
-impl<E: Extension> QRead<PropertyUpdate<E>> for PropertyUpdate<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "propertyupdate").await?;
- let collected_items = xml.collect::<PropertyUpdateItem<E>>().await?;
- xml.close().await?;
- Ok(PropertyUpdate(collected_items))
- }
-}
-
-/// Generic response
-impl<E: Extension, N: Node<N>> QRead<Multistatus<E,N>> for Multistatus<E,N> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "multistatus").await?;
- let mut responses = Vec::new();
- let mut responsedescription = None;
-
- loop {
- let mut dirty = false;
- xml.maybe_push(&mut responses, &mut dirty).await?;
- xml.maybe_read(&mut responsedescription, &mut dirty).await?;
- if !dirty {
- match xml.peek() {
- Event::End(_) => break,
- _ => xml.skip().await?,
- };
- }
- }
-
- xml.close().await?;
- Ok(Multistatus { responses, responsedescription })
- }
-}
-
-// LOCK REQUEST
-impl QRead<LockInfo> for LockInfo {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "lockinfo").await?;
- let (mut m_scope, mut m_type, mut owner) = (None, None, None);
- loop {
- let mut dirty = false;
- xml.maybe_read::<LockScope>(&mut m_scope, &mut dirty).await?;
- xml.maybe_read::<LockType>(&mut m_type, &mut dirty).await?;
- xml.maybe_read::<Owner>(&mut owner, &mut dirty).await?;
-
- if !dirty {
- match xml.peek() {
- Event::End(_) => break,
- _ => xml.skip().await?,
- };
- }
- }
- xml.close().await?;
- match (m_scope, m_type) {
- (Some(lockscope), Some(locktype)) => Ok(LockInfo { lockscope, locktype, owner }),
- _ => Err(ParsingError::MissingChild),
- }
- }
-}
-
-// LOCK RESPONSE
-impl<E: Extension> QRead<PropValue<E>> for PropValue<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "prop").await?;
- let mut acc = xml.collect::<Property<E>>().await?;
- xml.close().await?;
- Ok(PropValue(acc))
- }
-}
-
-
-/// Error response
-impl<E: Extension> QRead<Error<E>> for Error<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "error").await?;
- let violations = xml.collect::<Violation<E>>().await?;
- xml.close().await?;
- Ok(Error(violations))
- }
-}
-
-
-
-// ---- INNER XML
-impl<E: Extension, N: Node<N>> QRead<Response<E,N>> for Response<E,N> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "response").await?;
- let (mut status, mut error, mut responsedescription, mut location) = (None, None, None, None);
- let mut href = Vec::new();
- let mut propstat = Vec::new();
-
- loop {
- let mut dirty = false;
- xml.maybe_read::<Status>(&mut status, &mut dirty).await?;
- xml.maybe_push::<Href>(&mut href, &mut dirty).await?;
- xml.maybe_push::<PropStat<E,N>>(&mut propstat, &mut dirty).await?;
- xml.maybe_read::<Error<E>>(&mut error, &mut dirty).await?;
- xml.maybe_read::<ResponseDescription>(&mut responsedescription, &mut dirty).await?;
- xml.maybe_read::<Location>(&mut location, &mut dirty).await?;
-
- if !dirty {
- match xml.peek() {
- Event::End(_) => break,
- _ => { xml.skip().await? },
- };
- }
- }
-
- xml.close().await?;
- match (status, &propstat[..], &href[..]) {
- (Some(status), &[], &[_, ..]) => Ok(Response {
- status_or_propstat: StatusOrPropstat::Status(href, status),
- error, responsedescription, location,
- }),
- (None, &[_, ..], &[_, ..]) => Ok(Response {
- status_or_propstat: StatusOrPropstat::PropStat(href.into_iter().next().unwrap(), propstat),
- error, responsedescription, location,
- }),
- (Some(_), &[_, ..], _) => Err(ParsingError::InvalidValue),
- _ => Err(ParsingError::MissingChild),
- }
- }
-}
-
-impl<E: Extension, N: Node<N>> QRead<PropStat<E,N>> for PropStat<E,N> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "propstat").await?;
-
- let (mut m_prop, mut m_status, mut error, mut responsedescription) = (None, None, None, None);
-
- loop {
- let mut dirty = false;
- xml.maybe_read::<N>(&mut m_prop, &mut dirty).await?;
- xml.maybe_read::<Status>(&mut m_status, &mut dirty).await?;
- xml.maybe_read::<Error<E>>(&mut error, &mut dirty).await?;
- xml.maybe_read::<ResponseDescription>(&mut responsedescription, &mut dirty).await?;
-
- if !dirty {
- match xml.peek() {
- Event::End(_) => break,
- _ => xml.skip().await?,
- };
- }
- }
-
- xml.close().await?;
- match (m_prop, m_status) {
- (Some(prop), Some(status)) => Ok(PropStat { prop, status, error, responsedescription }),
- _ => Err(ParsingError::MissingChild),
- }
- }
-}
-
-impl QRead<Status> for Status {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "status").await?;
- let fullcode = xml.tag_string().await?;
- let txtcode = fullcode.splitn(3, ' ').nth(1).ok_or(ParsingError::InvalidValue)?;
- let code = http::status::StatusCode::from_bytes(txtcode.as_bytes()).or(Err(ParsingError::InvalidValue))?;
- xml.close().await?;
- Ok(Status(code))
- }
-}
-
-impl QRead<ResponseDescription> for ResponseDescription {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "responsedescription").await?;
- let cnt = xml.tag_string().await?;
- xml.close().await?;
- Ok(ResponseDescription(cnt))
- }
-}
-
-impl QRead<Location> for Location {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "location").await?;
- let href = xml.find::<Href>().await?;
- xml.close().await?;
- Ok(Location(href))
- }
-}
-
-impl<E: Extension> QRead<PropertyUpdateItem<E>> for PropertyUpdateItem<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- match Remove::qread(xml).await {
- Err(ParsingError::Recoverable) => (),
- otherwise => return otherwise.map(PropertyUpdateItem::Remove),
- }
- Set::qread(xml).await.map(PropertyUpdateItem::Set)
- }
-}
-
-impl<E: Extension> QRead<Remove<E>> for Remove<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "remove").await?;
- let propname = xml.find::<PropName<E>>().await?;
- xml.close().await?;
- Ok(Remove(propname))
- }
-}
-
-impl<E: Extension> QRead<Set<E>> for Set<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "set").await?;
- let propvalue = xml.find::<PropValue<E>>().await?;
- xml.close().await?;
- Ok(Set(propvalue))
- }
-}
-
-impl<E: Extension> QRead<Violation<E>> for Violation<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- if xml.maybe_open(DAV_URN, "lock-token-matches-request-uri").await?.is_some() {
- xml.close().await?;
- Ok(Violation::LockTokenMatchesRequestUri)
- } else if xml.maybe_open(DAV_URN, "lock-token-submitted").await?.is_some() {
- let links = xml.collect::<Href>().await?;
- xml.close().await?;
- Ok(Violation::LockTokenSubmitted(links))
- } else if xml.maybe_open(DAV_URN, "no-conflicting-lock").await?.is_some() {
- let links = xml.collect::<Href>().await?;
- xml.close().await?;
- Ok(Violation::NoConflictingLock(links))
- } else if xml.maybe_open(DAV_URN, "no-external-entities").await?.is_some() {
- xml.close().await?;
- Ok(Violation::NoExternalEntities)
- } else if xml.maybe_open(DAV_URN, "preserved-live-properties").await?.is_some() {
- xml.close().await?;
- Ok(Violation::PreservedLiveProperties)
- } else if xml.maybe_open(DAV_URN, "propfind-finite-depth").await?.is_some() {
- xml.close().await?;
- Ok(Violation::PropfindFiniteDepth)
- } else if xml.maybe_open(DAV_URN, "cannot-modify-protected-property").await?.is_some() {
- xml.close().await?;
- Ok(Violation::CannotModifyProtectedProperty)
- } else {
- E::Error::qread(xml).await.map(Violation::Extension)
- }
- }
-}
-
-impl<E: Extension> QRead<Include<E>> for Include<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "include").await?;
- let acc = xml.collect::<PropertyRequest<E>>().await?;
- xml.close().await?;
- Ok(Include(acc))
- }
-}
-
-impl<E: Extension> QRead<PropName<E>> for PropName<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "prop").await?;
- let acc = xml.collect::<PropertyRequest<E>>().await?;
- xml.close().await?;
- Ok(PropName(acc))
- }
-}
-
-impl<E: Extension> QRead<PropertyRequest<E>> for PropertyRequest<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- let maybe = if xml.maybe_open(DAV_URN, "creationdate").await?.is_some() {
- Some(PropertyRequest::CreationDate)
- } else if xml.maybe_open(DAV_URN, "displayname").await?.is_some() {
- Some(PropertyRequest::DisplayName)
- } else if xml.maybe_open(DAV_URN, "getcontentlanguage").await?.is_some() {
- Some(PropertyRequest::GetContentLanguage)
- } else if xml.maybe_open(DAV_URN, "getcontentlength").await?.is_some() {
- Some(PropertyRequest::GetContentLength)
- } else if xml.maybe_open(DAV_URN, "getcontenttype").await?.is_some() {
- Some(PropertyRequest::GetContentType)
- } else if xml.maybe_open(DAV_URN, "getetag").await?.is_some() {
- Some(PropertyRequest::GetEtag)
- } else if xml.maybe_open(DAV_URN, "getlastmodified").await?.is_some() {
- Some(PropertyRequest::GetLastModified)
- } else if xml.maybe_open(DAV_URN, "lockdiscovery").await?.is_some() {
- Some(PropertyRequest::LockDiscovery)
- } else if xml.maybe_open(DAV_URN, "resourcetype").await?.is_some() {
- Some(PropertyRequest::ResourceType)
- } else if xml.maybe_open(DAV_URN, "supportedlock").await?.is_some() {
- Some(PropertyRequest::SupportedLock)
- } else {
- None
- };
-
- match maybe {
- Some(pr) => {
- xml.close().await?;
- Ok(pr)
- },
- None => E::PropertyRequest::qread(xml).await.map(PropertyRequest::Extension),
- }
- }
-}
-
-impl<E: Extension> QRead<Property<E>> for Property<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- use chrono::{DateTime, FixedOffset, TimeZone};
-
- // Core WebDAV properties
- if xml.maybe_open(DAV_URN, "creationdate").await?.is_some() {
- let datestr = xml.tag_string().await?;
- xml.close().await?;
- return Ok(Property::CreationDate(DateTime::parse_from_rfc3339(datestr.as_str())?))
- } else if xml.maybe_open(DAV_URN, "displayname").await?.is_some() {
- let name = xml.tag_string().await?;
- xml.close().await?;
- return Ok(Property::DisplayName(name))
- } else if xml.maybe_open(DAV_URN, "getcontentlanguage").await?.is_some() {
- let lang = xml.tag_string().await?;
- xml.close().await?;
- return Ok(Property::GetContentLanguage(lang))
- } else if xml.maybe_open(DAV_URN, "getcontentlength").await?.is_some() {
- let cl = xml.tag_string().await?.parse::<u64>()?;
- xml.close().await?;
- return Ok(Property::GetContentLength(cl))
- } else if xml.maybe_open(DAV_URN, "getcontenttype").await?.is_some() {
- let ct = xml.tag_string().await?;
- xml.close().await?;
- return Ok(Property::GetContentType(ct))
- } else if xml.maybe_open(DAV_URN, "getetag").await?.is_some() {
- let etag = xml.tag_string().await?;
- xml.close().await?;
- return Ok(Property::GetEtag(etag))
- } else if xml.maybe_open(DAV_URN, "getlastmodified").await?.is_some() {
- let datestr = xml.tag_string().await?;
- xml.close().await?;
- return Ok(Property::GetLastModified(DateTime::parse_from_rfc2822(datestr.as_str())?))
- } else if xml.maybe_open(DAV_URN, "lockdiscovery").await?.is_some() {
- let acc = xml.collect::<ActiveLock>().await?;
- xml.close().await?;
- return Ok(Property::LockDiscovery(acc))
- } else if xml.maybe_open(DAV_URN, "resourcetype").await?.is_some() {
- let acc = xml.collect::<ResourceType<E>>().await?;
- xml.close().await?;
- return Ok(Property::ResourceType(acc))
- } else if xml.maybe_open(DAV_URN, "supportedlock").await?.is_some() {
- let acc = xml.collect::<LockEntry>().await?;
- xml.close().await?;
- return Ok(Property::SupportedLock(acc))
- }
-
- // Option 2: an extension property, delegating
- E::Property::qread(xml).await.map(Property::Extension)
- }
-}
-
-impl QRead<ActiveLock> for ActiveLock {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "activelock").await?;
- let (mut m_scope, mut m_type, mut m_depth, mut owner, mut timeout, mut locktoken, mut m_root) =
- (None, None, None, None, None, None, None);
-
- loop {
- let mut dirty = false;
- xml.maybe_read::<LockScope>(&mut m_scope, &mut dirty).await?;
- xml.maybe_read::<LockType>(&mut m_type, &mut dirty).await?;
- xml.maybe_read::<Depth>(&mut m_depth, &mut dirty).await?;
- xml.maybe_read::<Owner>(&mut owner, &mut dirty).await?;
- xml.maybe_read::<Timeout>(&mut timeout, &mut dirty).await?;
- xml.maybe_read::<LockToken>(&mut locktoken, &mut dirty).await?;
- xml.maybe_read::<LockRoot>(&mut m_root, &mut dirty).await?;
-
- if !dirty {
- match xml.peek() {
- Event::End(_) => break,
- _ => { xml.skip().await?; },
- }
- }
- }
-
- xml.close().await?;
- match (m_scope, m_type, m_depth, m_root) {
- (Some(lockscope), Some(locktype), Some(depth), Some(lockroot)) =>
- Ok(ActiveLock { lockscope, locktype, depth, owner, timeout, locktoken, lockroot }),
- _ => Err(ParsingError::MissingChild),
- }
- }
-}
-
-impl QRead<Depth> for Depth {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "depth").await?;
- let depth_str = xml.tag_string().await?;
- xml.close().await?;
- match depth_str.as_str() {
- "0" => Ok(Depth::Zero),
- "1" => Ok(Depth::One),
- "infinity" => Ok(Depth::Infinity),
- _ => Err(ParsingError::WrongToken),
- }
- }
-}
-
-impl QRead<Owner> for Owner {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "owner").await?;
-
- let mut owner = Owner::Unknown;
- loop {
- match xml.peek() {
- Event::Text(_) | Event::CData(_) => {
- let txt = xml.tag_string().await?;
- if matches!(owner, Owner::Unknown) {
- owner = Owner::Txt(txt);
- }
- }
- Event::Start(_) | Event::Empty(_) => {
- match Href::qread(xml).await {
- Ok(href) => { owner = Owner::Href(href); },
- Err(ParsingError::Recoverable) => { xml.skip().await?; },
- Err(e) => return Err(e),
- }
- }
- Event::End(_) => break,
- _ => { xml.skip().await?; },
- }
- };
- xml.close().await?;
- Ok(owner)
- }
-}
-
-impl QRead<Timeout> for Timeout {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- const SEC_PFX: &str = "SEC_PFX";
- xml.open(DAV_URN, "timeout").await?;
-
- let timeout = match xml.tag_string().await?.as_str() {
- "Infinite" => Timeout::Infinite,
- seconds => match seconds.strip_prefix(SEC_PFX) {
- Some(secs) => Timeout::Seconds(secs.parse::<u32>()?),
- None => return Err(ParsingError::InvalidValue),
- },
- };
-
- xml.close().await?;
- Ok(timeout)
- }
-}
-
-impl QRead<LockToken> for LockToken {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "locktoken").await?;
- let href = Href::qread(xml).await?;
- xml.close().await?;
- Ok(LockToken(href))
- }
-}
-
-impl QRead<LockRoot> for LockRoot {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "lockroot").await?;
- let href = Href::qread(xml).await?;
- xml.close().await?;
- Ok(LockRoot(href))
- }
-}
-
-impl<E: Extension> QRead<ResourceType<E>> for ResourceType<E> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- if xml.maybe_open(DAV_URN, "collection").await?.is_some() {
- xml.close().await?;
- return Ok(ResourceType::Collection)
- }
-
- E::ResourceType::qread(xml).await.map(ResourceType::Extension)
- }
-}
-
-impl QRead<LockEntry> for LockEntry {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "lockentry").await?;
- let (mut maybe_scope, mut maybe_type) = (None, None);
-
- loop {
- let mut dirty = false;
- xml.maybe_read::<LockScope>(&mut maybe_scope, &mut dirty).await?;
- xml.maybe_read::<LockType>(&mut maybe_type, &mut dirty).await?;
- if !dirty {
- match xml.peek() {
- Event::End(_) => break,
- _ => xml.skip().await?,
- };
- }
- }
-
- xml.close().await?;
- match (maybe_scope, maybe_type) {
- (Some(lockscope), Some(locktype)) => Ok(LockEntry { lockscope, locktype }),
- _ => Err(ParsingError::MissingChild),
- }
- }
-}
-
-impl QRead<LockScope> for LockScope {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "lockscope").await?;
-
- let lockscope = loop {
- if xml.maybe_open(DAV_URN, "exclusive").await?.is_some() {
- xml.close().await?;
- break LockScope::Exclusive
- } else if xml.maybe_open(DAV_URN, "shared").await?.is_some() {
- xml.close().await?;
- break LockScope::Shared
- }
-
- xml.skip().await?;
- };
-
- xml.close().await?;
- Ok(lockscope)
- }
-}
-
-impl QRead<LockType> for LockType {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "locktype").await?;
-
- let locktype = loop {
- if xml.maybe_open(DAV_URN, "write").await?.is_some() {
- xml.close().await?;
- break LockType::Write
- }
-
- xml.skip().await?;
- };
-
- xml.close().await?;
- Ok(locktype)
- }
-}
-
-impl QRead<Href> for Href {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<Self, ParsingError> {
- xml.open(DAV_URN, "href").await?;
- let mut url = xml.tag_string().await?;
- xml.close().await?;
- Ok(Href(url))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use chrono::{FixedOffset, DateTime, TimeZone, Utc};
- use crate::dav::realization::Core;
-
- #[tokio::test]
- async fn basic_propfind_propname() {
- let src = r#"<?xml version="1.0" encoding="utf-8" ?>
-<rando/>
-<garbage><old/></garbage>
-<D:propfind xmlns:D="DAV:">
- <D:propname/>
-</D:propfind>
-"#;
-
- let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap();
- let got = rdr.find::<PropFind::<Core>>().await.unwrap();
-
- assert_eq!(got, PropFind::<Core>::PropName);
- }
-
- #[tokio::test]
- async fn basic_propfind_prop() {
- let src = r#"<?xml version="1.0" encoding="utf-8" ?>
-<rando/>
-<garbage><old/></garbage>
-<D:propfind xmlns:D="DAV:">
- <D:prop>
- <D:displayname/>
- <D:getcontentlength/>
- <D:getcontenttype/>
- <D:getetag/>
- <D:getlastmodified/>
- <D:resourcetype/>
- <D:supportedlock/>
- </D:prop>
-</D:propfind>
-"#;
-
- let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap();
- let got = rdr.find::<PropFind::<Core>>().await.unwrap();
-
- assert_eq!(got, PropFind::Prop(PropName(vec![
- PropertyRequest::DisplayName,
- PropertyRequest::GetContentLength,
- PropertyRequest::GetContentType,
- PropertyRequest::GetEtag,
- PropertyRequest::GetLastModified,
- PropertyRequest::ResourceType,
- PropertyRequest::SupportedLock,
- ])));
- }
-
- #[tokio::test]
- async fn rfc_lock_error() {
- let src = r#"<?xml version="1.0" encoding="utf-8" ?>
- <D:error xmlns:D="DAV:">
- <D:lock-token-submitted>
- <D:href>/locked/</D:href>
- </D:lock-token-submitted>
- </D:error>"#;
-
- let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap();
- let got = rdr.find::<Error::<Core>>().await.unwrap();
-
- assert_eq!(got, Error(vec![
- Violation::LockTokenSubmitted(vec![
- Href("/locked/".into())
- ])
- ]));
- }
-
-
- #[tokio::test]
- async fn rfc_propertyupdate() {
- let src = r#"<?xml version="1.0" encoding="utf-8" ?>
- <D:propertyupdate xmlns:D="DAV:"
- xmlns:Z="http://ns.example.com/standards/z39.50/">
- <D:set>
- <D:prop>
- <Z:Authors>
- <Z:Author>Jim Whitehead</Z:Author>
- <Z:Author>Roy Fielding</Z:Author>
- </Z:Authors>
- </D:prop>
- </D:set>
- <D:remove>
- <D:prop><Z:Copyright-Owner/></D:prop>
- </D:remove>
- </D:propertyupdate>"#;
-
- let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap();
- let got = rdr.find::<PropertyUpdate::<Core>>().await.unwrap();
-
- assert_eq!(got, PropertyUpdate(vec![
- PropertyUpdateItem::Set(Set(PropValue(vec![]))),
- PropertyUpdateItem::Remove(Remove(PropName(vec![]))),
- ]));
- }
-
- #[tokio::test]
- async fn rfc_lockinfo() {
- let src = r#"
-<?xml version="1.0" encoding="utf-8" ?>
-<D:lockinfo xmlns:D='DAV:'>
- <D:lockscope><D:exclusive/></D:lockscope>
- <D:locktype><D:write/></D:locktype>
- <D:owner>
- <D:href>http://example.org/~ejw/contact.html</D:href>
- </D:owner>
-</D:lockinfo>
-"#;
-
- let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap();
- let got = rdr.find::<LockInfo>().await.unwrap();
-
- assert_eq!(got, LockInfo {
- lockscope: LockScope::Exclusive,
- locktype: LockType::Write,
- owner: Some(Owner::Href(Href("http://example.org/~ejw/contact.html".into()))),
- });
- }
-
- #[tokio::test]
- async fn rfc_multistatus_name() {
- let src = r#"
-<?xml version="1.0" encoding="utf-8" ?>
- <multistatus xmlns="DAV:">
- <response>
- <href>http://www.example.com/container/</href>
- <propstat>
- <prop xmlns:R="http://ns.example.com/boxschema/">
- <R:bigbox/>
- <R:author/>
- <creationdate/>
- <displayname/>
- <resourcetype/>
- <supportedlock/>
- </prop>
- <status>HTTP/1.1 200 OK</status>
- </propstat>
- </response>
- <response>
- <href>http://www.example.com/container/front.html</href>
- <propstat>
- <prop xmlns:R="http://ns.example.com/boxschema/">
- <R:bigbox/>
- <creationdate/>
- <displayname/>
- <getcontentlength/>
- <getcontenttype/>
- <getetag/>
- <getlastmodified/>
- <resourcetype/>
- <supportedlock/>
- </prop>
- <status>HTTP/1.1 200 OK</status>
- </propstat>
- </response>
- </multistatus>
-"#;
-
- let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap();
- let got = rdr.find::<Multistatus::<Core, PropName<Core>>>().await.unwrap();
-
- assert_eq!(got, Multistatus {
- responses: vec![
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("http://www.example.com/container/".into()),
- vec![PropStat {
- prop: PropName(vec![
- PropertyRequest::CreationDate,
- PropertyRequest::DisplayName,
- PropertyRequest::ResourceType,
- PropertyRequest::SupportedLock,
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }],
- ),
- error: None,
- responsedescription: None,
- location: None,
- },
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("http://www.example.com/container/front.html".into()),
- vec![PropStat {
- prop: PropName(vec![
- PropertyRequest::CreationDate,
- PropertyRequest::DisplayName,
- PropertyRequest::GetContentLength,
- PropertyRequest::GetContentType,
- PropertyRequest::GetEtag,
- PropertyRequest::GetLastModified,
- PropertyRequest::ResourceType,
- PropertyRequest::SupportedLock,
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }],
- ),
- error: None,
- responsedescription: None,
- location: None,
- },
- ],
- responsedescription: None,
- });
- }
-
-
- #[tokio::test]
- async fn rfc_multistatus_value() {
- let src = r#"
- <?xml version="1.0" encoding="utf-8" ?>
- <D:multistatus xmlns:D="DAV:">
- <D:response>
- <D:href>/container/</D:href>
- <D:propstat>
- <D:prop xmlns:R="http://ns.example.com/boxschema/">
- <R:bigbox><R:BoxType>Box type A</R:BoxType></R:bigbox>
- <R:author><R:Name>Hadrian</R:Name></R:author>
- <D:creationdate>1997-12-01T17:42:21-08:00</D:creationdate>
- <D:displayname>Example collection</D:displayname>
- <D:resourcetype><D:collection/></D:resourcetype>
- <D:supportedlock>
- <D:lockentry>
- <D:lockscope><D:exclusive/></D:lockscope>
- <D:locktype><D:write/></D:locktype>
- </D:lockentry>
- <D:lockentry>
- <D:lockscope><D:shared/></D:lockscope>
- <D:locktype><D:write/></D:locktype>
- </D:lockentry>
- </D:supportedlock>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
- <D:response>
- <D:href>/container/front.html</D:href>
- <D:propstat>
- <D:prop xmlns:R="http://ns.example.com/boxschema/">
- <R:bigbox><R:BoxType>Box type B</R:BoxType>
- </R:bigbox>
- <D:creationdate>1997-12-01T18:27:21-08:00</D:creationdate>
- <D:displayname>Example HTML resource</D:displayname>
- <D:getcontentlength>4525</D:getcontentlength>
- <D:getcontenttype>text/html</D:getcontenttype>
- <D:getetag>"zzyzx"</D:getetag>
- <D:getlastmodified
- >Mon, 12 Jan 1998 09:25:56 GMT</D:getlastmodified>
- <D:resourcetype/>
- <D:supportedlock>
- <D:lockentry>
- <D:lockscope><D:exclusive/></D:lockscope>
- <D:locktype><D:write/></D:locktype>
- </D:lockentry>
- <D:lockentry>
- <D:lockscope><D:shared/></D:lockscope>
- <D:locktype><D:write/></D:locktype>
- </D:lockentry>
- </D:supportedlock>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
- </D:multistatus>"#;
-
- let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap();
- let got = rdr.find::<Multistatus::<Core, PropValue<Core>>>().await.unwrap();
-
- assert_eq!(got, Multistatus {
- responses: vec![
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("/container/".into()),
- vec![PropStat {
- prop: PropValue(vec![
- Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 17, 42, 21).unwrap()),
- Property::DisplayName("Example collection".into()),
- Property::ResourceType(vec![ResourceType::Collection]),
- Property::SupportedLock(vec![
- LockEntry {
- lockscope: LockScope::Exclusive,
- locktype: LockType::Write,
- },
- LockEntry {
- lockscope: LockScope::Shared,
- locktype: LockType::Write,
- },
- ]),
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }],
- ),
- error: None,
- responsedescription: None,
- location: None,
-
- },
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("/container/front.html".into()),
- vec![PropStat {
- prop: PropValue(vec![
- Property::CreationDate(FixedOffset::west_opt(8 * 3600).unwrap().with_ymd_and_hms(1997, 12, 01, 18, 27, 21).unwrap()),
- Property::DisplayName("Example HTML resource".into()),
- Property::GetContentLength(4525),
- Property::GetContentType("text/html".into()),
- Property::GetEtag(r#""zzyzx""#.into()),
- Property::GetLastModified(FixedOffset::west_opt(0).unwrap().with_ymd_and_hms(1998, 01, 12, 09, 25, 56).unwrap()),
- //Property::ResourceType(vec![]),
- Property::SupportedLock(vec![
- LockEntry {
- lockscope: LockScope::Exclusive,
- locktype: LockType::Write,
- },
- LockEntry {
- lockscope: LockScope::Shared,
- locktype: LockType::Write,
- },
- ]),
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }],
- ),
- error: None,
- responsedescription: None,
- location: None,
-
- },
- ],
- responsedescription: None,
- });
- }
-
-}
diff --git a/src/dav/encoder.rs b/src/dav/encoder.rs
deleted file mode 100644
index 4de5440..0000000
--- a/src/dav/encoder.rs
+++ /dev/null
@@ -1,1117 +0,0 @@
-use std::io::Cursor;
-
-use quick_xml::Error as QError;
-use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText};
-use quick_xml::writer::ElementWriter;
-use quick_xml::name::PrefixDeclaration;
-use tokio::io::AsyncWrite;
-use super::types::*;
-use super::xml::{Node, Writer,QWrite,IWrite};
-
-
-// --- XML ROOTS
-
-/// PROPFIND REQUEST
-impl<E: Extension> QWrite for PropFind<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("propfind");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- match self {
- Self::PropName => {
- let empty_propname = xml.create_dav_element("propname");
- xml.q.write_event_async(Event::Empty(empty_propname)).await?
- },
- Self::AllProp(maybe_include) => {
- let empty_allprop = xml.create_dav_element("allprop");
- xml.q.write_event_async(Event::Empty(empty_allprop)).await?;
- if let Some(include) = maybe_include {
- include.qwrite(xml).await?;
- }
- },
- Self::Prop(propname) => propname.qwrite(xml).await?,
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-/// PROPPATCH REQUEST
-impl<E: Extension> QWrite for PropertyUpdate<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("propertyupdate");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for update in self.0.iter() {
- update.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-
-/// PROPFIND RESPONSE, PROPPATCH RESPONSE, COPY RESPONSE, MOVE RESPONSE
-/// DELETE RESPONSE,
-impl<E: Extension, N: Node<N>> QWrite for Multistatus<E,N> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("multistatus");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for response in self.responses.iter() {
- response.qwrite(xml).await?;
- }
- if let Some(description) = &self.responsedescription {
- description.qwrite(xml).await?;
- }
-
- xml.q.write_event_async(Event::End(end)).await?;
- Ok(())
- }
-}
-
-/// LOCK REQUEST
-impl QWrite for LockInfo {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("lockinfo");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.lockscope.qwrite(xml).await?;
- self.locktype.qwrite(xml).await?;
- if let Some(owner) = &self.owner {
- owner.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-/// SOME LOCK RESPONSES
-impl<E: Extension> QWrite for PropValue<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("prop");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for propval in &self.0 {
- propval.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-/// Error response
-impl<E: Extension> QWrite for Error<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("error");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for violation in &self.0 {
- violation.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-// --- XML inner elements
-impl<E: Extension> QWrite for PropertyUpdateItem<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::Set(set) => set.qwrite(xml).await,
- Self::Remove(rm) => rm.qwrite(xml).await,
- }
- }
-}
-
-impl<E: Extension> QWrite for Set<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("set");
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension> QWrite for Remove<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("remove");
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-
-impl<E: Extension> QWrite for PropName<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("prop");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for propname in &self.0 {
- propname.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-
-impl QWrite for Href {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("href");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(&self.0))).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension, N: Node<N>> QWrite for Response<E,N> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("response");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.status_or_propstat.qwrite(xml).await?;
- if let Some(error) = &self.error {
- error.qwrite(xml).await?;
- }
- if let Some(responsedescription) = &self.responsedescription {
- responsedescription.qwrite(xml).await?;
- }
- if let Some(location) = &self.location {
- location.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension, N: Node<N>> QWrite for StatusOrPropstat<E,N> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::Status(many_href, status) => {
- for href in many_href.iter() {
- href.qwrite(xml).await?;
- }
- status.qwrite(xml).await
- },
- Self::PropStat(href, propstat_list) => {
- href.qwrite(xml).await?;
- for propstat in propstat_list.iter() {
- propstat.qwrite(xml).await?;
- }
- Ok(())
- }
- }
- }
-}
-
-impl QWrite for Status {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("status");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
-
- let txt = format!("HTTP/1.1 {} {}", self.0.as_str(), self.0.canonical_reason().unwrap_or("No reason"));
- xml.q.write_event_async(Event::Text(BytesText::new(&txt))).await?;
-
- xml.q.write_event_async(Event::End(end)).await?;
-
- Ok(())
- }
-}
-
-impl QWrite for ResponseDescription {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("responsedescription");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(&self.0))).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for Location {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("location");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension, N: Node<N>> QWrite for PropStat<E,N> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("propstat");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.prop.qwrite(xml).await?;
- self.status.qwrite(xml).await?;
- if let Some(error) = &self.error {
- error.qwrite(xml).await?;
- }
- if let Some(description) = &self.responsedescription {
- description.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await?;
-
- Ok(())
- }
-}
-
-impl<E: Extension> QWrite for Property<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- use Property::*;
- match self {
- CreationDate(date) => {
- // <D:creationdate>1997-12-01T17:42:21-08:00</D:creationdate>
- let start = xml.create_dav_element("creationdate");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(&date.to_rfc3339()))).await?;
- xml.q.write_event_async(Event::End(end)).await?;
- },
- DisplayName(name) => {
- // <D:displayname>Example collection</D:displayname>
- let start = xml.create_dav_element("displayname");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(name))).await?;
- xml.q.write_event_async(Event::End(end)).await?;
- },
- GetContentLanguage(lang) => {
- let start = xml.create_dav_element("getcontentlanguage");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(lang))).await?;
- xml.q.write_event_async(Event::End(end)).await?;
- },
- GetContentLength(len) => {
- // <D:getcontentlength>4525</D:getcontentlength>
- let start = xml.create_dav_element("getcontentlength");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(&len.to_string()))).await?;
- xml.q.write_event_async(Event::End(end)).await?;
- },
- GetContentType(ct) => {
- // <D:getcontenttype>text/html</D:getcontenttype>
- let start = xml.create_dav_element("getcontenttype");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(&ct))).await?;
- xml.q.write_event_async(Event::End(end)).await?;
- },
- GetEtag(et) => {
- // <D:getetag>"zzyzx"</D:getetag>
- let start = xml.create_dav_element("getetag");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(et))).await?;
- xml.q.write_event_async(Event::End(end)).await?;
- },
- GetLastModified(date) => {
- // <D:getlastmodified>Mon, 12 Jan 1998 09:25:56 GMT</D:getlastmodified>
- let start = xml.create_dav_element("getlastmodified");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- xml.q.write_event_async(Event::Text(BytesText::new(&date.to_rfc2822()))).await?;
- xml.q.write_event_async(Event::End(end)).await?;
- },
- LockDiscovery(many_locks) => {
- // <D:lockdiscovery><D:activelock> ... </D:activelock></D:lockdiscovery>
- let start = xml.create_dav_element("lockdiscovery");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for lock in many_locks.iter() {
- lock.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await?;
- },
- ResourceType(many_types) => {
- // <D:resourcetype><D:collection/></D:resourcetype>
-
- // <D:resourcetype/>
-
- // <x:resourcetype xmlns:x="DAV:">
- // <x:collection/>
- // <f:search-results xmlns:f="http://www.example.com/ns"/>
- // </x:resourcetype>
-
- let start = xml.create_dav_element("resourcetype");
- if many_types.is_empty() {
- xml.q.write_event_async(Event::Empty(start)).await?;
- } else {
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for restype in many_types.iter() {
- restype.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await?;
- }
- },
- SupportedLock(many_entries) => {
- // <D:supportedlock/>
-
- // <D:supportedlock> <D:lockentry> ... </D:lockentry> </D:supportedlock>
-
- let start = xml.create_dav_element("supportedlock");
- if many_entries.is_empty() {
- xml.q.write_event_async(Event::Empty(start)).await?;
- } else {
- let end = start.to_end();
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for entry in many_entries.iter() {
- entry.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await?;
- }
- },
- Extension(inner) => inner.qwrite(xml).await?,
- };
- Ok(())
- }
-}
-
-impl<E: Extension> QWrite for ResourceType<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- match self {
- Self::Collection => {
- let empty_collection = xml.create_dav_element("collection");
- xml.q.write_event_async(Event::Empty(empty_collection)).await
- },
- Self::Extension(inner) => inner.qwrite(xml).await,
- }
- }
-}
-
-impl<E: Extension> QWrite for Include<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("include");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for prop in self.0.iter() {
- prop.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension> QWrite for PropertyRequest<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- use PropertyRequest::*;
- let mut atom = async |c| {
- let empty_tag = xml.create_dav_element(c);
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- };
-
- match self {
- CreationDate => atom("creationdate").await,
- DisplayName => atom("displayname").await,
- GetContentLanguage => atom("getcontentlanguage").await,
- GetContentLength => atom("getcontentlength").await,
- GetContentType => atom("getcontenttype").await,
- GetEtag => atom("getetag").await,
- GetLastModified => atom("getlastmodified").await,
- LockDiscovery => atom("lockdiscovery").await,
- ResourceType => atom("resourcetype").await,
- SupportedLock => atom("supportedlock").await,
- Extension(inner) => inner.qwrite(xml).await,
- }
- }
-}
-
-impl QWrite for ActiveLock {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- // <D:activelock>
- // <D:locktype><D:write/></D:locktype>
- // <D:lockscope><D:exclusive/></D:lockscope>
- // <D:depth>infinity</D:depth>
- // <D:owner>
- // <D:href>http://example.org/~ejw/contact.html</D:href>
- // </D:owner>
- // <D:timeout>Second-604800</D:timeout>
- // <D:locktoken>
- // <D:href>urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4</D:href>
- // </D:locktoken>
- // <D:lockroot>
- // <D:href>http://example.com/workspace/webdav/proposal.doc</D:href>
- // </D:lockroot>
- // </D:activelock>
- let start = xml.create_dav_element("activelock");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.locktype.qwrite(xml).await?;
- self.lockscope.qwrite(xml).await?;
- self.depth.qwrite(xml).await?;
- if let Some(owner) = &self.owner {
- owner.qwrite(xml).await?;
- }
- if let Some(timeout) = &self.timeout {
- timeout.qwrite(xml).await?;
- }
- if let Some(locktoken) = &self.locktoken {
- locktoken.qwrite(xml).await?;
- }
- self.lockroot.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for LockType {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("locktype");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- match self {
- Self::Write => {
- let empty_write = xml.create_dav_element("write");
- xml.q.write_event_async(Event::Empty(empty_write)).await?
- },
- };
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for LockScope {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("lockscope");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- match self {
- Self::Exclusive => {
- let empty_tag = xml.create_dav_element("exclusive");
- xml.q.write_event_async(Event::Empty(empty_tag)).await?
- },
- Self::Shared => {
- let empty_tag = xml.create_dav_element("shared");
- xml.q.write_event_async(Event::Empty(empty_tag)).await?
- },
- };
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for Owner {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("owner");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- match self {
- Self::Txt(txt) => xml.q.write_event_async(Event::Text(BytesText::new(&txt))).await?,
- Self::Href(href) => href.qwrite(xml).await?,
- Self::Unknown => (),
- }
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for Depth {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("depth");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- match self {
- Self::Zero => xml.q.write_event_async(Event::Text(BytesText::new("0"))).await?,
- Self::One => xml.q.write_event_async(Event::Text(BytesText::new("1"))).await?,
- Self::Infinity => xml.q.write_event_async(Event::Text(BytesText::new("infinity"))).await?,
- };
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for Timeout {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("timeout");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- match self {
- Self::Seconds(count) => {
- let txt = format!("Second-{}", count);
- xml.q.write_event_async(Event::Text(BytesText::new(&txt))).await?
- },
- Self::Infinite => xml.q.write_event_async(Event::Text(BytesText::new("Infinite"))).await?
- };
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for LockToken {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("locktoken");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for LockRoot {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("lockroot");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.0.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl QWrite for LockEntry {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let start = xml.create_dav_element("lockentry");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- self.lockscope.qwrite(xml).await?;
- self.locktype.qwrite(xml).await?;
- xml.q.write_event_async(Event::End(end)).await
- }
-}
-
-impl<E: Extension> QWrite for Violation<E> {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> {
- let mut atom = async |c| {
- let empty_tag = xml.create_dav_element(c);
- xml.q.write_event_async(Event::Empty(empty_tag)).await
- };
-
- match self {
- Violation::LockTokenMatchesRequestUri => atom("lock-token-matches-request-uri").await,
- Violation::LockTokenSubmitted(hrefs) if hrefs.is_empty() => atom("lock-token-submitted").await,
- Violation::LockTokenSubmitted(hrefs) => {
- let start = xml.create_dav_element("lock-token-submitted");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for href in hrefs {
- href.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- },
- Violation::NoConflictingLock(hrefs) if hrefs.is_empty() => atom("no-conflicting-lock").await,
- Violation::NoConflictingLock(hrefs) => {
- let start = xml.create_dav_element("no-conflicting-lock");
- let end = start.to_end();
-
- xml.q.write_event_async(Event::Start(start.clone())).await?;
- for href in hrefs {
- href.qwrite(xml).await?;
- }
- xml.q.write_event_async(Event::End(end)).await
- },
- Violation::NoExternalEntities => atom("no-external-entities").await,
- Violation::PreservedLiveProperties => atom("preserved-live-properties").await,
- Violation::PropfindFiniteDepth => atom("propfind-finite-depth").await,
- Violation::CannotModifyProtectedProperty => atom("cannot-modify-protected-property").await,
- Violation::Extension(inner) => inner.qwrite(xml).await,
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::dav::realization::Core;
- use tokio::io::AsyncWriteExt;
-
- /// To run only the unit tests and avoid the behavior ones:
- /// cargo test --bin aerogramme
-
- async fn serialize(elem: &impl QWrite) -> String {
- let mut buffer = Vec::new();
- let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
- let q = quick_xml::writer::Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
- let ns_to_apply = vec![ ("xmlns:D".into(), "DAV:".into()) ];
- let mut writer = Writer { q, ns_to_apply };
-
- elem.qwrite(&mut writer).await.expect("xml serialization");
- tokio_buffer.flush().await.expect("tokio buffer flush");
- let got = std::str::from_utf8(buffer.as_slice()).unwrap();
-
- return got.into()
- }
-
- #[tokio::test]
- async fn basic_href() {
-
- let got = serialize(
- &Href("/SOGo/dav/so/".into())
- ).await;
- let expected = r#"<D:href xmlns:D="DAV:">/SOGo/dav/so/</D:href>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn basic_multistatus() {
- let got = serialize(
- &Multistatus::<Core, PropName<Core>> {
- responses: vec![],
- responsedescription: Some(ResponseDescription("Hello world".into()))
- },
- ).await;
-
- let expected = r#"<D:multistatus xmlns:D="DAV:">
- <D:responsedescription>Hello world</D:responsedescription>
-</D:multistatus>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
-
- #[tokio::test]
- async fn rfc_error_delete_locked() {
- let got = serialize(
- &Error::<Core>(vec![
- Violation::LockTokenSubmitted(vec![
- Href("/locked/".into())
- ])
- ]),
- ).await;
-
- let expected = r#"<D:error xmlns:D="DAV:">
- <D:lock-token-submitted>
- <D:href>/locked/</D:href>
- </D:lock-token-submitted>
-</D:error>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_propname_req() {
- let got = serialize(
- &PropFind::<Core>::PropName,
- ).await;
-
- let expected = r#"<D:propfind xmlns:D="DAV:">
- <D:propname/>
-</D:propfind>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_propname_res() {
- let got = serialize(
- &Multistatus::<Core, PropName<Core>> {
- responses: vec![
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("http://www.example.com/container/".into()),
- vec![PropStat {
- prop: PropName(vec![
- PropertyRequest::CreationDate,
- PropertyRequest::DisplayName,
- PropertyRequest::ResourceType,
- PropertyRequest::SupportedLock,
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }]
- ),
- error: None,
- responsedescription: None,
- location: None,
- },
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("http://www.example.com/container/front.html".into()),
- vec![PropStat {
- prop: PropName(vec![
- PropertyRequest::CreationDate,
- PropertyRequest::DisplayName,
- PropertyRequest::GetContentLength,
- PropertyRequest::GetContentType,
- PropertyRequest::GetEtag,
- PropertyRequest::GetLastModified,
- PropertyRequest::ResourceType,
- PropertyRequest::SupportedLock,
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }
- ]),
- error: None,
- responsedescription: None,
- location: None,
- },
- ],
- responsedescription: None,
- },
- ).await;
-
- let expected = r#"<D:multistatus xmlns:D="DAV:">
- <D:response>
- <D:href>http://www.example.com/container/</D:href>
- <D:propstat>
- <D:prop>
- <D:creationdate/>
- <D:displayname/>
- <D:resourcetype/>
- <D:supportedlock/>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
- <D:response>
- <D:href>http://www.example.com/container/front.html</D:href>
- <D:propstat>
- <D:prop>
- <D:creationdate/>
- <D:displayname/>
- <D:getcontentlength/>
- <D:getcontenttype/>
- <D:getetag/>
- <D:getlastmodified/>
- <D:resourcetype/>
- <D:supportedlock/>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
-</D:multistatus>"#;
-
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_allprop_req() {
- let got = serialize(
- &PropFind::<Core>::AllProp(None),
- ).await;
-
- let expected = r#"<D:propfind xmlns:D="DAV:">
- <D:allprop/>
-</D:propfind>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_allprop_res() {
- use chrono::{DateTime,FixedOffset,TimeZone};
- let got = serialize(
- &Multistatus::<Core, PropValue<Core>> {
- responses: vec![
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("/container/".into()),
- vec![PropStat {
- prop: PropValue(vec![
- Property::CreationDate(FixedOffset::west_opt(8 * 3600)
- .unwrap()
- .with_ymd_and_hms(1997, 12, 1, 17, 42, 21)
- .unwrap()),
- Property::DisplayName("Example collection".into()),
- Property::ResourceType(vec![ResourceType::Collection]),
- Property::SupportedLock(vec![
- LockEntry {
- lockscope: LockScope::Exclusive,
- locktype: LockType::Write,
- },
- LockEntry {
- lockscope: LockScope::Shared,
- locktype: LockType::Write,
- },
- ]),
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }]
- ),
- error: None,
- responsedescription: None,
- location: None,
- },
- Response {
- status_or_propstat: StatusOrPropstat::PropStat(
- Href("/container/front.html".into()),
- vec![PropStat {
- prop: PropValue(vec![
- Property::CreationDate(FixedOffset::west_opt(8 * 3600)
- .unwrap()
- .with_ymd_and_hms(1997, 12, 1, 18, 27, 21)
- .unwrap()),
- Property::DisplayName("Example HTML resource".into()),
- Property::GetContentLength(4525),
- Property::GetContentType("text/html".into()),
- Property::GetEtag(r#""zzyzx""#.into()),
- Property::GetLastModified(FixedOffset::east_opt(0)
- .unwrap()
- .with_ymd_and_hms(1998, 1, 12, 9, 25, 56)
- .unwrap()),
- Property::ResourceType(vec![]),
- Property::SupportedLock(vec![
- LockEntry {
- lockscope: LockScope::Exclusive,
- locktype: LockType::Write,
- },
- LockEntry {
- lockscope: LockScope::Shared,
- locktype: LockType::Write,
- },
- ]),
- ]),
- status: Status(http::status::StatusCode::OK),
- error: None,
- responsedescription: None,
- }]
- ),
- error: None,
- responsedescription: None,
- location: None,
- },
- ],
- responsedescription: None,
- }
- ).await;
-
- let expected = r#"<D:multistatus xmlns:D="DAV:">
- <D:response>
- <D:href>/container/</D:href>
- <D:propstat>
- <D:prop>
- <D:creationdate>1997-12-01T17:42:21-08:00</D:creationdate>
- <D:displayname>Example collection</D:displayname>
- <D:resourcetype>
- <D:collection/>
- </D:resourcetype>
- <D:supportedlock>
- <D:lockentry>
- <D:lockscope>
- <D:exclusive/>
- </D:lockscope>
- <D:locktype>
- <D:write/>
- </D:locktype>
- </D:lockentry>
- <D:lockentry>
- <D:lockscope>
- <D:shared/>
- </D:lockscope>
- <D:locktype>
- <D:write/>
- </D:locktype>
- </D:lockentry>
- </D:supportedlock>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
- <D:response>
- <D:href>/container/front.html</D:href>
- <D:propstat>
- <D:prop>
- <D:creationdate>1997-12-01T18:27:21-08:00</D:creationdate>
- <D:displayname>Example HTML resource</D:displayname>
- <D:getcontentlength>4525</D:getcontentlength>
- <D:getcontenttype>text/html</D:getcontenttype>
- <D:getetag>&quot;zzyzx&quot;</D:getetag>
- <D:getlastmodified>Mon, 12 Jan 1998 09:25:56 +0000</D:getlastmodified>
- <D:resourcetype/>
- <D:supportedlock>
- <D:lockentry>
- <D:lockscope>
- <D:exclusive/>
- </D:lockscope>
- <D:locktype>
- <D:write/>
- </D:locktype>
- </D:lockentry>
- <D:lockentry>
- <D:lockscope>
- <D:shared/>
- </D:lockscope>
- <D:locktype>
- <D:write/>
- </D:locktype>
- </D:lockentry>
- </D:supportedlock>
- </D:prop>
- <D:status>HTTP/1.1 200 OK</D:status>
- </D:propstat>
- </D:response>
-</D:multistatus>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_allprop_include() {
- let got = serialize(
- &PropFind::<Core>::AllProp(Some(Include(vec![
- PropertyRequest::DisplayName,
- PropertyRequest::ResourceType,
- ]))),
- ).await;
-
- let expected = r#"<D:propfind xmlns:D="DAV:">
- <D:allprop/>
- <D:include>
- <D:displayname/>
- <D:resourcetype/>
- </D:include>
-</D:propfind>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_propertyupdate() {
- let got = serialize(
- &PropertyUpdate::<Core>(vec![
- PropertyUpdateItem::Set(Set(PropValue(vec![
- Property::GetContentLanguage("fr-FR".into()),
- ]))),
- PropertyUpdateItem::Remove(Remove(PropName(vec![
- PropertyRequest::DisplayName,
- ]))),
- ]),
- ).await;
-
- let expected = r#"<D:propertyupdate xmlns:D="DAV:">
- <D:set>
- <D:prop>
- <D:getcontentlanguage>fr-FR</D:getcontentlanguage>
- </D:prop>
- </D:set>
- <D:remove>
- <D:prop>
- <D:displayname/>
- </D:prop>
- </D:remove>
-</D:propertyupdate>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_delete_locked2() {
- let got = serialize(
- &Multistatus::<Core, PropValue<Core>> {
- responses: vec![Response {
- status_or_propstat: StatusOrPropstat::Status(
- vec![Href("http://www.example.com/container/resource3".into())],
- Status(http::status::StatusCode::from_u16(423).unwrap())
- ),
- error: Some(Error(vec![Violation::LockTokenSubmitted(vec![])])),
- responsedescription: None,
- location: None,
- }],
- responsedescription: None,
- },
- ).await;
-
- let expected = r#"<D:multistatus xmlns:D="DAV:">
- <D:response>
- <D:href>http://www.example.com/container/resource3</D:href>
- <D:status>HTTP/1.1 423 Locked</D:status>
- <D:error>
- <D:lock-token-submitted/>
- </D:error>
- </D:response>
-</D:multistatus>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_simple_lock_request() {
- let got = serialize(
- &LockInfo {
- lockscope: LockScope::Exclusive,
- locktype: LockType::Write,
- owner: Some(Owner::Href(Href("http://example.org/~ejw/contact.html".into()))),
- },
- ).await;
-
- let expected = r#"<D:lockinfo xmlns:D="DAV:">
- <D:lockscope>
- <D:exclusive/>
- </D:lockscope>
- <D:locktype>
- <D:write/>
- </D:locktype>
- <D:owner>
- <D:href>http://example.org/~ejw/contact.html</D:href>
- </D:owner>
-</D:lockinfo>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-
- #[tokio::test]
- async fn rfc_simple_lock_response() {
- let got = serialize(
- &PropValue::<Core>(vec![
- Property::LockDiscovery(vec![ActiveLock {
- lockscope: LockScope::Exclusive,
- locktype: LockType::Write,
- depth: Depth::Infinity,
- owner: Some(Owner::Href(Href("http://example.org/~ejw/contact.html".into()))),
- timeout: Some(Timeout::Seconds(604800)),
- locktoken: Some(LockToken(Href("urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4".into()))),
- lockroot: LockRoot(Href("http://example.com/workspace/webdav/proposal.doc".into())),
- }]),
- ]),
- ).await;
-
- let expected = r#"<D:prop xmlns:D="DAV:">
- <D:lockdiscovery>
- <D:activelock>
- <D:locktype>
- <D:write/>
- </D:locktype>
- <D:lockscope>
- <D:exclusive/>
- </D:lockscope>
- <D:depth>infinity</D:depth>
- <D:owner>
- <D:href>http://example.org/~ejw/contact.html</D:href>
- </D:owner>
- <D:timeout>Second-604800</D:timeout>
- <D:locktoken>
- <D:href>urn:uuid:e71d4fae-5dec-22d6-fea5-00a0c91e6be4</D:href>
- </D:locktoken>
- <D:lockroot>
- <D:href>http://example.com/workspace/webdav/proposal.doc</D:href>
- </D:lockroot>
- </D:activelock>
- </D:lockdiscovery>
-</D:prop>"#;
-
- assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
- }
-}
diff --git a/src/dav/error.rs b/src/dav/error.rs
deleted file mode 100644
index 78c6d6b..0000000
--- a/src/dav/error.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use quick_xml::events::attributes::AttrError;
-
-#[derive(Debug)]
-pub enum ParsingError {
- Recoverable,
- MissingChild,
- NamespacePrefixAlreadyUsed,
- WrongToken,
- TagNotFound,
- InvalidValue,
- Utf8Error(std::str::Utf8Error),
- QuickXml(quick_xml::Error),
- Chrono(chrono::format::ParseError),
- Int(std::num::ParseIntError),
- Eof
-}
-impl From<AttrError> for ParsingError {
- fn from(value: AttrError) -> Self {
- Self::QuickXml(value.into())
- }
-}
-impl From<quick_xml::Error> for ParsingError {
- fn from(value: quick_xml::Error) -> Self {
- Self::QuickXml(value)
- }
-}
-impl From<std::str::Utf8Error> for ParsingError {
- fn from(value: std::str::Utf8Error) -> Self {
- Self::Utf8Error(value)
- }
-}
-impl From<chrono::format::ParseError> for ParsingError {
- fn from(value: chrono::format::ParseError) -> Self {
- Self::Chrono(value)
- }
-}
-
-impl From<std::num::ParseIntError> for ParsingError {
- fn from(value: std::num::ParseIntError) -> Self {
- Self::Int(value)
- }
-}
diff --git a/src/dav/mod.rs b/src/dav/mod.rs
deleted file mode 100644
index 906cfdd..0000000
--- a/src/dav/mod.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-// utils
-pub mod error;
-pub mod xml;
-
-// webdav
-pub mod types;
-pub mod encoder;
-pub mod decoder;
-
-// calendar
-mod caltypes;
-mod calencoder;
-mod caldecoder;
-
-// wip
-mod acltypes;
-mod versioningtypes;
-
-// final type
-pub mod realization;
-
-
-use std::net::SocketAddr;
-
-use anyhow::{anyhow, Result};
-use base64::Engine;
-use hyper::service::service_fn;
-use hyper::{Request, Response, body::Bytes};
-use hyper::server::conn::http1 as http;
-use hyper_util::rt::TokioIo;
-use http_body_util::Full;
-use futures::stream::{FuturesUnordered, StreamExt};
-use tokio::net::TcpListener;
-use tokio::sync::watch;
-
-use crate::config::DavUnsecureConfig;
-use crate::login::ArcLoginProvider;
-use crate::user::User;
-
-pub struct Server {
- bind_addr: SocketAddr,
- login_provider: ArcLoginProvider,
-}
-
-pub fn new_unsecure(config: DavUnsecureConfig, login: ArcLoginProvider) -> Server {
- Server {
- bind_addr: config.bind_addr,
- login_provider: login,
- }
-}
-
-impl Server {
- pub async fn run(self: Self, mut must_exit: watch::Receiver<bool>) -> Result<()> {
- let tcp = TcpListener::bind(self.bind_addr).await?;
- tracing::info!("DAV server listening on {:#}", self.bind_addr);
-
- let mut connections = FuturesUnordered::new();
- while !*must_exit.borrow() {
- let wait_conn_finished = async {
- if connections.is_empty() {
- futures::future::pending().await
- } else {
- connections.next().await
- }
- };
- let (socket, remote_addr) = tokio::select! {
- a = tcp.accept() => a?,
- _ = wait_conn_finished => continue,
- _ = must_exit.changed() => continue,
- };
- tracing::info!("Accepted connection from {}", remote_addr);
- let stream = TokioIo::new(socket);
- let login = self.login_provider.clone();
- let conn = tokio::spawn(async move {
- //@FIXME should create a generic "public web" server on which "routers" could be
- //abitrarily bound
- //@FIXME replace with a handler supporting http2 and TLS
- match http::Builder::new().serve_connection(stream, service_fn(|req: Request<hyper::body::Incoming>| {
- let login = login.clone();
- async move {
- auth(login, req).await
- }
- })).await {
- Err(e) => tracing::warn!(err=?e, "connection failed"),
- Ok(()) => tracing::trace!("connection terminated with success"),
- }
- });
- connections.push(conn);
- }
- drop(tcp);
-
- tracing::info!("Server shutting down, draining remaining connections...");
- while connections.next().await.is_some() {}
-
- Ok(())
- }
-}
-
-//@FIXME We should not support only BasicAuth
-async fn auth(
- login: ArcLoginProvider,
- req: Request<impl hyper::body::Body>,
-) -> Result<Response<Full<Bytes>>> {
-
- let auth_val = match req.headers().get("Authorization") {
- Some(hv) => hv.to_str()?,
- None => return Ok(Response::builder()
- .status(401)
- .body(Full::new(Bytes::from("Missing Authorization field")))?),
- };
-
- let b64_creds_maybe_padded = match auth_val.split_once(" ") {
- Some(("Basic", b64)) => b64,
- _ => return Ok(Response::builder()
- .status(400)
- .body(Full::new(Bytes::from("Unsupported Authorization field")))?),
- };
-
- // base64urlencoded may have trailing equals, base64urlsafe has not
- // theoretically authorization is padded but "be liberal in what you accept"
- let b64_creds_clean = b64_creds_maybe_padded.trim_end_matches('=');
-
- // Decode base64
- let creds = base64::engine::general_purpose::STANDARD_NO_PAD.decode(b64_creds_clean)?;
- let str_creds = std::str::from_utf8(&creds)?;
-
- // Split username and password
- let (username, password) = str_creds
- .split_once(':')
- .ok_or(anyhow!("Missing colon in Authorization, can't split decoded value into a username/password pair"))?;
-
- // Call login provider
- let creds = match login.login(username, password).await {
- Ok(c) => c,
- Err(e) => return Ok(Response::builder()
- .status(401)
- .body(Full::new(Bytes::from("Wrong credentials")))?),
- };
-
- // Build a user
- let user = User::new(username.into(), creds).await?;
-
- // Call router with user
- router(user, req).await
-}
-
-async fn router(user: std::sync::Arc<User>, req: Request<impl hyper::body::Body>) -> Result<Response<Full<Bytes>>> {
- let path_segments: Vec<_> = req.uri().path().split("/").filter(|s| *s != "").collect();
- match path_segments.as_slice() {
- [] => tracing::info!("root"),
- [ username, ..] if *username != user.username => return Ok(Response::builder()
- .status(403)
- .body(Full::new(Bytes::from("Accessing other user ressources is not allowed")))?),
- [ _ ] => tracing::info!("user home"),
- [ _, "calendar" ] => tracing::info!("user calendars"),
- [ _, "calendar", colname ] => tracing::info!(name=colname, "selected calendar"),
- [ _, "calendar", colname, member ] => tracing::info!(name=colname, obj=member, "selected event"),
- _ => return Ok(Response::builder()
- .status(404)
- .body(Full::new(Bytes::from("Resource not found")))?),
- }
- Ok(Response::new(Full::new(Bytes::from("Hello World!"))))
-}
-
-async fn collections(user: std::sync::Arc<User>, req: Request<impl hyper::body::Body>) -> Result<Response<Full<Bytes>>> {
- unimplemented!();
-}
diff --git a/src/dav/realization.rs b/src/dav/realization.rs
deleted file mode 100644
index 33a556e..0000000
--- a/src/dav/realization.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use super::types as dav;
-use super::caltypes as cal;
-use super::xml;
-use super::error;
-
-#[derive(Debug, PartialEq)]
-pub struct Disabled(());
-impl xml::QRead<Disabled> for Disabled {
- async fn qread(xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
- Err(error::ParsingError::Recoverable)
- }
-}
-impl xml::QWrite for Disabled {
- async fn qwrite(&self, xml: &mut xml::Writer<impl xml::IWrite>) -> Result<(), quick_xml::Error> {
- unreachable!();
- }
-}
-
-/// The base WebDAV
-///
-/// Any extension is kooh is disabled through an object we can't build
-/// due to a private inner element.
-#[derive(Debug, PartialEq)]
-pub struct Core {}
-impl dav::Extension for Core {
- type Error = Disabled;
- type Property = Disabled;
- type PropertyRequest = Disabled;
- type ResourceType = Disabled;
-}
-
-// WebDAV with the base Calendar implementation (RFC4791)
-#[derive(Debug, PartialEq)]
-pub struct Calendar {}
-impl dav::Extension for Calendar
-{
- type Error = cal::Violation;
- type Property = cal::Property;
- type PropertyRequest = cal::PropertyRequest;
- type ResourceType = cal::ResourceType;
-}
-
diff --git a/src/dav/types.rs b/src/dav/types.rs
deleted file mode 100644
index 5ea38d1..0000000
--- a/src/dav/types.rs
+++ /dev/null
@@ -1,950 +0,0 @@
-#![allow(dead_code)]
-use std::fmt::Debug;
-
-use chrono::{DateTime,FixedOffset};
-use super::xml;
-use super::error;
-
-/// It's how we implement a DAV extension
-/// (That's the dark magic part...)
-pub trait Extension: std::fmt::Debug + PartialEq {
- type Error: xml::Node<Self::Error>;
- type Property: xml::Node<Self::Property>;
- type PropertyRequest: xml::Node<Self::PropertyRequest>;
- type ResourceType: xml::Node<Self::ResourceType>;
-}
-
-/// 14.1. activelock XML Element
-///
-/// Name: activelock
-///
-/// Purpose: Describes a lock on a resource.
-/// <!ELEMENT activelock (lockscope, locktype, depth, owner?, timeout?,
-/// locktoken?, lockroot)>
-#[derive(Debug, PartialEq)]
-pub struct ActiveLock {
- pub lockscope: LockScope,
- pub locktype: LockType,
- pub depth: Depth,
- pub owner: Option<Owner>,
- pub timeout: Option<Timeout>,
- pub locktoken: Option<LockToken>,
- pub lockroot: LockRoot,
-}
-
-/// 14.3 collection XML Element
-///
-/// Name: collection
-///
-/// Purpose: Identifies the associated resource as a collection. The
-/// DAV:resourcetype property of a collection resource MUST contain
-/// this element. It is normally empty but extensions may add sub-
-/// elements.
-///
-/// <!ELEMENT collection EMPTY >
-#[derive(Debug, PartialEq)]
-pub struct Collection{}
-
-/// 14.4 depth XML Element
-///
-/// Name: depth
-///
-/// Purpose: Used for representing depth values in XML content (e.g.,
-/// in lock information).
-///
-/// Value: "0" | "1" | "infinity"
-///
-/// <!ELEMENT depth (#PCDATA) >
-#[derive(Debug, PartialEq)]
-pub enum Depth {
- Zero,
- One,
- Infinity
-}
-
-/// 14.5 error XML Element
-///
-/// Name: error
-///
-/// Purpose: Error responses, particularly 403 Forbidden and 409
-/// Conflict, sometimes need more information to indicate what went
-/// wrong. In these cases, servers MAY return an XML response body
-/// with a document element of 'error', containing child elements
-/// identifying particular condition codes.
-///
-/// Description: Contains at least one XML element, and MUST NOT
-/// contain text or mixed content. Any element that is a child of the
-/// 'error' element is considered to be a precondition or
-/// postcondition code. Unrecognized elements MUST be ignored.
-///
-/// <!ELEMENT error ANY >
-#[derive(Debug, PartialEq)]
-pub struct Error<E: Extension>(pub Vec<Violation<E>>);
-#[derive(Debug, PartialEq)]
-pub enum Violation<E: Extension> {
- /// Name: lock-token-matches-request-uri
- ///
- /// Use with: 409 Conflict
- ///
- /// Purpose: (precondition) -- A request may include a Lock-Token header
- /// to identify a lock for the UNLOCK method. However, if the
- /// Request-URI does not fall within the scope of the lock identified
- /// by the token, the server SHOULD use this error. The lock may have
- /// a scope that does not include the Request-URI, or the lock could
- /// have disappeared, or the token may be invalid.
- LockTokenMatchesRequestUri,
-
- /// Name: lock-token-submitted (precondition)
- ///
- /// Use with: 423 Locked
- ///
- /// Purpose: The request could not succeed because a lock token should
- /// have been submitted. This element, if present, MUST contain at
- /// least one URL of a locked resource that prevented the request. In
- /// cases of MOVE, COPY, and DELETE where collection locks are
- /// involved, it can be difficult for the client to find out which
- /// locked resource made the request fail -- but the server is only
- /// responsible for returning one such locked resource. The server
- /// MAY return every locked resource that prevented the request from
- /// succeeding if it knows them all.
- ///
- /// <!ELEMENT lock-token-submitted (href+) >
- LockTokenSubmitted(Vec<Href>),
-
- /// Name: no-conflicting-lock (precondition)
- ///
- /// Use with: Typically 423 Locked
- ///
- /// Purpose: A LOCK request failed due the presence of an already
- /// existing conflicting lock. Note that a lock can be in conflict
- /// although the resource to which the request was directed is only
- /// indirectly locked. In this case, the precondition code can be
- /// used to inform the client about the resource that is the root of
- /// the conflicting lock, avoiding a separate lookup of the
- /// "lockdiscovery" property.
- ///
- /// <!ELEMENT no-conflicting-lock (href)* >
- NoConflictingLock(Vec<Href>),
-
- /// Name: no-external-entities
- ///
- /// Use with: 403 Forbidden
- ///
- /// Purpose: (precondition) -- If the server rejects a client request
- /// because the request body contains an external entity, the server
- /// SHOULD use this error.
- NoExternalEntities,
-
- /// Name: preserved-live-properties
- ///
- /// Use with: 409 Conflict
- ///
- /// Purpose: (postcondition) -- The server received an otherwise-valid
- /// MOVE or COPY request, but cannot maintain the live properties with
- /// the same behavior at the destination. It may be that the server
- /// only supports some live properties in some parts of the
- /// repository, or simply has an internal error.
- PreservedLiveProperties,
-
- /// Name: propfind-finite-depth
- ///
- /// Use with: 403 Forbidden
- ///
- /// Purpose: (precondition) -- This server does not allow infinite-depth
- /// PROPFIND requests on collections.
- PropfindFiniteDepth,
-
-
- /// Name: cannot-modify-protected-property
- ///
- /// Use with: 403 Forbidden
- ///
- /// Purpose: (precondition) -- The client attempted to set a protected
- /// property in a PROPPATCH (such as DAV:getetag). See also
- /// [RFC3253], Section 3.12.
- CannotModifyProtectedProperty,
-
- /// Specific errors
- Extension(E::Error),
-}
-
-/// 14.6. exclusive XML Element
-///
-/// Name: exclusive
-///
-/// Purpose: Specifies an exclusive lock.
-///
-/// <!ELEMENT exclusive EMPTY >
-#[derive(Debug, PartialEq)]
-pub struct Exclusive {}
-
-/// 14.7. href XML Element
-///
-/// Name: href
-///
-/// Purpose: MUST contain a URI or a relative reference.
-///
-/// Description: There may be limits on the value of 'href' depending
-/// on the context of its use. Refer to the specification text where
-/// 'href' is used to see what limitations apply in each case.
-///
-/// Value: Simple-ref
-///
-/// <!ELEMENT href (#PCDATA)>
-#[derive(Debug, PartialEq)]
-pub struct Href(pub String);
-
-
-/// 14.8. include XML Element
-///
-/// Name: include
-///
-/// Purpose: Any child element represents the name of a property to be
-/// included in the PROPFIND response. All elements inside an
-/// 'include' XML element MUST define properties related to the
-/// resource, although possible property names are in no way limited
-/// to those property names defined in this document or other
-/// standards. This element MUST NOT contain text or mixed content.
-///
-/// <!ELEMENT include ANY >
-#[derive(Debug, PartialEq)]
-pub struct Include<E: Extension>(pub Vec<PropertyRequest<E>>);
-
-/// 14.9. location XML Element
-///
-/// Name: location
-///
-/// Purpose: HTTP defines the "Location" header (see [RFC2616], Section
-/// 14.30) for use with some status codes (such as 201 and the 300
-/// series codes). When these codes are used inside a 'multistatus'
-/// element, the 'location' element can be used to provide the
-/// accompanying Location header value.
-///
-/// Description: Contains a single href element with the same value
-/// that would be used in a Location header.
-///
-/// <!ELEMENT location (href)>
-#[derive(Debug, PartialEq)]
-pub struct Location(pub Href);
-
-/// 14.10. lockentry XML Element
-///
-/// Name: lockentry
-///
-/// Purpose: Defines the types of locks that can be used with the
-/// resource.
-///
-/// <!ELEMENT lockentry (lockscope, locktype) >
-#[derive(Debug, PartialEq)]
-pub struct LockEntry {
- pub lockscope: LockScope,
- pub locktype: LockType,
-}
-
-/// 14.11. lockinfo XML Element
-///
-/// Name: lockinfo
-///
-/// Purpose: The 'lockinfo' XML element is used with a LOCK method to
-/// specify the type of lock the client wishes to have created.
-///
-/// <!ELEMENT lockinfo (lockscope, locktype, owner?) >
-#[derive(Debug, PartialEq)]
-pub struct LockInfo {
- pub lockscope: LockScope,
- pub locktype: LockType,
- pub owner: Option<Owner>,
-}
-
-/// 14.12. lockroot XML Element
-///
-/// Name: lockroot
-///
-/// Purpose: Contains the root URL of the lock, which is the URL
-/// through which the resource was addressed in the LOCK request.
-///
-/// Description: The href element contains the root of the lock. The
-/// server SHOULD include this in all DAV:lockdiscovery property
-/// values and the response to LOCK requests.
-///
-/// <!ELEMENT lockroot (href) >
-#[derive(Debug, PartialEq)]
-pub struct LockRoot(pub Href);
-
-/// 14.13. lockscope XML Element
-///
-/// Name: lockscope
-///
-/// Purpose: Specifies whether a lock is an exclusive lock, or a shared
-/// lock.
-/// <!ELEMENT lockscope (exclusive | shared) >
-#[derive(Debug, PartialEq)]
-pub enum LockScope {
- Exclusive,
- Shared
-}
-
-/// 14.14. locktoken XML Element
-///
-/// Name: locktoken
-///
-/// Purpose: The lock token associated with a lock.
-///
-/// Description: The href contains a single lock token URI, which
-/// refers to the lock.
-///
-/// <!ELEMENT locktoken (href) >
-#[derive(Debug, PartialEq)]
-pub struct LockToken(pub Href);
-
-/// 14.15. locktype XML Element
-///
-/// Name: locktype
-///
-/// Purpose: Specifies the access type of a lock. At present, this
-/// specification only defines one lock type, the write lock.
-///
-/// <!ELEMENT locktype (write) >
-#[derive(Debug, PartialEq)]
-pub enum LockType {
- /// 14.30. write XML Element
- ///
- /// Name: write
- ///
- /// Purpose: Specifies a write lock.
- ///
- ///
- /// <!ELEMENT write EMPTY >
- Write
-}
-
-/// 14.16. multistatus XML Element
-///
-/// Name: multistatus
-///
-/// Purpose: Contains multiple response messages.
-///
-/// Description: The 'responsedescription' element at the top level is
-/// used to provide a general message describing the overarching
-/// nature of the response. If this value is available, an
-/// application may use it instead of presenting the individual
-/// response descriptions contained within the responses.
-///
-/// <!ELEMENT multistatus (response*, responsedescription?) >
-#[derive(Debug, PartialEq)]
-pub struct Multistatus<E: Extension, N: xml::Node<N>> {
- pub responses: Vec<Response<E, N>>,
- pub responsedescription: Option<ResponseDescription>,
-}
-
-/// 14.17. owner XML Element
-///
-/// Name: owner
-///
-/// Purpose: Holds client-supplied information about the creator of a
-/// lock.
-///
-/// Description: Allows a client to provide information sufficient for
-/// either directly contacting a principal (such as a telephone number
-/// or Email URI), or for discovering the principal (such as the URL
-/// of a homepage) who created a lock. The value provided MUST be
-/// treated as a dead property in terms of XML Information Item
-/// preservation. The server MUST NOT alter the value unless the
-/// owner value provided by the client is empty. For a certain amount
-/// of interoperability between different client implementations, if
-/// clients have URI-formatted contact information for the lock
-/// creator suitable for user display, then clients SHOULD put those
-/// URIs in 'href' child elements of the 'owner' element.
-///
-/// Extensibility: MAY be extended with child elements, mixed content,
-/// text content or attributes.
-///
-/// <!ELEMENT owner ANY >
-//@FIXME might need support for an extension
-#[derive(Debug, PartialEq)]
-pub enum Owner {
- Txt(String),
- Href(Href),
- Unknown,
-}
-
-/// 14.18. prop XML Element
-///
-/// Name: prop
-///
-/// Purpose: Contains properties related to a resource.
-///
-/// Description: A generic container for properties defined on
-/// resources. All elements inside a 'prop' XML element MUST define
-/// properties related to the resource, although possible property
-/// names are in no way limited to those property names defined in
-/// this document or other standards. This element MUST NOT contain
-/// text or mixed content.
-///
-/// <!ELEMENT prop ANY >
-#[derive(Debug, PartialEq)]
-pub struct PropName<E: Extension>(pub Vec<PropertyRequest<E>>);
-
-#[derive(Debug, PartialEq)]
-pub struct PropValue<E: Extension>(pub Vec<Property<E>>);
-
-/// 14.19. propertyupdate XML Element
-///
-/// Name: propertyupdate
-///
-/// Purpose: Contains a request to alter the properties on a resource.
-///
-/// Description: This XML element is a container for the information
-/// required to modify the properties on the resource.
-///
-/// <!ELEMENT propertyupdate (remove | set)+ >
-#[derive(Debug, PartialEq)]
-pub struct PropertyUpdate<E: Extension>(pub Vec<PropertyUpdateItem<E>>);
-
-#[derive(Debug, PartialEq)]
-pub enum PropertyUpdateItem<E: Extension> {
- Remove(Remove<E>),
- Set(Set<E>),
-}
-
-/// 14.2 allprop XML Element
-///
-/// Name: allprop
-///
-/// Purpose: Specifies that all names and values of dead properties and
-/// the live properties defined by this document existing on the
-/// resource are to be returned.
-///
-/// <!ELEMENT allprop EMPTY >
-///
-/// ---
-///
-/// 14.21. propname XML Element
-///
-/// Name: propname
-///
-/// Purpose: Specifies that only a list of property names on the
-/// resource is to be returned.
-///
-/// <!ELEMENT propname EMPTY >
-///
-/// ---
-///
-/// 14.20. propfind XML Element
-///
-/// Name: propfind
-///
-/// Purpose: Specifies the properties to be returned from a PROPFIND
-/// method. Four special elements are specified for use with
-/// 'propfind': 'prop', 'allprop', 'include', and 'propname'. If
-/// 'prop' is used inside 'propfind', it MUST NOT contain property
-/// values.
-///
-/// <!ELEMENT propfind ( propname | (allprop, include?) | prop ) >
-#[derive(Debug, PartialEq)]
-pub enum PropFind<E: Extension> {
- PropName,
- AllProp(Option<Include<E>>),
- Prop(PropName<E>),
-}
-
-/// 14.22 propstat XML Element
-///
-/// Name: propstat
-///
-/// Purpose: Groups together a prop and status element that is
-/// associated with a particular 'href' element.
-///
-/// Description: The propstat XML element MUST contain one prop XML
-/// element and one status XML element. The contents of the prop XML
-/// element MUST only list the names of properties to which the result
-/// in the status element applies. The optional precondition/
-/// postcondition element and 'responsedescription' text also apply to
-/// the properties named in 'prop'.
-///
-/// <!ELEMENT propstat (prop, status, error?, responsedescription?) >
-#[derive(Debug, PartialEq)]
-pub struct PropStat<E: Extension, N: xml::Node<N>> {
- pub prop: N,
- pub status: Status,
- pub error: Option<Error<E>>,
- pub responsedescription: Option<ResponseDescription>,
-}
-
-/// 14.23. remove XML Element
-///
-/// Name: remove
-///
-/// Purpose: Lists the properties to be removed from a resource.
-///
-/// Description: Remove instructs that the properties specified in prop
-/// should be removed. Specifying the removal of a property that does
-/// not exist is not an error. All the XML elements in a 'prop' XML
-/// element inside of a 'remove' XML element MUST be empty, as only
-/// the names of properties to be removed are required.
-///
-/// <!ELEMENT remove (prop) >
-#[derive(Debug, PartialEq)]
-pub struct Remove<E: Extension>(pub PropName<E>);
-
-/// 14.24. response XML Element
-///
-/// Name: response
-///
-/// Purpose: Holds a single response describing the effect of a method
-/// on resource and/or its properties.
-///
-/// Description: The 'href' element contains an HTTP URL pointing to a
-/// WebDAV resource when used in the 'response' container. A
-/// particular 'href' value MUST NOT appear more than once as the
-/// child of a 'response' XML element under a 'multistatus' XML
-/// element. This requirement is necessary in order to keep
-/// processing costs for a response to linear time. Essentially, this
-/// prevents having to search in order to group together all the
-/// responses by 'href'. There are, however, no requirements
-/// regarding ordering based on 'href' values. The optional
-/// precondition/postcondition element and 'responsedescription' text
-/// can provide additional information about this resource relative to
-/// the request or result.
-///
-/// <!ELEMENT response (href, ((href*, status)|(propstat+)),
-/// error?, responsedescription? , location?) >
-///
-/// --- rewritten as ---
-/// <!ELEMENT response ((href+, status)|(href, propstat+), error?, responsedescription?, location?>
-#[derive(Debug, PartialEq)]
-pub enum StatusOrPropstat<E: Extension, N: xml::Node<N>> {
- // One status, multiple hrefs...
- Status(Vec<Href>, Status),
- // A single href, multiple properties...
- PropStat(Href, Vec<PropStat<E, N>>),
-}
-
-#[derive(Debug, PartialEq)]
-pub struct Response<E: Extension, N: xml::Node<N>> {
- pub status_or_propstat: StatusOrPropstat<E, N>,
- pub error: Option<Error<E>>,
- pub responsedescription: Option<ResponseDescription>,
- pub location: Option<Location>,
-}
-
-/// 14.25. responsedescription XML Element
-///
-/// Name: responsedescription
-///
-/// Purpose: Contains information about a status response within a
-/// Multi-Status.
-///
-/// Description: Provides information suitable to be presented to a
-/// user.
-///
-/// <!ELEMENT responsedescription (#PCDATA) >
-#[derive(Debug, PartialEq)]
-pub struct ResponseDescription(pub String);
-
-/// 14.26. set XML Element
-///
-/// Name: set
-///
-/// Purpose: Lists the property values to be set for a resource.
-///
-/// Description: The 'set' element MUST contain only a 'prop' element.
-/// The elements contained by the 'prop' element inside the 'set'
-/// element MUST specify the name and value of properties that are set
-/// on the resource identified by Request-URI. If a property already
-/// exists, then its value is replaced. Language tagging information
-/// appearing in the scope of the 'prop' element (in the "xml:lang"
-/// attribute, if present) MUST be persistently stored along with the
-/// property, and MUST be subsequently retrievable using PROPFIND.
-///
-/// <!ELEMENT set (prop) >
-#[derive(Debug, PartialEq)]
-pub struct Set<E: Extension>(pub PropValue<E>);
-
-/// 14.27. shared XML Element
-///
-/// Name: shared
-///
-/// Purpose: Specifies a shared lock.
-///
-///
-/// <!ELEMENT shared EMPTY >
-#[derive(Debug, PartialEq)]
-pub struct Shared {}
-
-
-/// 14.28. status XML Element
-///
-/// Name: status
-///
-/// Purpose: Holds a single HTTP status-line.
-///
-/// Value: status-line (defined in Section 6.1 of [RFC2616])
-///
-/// <!ELEMENT status (#PCDATA) >
-//@FIXME: Better typing is possible with an enum for example
-#[derive(Debug, PartialEq)]
-pub struct Status(pub http::status::StatusCode);
-
-/// 14.29. timeout XML Element
-///
-/// Name: timeout
-///
-/// Purpose: The number of seconds remaining before a lock expires.
-///
-/// Value: TimeType (defined in Section 10.7)
-///
-///
-/// <!ELEMENT timeout (#PCDATA) >
-///
-/// TimeOut = "Timeout" ":" 1#TimeType
-/// TimeType = ("Second-" DAVTimeOutVal | "Infinite")
-/// ; No LWS allowed within TimeType
-/// DAVTimeOutVal = 1*DIGIT
-///
-/// Clients MAY include Timeout request headers in their LOCK requests.
-/// However, the server is not required to honor or even consider these
-/// requests. Clients MUST NOT submit a Timeout request header with any
-/// method other than a LOCK method.
-///
-/// The "Second" TimeType specifies the number of seconds that will
-/// elapse between granting of the lock at the server, and the automatic
-/// removal of the lock. The timeout value for TimeType "Second" MUST
-/// NOT be greater than 2^32-1.
-#[derive(Debug, PartialEq)]
-pub enum Timeout {
- Seconds(u32),
- Infinite,
-}
-
-
-/// 15. DAV Properties
-///
-/// For DAV properties, the name of the property is also the same as the
-/// name of the XML element that contains its value. In the section
-/// below, the final line of each section gives the element type
-/// declaration using the format defined in [REC-XML]. The "Value"
-/// field, where present, specifies further restrictions on the allowable
-/// contents of the XML element using BNF (i.e., to further restrict the
-/// values of a PCDATA element).
-///
-/// A protected property is one that cannot be changed with a PROPPATCH
-/// request. There may be other requests that would result in a change
-/// to a protected property (as when a LOCK request affects the value of
-/// DAV:lockdiscovery). Note that a given property could be protected on
-/// one type of resource, but not protected on another type of resource.
-///
-/// A computed property is one with a value defined in terms of a
-/// computation (based on the content and other properties of that
-/// resource, or even of some other resource). A computed property is
-/// always a protected property.
-///
-/// COPY and MOVE behavior refers to local COPY and MOVE operations.
-///
-/// For properties defined based on HTTP GET response headers (DAV:get*),
-/// the header value could include LWS as defined in [RFC2616], Section
-/// 4.2. Server implementors SHOULD strip LWS from these values before
-/// using as WebDAV property values.
-#[derive(Debug, PartialEq)]
-pub enum PropertyRequest<E: Extension> {
- CreationDate,
- DisplayName,
- GetContentLanguage,
- GetContentLength,
- GetContentType,
- GetEtag,
- GetLastModified,
- LockDiscovery,
- ResourceType,
- SupportedLock,
- Extension(E::PropertyRequest),
-}
-
-#[derive(Debug, PartialEq)]
-pub enum Property<E: Extension> {
- /// 15.1. creationdate Property
- ///
- /// Name: creationdate
- ///
- /// Purpose: Records the time and date the resource was created.
- ///
- /// Value: date-time (defined in [RFC3339], see the ABNF in Section
- /// 5.6.)
- ///
- /// Protected: MAY be protected. Some servers allow DAV:creationdate
- /// to be changed to reflect the time the document was created if that
- /// is more meaningful to the user (rather than the time it was
- /// uploaded). Thus, clients SHOULD NOT use this property in
- /// synchronization logic (use DAV:getetag instead).
- ///
- /// COPY/MOVE behavior: This property value SHOULD be kept during a
- /// MOVE operation, but is normally re-initialized when a resource is
- /// created with a COPY. It should not be set in a COPY.
- ///
- /// Description: The DAV:creationdate property SHOULD be defined on all
- /// DAV compliant resources. If present, it contains a timestamp of
- /// the moment when the resource was created. Servers that are
- /// incapable of persistently recording the creation date SHOULD
- /// instead leave it undefined (i.e. report "Not Found").
- ///
- /// <!ELEMENT creationdate (#PCDATA) >
- CreationDate(DateTime<FixedOffset>),
-
- /// 15.2. displayname Property
- ///
- /// Name: displayname
- ///
- /// Purpose: Provides a name for the resource that is suitable for
- /// presentation to a user.
- ///
- /// Value: Any text.
- ///
- /// Protected: SHOULD NOT be protected. Note that servers implementing
- /// [RFC2518] might have made this a protected property as this is a
- /// new requirement.
- ///
- /// COPY/MOVE behavior: This property value SHOULD be preserved in COPY
- /// and MOVE operations.
- ///
- /// Description: Contains a description of the resource that is
- /// suitable for presentation to a user. This property is defined on
- /// the resource, and hence SHOULD have the same value independent of
- /// the Request-URI used to retrieve it (thus, computing this property
- /// based on the Request-URI is deprecated). While generic clients
- /// might display the property value to end users, client UI designers
- /// must understand that the method for identifying resources is still
- /// the URL. Changes to DAV:displayname do not issue moves or copies
- /// to the server, but simply change a piece of meta-data on the
- /// individual resource. Two resources can have the same DAV:
- /// displayname value even within the same collection.
- ///
- /// <!ELEMENT displayname (#PCDATA) >
- DisplayName(String),
-
-
- /// 15.3. getcontentlanguage Property
- ///
- /// Name: getcontentlanguage
- ///
- /// Purpose: Contains the Content-Language header value (from Section
- /// 14.12 of [RFC2616]) as it would be returned by a GET without
- /// accept headers.
- ///
- /// Value: language-tag (language-tag is defined in Section 3.10 of
- /// [RFC2616])
- ///
- /// Protected: SHOULD NOT be protected, so that clients can reset the
- /// language. Note that servers implementing [RFC2518] might have
- /// made this a protected property as this is a new requirement.
- ///
- /// COPY/MOVE behavior: This property value SHOULD be preserved in COPY
- /// and MOVE operations.
- ///
- /// Description: The DAV:getcontentlanguage property MUST be defined on
- /// any DAV-compliant resource that returns the Content-Language
- /// header on a GET.
- ///
- /// <!ELEMENT getcontentlanguage (#PCDATA) >
- GetContentLanguage(String),
-
- /// 15.4. getcontentlength Property
- ///
- /// Name: getcontentlength
- ///
- /// Purpose: Contains the Content-Length header returned by a GET
- /// without accept headers.
- ///
- /// Value: See Section 14.13 of [RFC2616].
- ///
- /// Protected: This property is computed, therefore protected.
- ///
- /// Description: The DAV:getcontentlength property MUST be defined on
- /// any DAV-compliant resource that returns the Content-Length header
- /// in response to a GET.
- ///
- /// COPY/MOVE behavior: This property value is dependent on the size of
- /// the destination resource, not the value of the property on the
- /// source resource.
- ///
- /// <!ELEMENT getcontentlength (#PCDATA) >
- GetContentLength(u64),
-
- /// 15.5. getcontenttype Property
- ///
- /// Name: getcontenttype
- ///
- /// Purpose: Contains the Content-Type header value (from Section 14.17
- /// of [RFC2616]) as it would be returned by a GET without accept
- /// headers.
- ///
- /// Value: media-type (defined in Section 3.7 of [RFC2616])
- ///
- /// Protected: Potentially protected if the server prefers to assign
- /// content types on its own (see also discussion in Section 9.7.1).
- ///
- /// COPY/MOVE behavior: This property value SHOULD be preserved in COPY
- /// and MOVE operations.
- ///
- /// Description: This property MUST be defined on any DAV-compliant
- /// resource that returns the Content-Type header in response to a
- /// GET.
- ///
- /// <!ELEMENT getcontenttype (#PCDATA) >
- GetContentType(String),
-
- /// 15.6. getetag Property
- ///
- /// Name: getetag
- ///
- /// Purpose: Contains the ETag header value (from Section 14.19 of
- /// [RFC2616]) as it would be returned by a GET without accept
- /// headers.
- ///
- /// Value: entity-tag (defined in Section 3.11 of [RFC2616])
- ///
- /// Protected: MUST be protected because this value is created and
- /// controlled by the server.
- ///
- /// COPY/MOVE behavior: This property value is dependent on the final
- /// state of the destination resource, not the value of the property
- /// on the source resource. Also note the considerations in
- /// Section 8.8.
- ///
- /// Description: The getetag property MUST be defined on any DAV-
- /// compliant resource that returns the Etag header. Refer to Section
- /// 3.11 of RFC 2616 for a complete definition of the semantics of an
- /// ETag, and to Section 8.6 for a discussion of ETags in WebDAV.
- ///
- /// <!ELEMENT getetag (#PCDATA) >
- GetEtag(String),
-
- /// 15.7. getlastmodified Property
- ///
- /// Name: getlastmodified
- ///
- /// Purpose: Contains the Last-Modified header value (from Section
- /// 14.29 of [RFC2616]) as it would be returned by a GET method
- /// without accept headers.
- ///
- /// Value: rfc1123-date (defined in Section 3.3.1 of [RFC2616])
- ///
- /// Protected: SHOULD be protected because some clients may rely on the
- /// value for appropriate caching behavior, or on the value of the
- /// Last-Modified header to which this property is linked.
- ///
- /// COPY/MOVE behavior: This property value is dependent on the last
- /// modified date of the destination resource, not the value of the
- /// property on the source resource. Note that some server
- /// implementations use the file system date modified value for the
- /// DAV:getlastmodified value, and this can be preserved in a MOVE
- /// even when the HTTP Last-Modified value SHOULD change. Note that
- /// since [RFC2616] requires clients to use ETags where provided, a
- /// server implementing ETags can count on clients using a much better
- /// mechanism than modification dates for offline synchronization or
- /// cache control. Also note the considerations in Section 8.8.
- ///
- /// Description: The last-modified date on a resource SHOULD only
- /// reflect changes in the body (the GET responses) of the resource.
- /// A change in a property only SHOULD NOT cause the last-modified
- /// date to change, because clients MAY rely on the last-modified date
- /// to know when to overwrite the existing body. The DAV:
- /// getlastmodified property MUST be defined on any DAV-compliant
- /// resource that returns the Last-Modified header in response to a
- /// GET.
- ///
- /// <!ELEMENT getlastmodified (#PCDATA) >
- GetLastModified(DateTime<FixedOffset>),
-
- /// 15.8. lockdiscovery Property
- ///
- /// Name: lockdiscovery
- ///
- /// Purpose: Describes the active locks on a resource
- ///
- /// Protected: MUST be protected. Clients change the list of locks
- /// through LOCK and UNLOCK, not through PROPPATCH.
- ///
- /// COPY/MOVE behavior: The value of this property depends on the lock
- /// state of the destination, not on the locks of the source resource.
- /// Recall that locks are not moved in a MOVE operation.
- ///
- /// Description: Returns a listing of who has a lock, what type of lock
- /// he has, the timeout type and the time remaining on the timeout,
- /// and the associated lock token. Owner information MAY be omitted
- /// if it is considered sensitive. If there are no locks, but the
- /// server supports locks, the property will be present but contain
- /// zero 'activelock' elements. If there are one or more locks, an
- /// 'activelock' element appears for each lock on the resource. This
- /// property is NOT lockable with respect to write locks (Section 7).
- ///
- /// <!ELEMENT lockdiscovery (activelock)* >
- LockDiscovery(Vec<ActiveLock>),
-
-
- /// 15.9. resourcetype Property
- ///
- /// Name: resourcetype
- ///
- /// Purpose: Specifies the nature of the resource.
- ///
- /// Protected: SHOULD be protected. Resource type is generally decided
- /// through the operation creating the resource (MKCOL vs PUT), not by
- /// PROPPATCH.
- ///
- /// COPY/MOVE behavior: Generally a COPY/MOVE of a resource results in
- /// the same type of resource at the destination.
- ///
- /// Description: MUST be defined on all DAV-compliant resources. Each
- /// child element identifies a specific type the resource belongs to,
- /// such as 'collection', which is the only resource type defined by
- /// this specification (see Section 14.3). If the element contains
- /// the 'collection' child element plus additional unrecognized
- /// elements, it should generally be treated as a collection. If the
- /// element contains no recognized child elements, it should be
- /// treated as a non-collection resource. The default value is empty.
- /// This element MUST NOT contain text or mixed content. Any custom
- /// child element is considered to be an identifier for a resource
- /// type.
- ///
- /// Example: (fictional example to show extensibility)
- ///
- /// <x:resourcetype xmlns:x="DAV:">
- /// <x:collection/>
- /// <f:search-results xmlns:f="http://www.example.com/ns"/>
- /// </x:resourcetype>
- ResourceType(Vec<ResourceType<E>>),
-
- /// 15.10. supportedlock Property
- ///
- /// Name: supportedlock
- ///
- /// Purpose: To provide a listing of the lock capabilities supported by
- /// the resource.
- ///
- /// Protected: MUST be protected. Servers, not clients, determine what
- /// lock mechanisms are supported.
- /// COPY/MOVE behavior: This property value is dependent on the kind of
- /// locks supported at the destination, not on the value of the
- /// property at the source resource. Servers attempting to COPY to a
- /// destination should not attempt to set this property at the
- /// destination.
- ///
- /// Description: Returns a listing of the combinations of scope and
- /// access types that may be specified in a lock request on the
- /// resource. Note that the actual contents are themselves controlled
- /// by access controls, so a server is not required to provide
- /// information the client is not authorized to see. This property is
- /// NOT lockable with respect to write locks (Section 7).
- ///
- /// <!ELEMENT supportedlock (lockentry)* >
- SupportedLock(Vec<LockEntry>),
-
- /// Any extension
- Extension(E::Property),
-}
-
-#[derive(Debug, PartialEq)]
-pub enum ResourceType<E: Extension> {
- Collection,
- Extension(E::ResourceType),
-}
diff --git a/src/dav/versioningtypes.rs b/src/dav/versioningtypes.rs
deleted file mode 100644
index 6c1c204..0000000
--- a/src/dav/versioningtypes.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-//@FIXME required for a full DAV implementation
-// See section 7.1 of the CalDAV RFC
-// It seems it's mainly due to the fact that the REPORT method is re-used.
diff --git a/src/dav/xml.rs b/src/dav/xml.rs
deleted file mode 100644
index 02263fd..0000000
--- a/src/dav/xml.rs
+++ /dev/null
@@ -1,273 +0,0 @@
-use tokio::io::{AsyncWrite, AsyncBufRead};
-use quick_xml::events::{Event, BytesEnd, BytesStart, BytesText};
-use quick_xml::name::{Namespace, QName, PrefixDeclaration, ResolveResult, ResolveResult::*};
-use quick_xml::reader::NsReader;
-
-use super::error::ParsingError;
-
-// Constants
-pub const DAV_URN: &[u8] = b"DAV:";
-pub const CAL_URN: &[u8] = b"urn:ietf:params:xml:ns:caldav";
-pub const CARD_URN: &[u8] = b"urn:ietf:params:xml:ns:carddav";
-
-// Async traits
-pub trait IWrite = AsyncWrite + Unpin;
-pub trait IRead = AsyncBufRead + Unpin;
-
-// Serialization/Deserialization traits
-pub trait QWrite {
- async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), quick_xml::Error>;
-}
-pub trait QRead<T> {
- async fn qread(xml: &mut Reader<impl IRead>) -> Result<T, ParsingError>;
-}
-
-// The representation of an XML node in Rust
-pub trait Node<T> = QRead<T> + QWrite + std::fmt::Debug + PartialEq;
-
-// ---------------
-
-/// Transform a Rust object into an XML stream of characters
-pub struct Writer<T: IWrite> {
- pub q: quick_xml::writer::Writer<T>,
- pub ns_to_apply: Vec<(String, String)>,
-}
-impl<T: IWrite> Writer<T> {
- pub fn create_dav_element(&mut self, name: &str) -> BytesStart<'static> {
- self.create_ns_element("D", name)
- }
- pub fn create_cal_element(&mut self, name: &str) -> BytesStart<'static> {
- self.create_ns_element("C", name)
- }
-
- fn create_ns_element(&mut self, ns: &str, name: &str) -> BytesStart<'static> {
- let mut start = BytesStart::new(format!("{}:{}", ns, name));
- if !self.ns_to_apply.is_empty() {
- start.extend_attributes(self.ns_to_apply.iter().map(|(k, n)| (k.as_str(), n.as_str())));
- self.ns_to_apply.clear()
- }
- start
- }
-}
-
-/// Transform an XML stream of characters into a Rust object
-pub struct Reader<T: IRead> {
- pub rdr: NsReader<T>,
- cur: Event<'static>,
- parents: Vec<Event<'static>>,
- buf: Vec<u8>,
-}
-impl<T: IRead> Reader<T> {
- pub async fn new(mut rdr: NsReader<T>) -> Result<Self, ParsingError> {
- let mut buf: Vec<u8> = vec![];
- let cur = rdr.read_event_into_async(&mut buf).await?.into_owned();
- let parents = vec![];
- buf.clear();
- Ok(Self { cur, parents, rdr, buf })
- }
-
- /// read one more tag
- /// do not expose it publicly
- async fn next(&mut self) -> Result<Event<'static>, ParsingError> {
- let evt = self.rdr.read_event_into_async(&mut self.buf).await?.into_owned();
- self.buf.clear();
- let old_evt = std::mem::replace(&mut self.cur, evt);
- Ok(old_evt)
- }
-
- /// skip a node at current level
- /// I would like to make this one private but not ready
- pub async fn skip(&mut self) -> Result<Event<'static>, ParsingError> {
- //println!("skipping inside node {:?}", self.parents.last());
- match &self.cur {
- Event::Start(b) => {
- let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?;
- self.next().await
- },
- Event::End(_) => Err(ParsingError::WrongToken),
- Event::Eof => Err(ParsingError::Eof),
- _ => self.next().await,
- }
- }
-
- /// check if this is the desired tag
- fn is_tag(&self, ns: &[u8], key: &str) -> bool {
- let qname = match self.peek() {
- Event::Start(bs) | Event::Empty(bs) => bs.name(),
- Event::End(be) => be.name(),
- _ => return false,
- };
-
- let (extr_ns, local) = self.rdr.resolve_element(qname);
-
- if local.into_inner() != key.as_bytes() {
- return false
- }
-
- match extr_ns {
- ResolveResult::Bound(v) => v.into_inner() == ns,
- _ => false,
- }
- }
-
- fn parent_has_child(&self) -> bool {
- matches!(self.parents.last(), Some(Event::Start(_)) | None)
- }
-
- fn ensure_parent_has_child(&self) -> Result<(), ParsingError> {
- match self.parent_has_child() {
- true => Ok(()),
- false => Err(ParsingError::Recoverable),
- }
- }
-
- pub fn peek(&self) -> &Event<'static> {
- &self.cur
- }
-
- // NEW API
- pub async fn tag_string(&mut self) -> Result<String, ParsingError> {
- self.ensure_parent_has_child()?;
-
- let mut acc = String::new();
- loop {
- match self.peek() {
- Event::CData(unescaped) => {
- acc.push_str(std::str::from_utf8(unescaped.as_ref())?);
- self.next().await?
- },
- Event::Text(escaped) => {
- acc.push_str(escaped.unescape()?.as_ref());
- self.next().await?
- }
- Event::End(_) | Event::Start(_) | Event::Empty(_) => return Ok(acc),
- _ => self.next().await?,
- };
- }
- }
-
- pub async fn maybe_read<N: Node<N>>(&mut self, t: &mut Option<N>, dirty: &mut bool) -> Result<(), ParsingError> {
- if !self.parent_has_child() {
- return Ok(())
- }
-
- match N::qread(self).await {
- Ok(v) => {
- *t = Some(v);
- *dirty = true;
- Ok(())
- },
- Err(ParsingError::Recoverable) => Ok(()),
- Err(e) => Err(e),
- }
- }
-
- pub async fn maybe_push<N: Node<N>>(&mut self, t: &mut Vec<N>, dirty: &mut bool) -> Result<(), ParsingError> {
- if !self.parent_has_child() {
- return Ok(())
- }
-
- match N::qread(self).await {
- Ok(v) => {
- t.push(v);
- *dirty = true;
- Ok(())
- },
- Err(ParsingError::Recoverable) => Ok(()),
- Err(e) => Err(e),
- }
- }
-
- pub async fn find<N: Node<N>>(&mut self) -> Result<N, ParsingError> {
- self.ensure_parent_has_child()?;
-
- loop {
- // Try parse
- match N::qread(self).await {
- Err(ParsingError::Recoverable) => (),
- otherwise => return otherwise,
- }
-
- // If recovered, skip the element
- self.skip().await?;
- }
- }
-
- pub async fn maybe_find<N: Node<N>>(&mut self) -> Result<Option<N>, ParsingError> {
- self.ensure_parent_has_child()?;
-
- loop {
- // Try parse
- match N::qread(self).await {
- Err(ParsingError::Recoverable) => (),
- otherwise => return otherwise.map(Some),
- }
-
- match self.peek() {
- Event::End(_) => return Ok(None),
- _ => self.skip().await?,
- };
- }
- }
-
- pub async fn collect<N: Node<N>>(&mut self) -> Result<Vec<N>, ParsingError> {
- self.ensure_parent_has_child()?;
- let mut acc = Vec::new();
-
- loop {
- match N::qread(self).await {
- Err(ParsingError::Recoverable) => match self.peek() {
- Event::End(_) => return Ok(acc),
- _ => {
- self.skip().await?;
- },
- },
- Ok(v) => acc.push(v),
- Err(e) => return Err(e),
- }
- }
- }
-
- pub async fn open(&mut self, ns: &[u8], key: &str) -> Result<Event<'static>, ParsingError> {
- let evt = match self.peek() {
- Event::Empty(_) if self.is_tag(ns, key) => self.cur.clone(),
- Event::Start(_) if self.is_tag(ns, key) => self.next().await?,
- _ => return Err(ParsingError::Recoverable),
- };
-
- //println!("open tag {:?}", evt);
- self.parents.push(evt.clone());
- Ok(evt)
- }
-
- pub async fn maybe_open(&mut self, ns: &[u8], key: &str) -> Result<Option<Event<'static>>, ParsingError> {
- match self.open(ns, key).await {
- Ok(v) => Ok(Some(v)),
- Err(ParsingError::Recoverable) => Ok(None),
- Err(e) => Err(e),
- }
- }
-
- // find stop tag
- pub async fn close(&mut self) -> Result<Event<'static>, ParsingError> {
- //println!("close tag {:?}", self.parents.last());
-
- // Handle the empty case
- if !self.parent_has_child() {
- self.parents.pop();
- return self.next().await
- }
-
- // Handle the start/end case
- loop {
- match self.peek() {
- Event::End(_) => {
- self.parents.pop();
- return self.next().await
- },
- _ => self.skip().await?,
- };
- }
- }
-}
-