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::{QRead, Reader, IRead, DAV_URN, CAL_URN}; // ---- ROOT ---- /// Propfind request impl QRead> for PropFind { async fn qread(xml: &mut Reader) -> Result, ParsingError> { // Find propfind xml.tag_start(DAV_URN, "propfind").await?; // Find any tag let propfind: PropFind = loop { match xml.peek() { Event::Start(_) if xml.is_tag(DAV_URN, "allprop") => { xml.tag_start(DAV_URN, "allprop").await?; let r = PropFind::AllProp(Include::qread(xml).await?); xml.tag_stop(DAV_URN, "allprop").await?; break r }, Event::Start(_) if xml.is_tag(DAV_URN, "prop") => { let propname = PropName::qread(xml).await?.ok_or(ParsingError::MissingChild)?; break PropFind::Prop(propname); }, Event::Empty(_) if xml.is_tag(DAV_URN, "allprop") => { xml.next().await?; break PropFind::AllProp(None) }, Event::Empty(_) if xml.is_tag(DAV_URN, "propname") => { xml.next().await?; break PropFind::PropName }, _ => { xml.skip().await?; }, } }; // Close tag xml.tag_stop(DAV_URN, "propfind").await?; Ok(Some(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?; 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 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 { 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> { xml.tag_start(DAV_URN, "include").await?; 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::End(_) => break, _ => { xml.skip().await?; }, } } xml.tag_stop(DAV_URN, "include").await?; Ok(Some(Include(acc))) } } impl QRead> for PropName { 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) = PropertyRequest::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(PropName(acc))) } } impl QRead> for PropertyRequest { async fn qread(xml: &mut Reader) -> Result, ParsingError> { 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?; } } // 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)) } } 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 = xml.tag_string().await?; xml.tag_stop(DAV_URN, "href").await?; Ok(Some(Href(url))) } } #[cfg(test)] mod tests { use super::*; use crate::dav::realization::Core; #[tokio::test] async fn basic_propfind_propname() { let src = r#" "#; let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); let got = PropFind::::qread(&mut rdr).await.unwrap().unwrap(); assert_eq!(got, PropFind::::PropName); } #[tokio::test] async fn basic_propfind_prop() { let src = r#" "#; let mut rdr = Reader::new(NsReader::from_reader(src.as_bytes())).await.unwrap(); let got = PropFind::::qread(&mut rdr).await.unwrap().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#" /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()) ]) ])); } }