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 QRead> for PropFind { async fn qread(xml: &mut Reader) -> Result, ParsingError> { // Find propfind @@ -48,6 +50,37 @@ impl QRead> for PropFind { } } +/// PROPPATCH request +impl QRead> for PropertyUpdate { + async fn qread(xml: &mut Reader) -> Result, 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 QRead> for Error { async fn qread(xml: &mut Reader) -> Result, ParsingError> { xml.tag_start(DAV_URN, "error").await?; @@ -66,7 +99,56 @@ impl QRead> for Error { } } + + // ---- INNER XML +impl QRead> for PropertyUpdateItem { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + if let Some(rm) = Remove::qread(xml).await? { + return Ok(Some(PropertyUpdateItem::Remove(rm))) + } + Ok(Set::qread(xml).await?.map(PropertyUpdateItem::Set)) + } +} + +impl QRead> for Remove { + async fn qread(xml: &mut Reader) -> Result, 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 QRead> for Set { + async fn qread(xml: &mut Reader) -> Result, 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 QRead> for Violation { async fn qread(xml: &mut Reader) -> Result, ParsingError> { loop { @@ -163,13 +245,17 @@ impl QRead> for Violation { impl QRead> for Include { async fn qread(xml: &mut Reader) -> Result, ParsingError> { xml.tag_start(DAV_URN, "include").await?; - let mut acc: Vec> = 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 QRead> for Include { impl QRead> for PropName { async fn qread(xml: &mut Reader) -> Result, ParsingError> { xml.tag_start(DAV_URN, "prop").await?; - let mut acc: Vec> = 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,46 +288,270 @@ impl QRead> for PropName { impl QRead> for PropertyRequest { async fn qread(xml: &mut Reader) -> Result, 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, }; - - let mut maybe_res = None; - - // 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?; - } + // Close the current tag if we read something + if maybe_res.is_some() { + xml.skip().await?; } - - // Option 2: an extension property, delegating - if maybe_res.is_none() { - maybe_res = E::PropertyRequest::qread(xml).await?.map(PropertyRequest::Extension); - } - - return Ok(maybe_res) } + + // Option 2: an extension property, delegating + if maybe_res.is_none() { + maybe_res = E::PropertyRequest::qread(xml).await?.map(PropertyRequest::Extension); + } + + Ok(maybe_res) + } +} + + +impl QRead> for PropValue { + async fn qread(xml: &mut Reader) -> Result, 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; + } + + // 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> for Property { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + use chrono::{DateTime, FixedOffset, TimeZone}; + + 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" => { + 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::()?; + 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 for ActiveLock { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + unimplemented!(); + } +} + +impl QRead> for ResourceType { + async fn qread(xml: &mut Reader) -> Result, 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 for LockEntry { + async fn qread(xml: &mut Reader) -> Result, 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, + _ => { 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 for LockScope { + async fn qread(xml: &mut Reader) -> Result, 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::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 for LockType { + async fn qread(xml: &mut Reader) -> Result, 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)) } } @@ -248,22 +562,7 @@ impl QRead for Href { _ => return Ok(None), }; - let mut url = String::new(); - loop { - match xml.peek() { - 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? - }, - Event::Text(escaped) => { - url.push_str(escaped.unescape()?.as_ref()); - xml.next().await? - } - _ => xml.skip().await?, - }; - } + 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 QWrite for PropValue { } } +/// Error response +impl QWrite for Error { + async fn qwrite(&self, xml: &mut Writer) -> 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 QWrite for PropertyUpdateItem { async fn qwrite(&self, xml: &mut Writer) -> Result<(), QError> { @@ -584,19 +598,6 @@ impl QWrite for LockEntry { } } -impl QWrite for Error { - async fn qwrite(&self, xml: &mut Writer) -> 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 QWrite for Violation { async fn qwrite(&self, xml: &mut Writer) -> 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 for ParsingError { @@ -25,3 +27,14 @@ impl From for ParsingError { Self::Utf8Error(value) } } +impl From for ParsingError { + fn from(value: chrono::format::ParseError) -> Self { + Self::Chrono(value) + } +} + +impl From 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 Reader { } self.next().await } + + pub async fn tag_string(&mut self) -> Result { + 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?, + }; + } + } }