Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
parser::{DavParser, Token, tokenizer::Tokenizer},
|
||||
schema::{
|
||||
Element, NamedElement, Namespace,
|
||||
property::{DavValue, Privilege},
|
||||
request::{
|
||||
Acl, AclPrincipalPropSet, DavPropertyValue, PrincipalMatch, PrincipalMatchProperties,
|
||||
PrincipalPropertySearch, PropertySearch,
|
||||
},
|
||||
response::{Ace, GrantDeny, Href, List, Principal},
|
||||
},
|
||||
};
|
||||
|
||||
impl DavParser for Acl {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
stream.expect_named_element(NamedElement::dav(Element::Acl))?;
|
||||
|
||||
let mut acl = Acl { aces: vec![] };
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Ace,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
acl.aces.push(Ace::parse(stream)?);
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
break;
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(acl)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for Ace {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut ace = Ace {
|
||||
principal: Principal::All,
|
||||
invert: false,
|
||||
grant_deny: GrantDeny::Grant(List(vec![])),
|
||||
protected: false,
|
||||
inherited: None,
|
||||
};
|
||||
let mut depth = 1;
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Principal,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
ace.principal = Principal::parse(stream)?;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Invert,
|
||||
},
|
||||
..
|
||||
} if depth == 1 => {
|
||||
ace.invert = true;
|
||||
depth += 1;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Protected,
|
||||
},
|
||||
..
|
||||
} if depth == 1 => {
|
||||
ace.protected = true;
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Inherited,
|
||||
},
|
||||
..
|
||||
} if depth == 1 => {
|
||||
stream.expect_named_element(NamedElement::dav(Element::Href))?;
|
||||
ace.inherited = stream.collect_string_value()?.map(Href);
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Grant,
|
||||
},
|
||||
..
|
||||
} if depth == 1 => {
|
||||
ace.grant_deny = GrantDeny::Grant(List(stream.collect_privileges()?));
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Deny,
|
||||
},
|
||||
..
|
||||
} if depth == 1 => {
|
||||
ace.grant_deny = GrantDeny::Deny(List(stream.collect_privileges()?));
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ace)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for Principal {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let result = match stream.unwrap_named_element()? {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Href,
|
||||
} => Principal::Href(Href(stream.collect_string_value()?.unwrap_or_default())),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::All,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
Principal::All
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Authenticated,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
Principal::Authenticated
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Unauthenticated,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
Principal::Unauthenticated
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Property,
|
||||
} => {
|
||||
let property = stream.collect_properties(Vec::new())?;
|
||||
Principal::Property(List(
|
||||
property
|
||||
.into_iter()
|
||||
.map(|prop| DavPropertyValue::new(prop, DavValue::Null))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Self_,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
Principal::Self_
|
||||
}
|
||||
other => return Err(other.into_unexpected()),
|
||||
};
|
||||
stream.expect_element_end()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizer<'_> {
|
||||
pub fn collect_privileges(&mut self) -> crate::parser::Result<Vec<Privilege>> {
|
||||
let mut privileges = Vec::new();
|
||||
let mut depth = 1;
|
||||
|
||||
loop {
|
||||
match self.token()? {
|
||||
Token::ElementStart { name, .. } => {
|
||||
if let Some(privilege) = Privilege::from_element(name) {
|
||||
privileges.push(privilege);
|
||||
self.expect_element_end()?;
|
||||
} else {
|
||||
depth += 1;
|
||||
}
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
self.seek_element_end()?;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(privileges)
|
||||
}
|
||||
}
|
||||
|
||||
impl Privilege {
|
||||
pub fn from_element(element: NamedElement) -> Option<Self> {
|
||||
match (element.ns, element.element) {
|
||||
(Namespace::Dav, Element::Read) => Some(Privilege::Read),
|
||||
(Namespace::Dav, Element::Write) => Some(Privilege::Write),
|
||||
(Namespace::Dav, Element::WriteProperties) => Some(Privilege::WriteProperties),
|
||||
(Namespace::Dav, Element::WriteContent) => Some(Privilege::WriteContent),
|
||||
(Namespace::Dav, Element::Unlock) => Some(Privilege::Unlock),
|
||||
(Namespace::Dav, Element::ReadAcl) => Some(Privilege::ReadAcl),
|
||||
(Namespace::Dav, Element::ReadCurrentUserPrivilegeSet) => {
|
||||
Some(Privilege::ReadCurrentUserPrivilegeSet)
|
||||
}
|
||||
(Namespace::Dav, Element::WriteAcl) => Some(Privilege::WriteAcl),
|
||||
(Namespace::Dav, Element::Bind) => Some(Privilege::Bind),
|
||||
(Namespace::Dav, Element::Unbind) => Some(Privilege::Unbind),
|
||||
(Namespace::Dav, Element::All) => Some(Privilege::All),
|
||||
(Namespace::CalDav, Element::ReadFreeBusy) => Some(Privilege::ReadFreeBusy),
|
||||
(Namespace::CalDav, Element::ScheduleDeliver) => Some(Privilege::ScheduleDeliver),
|
||||
(Namespace::CalDav, Element::ScheduleDeliverInvite) => {
|
||||
Some(Privilege::ScheduleDeliverInvite)
|
||||
}
|
||||
(Namespace::CalDav, Element::ScheduleDeliverReply) => {
|
||||
Some(Privilege::ScheduleDeliverReply)
|
||||
}
|
||||
(Namespace::CalDav, Element::ScheduleQueryFreebusy) => {
|
||||
Some(Privilege::ScheduleQueryFreeBusy)
|
||||
}
|
||||
(Namespace::CalDav, Element::ScheduleSend) => Some(Privilege::ScheduleSend),
|
||||
(Namespace::CalDav, Element::ScheduleSendInvite) => Some(Privilege::ScheduleSendInvite),
|
||||
(Namespace::CalDav, Element::ScheduleSendReply) => Some(Privilege::ScheduleSendReply),
|
||||
(Namespace::CalDav, Element::ScheduleSendFreebusy) => {
|
||||
Some(Privilege::ScheduleSendFreeBusy)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for AclPrincipalPropSet {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut acps = AclPrincipalPropSet { properties: vec![] };
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
acps.properties = stream.collect_properties(acps.properties)?;
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
break;
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(acps)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for PrincipalMatch {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut pm = PrincipalMatch {
|
||||
principal_properties: PrincipalMatchProperties::Self_,
|
||||
properties: vec![],
|
||||
};
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::PrincipalProperty,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
pm.principal_properties = PrincipalMatchProperties::Properties(
|
||||
stream.collect_properties(Vec::new())?,
|
||||
);
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Self_,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
pm.principal_properties = PrincipalMatchProperties::Self_;
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
pm.properties = stream.collect_properties(pm.properties)?;
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
break;
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(pm)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for PrincipalPropertySearch {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut pps = PrincipalPropertySearch {
|
||||
property_search: vec![],
|
||||
properties: vec![],
|
||||
apply_to_principal_collection_set: false,
|
||||
};
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::PropertySearch,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
if let Some(prop) = PropertySearch::parse(stream)? {
|
||||
pps.property_search.push(prop);
|
||||
}
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
pps.properties = stream.collect_properties(pps.properties)?;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::ApplyToPrincipalCollectionSet,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
pps.apply_to_principal_collection_set = true;
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
break;
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(pps)
|
||||
}
|
||||
}
|
||||
|
||||
impl PropertySearch {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Option<Self>> {
|
||||
let mut property = None;
|
||||
let mut match_ = None;
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
property = stream.collect_properties(Vec::new())?.into_iter().next();
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Match,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
match_ = stream.collect_string_value()?;
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
break;
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(property.map(|property| PropertySearch {
|
||||
property,
|
||||
match_: match_.unwrap_or_default(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
parser::{DavParser, Token, tokenizer::Tokenizer},
|
||||
schema::{
|
||||
Element, NamedElement, Namespace,
|
||||
property::{LockScope, LockType},
|
||||
request::LockInfo,
|
||||
},
|
||||
};
|
||||
use types::dead_property::DeadProperty;
|
||||
|
||||
impl DavParser for LockInfo {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut lockinfo = LockInfo {
|
||||
lock_scope: LockScope::Exclusive,
|
||||
lock_type: LockType::Write,
|
||||
owner: None,
|
||||
};
|
||||
|
||||
if stream.expect_named_element_or_eof(NamedElement::dav(Element::Lockinfo))? {
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Lockscope,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
lockinfo.lock_scope = LockScope::parse(stream)?;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Locktype,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
lockinfo.lock_type = LockType::parse(stream)?;
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Owner,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
lockinfo.owner = Some(DeadProperty::parse(stream)?);
|
||||
}
|
||||
Token::ElementEnd | Token::Eof => {
|
||||
break;
|
||||
}
|
||||
other => {
|
||||
return Err(other.into_unexpected());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(lockinfo)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for LockScope {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
match stream.unwrap_named_element()? {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Exclusive,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
stream.expect_element_end()?;
|
||||
Ok(LockScope::Exclusive)
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Shared,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
stream.expect_element_end()?;
|
||||
Ok(LockScope::Shared)
|
||||
}
|
||||
other => Err(other.into_unexpected()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for LockType {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
match stream.unwrap_named_element()? {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Write,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
stream.expect_element_end()?;
|
||||
Ok(LockType::Write)
|
||||
}
|
||||
other => Err(other.into_unexpected()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
parser::{DavParser, Token, tokenizer::Tokenizer},
|
||||
schema::{Element, NamedElement, Namespace, request::MkCol},
|
||||
};
|
||||
|
||||
impl DavParser for MkCol {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut mkcol = MkCol {
|
||||
is_mkcalendar: false,
|
||||
props: Vec::new(),
|
||||
};
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Mkcol,
|
||||
},
|
||||
..
|
||||
} => {}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::Mkcalendar,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
mkcol.is_mkcalendar = true;
|
||||
}
|
||||
Token::Eof => {
|
||||
return Ok(mkcol);
|
||||
}
|
||||
other => return Err(other.into_unexpected()),
|
||||
};
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Set,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
stream.expect_named_element(NamedElement::dav(Element::Prop))?;
|
||||
stream.collect_property_values(&mut mkcol.props)?;
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
Token::ElementEnd | Token::Eof => {
|
||||
break;
|
||||
}
|
||||
token => return Err(token.into_unexpected()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mkcol)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
parser::{DavParser, RawElement, Token, tokenizer::Tokenizer},
|
||||
schema::Namespace,
|
||||
};
|
||||
use types::dead_property::{DeadElementTag, DeadProperty, DeadPropertyTag};
|
||||
|
||||
pub mod acl;
|
||||
pub mod lockinfo;
|
||||
pub mod mkcol;
|
||||
pub mod propertyupdate;
|
||||
pub mod propfind;
|
||||
pub mod report;
|
||||
|
||||
impl DavParser for DeadProperty {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut depth = 1;
|
||||
let mut items = DeadProperty::default();
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart { raw, .. } | Token::UnknownElement(raw) => {
|
||||
items.0.push(DeadPropertyTag::ElementStart((&raw).into()));
|
||||
depth += 1;
|
||||
}
|
||||
Token::ElementEnd => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
items.0.push(DeadPropertyTag::ElementEnd);
|
||||
}
|
||||
Token::Text(text) => {
|
||||
items.0.push(DeadPropertyTag::Text(text.into_owned()));
|
||||
}
|
||||
Token::Bytes(bytes) => {
|
||||
items.0.push(DeadPropertyTag::Text(
|
||||
String::from_utf8_lossy(&bytes).into_owned(),
|
||||
));
|
||||
}
|
||||
Token::Eof => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(items)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait NsDeadProperty {
|
||||
fn single_with_ns(namespace: Namespace, name: &str) -> Self;
|
||||
}
|
||||
|
||||
impl NsDeadProperty for DeadProperty {
|
||||
fn single_with_ns(namespace: Namespace, name: &str) -> Self {
|
||||
DeadProperty(vec![
|
||||
DeadPropertyTag::ElementStart(DeadElementTag {
|
||||
name: format!("{}:{name}", namespace.prefix()),
|
||||
attrs: None,
|
||||
}),
|
||||
DeadPropertyTag::ElementEnd,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&RawElement<'_>> for DeadElementTag {
|
||||
fn from(raw: &RawElement<'_>) -> Self {
|
||||
let name = std::str::from_utf8(raw.element.local_name().as_ref())
|
||||
.unwrap_or("invalid-utf8")
|
||||
.trim_ascii()
|
||||
.to_string();
|
||||
let mut attrs = String::with_capacity(raw.element.attributes_raw().len());
|
||||
if let Some(namespace) = &raw.namespace {
|
||||
attrs.push_str("xmlns=\"");
|
||||
attrs.push_str(std::str::from_utf8(namespace).unwrap_or("invalid-utf8"));
|
||||
attrs.push('"');
|
||||
}
|
||||
|
||||
for attr in raw.element.attributes().flatten() {
|
||||
if attr.key.as_ref() == b"xmlns" || attr.key.as_ref().starts_with(b"xmlns:") {
|
||||
// Skip namespace attributes
|
||||
continue;
|
||||
}
|
||||
if let (Ok(key), Ok(value)) = (
|
||||
std::str::from_utf8(attr.key.as_ref()),
|
||||
std::str::from_utf8(attr.value.as_ref()),
|
||||
) {
|
||||
if !attrs.is_empty() {
|
||||
attrs.push(' ');
|
||||
}
|
||||
attrs.push_str(key);
|
||||
attrs.push('=');
|
||||
attrs.push('"');
|
||||
attrs.push_str(value);
|
||||
attrs.push('"');
|
||||
}
|
||||
}
|
||||
|
||||
DeadElementTag {
|
||||
name,
|
||||
attrs: (!attrs.is_empty()).then_some(attrs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use calcard::vcard::VCardVersion;
|
||||
|
||||
use crate::{
|
||||
parser::{DavParser, tokenizer::Tokenizer},
|
||||
schema::{
|
||||
property::{CardDavProperty, DavProperty},
|
||||
request::{Acl, LockInfo, MkCol, PropFind, PropertyUpdate, Report},
|
||||
},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn parse_address_data_version() {
|
||||
let xml = r#"<?xml version="1.0" encoding="utf-8" ?>
|
||||
<C:addressbook-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav">
|
||||
<D:prop>
|
||||
<C:address-data content-type="text/vcard" version="3.0">
|
||||
<C:prop name="FN"/>
|
||||
</C:address-data>
|
||||
</D:prop>
|
||||
</C:addressbook-query>"#;
|
||||
|
||||
let mut tokenizer = Tokenizer::new(xml.as_bytes());
|
||||
let report = Report::parse(&mut tokenizer).unwrap();
|
||||
let Report::AddressbookQuery(query) = report else {
|
||||
panic!("expected addressbook-query, got {report:?}");
|
||||
};
|
||||
let PropFind::Prop(properties) = query.properties else {
|
||||
panic!("expected prop, got {:?}", query.properties);
|
||||
};
|
||||
|
||||
let version = properties.iter().find_map(|property| match property {
|
||||
DavProperty::CardDav(CardDavProperty::AddressData { version, .. }) => Some(*version),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
assert_eq!(version, Some(Some(VCardVersion::V3_0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_requests() {
|
||||
for entry in std::fs::read_dir("resources/requests").unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
|
||||
if path.extension().map(|ext| ext == "xml").unwrap_or(false) {
|
||||
println!("Parsing: {:?}", path);
|
||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
||||
let xml = std::fs::read_to_string(&path).unwrap();
|
||||
let mut tokenizer = Tokenizer::new(xml.as_bytes());
|
||||
|
||||
let json_path = path.with_extension("json");
|
||||
let json_output = match filename.split_once('-').unwrap().0 {
|
||||
"propfind" => match PropFind::parse(&mut tokenizer) {
|
||||
Ok(propfind) => serde_json::to_string_pretty(&propfind).unwrap(),
|
||||
Err(_) => String::new(),
|
||||
},
|
||||
"propertyupdate" => serde_json::to_string_pretty(
|
||||
&PropertyUpdate::parse(&mut tokenizer).unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
"mkcol" => serde_json::to_string_pretty(&MkCol::parse(&mut tokenizer).unwrap())
|
||||
.unwrap(),
|
||||
"lockinfo" => {
|
||||
serde_json::to_string_pretty(&LockInfo::parse(&mut tokenizer).unwrap())
|
||||
.unwrap()
|
||||
}
|
||||
"report" => {
|
||||
serde_json::to_string_pretty(&Report::parse(&mut tokenizer).unwrap())
|
||||
.unwrap()
|
||||
}
|
||||
"acl" => {
|
||||
serde_json::to_string_pretty(&Acl::parse(&mut tokenizer).unwrap()).unwrap()
|
||||
}
|
||||
_ => {
|
||||
panic!("Unknown method: {}", filename);
|
||||
}
|
||||
};
|
||||
|
||||
/*if json_path.exists() {
|
||||
let expected = std::fs::read_to_string(json_path).unwrap();
|
||||
assert_eq!(json_output, expected);
|
||||
} else {*/
|
||||
std::fs::write(json_path, json_output).unwrap();
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
parser::{DavParser, Token, tokenizer::Tokenizer},
|
||||
schema::{Element, NamedElement, Namespace, request::PropertyUpdate},
|
||||
};
|
||||
|
||||
impl DavParser for PropertyUpdate {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
stream.expect_named_element(NamedElement::dav(Element::Propertyupdate))?;
|
||||
let mut update = PropertyUpdate {
|
||||
set: Vec::with_capacity(4),
|
||||
remove: Vec::with_capacity(4),
|
||||
set_first: true,
|
||||
};
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Set,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
stream.expect_named_element(NamedElement::dav(Element::Prop))?;
|
||||
stream.collect_property_values(&mut update.set)?;
|
||||
stream.expect_element_end()?;
|
||||
update.set_first = update.remove.is_empty();
|
||||
}
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Remove,
|
||||
},
|
||||
..
|
||||
} => {
|
||||
stream.expect_named_element(NamedElement::dav(Element::Prop))?;
|
||||
update.remove = stream.collect_properties(update.remove)?;
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
Token::ElementEnd | Token::Eof => {
|
||||
break;
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
// Ignore unknown elements
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
token => return Err(token.into_unexpected()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(update)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
parser::{DavParser, Token, tokenizer::Tokenizer},
|
||||
schema::{Element, NamedElement, Namespace, request::PropFind},
|
||||
};
|
||||
|
||||
impl DavParser for PropFind {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
if stream.expect_named_element_or_eof(NamedElement::dav(Element::Propfind))? {
|
||||
match stream.unwrap_named_element()? {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Propname,
|
||||
} => Ok(PropFind::PropName),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Allprop,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
if matches!(
|
||||
stream.token()?,
|
||||
Token::ElementStart {
|
||||
name: NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Include
|
||||
},
|
||||
..
|
||||
}
|
||||
) {
|
||||
stream.collect_properties(Vec::new()).map(PropFind::AllProp)
|
||||
} else {
|
||||
Ok(PropFind::AllProp(vec![]))
|
||||
}
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
} => stream.collect_properties(Vec::new()).map(PropFind::Prop),
|
||||
element => Err(element.into_unexpected()),
|
||||
}
|
||||
} else {
|
||||
Ok(PropFind::AllProp(vec![]))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{
|
||||
Depth,
|
||||
parser::{
|
||||
DavParser, RawElement, Token, XmlValueParser, property::TimeRangeFromRaw,
|
||||
tokenizer::Tokenizer,
|
||||
},
|
||||
schema::{
|
||||
Attribute, Collation, Element, MatchType, NamedElement, Namespace,
|
||||
property::DavProperty,
|
||||
request::{
|
||||
AclPrincipalPropSet, AddressbookQuery, CalendarQuery, ExpandProperty,
|
||||
ExpandPropertyItem, Filter, FilterOp, FreeBusyQuery, MultiGet, PrincipalMatch,
|
||||
PrincipalPropertySearch, PropFind, Report, SyncCollection, TextMatch, Timezone,
|
||||
VCardPropertyWithGroup,
|
||||
},
|
||||
},
|
||||
};
|
||||
use calcard::{
|
||||
icalendar::{ICalendarComponentType, ICalendarParameterName, ICalendarProperty},
|
||||
vcard::VCardParameterName,
|
||||
};
|
||||
use types::{TimeRange, dead_property::DeadElementTag};
|
||||
|
||||
impl DavParser for Report {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
match stream.unwrap_named_element()? {
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::CalendarQuery,
|
||||
} => CalendarQuery::parse(stream).map(Report::CalendarQuery),
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::FreeBusyQuery,
|
||||
} => FreeBusyQuery::parse(stream).map(Report::FreeBusyQuery),
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::CalendarMultiget,
|
||||
} => MultiGet::parse(stream).map(Report::CalendarMultiGet),
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::AddressbookQuery,
|
||||
} => AddressbookQuery::parse(stream).map(Report::AddressbookQuery),
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::AddressbookMultiget,
|
||||
} => MultiGet::parse(stream).map(Report::AddressbookMultiGet),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::SyncCollection,
|
||||
} => SyncCollection::parse(stream).map(Report::SyncCollection),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::AclPrincipalPropSet,
|
||||
} => AclPrincipalPropSet::parse(stream).map(Report::AclPrincipalPropSet),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::PrincipalMatch,
|
||||
} => PrincipalMatch::parse(stream).map(Report::PrincipalMatch),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::PrincipalPropertySearch,
|
||||
} => PrincipalPropertySearch::parse(stream).map(Report::PrincipalPropertySearch),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::PrincipalSearchPropertySet,
|
||||
} => stream
|
||||
.expect_element_end()
|
||||
.map(|_| Report::PrincipalSearchPropertySet),
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::ExpandProperty,
|
||||
} => ExpandProperty::parse(stream).map(Report::ExpandProperty),
|
||||
other => Err(other.into_unexpected()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for CalendarQuery {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut cq = CalendarQuery {
|
||||
properties: PropFind::AllProp(vec![]),
|
||||
filters: vec![],
|
||||
timezone: Timezone::None,
|
||||
};
|
||||
let mut depth = 1;
|
||||
let mut components = Vec::with_capacity(3);
|
||||
let mut property = None;
|
||||
let mut parameter = None;
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart { name, raw } => match name {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Propname,
|
||||
} if depth == 1 => {
|
||||
cq.properties = PropFind::PropName;
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Allprop,
|
||||
} if depth == 1 => {
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
} if depth == 1 => {
|
||||
cq.properties = PropFind::Prop(stream.collect_properties(Vec::new())?);
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::Filter,
|
||||
} if depth == 1 => {
|
||||
depth += 1;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::Timezone,
|
||||
} if depth == 1 => {
|
||||
cq.timezone =
|
||||
Timezone::Name(stream.collect_string_value()?.unwrap_or_default());
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::TimezoneId,
|
||||
} if depth == 1 => {
|
||||
cq.timezone =
|
||||
Timezone::Id(stream.collect_string_value()?.unwrap_or_default());
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::CompFilter,
|
||||
} if depth >= 2 => {
|
||||
for attribute in raw.attributes::<ICalendarComponentType>() {
|
||||
if let Attribute::Name(name) = attribute? {
|
||||
components.push((name, depth));
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::PropFilter,
|
||||
} if depth >= 3 => {
|
||||
for attribute in raw.attributes::<ICalendarProperty>() {
|
||||
if let Attribute::Name(name) = attribute? {
|
||||
property = Some(name);
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::ParamFilter,
|
||||
} if depth >= 4 => {
|
||||
for attribute in raw.attributes::<ICalendarParameterName>() {
|
||||
if let Attribute::Name(name) = attribute? {
|
||||
parameter = Some(name);
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::IsNotDefined,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
if let Some(filter) = Filter::from_parts(
|
||||
components.iter().map(|(c, _)| c.clone()).collect(),
|
||||
property.clone(),
|
||||
parameter.clone(),
|
||||
FilterOp::Undefined,
|
||||
) {
|
||||
cq.filters.push(filter);
|
||||
}
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::TextMatch,
|
||||
} => {
|
||||
let mut tm = TextMatch::parse(raw)?;
|
||||
tm.value = stream.collect_string_value()?.unwrap_or_default();
|
||||
if let Some(filter) = Filter::from_parts(
|
||||
components.iter().map(|(c, _)| c.clone()).collect(),
|
||||
property.clone(),
|
||||
parameter.clone(),
|
||||
FilterOp::TextMatch(tm),
|
||||
) {
|
||||
cq.filters.push(filter);
|
||||
}
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::TimeRange,
|
||||
} => {
|
||||
let range = TimeRange::from_raw(&raw)?;
|
||||
stream.expect_element_end()?;
|
||||
if let Some(filter) = range.and_then(|range| {
|
||||
Filter::from_parts(
|
||||
components.iter().map(|(c, _)| c.clone()).collect(),
|
||||
property.clone(),
|
||||
parameter.clone(),
|
||||
FilterOp::TimeRange(range),
|
||||
)
|
||||
}) {
|
||||
cq.filters.push(filter);
|
||||
}
|
||||
}
|
||||
name => return Err(name.into_unexpected()),
|
||||
},
|
||||
Token::ElementEnd => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
if matches!(components.last(), Some((_, d)) if *d == depth) {
|
||||
if components.len() > 1
|
||||
&& cq
|
||||
.filters
|
||||
.last()
|
||||
.and_then(|c| c.components())
|
||||
.is_none_or(|c| c.len() < components.len())
|
||||
{
|
||||
cq.filters.push(Filter::Component {
|
||||
comp: components.iter().map(|(c, _)| c.clone()).collect(),
|
||||
op: FilterOp::Exists,
|
||||
});
|
||||
}
|
||||
components.pop();
|
||||
}
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
element => return Err(element.into_unexpected()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cq)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for AddressbookQuery {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut aq = AddressbookQuery {
|
||||
properties: PropFind::AllProp(vec![]),
|
||||
filters: vec![],
|
||||
limit: None,
|
||||
};
|
||||
let mut depth = 1;
|
||||
let mut property = None;
|
||||
let mut parameter = None;
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart { name, raw } => match name {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Propname,
|
||||
} if depth == 1 => {
|
||||
aq.properties = PropFind::PropName;
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Allprop,
|
||||
} if depth == 1 => {
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
} if depth == 1 => {
|
||||
aq.properties = PropFind::Prop(stream.collect_properties(Vec::new())?);
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::Filter,
|
||||
} if depth == 1 => {
|
||||
if let Some(filter) = Filter::parse(raw)? {
|
||||
aq.filters.push(filter);
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::Limit,
|
||||
} if depth == 1 => {
|
||||
stream.expect_named_element(NamedElement::carddav(Element::Nresults))?;
|
||||
if let Some(Ok(limit)) = stream.parse_value::<u32>()? {
|
||||
aq.limit = limit.into();
|
||||
}
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::PropFilter,
|
||||
} if depth == 2 => {
|
||||
let mut filter = None;
|
||||
for attribute in raw.attributes::<VCardPropertyWithGroup>() {
|
||||
match attribute? {
|
||||
Attribute::Name(name) => {
|
||||
property = Some(name);
|
||||
}
|
||||
Attribute::TestAllOf(all_of) => {
|
||||
filter =
|
||||
(if all_of { Filter::AllOf } else { Filter::AnyOf }).into();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(filter) = filter {
|
||||
aq.filters.push(filter);
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::ParamFilter,
|
||||
} if depth == 3 => {
|
||||
for attribute in raw.attributes::<VCardParameterName>() {
|
||||
if let Attribute::Name(name) = attribute? {
|
||||
parameter = Some(name);
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::IsNotDefined,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
if let Some(filter) = Filter::from_parts(
|
||||
(),
|
||||
property.clone(),
|
||||
parameter.clone(),
|
||||
FilterOp::Undefined,
|
||||
) {
|
||||
aq.filters.push(filter);
|
||||
}
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::CardDav,
|
||||
element: Element::TextMatch,
|
||||
} => {
|
||||
let mut tm = TextMatch::parse(raw)?;
|
||||
tm.value = stream.collect_string_value()?.unwrap_or_default();
|
||||
if let Some(filter) = Filter::from_parts(
|
||||
(),
|
||||
property.clone(),
|
||||
parameter.clone(),
|
||||
FilterOp::TextMatch(tm),
|
||||
) {
|
||||
aq.filters.push(filter);
|
||||
}
|
||||
}
|
||||
name => return Err(name.into_unexpected()),
|
||||
},
|
||||
Token::ElementEnd => {
|
||||
depth -= 1;
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
element => return Err(element.into_unexpected()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(aq)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for FreeBusyQuery {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
match stream.token()? {
|
||||
Token::ElementStart {
|
||||
name:
|
||||
NamedElement {
|
||||
ns: Namespace::CalDav,
|
||||
element: Element::TimeRange,
|
||||
},
|
||||
raw,
|
||||
} => TimeRange::from_raw(&raw).map(|range| FreeBusyQuery { range }),
|
||||
other => Err(other.into_unexpected()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for MultiGet {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut mg = MultiGet {
|
||||
properties: PropFind::AllProp(vec![]),
|
||||
hrefs: vec![],
|
||||
};
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart { name, .. } => match name {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Propname,
|
||||
} => {
|
||||
mg.properties = PropFind::PropName;
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Allprop,
|
||||
} => {
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
} => {
|
||||
mg.properties = PropFind::Prop(stream.collect_properties(Vec::new())?);
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Href,
|
||||
} => {
|
||||
if let Some(href) = stream.collect_string_value()? {
|
||||
mg.hrefs.push(href);
|
||||
}
|
||||
}
|
||||
name => return Err(name.into_unexpected()),
|
||||
},
|
||||
Token::ElementEnd => {
|
||||
break;
|
||||
}
|
||||
element => return Err(element.into_unexpected()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mg)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for SyncCollection {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut sc = SyncCollection {
|
||||
properties: PropFind::AllProp(vec![]),
|
||||
limit: None,
|
||||
sync_token: None,
|
||||
depth: Depth::None,
|
||||
};
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart { name, .. } => match name {
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Prop,
|
||||
} => {
|
||||
sc.properties = PropFind::Prop(stream.collect_properties(Vec::new())?);
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::Limit,
|
||||
} => {
|
||||
stream.expect_named_element(NamedElement::dav(Element::Nresults))?;
|
||||
if let Some(Ok(limit)) = stream.parse_value::<u32>()? {
|
||||
sc.limit = limit.into();
|
||||
}
|
||||
stream.expect_element_end()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::SyncToken,
|
||||
} => {
|
||||
sc.sync_token = stream.collect_string_value()?;
|
||||
}
|
||||
NamedElement {
|
||||
ns: Namespace::Dav,
|
||||
element: Element::SyncLevel,
|
||||
} => {
|
||||
if let Some(Ok(depth)) = stream.parse_value::<Depth>()? {
|
||||
sc.depth = depth;
|
||||
}
|
||||
}
|
||||
name => return Err(name.into_unexpected()),
|
||||
},
|
||||
Token::ElementEnd => {
|
||||
break;
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
element => return Err(element.into_unexpected()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(sc)
|
||||
}
|
||||
}
|
||||
|
||||
impl DavParser for ExpandProperty {
|
||||
fn parse(stream: &mut Tokenizer<'_>) -> crate::parser::Result<Self> {
|
||||
let mut ep = ExpandProperty { properties: vec![] };
|
||||
let mut depth = 1;
|
||||
|
||||
loop {
|
||||
match stream.token()? {
|
||||
Token::ElementStart { name, raw } => match name {
|
||||
NamedElement {
|
||||
ns,
|
||||
element: Element::Property,
|
||||
} => {
|
||||
for attribute in raw.attributes::<String>() {
|
||||
if let Attribute::Name(name) = attribute? {
|
||||
if let Some(property) = Element::try_parse(name.as_bytes())
|
||||
.copied()
|
||||
.and_then(|element| {
|
||||
DavProperty::from_element(NamedElement { ns, element })
|
||||
})
|
||||
{
|
||||
ep.properties.push(ExpandPropertyItem {
|
||||
property,
|
||||
depth: depth - 1,
|
||||
});
|
||||
} else {
|
||||
let attrs = raw.element.attributes_raw().trim_ascii();
|
||||
ep.properties.push(ExpandPropertyItem {
|
||||
property: DavProperty::DeadProperty(DeadElementTag {
|
||||
name,
|
||||
attrs: (!attrs.is_empty()).then(|| {
|
||||
String::from_utf8_lossy(attrs).into_owned()
|
||||
}),
|
||||
}),
|
||||
depth: depth - 1,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
}
|
||||
name => return Err(name.into_unexpected()),
|
||||
},
|
||||
Token::ElementEnd => {
|
||||
depth -= 1;
|
||||
|
||||
if depth == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Token::UnknownElement(_) => {
|
||||
stream.seek_element_end()?;
|
||||
}
|
||||
element => return Err(element.into_unexpected()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ep)
|
||||
}
|
||||
}
|
||||
|
||||
impl TextMatch {
|
||||
fn parse(raw: RawElement<'_>) -> crate::parser::Result<Self> {
|
||||
let mut tm = TextMatch {
|
||||
match_type: MatchType::Contains,
|
||||
value: String::new(),
|
||||
collation: Collation::AsciiCasemap,
|
||||
negate: false,
|
||||
};
|
||||
|
||||
for attribute in raw.attributes::<String>() {
|
||||
match attribute? {
|
||||
Attribute::MatchType(match_type) => {
|
||||
tm.match_type = match_type;
|
||||
}
|
||||
Attribute::NegateCondition(negate) => {
|
||||
tm.negate = negate;
|
||||
}
|
||||
Attribute::Collation(collation) => {
|
||||
tm.collation = collation;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(tm)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, C> Filter<A, B, C> {
|
||||
fn from_parts(comp: A, prop: Option<B>, param: Option<C>, op: FilterOp) -> Option<Self> {
|
||||
match (prop, param) {
|
||||
(Some(prop), Some(param)) => Some(Filter::Parameter {
|
||||
comp,
|
||||
prop,
|
||||
param,
|
||||
op,
|
||||
}),
|
||||
(Some(prop), None) => Some(Filter::Property { comp, prop, op }),
|
||||
(None, None) => Some(Filter::Component { comp, op }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn components(&self) -> Option<&A> {
|
||||
match self {
|
||||
Filter::Component { comp, .. } => Some(comp),
|
||||
Filter::Property { comp, .. } => Some(comp),
|
||||
Filter::Parameter { comp, .. } => Some(comp),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(raw: RawElement<'_>) -> crate::parser::Result<Option<Self>> {
|
||||
for attribute in raw.attributes::<String>() {
|
||||
if let Attribute::TestAllOf(all_of) = attribute? {
|
||||
return Ok(Some(if all_of { Filter::AllOf } else { Filter::AnyOf }));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl XmlValueParser for Depth {
|
||||
fn parse_bytes(bytes: &[u8]) -> Option<Self> {
|
||||
Depth::parse(bytes)
|
||||
}
|
||||
|
||||
fn parse_str(text: &str) -> Option<Self> {
|
||||
Depth::parse(text.as_bytes())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user