mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-10-11 04:55:51 +08:00
Store dead element's namespaces (#1545)
This commit is contained in:
parent
1b1f85a156
commit
85bc434fbf
15 changed files with 198 additions and 86 deletions
|
@ -5,8 +5,8 @@
|
||||||
{
|
{
|
||||||
"type": "ElementStart",
|
"type": "ElementStart",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "D:href",
|
"name": "href",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"DAV:\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
{
|
{
|
||||||
"type": "ElementStart",
|
"type": "ElementStart",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "D:href",
|
"name": "href",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"DAV:\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,29 +4,29 @@
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "R:bigbox",
|
"name": "bigbox",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://ns.example.com/boxschema/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "R:author",
|
"name": "author",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://ns.example.com/boxschema/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "R:DingALing",
|
"name": "DingALing",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://ns.example.com/boxschema/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "R:Random",
|
"name": "Random",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://ns.example.com/boxschema/\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "apple:calendar-color",
|
"name": "calendar-color",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://apple.com/ns/ical/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
36
crates/dav-proto/resources/requests/propfind-010.json
Normal file
36
crates/dav-proto/resources/requests/propfind-010.json
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"type": "Prop",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"type": "WebDav",
|
||||||
|
"data": {
|
||||||
|
"type": "ResourceType"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "WebDav",
|
||||||
|
"data": {
|
||||||
|
"type": "DisplayName"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "WebDav",
|
||||||
|
"data": {
|
||||||
|
"type": "SyncToken"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "WebDav",
|
||||||
|
"data": {
|
||||||
|
"type": "GetCTag"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "DeadProperty",
|
||||||
|
"data": {
|
||||||
|
"name": "me-card",
|
||||||
|
"attrs": "xmlns=\"http://calendarserver.org/ns/\" hello=\"world & test\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
crates/dav-proto/resources/requests/propfind-010.xml
Normal file
1
crates/dav-proto/resources/requests/propfind-010.xml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:carddav"><d:prop><d:resourcetype /><d:displayname/><d:sync-token></d:sync-token><cs:getctag /><cs:me-card hello="world & test"/></d:prop></d:propfind>
|
|
@ -14,8 +14,8 @@
|
||||||
"property": {
|
"property": {
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "B:title",
|
"name": "title",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://www.example.com/ns/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"match_": "Sales"
|
"match_": "Sales"
|
||||||
|
@ -31,29 +31,29 @@
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "B:department",
|
"name": "department",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://www.example.com/ns/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "B:phone",
|
"name": "phone",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://www.example.com/ns/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "B:office",
|
"name": "office",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://www.example.com/ns/\""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "DeadProperty",
|
"type": "DeadProperty",
|
||||||
"data": {
|
"data": {
|
||||||
"name": "B:salary",
|
"name": "salary",
|
||||||
"attrs": null
|
"attrs": "xmlns=\"http://www.example.com/ns/\""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -20,11 +20,14 @@ pub mod tokenizer;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
Xml(quick_xml::Error),
|
Xml(Box<quick_xml::Error>),
|
||||||
UnexpectedToken {
|
UnexpectedToken(Box<UnexpectedToken>),
|
||||||
expected: Option<Token<'static>>,
|
}
|
||||||
found: Token<'static>,
|
|
||||||
},
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct UnexpectedToken {
|
||||||
|
pub expected: Option<Token<'static>>,
|
||||||
|
pub found: Token<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -43,7 +46,10 @@ pub enum Token<'x> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RawElement<'x>(pub BytesStart<'x>);
|
pub struct RawElement<'x> {
|
||||||
|
pub element: BytesStart<'x>,
|
||||||
|
pub namespace: Option<Cow<'static, [u8]>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait DavParser: Sized {
|
pub trait DavParser: Sized {
|
||||||
fn parse(stream: &mut Tokenizer<'_>) -> Result<Self>;
|
fn parse(stream: &mut Tokenizer<'_>) -> Result<Self>;
|
||||||
|
@ -89,20 +95,50 @@ impl Token<'_> {
|
||||||
match self {
|
match self {
|
||||||
Token::ElementStart { name, raw } => Token::ElementStart {
|
Token::ElementStart { name, raw } => Token::ElementStart {
|
||||||
name,
|
name,
|
||||||
raw: RawElement(raw.0.into_owned()),
|
raw: raw.into_owned(),
|
||||||
},
|
},
|
||||||
Token::ElementEnd => Token::ElementEnd,
|
Token::ElementEnd => Token::ElementEnd,
|
||||||
Token::Bytes(bytes) => Token::Bytes(bytes.into_owned().into()),
|
Token::Bytes(bytes) => Token::Bytes(bytes.into_owned().into()),
|
||||||
Token::Text(text) => Token::Text(text.into_owned().into()),
|
Token::Text(text) => Token::Text(text.into_owned().into()),
|
||||||
Token::UnknownElement(raw) => Token::UnknownElement(RawElement(raw.0.into_owned())),
|
Token::UnknownElement(raw) => Token::UnknownElement(raw.into_owned()),
|
||||||
Token::Eof => Token::Eof,
|
Token::Eof => Token::Eof,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_unexpected(self) -> Error {
|
pub fn into_unexpected(self) -> Error {
|
||||||
Error::UnexpectedToken {
|
Error::UnexpectedToken(Box::new(UnexpectedToken {
|
||||||
expected: None,
|
expected: None,
|
||||||
found: self.into_owned(),
|
found: self.into_owned(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'x> RawElement<'x> {
|
||||||
|
pub fn new(element: BytesStart<'x>) -> Self {
|
||||||
|
RawElement {
|
||||||
|
element,
|
||||||
|
namespace: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_namespace(self, namespace: quick_xml::name::Namespace<'_>) -> Self {
|
||||||
|
RawElement {
|
||||||
|
element: self.element,
|
||||||
|
namespace: Some(Cow::Owned(namespace.into_inner().to_vec())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_namespace_static(self, namespace: &'static [u8]) -> Self {
|
||||||
|
RawElement {
|
||||||
|
element: self.element,
|
||||||
|
namespace: Some(Cow::Borrowed(namespace)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_owned(self) -> RawElement<'static> {
|
||||||
|
RawElement {
|
||||||
|
element: self.element.into_owned(),
|
||||||
|
namespace: self.namespace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,16 +159,17 @@ impl PartialEq for Token<'_> {
|
||||||
) => {
|
) => {
|
||||||
l_name == r_name
|
l_name == r_name
|
||||||
&& l_raw
|
&& l_raw
|
||||||
.0
|
.element
|
||||||
.attributes_raw()
|
.attributes_raw()
|
||||||
.trim_ascii()
|
.trim_ascii()
|
||||||
.eq_ignore_ascii_case(r_raw.0.attributes_raw().trim_ascii())
|
.eq_ignore_ascii_case(r_raw.element.attributes_raw().trim_ascii())
|
||||||
}
|
}
|
||||||
(Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0,
|
(Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0,
|
||||||
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
|
(Self::Text(l0), Self::Text(r0)) => l0 == r0,
|
||||||
(Self::UnknownElement(l0), Self::UnknownElement(r0)) => {
|
(Self::UnknownElement(l0), Self::UnknownElement(r0)) => l0
|
||||||
l0.0.as_ref().eq_ignore_ascii_case(r0.0.as_ref())
|
.element
|
||||||
}
|
.as_ref()
|
||||||
|
.eq_ignore_ascii_case(r0.element.as_ref()),
|
||||||
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,19 +177,19 @@ impl PartialEq for Token<'_> {
|
||||||
|
|
||||||
impl NamedElement {
|
impl NamedElement {
|
||||||
pub fn into_unexpected(self) -> Error {
|
pub fn into_unexpected(self) -> Error {
|
||||||
Error::UnexpectedToken {
|
Error::UnexpectedToken(Box::new(UnexpectedToken {
|
||||||
expected: None,
|
expected: None,
|
||||||
found: Token::ElementStart {
|
found: Token::ElementStart {
|
||||||
name: self,
|
name: self,
|
||||||
raw: RawElement(BytesStart::new("")),
|
raw: RawElement::new(BytesStart::new("")),
|
||||||
},
|
},
|
||||||
}
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RawElement<'_> {
|
impl Default for RawElement<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
RawElement(BytesStart::new(""))
|
RawElement::new(BytesStart::new(""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,9 +197,9 @@ impl Display for Error {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::Xml(err) => write!(f, "XML error: {}", err),
|
Error::Xml(err) => write!(f, "XML error: {}", err),
|
||||||
Error::UnexpectedToken { expected, found } => {
|
Error::UnexpectedToken(err) => {
|
||||||
write!(f, "Unexpected token: {found:?}")?;
|
write!(f, "Unexpected token: {:?}", err.found)?;
|
||||||
if let Some(expected) = expected {
|
if let Some(expected) = &err.expected {
|
||||||
write!(f, ", expected: {expected:?}")?;
|
write!(f, ", expected: {expected:?}")?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl Tokenizer<'_> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Token::UnknownElement(name) => {
|
Token::UnknownElement(name) => {
|
||||||
elements.push(DavProperty::DeadProperty(name.into()));
|
elements.push(DavProperty::DeadProperty((&name).into()));
|
||||||
self.expect_element_end()?;
|
self.expect_element_end()?;
|
||||||
}
|
}
|
||||||
token => return Err(token.into_unexpected()),
|
token => return Err(token.into_unexpected()),
|
||||||
|
@ -369,7 +369,7 @@ impl Tokenizer<'_> {
|
||||||
}
|
}
|
||||||
Token::UnknownElement(raw) => {
|
Token::UnknownElement(raw) => {
|
||||||
elements.push(DavPropertyValue {
|
elements.push(DavPropertyValue {
|
||||||
property: DavProperty::DeadProperty(raw.into()),
|
property: DavProperty::DeadProperty((&raw).into()),
|
||||||
value: DavValue::DeadProperty(DeadProperty::parse(self)?),
|
value: DavValue::DeadProperty(DeadProperty::parse(self)?),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use quick_xml::{
|
||||||
|
|
||||||
use crate::schema::{Attribute, AttributeValue, Element, NamedElement, Namespace};
|
use crate::schema::{Attribute, AttributeValue, Element, NamedElement, Namespace};
|
||||||
|
|
||||||
use super::{Error, RawElement, Token, XmlValueParser};
|
use super::{Error, RawElement, Token, UnexpectedToken, XmlValueParser};
|
||||||
|
|
||||||
pub struct Tokenizer<'x> {
|
pub struct Tokenizer<'x> {
|
||||||
xml: NsReader<&'x [u8]>,
|
xml: NsReader<&'x [u8]>,
|
||||||
|
@ -47,7 +47,10 @@ impl<'x> Tokenizer<'x> {
|
||||||
return Ok(Token::ElementEnd);
|
return Ok(Token::ElementEnd);
|
||||||
}
|
}
|
||||||
Event::Text(text) if text.iter().any(|ch| !ch.is_ascii_whitespace()) => {
|
Event::Text(text) if text.iter().any(|ch| !ch.is_ascii_whitespace()) => {
|
||||||
return text.unescape().map(Token::Text).map_err(Error::Xml);
|
return text
|
||||||
|
.unescape()
|
||||||
|
.map(Token::Text)
|
||||||
|
.map_err(|err| Error::Xml(Box::new(err)));
|
||||||
}
|
}
|
||||||
Event::CData(bytes) => return Ok(Token::Bytes(bytes.into_inner())),
|
Event::CData(bytes) => return Ok(Token::Bytes(bytes.into_inner())),
|
||||||
Event::Eof => return Ok(Token::Eof),
|
Event::Eof => return Ok(Token::Eof),
|
||||||
|
@ -59,26 +62,29 @@ impl<'x> Tokenizer<'x> {
|
||||||
// Parse element
|
// Parse element
|
||||||
let name = tag.name();
|
let name = tag.name();
|
||||||
match resolve_result {
|
match resolve_result {
|
||||||
ResolveResult::Bound(ns) if !ns.as_ref().is_empty() => {
|
ResolveResult::Bound(raw_ns) if !raw_ns.as_ref().is_empty() => {
|
||||||
if let (Some(ns), Some(element)) = (
|
if let (Some(ns), Some(element)) = (
|
||||||
Namespace::try_parse(ns.as_ref()),
|
Namespace::try_parse(raw_ns.as_ref()),
|
||||||
Element::try_parse(name.local_name().as_ref()).copied(),
|
Element::try_parse(name.local_name().as_ref()).copied(),
|
||||||
) {
|
) {
|
||||||
return Ok(Token::ElementStart {
|
return Ok(Token::ElementStart {
|
||||||
name: NamedElement { ns, element },
|
name: NamedElement { ns, element },
|
||||||
raw: RawElement(tag),
|
raw: RawElement::new(tag)
|
||||||
|
.with_namespace_static(ns.namespace().as_bytes()),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return Ok(Token::UnknownElement(RawElement(tag)));
|
return Ok(Token::UnknownElement(
|
||||||
|
RawElement::new(tag).with_namespace(raw_ns),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResolveResult::Unknown(p) => {
|
ResolveResult::Unknown(p) => {
|
||||||
return Err(Error::Xml(quick_xml::Error::Namespace(
|
return Err(Error::Xml(Box::new(quick_xml::Error::Namespace(
|
||||||
quick_xml::name::NamespaceError::UnknownPrefix(p),
|
quick_xml::name::NamespaceError::UnknownPrefix(p),
|
||||||
)))
|
))))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Ok(Token::UnknownElement(RawElement(tag)));
|
return Ok(Token::UnknownElement(RawElement::new(tag)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,24 +93,24 @@ impl<'x> Tokenizer<'x> {
|
||||||
pub fn unwrap_named_element(&mut self) -> super::Result<NamedElement> {
|
pub fn unwrap_named_element(&mut self) -> super::Result<NamedElement> {
|
||||||
match self.token()? {
|
match self.token()? {
|
||||||
Token::ElementStart { name, .. } => Ok(name),
|
Token::ElementStart { name, .. } => Ok(name),
|
||||||
found => Err(Error::UnexpectedToken {
|
found => Err(Error::UnexpectedToken(Box::new(UnexpectedToken {
|
||||||
expected: None,
|
expected: None,
|
||||||
found: found.into_owned(),
|
found: found.into_owned(),
|
||||||
}),
|
}))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_named_element(&mut self, expected: NamedElement) -> super::Result<()> {
|
pub fn expect_named_element(&mut self, expected: NamedElement) -> super::Result<()> {
|
||||||
match self.token()? {
|
match self.token()? {
|
||||||
Token::ElementStart { name, .. } if name == expected => Ok(()),
|
Token::ElementStart { name, .. } if name == expected => Ok(()),
|
||||||
found => Err(Error::UnexpectedToken {
|
found => Err(Error::UnexpectedToken(Box::new(UnexpectedToken {
|
||||||
expected: Token::ElementStart {
|
expected: Token::ElementStart {
|
||||||
name: expected,
|
name: expected,
|
||||||
raw: RawElement::default(),
|
raw: RawElement::default(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
found: found.into_owned(),
|
found: found.into_owned(),
|
||||||
}),
|
}))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,24 +118,24 @@ impl<'x> Tokenizer<'x> {
|
||||||
match self.token()? {
|
match self.token()? {
|
||||||
Token::ElementStart { name, .. } if name == expected => Ok(true),
|
Token::ElementStart { name, .. } if name == expected => Ok(true),
|
||||||
Token::Eof => Ok(false),
|
Token::Eof => Ok(false),
|
||||||
found => Err(Error::UnexpectedToken {
|
found => Err(Error::UnexpectedToken(Box::new(UnexpectedToken {
|
||||||
expected: Token::ElementStart {
|
expected: Token::ElementStart {
|
||||||
name: expected,
|
name: expected,
|
||||||
raw: RawElement::default(),
|
raw: RawElement::default(),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
found: found.into_owned(),
|
found: found.into_owned(),
|
||||||
}),
|
}))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expect_element_end(&mut self) -> super::Result<()> {
|
pub fn expect_element_end(&mut self) -> super::Result<()> {
|
||||||
match self.token()? {
|
match self.token()? {
|
||||||
Token::ElementEnd => Ok(()),
|
Token::ElementEnd => Ok(()),
|
||||||
found => Err(Error::UnexpectedToken {
|
found => Err(Error::UnexpectedToken(Box::new(UnexpectedToken {
|
||||||
expected: Token::ElementEnd.into(),
|
expected: Token::ElementEnd.into(),
|
||||||
found: found.into_owned(),
|
found: found.into_owned(),
|
||||||
}),
|
}))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +255,7 @@ impl RawElement<'_> {
|
||||||
pub fn attributes<T: AttributeValue>(
|
pub fn attributes<T: AttributeValue>(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = super::Result<Attribute<T>>> + '_ {
|
) -> impl Iterator<Item = super::Result<Attribute<T>>> + '_ {
|
||||||
self.0.attributes().filter_map(|attr| match attr {
|
self.element.attributes().filter_map(|attr| match attr {
|
||||||
Ok(attr) => match attr.unescape_value() {
|
Ok(attr) => match attr.unescape_value() {
|
||||||
Ok(value) => Attribute::from_param(attr.key.as_ref(), value).map(Ok),
|
Ok(value) => Attribute::from_param(attr.key.as_ref(), value).map(Ok),
|
||||||
Err(err) => Some(Err(err.into())),
|
Err(err) => Some(Err(err.into())),
|
||||||
|
@ -261,13 +267,13 @@ impl RawElement<'_> {
|
||||||
|
|
||||||
impl From<quick_xml::Error> for Error {
|
impl From<quick_xml::Error> for Error {
|
||||||
fn from(err: quick_xml::Error) -> Self {
|
fn from(err: quick_xml::Error) -> Self {
|
||||||
Error::Xml(err)
|
Error::Xml(Box::new(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AttrError> for Error {
|
impl From<AttrError> for Error {
|
||||||
fn from(err: AttrError) -> Self {
|
fn from(err: AttrError) -> Self {
|
||||||
Error::Xml(err.into())
|
Error::Xml(Box::new(err.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ impl DavParser for DeadProperty {
|
||||||
loop {
|
loop {
|
||||||
match stream.token()? {
|
match stream.token()? {
|
||||||
Token::ElementStart { raw, .. } | Token::UnknownElement(raw) => {
|
Token::ElementStart { raw, .. } | Token::UnknownElement(raw) => {
|
||||||
items.0.push(DeadPropertyTag::ElementStart(raw.into()));
|
items.0.push(DeadPropertyTag::ElementStart((&raw).into()));
|
||||||
depth += 1;
|
depth += 1;
|
||||||
}
|
}
|
||||||
Token::ElementEnd => {
|
Token::ElementEnd => {
|
||||||
|
@ -142,14 +142,42 @@ impl ArchivedDeadElementTag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RawElement<'_>> for DeadElementTag {
|
impl From<&RawElement<'_>> for DeadElementTag {
|
||||||
fn from(raw: RawElement<'_>) -> Self {
|
fn from(raw: &RawElement<'_>) -> Self {
|
||||||
let name = String::from_utf8_lossy(raw.0.name().as_ref().trim_ascii()).into_owned();
|
let name = std::str::from_utf8(raw.element.local_name().as_ref())
|
||||||
let attr = raw.0.attributes_raw().trim_ascii();
|
.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 {
|
DeadElementTag {
|
||||||
name,
|
name,
|
||||||
attrs: (!attr.is_empty()).then(|| String::from_utf8_lossy(attr).into_owned()),
|
attrs: (!attrs.is_empty()).then_some(attrs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -528,7 +528,7 @@ impl DavParser for ExpandProperty {
|
||||||
depth: depth - 1,
|
depth: depth - 1,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let attrs = raw.0.attributes_raw().trim_ascii();
|
let attrs = raw.element.attributes_raw().trim_ascii();
|
||||||
ep.properties.push(ExpandPropertyItem {
|
ep.properties.push(ExpandPropertyItem {
|
||||||
property: DavProperty::DeadProperty(DeadElementTag {
|
property: DavProperty::DeadProperty(DeadElementTag {
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -66,10 +66,8 @@ impl Namespace {
|
||||||
Namespace::CalendarServer => "C",
|
Namespace::CalendarServer => "C",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<str> for Namespace {
|
pub fn namespace(&self) -> &'static str {
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
match self {
|
match self {
|
||||||
Namespace::Dav => "DAV:",
|
Namespace::Dav => "DAV:",
|
||||||
Namespace::CalDav => "urn:ietf:params:xml:ns:caldav",
|
Namespace::CalDav => "urn:ietf:params:xml:ns:caldav",
|
||||||
|
@ -79,6 +77,12 @@ impl AsRef<str> for Namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for Namespace {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.namespace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
|
||||||
pub enum Element {
|
pub enum Element {
|
||||||
|
|
|
@ -32,7 +32,7 @@ pub async fn test(test: &WebDavTest) {
|
||||||
.with_status(StatusCode::CREATED);
|
.with_status(StatusCode::CREATED);
|
||||||
let lock_token = response
|
let lock_token = response
|
||||||
.with_value(
|
.with_value(
|
||||||
"D:prop.D:lockdiscovery.D:activelock.D:owner.D:href",
|
"D:prop.D:lockdiscovery.D:activelock.D:owner.href",
|
||||||
"super-owner",
|
"super-owner",
|
||||||
)
|
)
|
||||||
.with_value("D:prop.D:lockdiscovery.D:activelock.D:depth", "infinity")
|
.with_value("D:prop.D:lockdiscovery.D:activelock.D:depth", "infinity")
|
||||||
|
@ -55,7 +55,7 @@ pub async fn test(test: &WebDavTest) {
|
||||||
.await
|
.await
|
||||||
.with_status(StatusCode::OK)
|
.with_status(StatusCode::OK)
|
||||||
.with_value(
|
.with_value(
|
||||||
"D:prop.D:lockdiscovery.D:activelock.D:owner.D:href",
|
"D:prop.D:lockdiscovery.D:activelock.D:owner.href",
|
||||||
"super-owner",
|
"super-owner",
|
||||||
)
|
)
|
||||||
.with_any_value(
|
.with_any_value(
|
||||||
|
@ -117,7 +117,7 @@ pub async fn test(test: &WebDavTest) {
|
||||||
props
|
props
|
||||||
.get(DavProperty::WebDav(WebDavProperty::LockDiscovery))
|
.get(DavProperty::WebDav(WebDavProperty::LockDiscovery))
|
||||||
.with_some_values([
|
.with_some_values([
|
||||||
"D:activelock.D:owner.D:href:super-owner",
|
"D:activelock.D:owner.href:super-owner",
|
||||||
"D:activelock.D:depth:infinity",
|
"D:activelock.D:depth:infinity",
|
||||||
format!("D:activelock.D:locktoken.D:href:{lock_token}").as_str(),
|
format!("D:activelock.D:locktoken.D:href:{lock_token}").as_str(),
|
||||||
format!("D:activelock.D:lockroot.D:href:{path}").as_str(),
|
format!("D:activelock.D:lockroot.D:href:{path}").as_str(),
|
||||||
|
|
|
@ -501,8 +501,8 @@ pub async fn test(test: &WebDavTest) {
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
DavProperty::DeadProperty(DeadElementTag::new(
|
DavProperty::DeadProperty(DeadElementTag::new(
|
||||||
"C:my-dead-element".to_string(),
|
"my-dead-element".to_string(),
|
||||||
None,
|
Some("xmlns=\"http://example.com/ns/\"".to_string()),
|
||||||
)),
|
)),
|
||||||
"this is a dead but exciting element",
|
"this is a dead but exciting element",
|
||||||
),
|
),
|
||||||
|
@ -514,8 +514,8 @@ pub async fn test(test: &WebDavTest) {
|
||||||
let mut props = vec![
|
let mut props = vec![
|
||||||
(
|
(
|
||||||
DavProperty::DeadProperty(DeadElementTag::new(
|
DavProperty::DeadProperty(DeadElementTag::new(
|
||||||
"C:my-dead-element".to_string(),
|
"my-dead-element".to_string(),
|
||||||
None,
|
Some("xmlns=\"http://example.com/ns/\"".to_string()),
|
||||||
)),
|
)),
|
||||||
"",
|
"",
|
||||||
),
|
),
|
||||||
|
@ -604,8 +604,8 @@ pub async fn test(test: &WebDavTest) {
|
||||||
let mut chunky_props = vec![
|
let mut chunky_props = vec![
|
||||||
DavProperty::WebDav(WebDavProperty::DisplayName),
|
DavProperty::WebDav(WebDavProperty::DisplayName),
|
||||||
DavProperty::DeadProperty(DeadElementTag::new(
|
DavProperty::DeadProperty(DeadElementTag::new(
|
||||||
"C:my-chunky-dead-element".to_string(),
|
"my-chunky-dead-element".to_string(),
|
||||||
None,
|
Some("xmlns=\"http://example.com/ns/\"".to_string()),
|
||||||
)),
|
)),
|
||||||
];
|
];
|
||||||
if !is_file {
|
if !is_file {
|
||||||
|
|
Loading…
Add table
Reference in a new issue