diff options
-rw-r--r-- | src/dav/decoder.rs | 409 | ||||
-rw-r--r-- | src/dav/encoder.rs | 27 | ||||
-rw-r--r-- | src/dav/error.rs | 13 | ||||
-rw-r--r-- | src/dav/xml.rs | 18 |
4 files changed, 399 insertions, 68 deletions
diff --git a/src/dav/decoder.rs b/src/dav/decoder.rs index 7de5d63..43e5c49 100644 --- a/src/dav/decoder.rs +++ b/src/dav/decoder.rs @@ -12,6 +12,8 @@ use super::error::ParsingError; use super::xml::{QRead, Reader, IRead, DAV_URN, CAL_URN}; // ---- ROOT ---- + +/// Propfind request impl<E: Extension> QRead<PropFind<E>> for PropFind<E> { async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { // Find propfind @@ -48,6 +50,37 @@ impl<E: Extension> QRead<PropFind<E>> for PropFind<E> { } } +/// PROPPATCH request +impl<E: Extension> QRead<PropertyUpdate<E>> for PropertyUpdate<E> { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + xml.tag_start(DAV_URN, "propertyupdate").await?; + let mut collected_items = Vec::new(); + loop { + // Try to collect a property item + if let Some(item) = PropertyUpdateItem::qread(xml).await? { + collected_items.push(item); + continue + } + + // Skip or stop otherwise + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + } + + xml.tag_stop(DAV_URN, "propertyupdate").await?; + Ok(Some(PropertyUpdate(collected_items))) + } +} + +//@TODO Multistatus + +//@TODO LockInfo + +//@TODO PropValue + +/// Error response impl<E: Extension> QRead<Error<E>> for Error<E> { async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { xml.tag_start(DAV_URN, "error").await?; @@ -66,7 +99,56 @@ impl<E: Extension> QRead<Error<E>> for Error<E> { } } + + // ---- INNER XML +impl<E: Extension> QRead<PropertyUpdateItem<E>> for PropertyUpdateItem<E> { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + if let Some(rm) = Remove::qread(xml).await? { + return Ok(Some(PropertyUpdateItem::Remove(rm))) + } + Ok(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<Option<Self>, ParsingError> { + match xml.peek() { + Event::Start(b) if xml.is_tag(DAV_URN, "remove") => xml.next().await?, + _ => return Ok(None), + }; + + let propname = loop { + match xml.peek() { + Event::Start(b) | Event::Empty(b) if xml.is_tag(DAV_URN, "prop") => break PropName::qread(xml).await?, + _ => xml.skip().await?, + }; + }; + + xml.tag_stop(DAV_URN, "remove").await?; + Ok(propname.map(Remove)) + } +} + +impl<E: Extension> QRead<Set<E>> for Set<E> { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + match xml.peek() { + Event::Start(b) if xml.is_tag(DAV_URN, "set") => xml.next().await?, + _ => return Ok(None), + }; + let propvalue = loop { + match xml.peek() { + Event::Start(b) | Event::Empty(b) if xml.is_tag(DAV_URN, "prop") => break PropValue::qread(xml).await?, + _ => xml.skip().await?, + }; + }; + + + xml.tag_stop(DAV_URN, "set").await?; + Ok(propvalue.map(Set)) + } +} + impl<E: Extension> QRead<Violation<E>> for Violation<E> { async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { loop { @@ -163,13 +245,17 @@ impl<E: Extension> QRead<Violation<E>> for Violation<E> { impl<E: Extension> QRead<Include<E>> for Include<E> { async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { xml.tag_start(DAV_URN, "include").await?; - let mut acc: Vec<PropertyRequest<E>> = Vec::new(); + let mut acc = Vec::new(); loop { + // Found a property + if let Some(prop) = PropertyRequest::qread(xml).await? { + acc.push(prop); + continue; + } + + // Otherwise skip or escape match xml.peek() { - Event::Start(_) | Event::Empty(_) => { - PropertyRequest::qread(xml).await?.map(|v| acc.push(v)); - }, - Event::End(_) if xml.is_tag(DAV_URN, "include") => break, + Event::End(_) => break, _ => { xml.skip().await?; }, } } @@ -181,13 +267,17 @@ impl<E: Extension> QRead<Include<E>> for Include<E> { impl<E: Extension> QRead<PropName<E>> for PropName<E> { async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { xml.tag_start(DAV_URN, "prop").await?; - let mut acc: Vec<PropertyRequest<E>> = Vec::new(); + let mut acc = Vec::new(); loop { + // Found a property + if let Some(prop) = PropertyRequest::qread(xml).await? { + acc.push(prop); + continue; + } + + // Otherwise skip or escape match xml.peek() { - Event::Start(_) | Event::Empty(_) => { - PropertyRequest::qread(xml).await?.map(|v| acc.push(v)); - }, - Event::End(_) if xml.is_tag(DAV_URN, "prop") => break, + Event::End(_) => break, _ => { xml.skip().await?; }, } } @@ -198,72 +288,281 @@ impl<E: Extension> QRead<PropName<E>> for PropName<E> { impl<E: Extension> QRead<PropertyRequest<E>> for PropertyRequest<E> { async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { - loop { - let bs = match xml.peek() { - Event::Start(b) | Event::Empty(b) => b, - _ => { - xml.skip().await?; - continue - }, + let bs = match xml.peek() { + Event::Start(b) | Event::Empty(b) => b, + _ => return Ok(None), + }; + + let mut maybe_res = None; + + // Option 1: a pure core DAV property + let (ns, loc) = xml.rdr.resolve_element(bs.name()); + if matches!(ns, Bound(Namespace(ns)) if ns == DAV_URN) { + maybe_res = match loc.into_inner() { + b"creationdate" => Some(PropertyRequest::CreationDate), + b"displayname" => Some(PropertyRequest::DisplayName), + b"getcontentlanguage" => Some(PropertyRequest::GetContentLanguage), + b"getcontentlength" => Some(PropertyRequest::GetContentLength), + b"getcontenttype" => Some(PropertyRequest::GetContentType), + b"getetag" => Some(PropertyRequest::GetEtag), + b"getlastmodified" => Some(PropertyRequest::GetLastModified), + b"lockdiscovery" => Some(PropertyRequest::LockDiscovery), + b"resourcetype" => Some(PropertyRequest::ResourceType), + b"supportedlock" => Some(PropertyRequest::SupportedLock), + _ => None, }; + // Close the current tag if we read something + if maybe_res.is_some() { + xml.skip().await?; + } + } - let mut maybe_res = None; + // Option 2: an extension property, delegating + if maybe_res.is_none() { + maybe_res = E::PropertyRequest::qread(xml).await?.map(PropertyRequest::Extension); + } - // Option 1: a pure DAV property - let (ns, loc) = xml.rdr.resolve_element(bs.name()); - if matches!(ns, Bound(Namespace(ns)) if ns == DAV_URN) { - maybe_res = match loc.into_inner() { - b"creationdate" => Some(PropertyRequest::CreationDate), - b"displayname" => Some(PropertyRequest::DisplayName), - b"getcontentlanguage" => Some(PropertyRequest::GetContentLanguage), - b"getcontentlength" => Some(PropertyRequest::GetContentLength), - b"getcontenttype" => Some(PropertyRequest::GetContentType), - b"getetag" => Some(PropertyRequest::GetEtag), - b"getlastmodified" => Some(PropertyRequest::GetLastModified), - b"lockdiscovery" => Some(PropertyRequest::LockDiscovery), - b"resourcetype" => Some(PropertyRequest::ResourceType), - b"supportedlock" => Some(PropertyRequest::SupportedLock), - _ => None, - }; - // Close the current tag if we read something - if maybe_res.is_some() { - xml.skip().await?; - } - } + Ok(maybe_res) + } +} - // Option 2: an extension property, delegating - if maybe_res.is_none() { - maybe_res = E::PropertyRequest::qread(xml).await?.map(PropertyRequest::Extension); + +impl<E: Extension> QRead<PropValue<E>> for PropValue<E> { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + xml.tag_start(DAV_URN, "prop").await?; + let mut acc = Vec::new(); + loop { + // Found a property + if let Some(prop) = Property::qread(xml).await? { + acc.push(prop); + continue; } - return Ok(maybe_res) + // Otherwise skip or escape + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } } + xml.tag_stop(DAV_URN, "prop").await?; + Ok(Some(PropValue(acc))) } } -impl QRead<Href> for Href { +impl<E: Extension> QRead<Property<E>> for Property<E> { async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { - match xml.peek() { - Event::Start(b) if xml.is_tag(DAV_URN, "href") => xml.next().await?, + use chrono::{DateTime, FixedOffset, TimeZone}; + + let bs = match xml.peek() { + Event::Start(b) | Event::Empty(b) => b, _ => return Ok(None), }; - let mut url = String::new(); + let mut maybe_res = None; + + // Option 1: a pure core DAV property + let (ns, loc) = xml.rdr.resolve_element(bs.name()); + if matches!(ns, Bound(Namespace(ns)) if ns == DAV_URN) { + maybe_res = match loc.into_inner() { + b"creationdate" => { + xml.next().await?; + let datestr = xml.tag_string().await?; + Some(Property::CreationDate(DateTime::parse_from_rfc3339(datestr.as_str())?)) + }, + b"displayname" => { + xml.next().await?; + Some(Property::DisplayName(xml.tag_string().await?)) + }, + b"getcontentlanguage" => { + xml.next().await?; + Some(Property::GetContentLanguage(xml.tag_string().await?)) + }, + b"getcontentlength" => { + xml.next().await?; + let cl = xml.tag_string().await?.parse::<u64>()?; + Some(Property::GetContentLength(cl)) + }, + b"getcontenttype" => { + xml.next().await?; + Some(Property::GetContentType(xml.tag_string().await?)) + }, + b"getetag" => { + xml.next().await?; + Some(Property::GetEtag(xml.tag_string().await?)) + }, + b"getlastmodified" => { + xml.next().await?; + xml.next().await?; + let datestr = xml.tag_string().await?; + Some(Property::CreationDate(DateTime::parse_from_rfc2822(datestr.as_str())?)) + }, + b"lockdiscovery" => { + // start tag + xml.next().await?; + + let mut acc = Vec::new(); + loop { + // If we find a lock + if let Some(lock) = ActiveLock::qread(xml).await? { + acc.push(lock); + continue + } + + // Otherwise + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + } + xml.tag_stop(DAV_URN, "lockdiscovery").await?; + Some(Property::LockDiscovery(acc)) + }, + b"resourcetype" => { + xml.next().await?; + + let mut acc = Vec::new(); + loop { + // If we find a resource type... + if let Some(restype) = ResourceType::qread(xml).await? { + acc.push(restype); + continue + } + + // Otherwise + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + } + xml.tag_stop(DAV_URN, "resourcetype").await?; + Some(Property::ResourceType(acc)) + }, + b"supportedlock" => { + xml.next().await?; + + let mut acc = Vec::new(); + loop { + // If we find a resource type... + if let Some(restype) = LockEntry::qread(xml).await? { + acc.push(restype); + continue + } + + // Otherwise + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + } + xml.tag_stop(DAV_URN, "supportedlock").await?; + Some(Property::SupportedLock(acc)) + }, + _ => None, + }; + } + + // Option 2: an extension property, delegating + if maybe_res.is_none() { + maybe_res = E::Property::qread(xml).await?.map(Property::Extension); + } + + Ok(maybe_res) + } +} + +impl QRead<ActiveLock> for ActiveLock { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + unimplemented!(); + } +} + +impl<E: Extension> QRead<ResourceType<E>> for ResourceType<E> { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + match xml.peek() { + Event::Empty(b) if xml.is_tag(DAV_URN, "collection") => { + xml.next().await?; + Ok(Some(ResourceType::Collection)) + }, + _ => Ok(E::ResourceType::qread(xml).await?.map(ResourceType::Extension)), + } + } +} + +impl QRead<LockEntry> for LockEntry { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + xml.tag_start(DAV_URN, "lockentry").await?; + let (mut maybe_scope, mut maybe_type) = (None, None); + loop { match xml.peek() { + Event::Start(b) if xml.is_tag(DAV_URN, "lockscope") => { + maybe_scope = LockScope::qread(xml).await?; + }, + Event::Start(b) if xml.is_tag(DAV_URN, "lockentry") => { + maybe_type = LockType::qread(xml).await?; + } Event::End(_) => break, - Event::Start(_) | Event::Empty(_) => return Err(ParsingError::WrongToken), - Event::CData(unescaped) => { - url.push_str(std::str::from_utf8(unescaped.as_ref())?); - xml.next().await? + _ => { xml.skip().await?; }, + } + } + + let lockentry = match (maybe_scope, maybe_type) { + (Some(lockscope), Some(locktype)) => LockEntry { lockscope, locktype }, + _ => return Err(ParsingError::MissingChild), + }; + + xml.tag_stop(DAV_URN, "lockentry").await?; + Ok(Some(lockentry)) + } +} + +impl QRead<LockScope> for LockScope { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + xml.tag_start(DAV_URN, "lockscope").await?; + let lockscope = loop { + match xml.peek() { + Event::Empty(b) if xml.is_tag(DAV_URN, "exclusive") => { + xml.next().await?; + break LockScope::Exclusive }, - Event::Text(escaped) => { - url.push_str(escaped.unescape()?.as_ref()); - xml.next().await? + Event::Empty(b) if xml.is_tag(DAV_URN, "shared") => { + xml.next().await?; + break LockScope::Shared } _ => xml.skip().await?, }; - } + }; + + xml.tag_stop(DAV_URN, "lockscope").await?; + Ok(Some(lockscope)) + } +} + +impl QRead<LockType> for LockType { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + xml.tag_start(DAV_URN, "locktype").await?; + let locktype = loop { + match xml.peek() { + Event::Empty(b) if xml.is_tag(DAV_URN, "write") => { + xml.next().await?; + break LockType::Write + } + _ => xml.skip().await?, + }; + }; + xml.tag_stop(DAV_URN, "locktype").await?; + Ok(Some(locktype)) + } +} + +impl QRead<Href> for Href { + async fn qread(xml: &mut Reader<impl IRead>) -> Result<Option<Self>, ParsingError> { + match xml.peek() { + Event::Start(b) if xml.is_tag(DAV_URN, "href") => xml.next().await?, + _ => return Ok(None), + }; + + let mut url = xml.tag_string().await?; xml.tag_stop(DAV_URN, "href").await?; Ok(Some(Href(url))) } diff --git a/src/dav/encoder.rs b/src/dav/encoder.rs index c0a5332..f3a1860 100644 --- a/src/dav/encoder.rs +++ b/src/dav/encoder.rs @@ -101,6 +101,20 @@ impl<E: Extension> QWrite for PropValue<E> { } } +/// 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> { @@ -584,19 +598,6 @@ impl QWrite for LockEntry { } } -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 - } -} - impl<E: Extension> QWrite for Violation<E> { async fn qwrite(&self, xml: &mut Writer<impl IWrite>) -> Result<(), QError> { let mut atom = async |c| { diff --git a/src/dav/error.rs b/src/dav/error.rs index b04d2ac..8cb60ba 100644 --- a/src/dav/error.rs +++ b/src/dav/error.rs @@ -8,6 +8,8 @@ pub enum ParsingError { TagNotFound, Utf8Error(std::str::Utf8Error), QuickXml(quick_xml::Error), + Chrono(chrono::format::ParseError), + Int(std::num::ParseIntError), Eof } impl From<AttrError> for ParsingError { @@ -25,3 +27,14 @@ impl From<std::str::Utf8Error> for ParsingError { 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/xml.rs b/src/dav/xml.rs index 1cce86a..bf02721 100644 --- a/src/dav/xml.rs +++ b/src/dav/xml.rs @@ -129,5 +129,23 @@ impl<T: IRead> Reader<T> { } self.next().await } + + pub async fn tag_string(&mut self) -> Result<String, ParsingError> { + 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?, + }; + } + } } |