From ba32a0d4a6810e4bf9d18f14086597c20212bbcb Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Wed, 6 Mar 2024 10:12:02 +0100 Subject: [PATCH] decode errors --- src/dav/decoder.rs | 168 +++++++++++++++++++++++++++++++++++++++++++-- src/dav/error.rs | 6 ++ src/dav/xml.rs | 3 + 3 files changed, 173 insertions(+), 4 deletions(-) diff --git a/src/dav/decoder.rs b/src/dav/decoder.rs index a7fdca5..7de5d63 100644 --- a/src/dav/decoder.rs +++ b/src/dav/decoder.rs @@ -11,6 +11,7 @@ use super::types::*; use super::error::ParsingError; use super::xml::{QRead, Reader, IRead, DAV_URN, CAL_URN}; +// ---- ROOT ---- impl QRead> for PropFind { async fn qread(xml: &mut Reader) -> Result, ParsingError> { // Find propfind @@ -47,6 +48,117 @@ impl QRead> for PropFind { } } +impl QRead> for Error { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + xml.tag_start(DAV_URN, "error").await?; + let mut violations = Vec::new(); + loop { + match xml.peek() { + Event::Start(_) | Event::Empty(_) => { + Violation::qread(xml).await?.map(|v| violations.push(v)); + }, + Event::End(_) if xml.is_tag(DAV_URN, "error") => break, + _ => { xml.skip().await?; }, + } + } + xml.tag_stop(DAV_URN, "error").await?; + Ok(Some(Error(violations))) + } +} + +// ---- INNER XML +impl QRead> for Violation { + 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 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"lock-token-matches-request-uri" => { + xml.next().await?; + Some(Violation::LockTokenMatchesRequestUri) + }, + b"lock-token-submitted" => { + // start tag + xml.next().await?; + + let mut links = Vec::new(); + loop { + // If we find a Href + if let Some(href) = Href::qread(xml).await? { + links.push(href); + continue + } + + // Otherwise + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + } + xml.tag_stop(DAV_URN, "lock-token-submitted").await?; + Some(Violation::LockTokenSubmitted(links)) + }, + b"no-conflicting-lock" => { + // start tag + xml.next().await?; + + let mut links = Vec::new(); + loop { + // If we find a Href + if let Some(href) = Href::qread(xml).await? { + links.push(href); + continue + } + + // Otherwise + match xml.peek() { + Event::End(_) => break, + _ => { xml.skip().await?; }, + } + } + xml.tag_stop(DAV_URN, "no-conflicting-lock").await?; + Some(Violation::NoConflictingLock(links)) + }, + b"no-external-entities" => { + xml.next().await?; + Some(Violation::NoExternalEntities) + }, + b"preserved-live-properties" => { + xml.next().await?; + Some(Violation::PreservedLiveProperties) + }, + b"propfind-finite-depth" => { + xml.next().await?; + Some(Violation::PropfindFiniteDepth) + }, + b"cannot-modify-protected-property" => { + xml.next().await?; + Some(Violation::CannotModifyProtectedProperty) + }, + _ => None, + }; + } + + // Option 2: an extension property, delegating + if maybe_res.is_none() { + maybe_res = E::Error::qread(xml).await?.map(Violation::Extension); + } + + return Ok(maybe_res) + } + } +} impl QRead> for Include { async fn qread(xml: &mut Reader) -> Result, ParsingError> { @@ -113,21 +225,50 @@ impl QRead> for PropertyRequest { b"supportedlock" => Some(PropertyRequest::SupportedLock), _ => None, }; + // Close the current tag if we read something + if maybe_res.is_some() { + xml.skip().await?; + } } - // Option 2: an extension property + // Option 2: an extension property, delegating if maybe_res.is_none() { maybe_res = E::PropertyRequest::qread(xml).await?.map(PropertyRequest::Extension); } - // Close the current tag - xml.skip().await?; - return Ok(maybe_res) } } } +impl QRead for Href { + async fn qread(xml: &mut Reader) -> Result, ParsingError> { + match xml.peek() { + Event::Start(b) if xml.is_tag(DAV_URN, "href") => xml.next().await?, + _ => 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?, + }; + } + xml.tag_stop(DAV_URN, "href").await?; + Ok(Some(Href(url))) + } +} + #[cfg(test)] mod tests { use super::*; @@ -180,4 +321,23 @@ mod tests { PropertyRequest::SupportedLock, ]))); } + + #[tokio::test] + async fn rfc_lock_error() { + let src = r#" + + + /locked/ + + "#; + + let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); + let got = Error::::qread(&mut rdr).await.unwrap().unwrap(); + + assert_eq!(got, Error(vec![ + Violation::LockTokenSubmitted(vec![ + Href("/locked/".into()) + ]) + ])); + } } diff --git a/src/dav/error.rs b/src/dav/error.rs index 5bd8ed3..b04d2ac 100644 --- a/src/dav/error.rs +++ b/src/dav/error.rs @@ -6,6 +6,7 @@ pub enum ParsingError { NamespacePrefixAlreadyUsed, WrongToken, TagNotFound, + Utf8Error(std::str::Utf8Error), QuickXml(quick_xml::Error), Eof } @@ -19,3 +20,8 @@ impl From for ParsingError { Self::QuickXml(value) } } +impl From for ParsingError { + fn from(value: std::str::Utf8Error) -> Self { + Self::Utf8Error(value) + } +} diff --git a/src/dav/xml.rs b/src/dav/xml.rs index 5ebda02..1cce86a 100644 --- a/src/dav/xml.rs +++ b/src/dav/xml.rs @@ -65,6 +65,7 @@ impl Reader { /// skip tag. Can't skip end, can't skip eof. pub async fn skip(&mut self) -> Result, ParsingError> { + println!("skip on {:?}", &self.evt); match &self.evt { Event::Start(b) => { let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?; @@ -107,6 +108,7 @@ impl Reader { /// find start tag pub async fn tag_start(&mut self, ns: &[u8], key: &str) -> Result, ParsingError> { + println!("search start tag {}", key); loop { match self.peek() { Event::Start(b) if self.is_tag(ns, key) => break, @@ -118,6 +120,7 @@ impl Reader { // find stop tag pub async fn tag_stop(&mut self, ns: &[u8], key: &str) -> Result, ParsingError> { + println!("search stop tag {}", key); loop { match self.peek() { Event::End(b) if self.is_tag(ns, key) => break,