aboutsummaryrefslogtreecommitdiff
path: root/aero-dav
diff options
context:
space:
mode:
Diffstat (limited to 'aero-dav')
-rw-r--r--aero-dav/fuzz/fuzz_targets/dav.rs4
-rw-r--r--aero-dav/src/caldecoder.rs37
-rw-r--r--aero-dav/src/calencoder.rs2
-rw-r--r--aero-dav/src/decoder.rs7
-rw-r--r--aero-dav/src/encoder.rs301
-rw-r--r--aero-dav/src/xml.rs7
6 files changed, 206 insertions, 152 deletions
diff --git a/aero-dav/fuzz/fuzz_targets/dav.rs b/aero-dav/fuzz/fuzz_targets/dav.rs
index a3c6ece..5bd28bc 100644
--- a/aero-dav/fuzz/fuzz_targets/dav.rs
+++ b/aero-dav/fuzz/fuzz_targets/dav.rs
@@ -9,6 +9,7 @@ use quick_xml::reader::NsReader;
use tokio::runtime::Runtime;
use tokio::io::AsyncWriteExt;
+// Split this file
const tokens: [&str; 63] = [
"0",
"1",
@@ -125,6 +126,9 @@ impl Tag {
#[derive(Arbitrary)]
enum XmlNode {
+ //@FIXME: build RFC3339 and RFC822 Dates with chrono based on timestamps
+ //@FIXME: add small numbers
+ //@FIXME: add http status code
Node(Tag, Vec<Self>),
Number(u64),
Text(Token),
diff --git a/aero-dav/src/caldecoder.rs b/aero-dav/src/caldecoder.rs
index fb840d6..3aae4ad 100644
--- a/aero-dav/src/caldecoder.rs
+++ b/aero-dav/src/caldecoder.rs
@@ -1,9 +1,39 @@
-//use super::types as dav;
+use super::types as dav;
use super::caltypes::*;
use super::xml;
use super::error;
// ---- ROOT ELEMENTS ---
+impl<E: dav::Extension> xml::QRead<MkCalendar<E>> for MkCalendar<E> {
+ async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
+ unreachable!();
+ }
+}
+
+impl<E: dav::Extension, N: xml::Node<N>> xml::QRead<MkCalendarResponse<E,N>> for MkCalendarResponse<E,N> {
+ async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
+ unreachable!();
+ }
+}
+
+impl<E: dav::Extension> xml::QRead<CalendarQuery<E>> for CalendarQuery<E> {
+ async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
+ unreachable!();
+ }
+}
+
+impl<E: dav::Extension> xml::QRead<CalendarMultiget<E>> for CalendarMultiget<E> {
+ async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
+ unreachable!();
+ }
+}
+
+impl xml::QRead<FreeBusyQuery> for FreeBusyQuery {
+ async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
+ unreachable!();
+ }
+}
+
// ---- EXTENSIONS ---
impl xml::QRead<Violation> for Violation {
@@ -31,3 +61,8 @@ impl xml::QRead<ResourceType> for ResourceType {
}
// ---- INNER XML ----
+impl xml::QRead<SupportedCollation> for SupportedCollation {
+ async fn qread(_xml: &mut xml::Reader<impl xml::IRead>) -> Result<Self, error::ParsingError> {
+ unreachable!();
+ }
+}
diff --git a/aero-dav/src/calencoder.rs b/aero-dav/src/calencoder.rs
index 67892ed..a25d767 100644
--- a/aero-dav/src/calencoder.rs
+++ b/aero-dav/src/calencoder.rs
@@ -666,7 +666,7 @@ mod tests {
use crate::types as dav;
use crate::realization::Calendar;
use tokio::io::AsyncWriteExt;
- use chrono::{Utc,TimeZone,DateTime};
+ use chrono::{Utc,TimeZone};
async fn serialize(elem: &impl QWrite) -> String {
let mut buffer = Vec::new();
diff --git a/aero-dav/src/decoder.rs b/aero-dav/src/decoder.rs
index 02bc376..28442a6 100644
--- a/aero-dav/src/decoder.rs
+++ b/aero-dav/src/decoder.rs
@@ -23,8 +23,8 @@ impl<E: Extension> QRead<PropFind<E>> for PropFind<E> {
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?;
+ let includ = xml.maybe_find::<Include<E>>().await?;
break PropFind::AllProp(includ)
}
@@ -594,8 +594,9 @@ impl QRead<Href> for Href {
#[cfg(test)]
mod tests {
use super::*;
- use chrono::{FixedOffset, DateTime, TimeZone, Utc};
+ use chrono::{FixedOffset, TimeZone};
use crate::realization::Core;
+ use quick_xml::reader::NsReader;
#[tokio::test]
async fn basic_propfind_propname() {
@@ -910,7 +911,7 @@ mod tests {
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::ResourceType(vec![]),
Property::SupportedLock(vec![
LockEntry {
lockscope: LockScope::Exclusive,
diff --git a/aero-dav/src/encoder.rs b/aero-dav/src/encoder.rs
index fd2f9ca..813efe6 100644
--- a/aero-dav/src/encoder.rs
+++ b/aero-dav/src/encoder.rs
@@ -633,6 +633,7 @@ impl<E: Extension> QWrite for Violation<E> {
#[cfg(test)]
mod tests {
use super::*;
+ use super::super::xml;
use crate::realization::Core;
use tokio::io::AsyncWriteExt;
@@ -653,43 +654,47 @@ mod tests {
return got.into()
}
+ async fn deserialize<T: xml::Node<T>>(src: &str) -> T {
+ let mut rdr = xml::Reader::new(quick_xml::reader::NsReader::from_reader(src.as_bytes())).await.unwrap();
+ rdr.find().await.unwrap()
+ }
+
#[tokio::test]
async fn basic_href() {
+ let orig = Href("/SOGo/dav/so/".into());
- let got = serialize(
- &Href("/SOGo/dav/so/".into())
- ).await;
+ let got = serialize(&orig).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");
+ assert_eq!(deserialize::<Href>(got.as_str()).await, orig)
}
#[tokio::test]
async fn basic_multistatus() {
- let got = serialize(
- &Multistatus::<Core, PropName<Core>> {
- responses: vec![],
- responsedescription: Some(ResponseDescription("Hello world".into()))
- },
- ).await;
+ let orig = Multistatus::<Core, PropName<Core>> {
+ responses: vec![],
+ responsedescription: Some(ResponseDescription("Hello world".into()))
+ };
+ let got = serialize(&orig).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");
+ assert_eq!(deserialize::<Multistatus::<Core, PropName<Core>>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_error_delete_locked() {
- let got = serialize(
- &Error::<Core>(vec![
+ let orig = Error::<Core>(vec![
Violation::LockTokenSubmitted(vec![
Href("/locked/".into())
])
- ]),
- ).await;
+ ]);
+ let got = serialize(&orig).await;
let expected = r#"<D:error xmlns:D="DAV:">
<D:lock-token-submitted>
@@ -698,72 +703,74 @@ mod tests {
</D:error>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
+ assert_eq!(deserialize::<Error<Core>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_propname_req() {
- let got = serialize(
- &PropFind::<Core>::PropName,
- ).await;
+ let orig = PropFind::<Core>::PropName;
+
+ let got = serialize(&orig).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");
+ assert_eq!(deserialize::<PropFind::<Core>>(got.as_str()).await, orig)
}
#[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 orig = 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,
+ };
+
+ let got = serialize(&orig).await;
let expected = r#"<D:multistatus xmlns:D="DAV:">
<D:response>
@@ -798,100 +805,102 @@ mod tests {
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
+ assert_eq!(deserialize::<Multistatus::<Core, PropName<Core>>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_allprop_req() {
- let got = serialize(
- &PropFind::<Core>::AllProp(None),
- ).await;
+ let orig = PropFind::<Core>::AllProp(None);
+ let got = serialize(&orig).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");
+ assert_eq!(deserialize::<PropFind::<Core>>(got.as_str()).await, orig)
}
#[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,
- },
- ]),
+ use chrono::{FixedOffset,TimeZone};
+
+ let orig = 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,
- }]
- ),
+ ]),
+ status: Status(http::status::StatusCode::OK),
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,
- },
- ]),
+ }]
+ ),
+ 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,
- }]
- ),
+ ]),
+ status: Status(http::status::StatusCode::OK),
error: None,
responsedescription: None,
- location: None,
- },
- ],
- responsedescription: None,
- }
- ).await;
+ }]
+ ),
+ error: None,
+ responsedescription: None,
+ location: None,
+ },
+ ],
+ responsedescription: None,
+ };
+
+ let got = serialize(&orig).await;
let expected = r#"<D:multistatus xmlns:D="DAV:">
<D:response>
@@ -961,16 +970,17 @@ mod tests {
</D:multistatus>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
+ assert_eq!(deserialize::<Multistatus::<Core, PropValue<Core>>>(got.as_str()).await, orig)
}
#[tokio::test]
async fn rfc_allprop_include() {
- let got = serialize(
- &PropFind::<Core>::AllProp(Some(Include(vec![
- PropertyRequest::DisplayName,
- PropertyRequest::ResourceType,
- ]))),
- ).await;
+ let orig = PropFind::<Core>::AllProp(Some(Include(vec![
+ PropertyRequest::DisplayName,
+ PropertyRequest::ResourceType,
+ ])));
+
+ let got = serialize(&orig).await;
let expected = r#"<D:propfind xmlns:D="DAV:">
<D:allprop/>
@@ -981,6 +991,7 @@ mod tests {
</D:propfind>"#;
assert_eq!(&got, expected, "\n---GOT---\n{got}\n---EXP---\n{expected}\n");
+ assert_eq!(deserialize::<PropFind::<Core>>(got.as_str()).await, orig)
}
#[tokio::test]
diff --git a/aero-dav/src/xml.rs b/aero-dav/src/xml.rs
index 98037ac..f9e04eb 100644
--- a/aero-dav/src/xml.rs
+++ b/aero-dav/src/xml.rs
@@ -79,7 +79,7 @@ impl<T: IRead> Reader<T> {
/// 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());
+ //println!("skipping inside node {:?} value {:?}", self.parents.last(), self.cur);
match &self.cur {
Event::Start(b) => {
let _span = self.rdr.read_to_end_into_async(b.to_end().name(), &mut self.buf).await?;
@@ -212,8 +212,10 @@ impl<T: IRead> Reader<T> {
}
pub async fn collect<N: Node<N>>(&mut self) -> Result<Vec<N>, ParsingError> {
- self.ensure_parent_has_child()?;
let mut acc = Vec::new();
+ if !self.parent_has_child() {
+ return Ok(acc)
+ }
loop {
match N::qread(self).await {
@@ -230,6 +232,7 @@ impl<T: IRead> Reader<T> {
}
pub async fn open(&mut self, ns: &[u8], key: &str) -> Result<Event<'static>, ParsingError> {
+ //println!("try open tag {:?}", key);
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?,