From d4a5a8247b7f754ce5e556b8616d1f7948b71dab Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 22 Mar 2022 23:29:25 +0100 Subject: [PATCH] minidom, tokio-xmpp: switch xml parsing to rxml --- minidom/Cargo.toml | 1 + minidom/src/element.rs | 263 ++++-------------------- minidom/src/error.rs | 45 +---- minidom/src/lib.rs | 1 + minidom/src/parser.rs | 15 +- minidom/src/tests.rs | 35 ++-- minidom/src/tree_builder.rs | 154 ++++++++++++++ parsers/src/caps.rs | 6 +- parsers/src/cert_management.rs | 3 +- parsers/src/ecaps2.rs | 6 +- parsers/src/ibr.rs | 3 +- parsers/src/jingle_ft.rs | 12 +- parsers/src/jingle_grouping.rs | 3 +- parsers/src/jingle_ice_udp.rs | 6 +- parsers/src/jingle_raw_udp.rs | 3 +- parsers/src/jingle_rtp.rs | 3 +- parsers/src/jingle_rtp_hdrext.rs | 3 +- parsers/src/jingle_ssma.rs | 6 +- parsers/src/legacy_omemo.rs | 9 +- parsers/src/mam.rs | 15 +- parsers/src/mam_prefs.rs | 6 +- parsers/src/media_element.rs | 6 +- parsers/src/muc/muc.rs | 6 +- parsers/src/muc/user.rs | 259 ++++++++++-------------- parsers/src/roster.rs | 23 +-- parsers/src/sm.rs | 2 +- parsers/src/time.rs | 2 +- tokio-xmpp/Cargo.toml | 3 +- tokio-xmpp/src/client/async_client.rs | 44 ++-- tokio-xmpp/src/error.rs | 51 +---- tokio-xmpp/src/lib.rs | 2 +- tokio-xmpp/src/xmpp_codec.rs | 276 +++++--------------------- tokio-xmpp/src/xmpp_stream.rs | 4 +- 33 files changed, 465 insertions(+), 811 deletions(-) create mode 100644 minidom/src/tree_builder.rs diff --git a/minidom/Cargo.toml b/minidom/Cargo.toml index bd951ed68092405f712217cc2bae093aa7f3385d..af8a1c81ac7a54a3ea347ff2ddb562c0e44cf449 100644 --- a/minidom/Cargo.toml +++ b/minidom/Cargo.toml @@ -22,3 +22,4 @@ gitlab = { repository = "xmpp-rs/xmpp-rs" } [dependencies] quick-xml = "0.22.0" +rxml = "^0.7.1" diff --git a/minidom/src/element.rs b/minidom/src/element.rs index b04d75270cbe46587e7c089b2446a2c6f880bc3c..d3f4d1c805746ac10cc0b71cf75cd30cc9645eeb 100644 --- a/minidom/src/element.rs +++ b/minidom/src/element.rs @@ -17,18 +17,17 @@ use crate::error::{Error, Result}; use crate::namespaces::NSChoice; use crate::node::Node; use crate::prefixes::{Namespace, Prefix, Prefixes}; +use crate::tree_builder::TreeBuilder; use std::collections::{btree_map, BTreeMap}; -use std::io::Write; +use std::io::{BufRead, Write}; use std::borrow::Cow; use std::str; use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, Event}; -use quick_xml::Reader as EventReader; use quick_xml::Writer as EventWriter; - -use std::io::BufRead; +use rxml::{EventRead, Lexer, PullDriver, RawParser}; use std::str::FromStr; @@ -84,8 +83,9 @@ pub struct Element { namespace: String, /// This is only used when deserializing. If you have to use a custom prefix use /// `ElementBuilder::prefix`. - prefix: Option, - prefixes: Prefixes, + pub(crate) prefix: Option, + /// Namespace declarations + pub prefixes: Prefixes, attributes: BTreeMap, children: Vec, } @@ -102,8 +102,7 @@ impl FromStr for Element { type Err = Error; fn from_str(s: &str) -> Result { - let mut reader = EventReader::from_str(s); - Element::from_reader(&mut reader) + Element::from_reader(s.as_bytes()) } } @@ -120,15 +119,8 @@ impl PartialEq for Element { } } -fn ensure_no_prefix>(s: &S) -> Result<()> { - match s.as_ref().split(':').count() { - 1 => Ok(()), - _ => Err(Error::InvalidElement), - } -} - impl Element { - fn new>( + pub(crate) fn new>( name: String, namespace: String, prefix: Option, @@ -136,8 +128,6 @@ impl Element { attributes: BTreeMap, children: Vec, ) -> Element { - ensure_no_prefix(&name).unwrap(); - // TODO: Return Result instead. Element { name, namespace, @@ -310,123 +300,18 @@ impl Element { namespace.into().compare(self.namespace.as_ref()) } - /// Parse a document from an `EventReader`. - pub fn from_reader(reader: &mut EventReader) -> Result { - let mut buf = Vec::new(); - - let mut prefixes = BTreeMap::new(); - let root: Element = loop { - let e = reader.read_event(&mut buf)?; - match e { - Event::Empty(ref e) | Event::Start(ref e) => { - break build_element(reader, e, &mut prefixes)?; - } - Event::Eof => { - return Err(Error::EndOfDocument); - } - Event::Comment { .. } => { - return Err(Error::NoComments); - } - Event::Text { .. } - | Event::End { .. } - | Event::CData { .. } - | Event::Decl { .. } - | Event::PI { .. } - | Event::DocType { .. } => (), // TODO: may need more errors - } - }; - - let mut stack = vec![root]; - let mut prefix_stack = vec![prefixes]; + /// Parse a document from a `BufRead`. + pub fn from_reader(reader: R) -> Result { + let mut tree_builder = TreeBuilder::new(); + let mut driver = PullDriver::wrap(reader, Lexer::new(), RawParser::new()); + while let Some(event) = driver.read()? { + tree_builder.process_event(event)?; - loop { - match reader.read_event(&mut buf)? { - Event::Empty(ref e) => { - let mut prefixes = prefix_stack.last().unwrap().clone(); - let elem = build_element(reader, e, &mut prefixes)?; - // Since there is no Event::End after, directly append it to the current node - stack.last_mut().unwrap().append_child(elem); - } - Event::Start(ref e) => { - let mut prefixes = prefix_stack.last().unwrap().clone(); - let elem = build_element(reader, e, &mut prefixes)?; - stack.push(elem); - prefix_stack.push(prefixes); - } - Event::End(ref e) => { - if stack.len() <= 1 { - break; - } - let prefixes = prefix_stack.pop().unwrap(); - let elem = stack.pop().unwrap(); - if let Some(to) = stack.last_mut() { - // TODO: check whether this is correct, we are comparing &[u8]s, not &strs - let elem_name = e.name(); - let mut split_iter = elem_name.splitn(2, |u| *u == 0x3A); - let possible_prefix = split_iter.next().unwrap(); // Can't be empty. - let opening_prefix = { - let mut tmp: Option> = None; - for (prefix, ns) in prefixes { - if ns == elem.namespace { - tmp = Some(prefix.clone()); - break; - } - } - match tmp { - Some(prefix) => prefix, - None => return Err(Error::InvalidPrefix), - } - }; - match split_iter.next() { - // There is a prefix on the closing tag - Some(name) => { - // Does the closing prefix match the opening prefix? - match opening_prefix { - Some(prefix) if possible_prefix == prefix.as_bytes() => (), - _ => return Err(Error::InvalidElementClosed), - } - // Does the closing tag name match the opening tag name? - if name != elem.name().as_bytes() { - return Err(Error::InvalidElementClosed); - } - } - // There was no prefix on the closing tag - None => { - // Is there a prefix on the opening tag? - if opening_prefix.is_some() { - return Err(Error::InvalidElementClosed); - } - // Does the opening tag name match the closing one? - if possible_prefix != elem.name().as_bytes() { - return Err(Error::InvalidElementClosed); - } - } - } - to.append_child(elem); - } - } - Event::Text(s) => { - let text = s.unescape_and_decode(reader)?; - if !text.is_empty() { - let current_elem = stack.last_mut().unwrap(); - current_elem.append_text_node(text); - } - } - Event::CData(s) => { - let text = s.unescape_and_decode(&reader)?; - if !text.is_empty() { - let current_elem = stack.last_mut().unwrap(); - current_elem.append_text_node(text); - } - } - Event::Eof => { - break; - } - Event::Comment(_) => return Err(Error::NoComments), - Event::Decl { .. } | Event::PI { .. } | Event::DocType { .. } => (), + if let Some(root) = tree_builder.root.take() { + return Ok(root); } } - Ok(stack.pop().unwrap()) + Err(Error::EndOfDocument) } /// Output a document to a `Writer`. @@ -822,68 +707,18 @@ impl Element { })?; self.children.remove(idx).into_element() } -} - -fn split_element_name>(s: S) -> Result<(Option, String)> { - let name_parts = s.as_ref().split(':').collect::>(); - match name_parts.len() { - 2 => Ok((Some(name_parts[0].to_owned()), name_parts[1].to_owned())), - 1 => Ok((None, name_parts[0].to_owned())), - _ => Err(Error::InvalidElement), - } -} -fn build_element( - reader: &EventReader, - event: &BytesStart, - prefixes: &mut BTreeMap, -) -> Result { - let (prefix, name) = split_element_name(str::from_utf8(event.name())?)?; - let mut local_prefixes = BTreeMap::new(); - - let attributes = event - .attributes() - .map(|o| { - let o = o?; - let key = str::from_utf8(o.key)?.to_owned(); - let value = o.unescape_and_decode_value(reader)?; - Ok((key, value)) - }) - .filter(|o| match *o { - Ok((ref key, ref value)) if key == "xmlns" => { - local_prefixes.insert(None, value.clone()); - prefixes.insert(None, value.clone()); - false - } - Ok((ref key, ref value)) if key.starts_with("xmlns:") => { - local_prefixes.insert(Some(key[6..].to_owned()), value.to_owned()); - prefixes.insert(Some(key[6..].to_owned()), value.to_owned()); - false + /// Remove the leading nodes up to the first child element and + /// return it + pub fn unshift_child(&mut self) -> Option { + while self.children.len() > 0 { + if let Some(el) = self.children.remove(0).into_element() { + return Some(el); } - _ => true, - }) - .collect::>>()?; - - let namespace: &String = { - if let Some(namespace) = local_prefixes.get(&prefix) { - namespace - } else if let Some(namespace) = prefixes.get(&prefix) { - namespace - } else { - return Err(Error::MissingNamespace); } - }; - - Ok(Element::new( - name, - namespace.clone(), - // Note that this will always be Some(_) as we can't distinguish between the None case and - // Some(None). At least we make sure the prefix has a namespace associated. - Some(prefix), - local_prefixes, - attributes, - Vec::new(), - )) + + None + } } /// An iterator over references to child elements of an `Element`. @@ -1067,9 +902,8 @@ mod tests { #[test] fn test_from_reader_simple() { - let xml = ""; - let mut reader = EventReader::from_str(xml); - let elem = Element::from_reader(&mut reader); + let xml = b""; + let elem = Element::from_reader(&xml[..]); let elem2 = Element::builder("foo", "ns1").build(); @@ -1078,9 +912,8 @@ mod tests { #[test] fn test_from_reader_nested() { - let xml = ""; - let mut reader = EventReader::from_str(xml); - let elem = Element::from_reader(&mut reader); + let xml = b""; + let elem = Element::from_reader(&xml[..]); let nested = Element::builder("bar", "ns1").attr("baz", "qxx").build(); let elem2 = Element::builder("foo", "ns1").append(nested).build(); @@ -1090,9 +923,8 @@ mod tests { #[test] fn test_from_reader_with_prefix() { - let xml = ""; - let mut reader = EventReader::from_str(xml); - let elem = Element::from_reader(&mut reader); + let xml = b""; + let elem = Element::from_reader(&xml[..]); let nested = Element::builder("bar", "ns1").attr("baz", "qxx").build(); let elem2 = Element::builder("foo", "ns1").append(nested).build(); @@ -1102,9 +934,8 @@ mod tests { #[test] fn test_from_reader_split_prefix() { - let xml = ""; - let mut reader = EventReader::from_str(xml); - let elem = Element::from_reader(&mut reader).unwrap(); + let xml = b""; + let elem = Element::from_reader(&xml[..]).unwrap(); assert_eq!(elem.name(), String::from("bar")); assert_eq!(elem.ns(), String::from("ns1")); @@ -1118,38 +949,32 @@ mod tests { #[test] fn parses_spectest_xml() { // From: https://gitlab.com/lumi/minidom-rs/issues/8 - let xml = r#" - + let xml = br#" "#; - let mut reader = EventReader::from_str(xml); - let _ = Element::from_reader(&mut reader).unwrap(); + let _ = Element::from_reader(&xml[..]).unwrap(); } #[test] fn does_not_unescape_cdata() { - let xml = "]]>"; - let mut reader = EventReader::from_str(xml); - let elem = Element::from_reader(&mut reader).unwrap(); + let xml = b"]]>"; + let elem = Element::from_reader(&xml[..]).unwrap(); assert_eq!(elem.text(), "'>blah"); } #[test] fn test_compare_all_ns() { - let xml = ""; - let mut reader = EventReader::from_str(xml); - let elem = Element::from_reader(&mut reader).unwrap(); + let xml = b""; + let elem = Element::from_reader(&xml[..]).unwrap(); let elem2 = elem.clone(); - let xml3 = ""; - let mut reader3 = EventReader::from_str(xml3); - let elem3 = Element::from_reader(&mut reader3).unwrap(); + let xml3 = b""; + let elem3 = Element::from_reader(&xml3[..]).unwrap(); - let xml4 = ""; - let mut reader4 = EventReader::from_str(xml4); - let elem4 = Element::from_reader(&mut reader4).unwrap(); + let xml4 = b""; + let elem4 = Element::from_reader(&xml4[..]).unwrap(); assert_eq!(elem, elem2); assert_eq!(elem, elem3); diff --git a/minidom/src/error.rs b/minidom/src/error.rs index b34375a0e9aefc03f44173f6f3e943c93b501dcd..ba23d259cad94a9f80d7271545f1a9b54743b11d 100644 --- a/minidom/src/error.rs +++ b/minidom/src/error.rs @@ -20,21 +20,12 @@ pub enum Error { /// An error from quick_xml. XmlError(::quick_xml::Error), - /// An UTF-8 conversion error. - Utf8Error(::std::str::Utf8Error), - - /// An I/O error, from std::io. - IoError(::std::io::Error), + /// Error from rxml parsing + ParserError(rxml::Error), /// An error which is returned when the end of the document was reached prematurely. EndOfDocument, - /// An error which is returned when an element is closed when it shouldn't be - InvalidElementClosed, - - /// An error which is returned when an elemet's name contains more colons than permitted - InvalidElement, - /// An error which is returned when an element being serialized doesn't contain a prefix /// (be it None or Some(_)). InvalidPrefix, @@ -42,9 +33,6 @@ pub enum Error { /// An error which is returned when an element doesn't contain a namespace MissingNamespace, - /// An error which is returned when a comment is to be parsed by minidom - NoComments, - /// An error which is returned when a prefixed is defined twice DuplicatePrefix, } @@ -53,14 +41,10 @@ impl StdError for Error { fn cause(&self) -> Option<&dyn StdError> { match self { Error::XmlError(e) => Some(e), - Error::Utf8Error(e) => Some(e), - Error::IoError(e) => Some(e), + Error::ParserError(e) => Some(e), Error::EndOfDocument => None, - Error::InvalidElementClosed => None, - Error::InvalidElement => None, Error::InvalidPrefix => None, Error::MissingNamespace => None, - Error::NoComments => None, Error::DuplicatePrefix => None, } } @@ -70,21 +54,12 @@ impl std::fmt::Display for Error { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Error::XmlError(e) => write!(fmt, "XML error: {}", e), - Error::Utf8Error(e) => write!(fmt, "UTF-8 error: {}", e), - Error::IoError(e) => write!(fmt, "IO error: {}", e), + Error::ParserError(e) => write!(fmt, "XML parser error: {}", e), Error::EndOfDocument => { write!(fmt, "the end of the document has been reached prematurely") } - Error::InvalidElementClosed => { - write!(fmt, "the XML is invalid, an element was wrongly closed") - } - Error::InvalidElement => write!(fmt, "the XML element is invalid"), Error::InvalidPrefix => write!(fmt, "the prefix is invalid"), Error::MissingNamespace => write!(fmt, "the XML element is missing a namespace",), - Error::NoComments => write!( - fmt, - "a comment has been found even though comments are forbidden" - ), Error::DuplicatePrefix => write!(fmt, "the prefix is already defined"), } } @@ -96,15 +71,9 @@ impl From<::quick_xml::Error> for Error { } } -impl From<::std::str::Utf8Error> for Error { - fn from(err: ::std::str::Utf8Error) -> Error { - Error::Utf8Error(err) - } -} - -impl From<::std::io::Error> for Error { - fn from(err: ::std::io::Error) -> Error { - Error::IoError(err) +impl From for Error { + fn from(err: rxml::Error) -> Error { + Error::ParserError(err) } } diff --git a/minidom/src/lib.rs b/minidom/src/lib.rs index 4ecf36f18b25c7718185095976c0149242160dbd..21aa8b41e46e90b1aefc6d4ea1efe4c12a702cf7 100644 --- a/minidom/src/lib.rs +++ b/minidom/src/lib.rs @@ -83,6 +83,7 @@ pub mod error; mod namespaces; pub mod node; mod prefixes; +pub mod tree_builder; #[cfg(test)] mod tests; diff --git a/minidom/src/parser.rs b/minidom/src/parser.rs index 49896c3ade75562144ac1e3639a4aa7b80aa7144..3f9766775005b0100dcb51d28b193fb7fa209222 100644 --- a/minidom/src/parser.rs +++ b/minidom/src/parser.rs @@ -10,16 +10,16 @@ use crate::element::Element; use crate::error::{Error, ParserError, Result}; +use crate::tree_builder::TreeBuilder; -use bytes::BytesMut; -use quick_xml::Reader as EventReader; -use std::cell::RefCell; +use rxml::{PushDriver, RawParser}; use std::str; /// Parser #[derive(Debug)] pub struct Parser { - buffer: RefCell, + driver: PushDriver, + tree_builder: TreeBuilder, state: ParserState, } @@ -90,14 +90,17 @@ impl Parser { /// Creates a new Parser pub fn new() -> Parser { Parser { - buffer: RefCell::new(BytesMut::new()), + driver: PushDriver::default(), + tree_builder: TreeBuilder::new(), state: ParserState::Empty, } } /// Feed bytes to the parser. pub fn feed(&mut self, bytes: BytesMut) -> Result<()> { - self.buffer.borrow_mut().unsplit(bytes); + self.driver.feed(bytes); + bytes.clear(); + let state = match self.state { ParserState::Empty => { // TODO: Try splitting xml prolog and stream header diff --git a/minidom/src/tests.rs b/minidom/src/tests.rs index 726379fdea6f3dfebaaaadbf313febe52bf6b3f2..63322e9b072979a4a0c765c0a8542d6513dbf072 100644 --- a/minidom/src/tests.rs +++ b/minidom/src/tests.rs @@ -13,9 +13,7 @@ use crate::element::Element; use crate::error::Error; -use quick_xml::Reader; - -const TEST_STRING: &'static str = r#"meownya"#; +const TEST_STRING: &'static [u8] = br#"meownya"#; fn build_test_tree() -> Element { let mut root = Element::builder("root", "root_ns") @@ -36,9 +34,8 @@ fn build_test_tree() -> Element { #[test] fn reader_works() { - let mut reader = Reader::from_str(TEST_STRING); assert_eq!( - Element::from_reader(&mut reader).unwrap(), + Element::from_reader(TEST_STRING).unwrap(), build_test_tree() ); } @@ -143,7 +140,7 @@ fn writer_works() { { root.write_to(&mut writer).unwrap(); } - assert_eq!(String::from_utf8(writer).unwrap(), TEST_STRING); + assert_eq!(writer, TEST_STRING); } #[test] @@ -153,7 +150,10 @@ fn writer_with_decl_works() { { root.write_to_decl(&mut writer).unwrap(); } - let result = format!(r#"{}"#, TEST_STRING); + let result = format!( + r#"{}"#, + String::from_utf8(TEST_STRING.to_owned()).unwrap() + ); assert_eq!(String::from_utf8(writer).unwrap(), result); } @@ -348,8 +348,7 @@ fn two_elements_with_same_arguments_different_order_are_equal() { #[test] fn namespace_attributes_works() { - let mut reader = Reader::from_str(TEST_STRING); - let root = Element::from_reader(&mut reader).unwrap(); + let root = Element::from_reader(TEST_STRING).unwrap(); assert_eq!("en", root.attr("xml:lang").unwrap()); assert_eq!( "fr", @@ -424,7 +423,7 @@ fn namespace_inherited_prefixed2() { fn fail_comments() { let elem: Result = "".parse(); match elem { - Err(Error::NoComments) => (), + Err(_) => (), _ => panic!(), }; } @@ -432,20 +431,16 @@ fn fail_comments() { #[test] fn xml_error() { match "".parse::() { - Err(crate::error::Error::XmlError(_)) => (), + Err(crate::error::Error::ParserError(rxml::Error::NotWellFormed( + rxml::error::WFError::ElementMismatch, + ))) => (), err => panic!("No or wrong error: {:?}", err), } match "() { - Err(crate::error::Error::XmlError(_)) => (), - err => panic!("No or wrong error: {:?}", err), - } -} - -#[test] -fn invalid_element_error() { - match "".parse::() { - Err(crate::error::Error::InvalidElement) => (), + Err(crate::error::Error::ParserError(rxml::Error::NotWellFormed( + rxml::error::WFError::InvalidEof(_), + ))) => (), err => panic!("No or wrong error: {:?}", err), } } diff --git a/minidom/src/tree_builder.rs b/minidom/src/tree_builder.rs new file mode 100644 index 0000000000000000000000000000000000000000..538fab6ae0f135e29166f3552a4c3ba5cfc73b1f --- /dev/null +++ b/minidom/src/tree_builder.rs @@ -0,0 +1,154 @@ +// Copyright (c) 2022 Astro + +//! SAX events to DOM tree conversion + +use crate::prefixes::{Prefix, Prefixes}; +use crate::{Element, Error}; +use rxml::RawEvent; +use std::collections::BTreeMap; + +/// Tree-building parser state +pub struct TreeBuilder { + next_tag: Option<(Prefix, String, Prefixes, BTreeMap)>, + /// Parsing stack + stack: Vec, + /// Namespace set stack by prefix + prefixes_stack: Vec, + /// Document root element if finished + pub root: Option, +} + +impl TreeBuilder { + /// Create a new one + pub fn new() -> Self { + TreeBuilder { + next_tag: None, + stack: vec![], + prefixes_stack: vec![], + root: None, + } + } + + /// Stack depth + pub fn depth(&self) -> usize { + self.stack.len() + } + + /// Get the top-most element from the stack but don't remove it + pub fn top(&mut self) -> Option<&Element> { + self.stack.last() + } + + /// Pop the top-most element from the stack + fn pop(&mut self) -> Option { + self.prefixes_stack.pop(); + self.stack.pop() + } + + /// Unshift the first child of the top element + pub fn unshift_child(&mut self) -> Option { + let depth = self.stack.len(); + if depth > 0 { + self.stack[depth - 1].unshift_child() + } else { + None + } + } + + /// Lookup XML namespace declaration for given prefix (or no prefix) + fn lookup_prefix(&self, prefix: &Option) -> Option<&str> { + for nss in self.prefixes_stack.iter().rev() { + if let Some(ns) = nss.get(prefix) { + return Some(ns); + } + } + + None + } + + fn process_end_tag(&mut self) -> Result<(), Error> { + if let Some(el) = self.pop() { + if self.depth() > 0 { + let top = self.stack.len() - 1; + self.stack[top].append_child(el); + } else { + self.root = Some(el); + } + } + + Ok(()) + } + + fn process_text(&mut self, text: String) { + if self.depth() > 0 { + let top = self.stack.len() - 1; + self.stack[top].append_text_node(text); + } + } + + /// Process a Event that you got out of a RawParser + pub fn process_event(&mut self, event: RawEvent) -> Result<(), Error> { + match event { + RawEvent::XMLDeclaration(_, _) => {} + + RawEvent::ElementHeadOpen(_, (prefix, name)) => { + self.next_tag = Some(( + prefix.map(|prefix| prefix.as_str().to_owned()), + name.as_str().to_owned(), + Prefixes::default(), + BTreeMap::new(), + )) + } + + RawEvent::Attribute(_, (prefix, name), value) => { + self.next_tag + .as_mut() + .map( + |(_, _, ref mut prefixes, ref mut attrs)| match (prefix, name) { + (None, xmlns) if xmlns == "xmlns" => { + prefixes.insert(None, value); + } + (Some(xmlns), prefix) if xmlns.as_str() == "xmlns" => { + prefixes.insert(Some(prefix.as_str().to_owned()), value); + } + (Some(prefix), name) => { + attrs.insert( + format!("{}:{}", prefix, name), + value.as_str().to_owned(), + ); + } + (None, name) => { + attrs.insert(name.as_str().to_owned(), value.as_str().to_owned()); + } + }, + ); + } + + RawEvent::ElementHeadClose(_) => { + if let Some((prefix, name, prefixes, attrs)) = self.next_tag.take() { + self.prefixes_stack.push(prefixes.clone()); + + let namespace = self + .lookup_prefix(&prefix.clone().map(|prefix| prefix.as_str().to_owned())) + .ok_or(Error::MissingNamespace)? + .to_owned(); + let el = Element::new( + name.as_str().to_owned(), + namespace, + Some(prefix.map(|prefix| prefix.as_str().to_owned())), + prefixes, + attrs, + vec![], + ); + self.stack.push(el); + } + } + + RawEvent::ElementFoot(_) => self.process_end_tag()?, + + RawEvent::Text(_, text) => self.process_text(text.as_str().to_owned()), + } + + Ok(()) + } +} diff --git a/parsers/src/caps.rs b/parsers/src/caps.rs index 9d4d2b40be59d40dea315862407c730c816956bc..6b01b4bda1b8cd55a1df0611b0054a0b35226458 100644 --- a/parsers/src/caps.rs +++ b/parsers/src/caps.rs @@ -265,8 +265,7 @@ mod tests { #[test] fn test_xep_5_2() { - let elem: Element = r#" - @@ -294,8 +293,7 @@ mod tests { #[test] fn test_xep_5_3() { - let elem: Element = r#" - diff --git a/parsers/src/cert_management.rs b/parsers/src/cert_management.rs index d93ebb375098b22adf3ea76204e20251c78584ce..0d7ed2f4edfd15182ce7a2b6ea834dde96ed049d 100644 --- a/parsers/src/cert_management.rs +++ b/parsers/src/cert_management.rs @@ -176,8 +176,7 @@ mod tests { #[test] fn list() { - let elem: Element = r#" - + let elem: Element = r#" Mobile Client AAAA diff --git a/parsers/src/ecaps2.rs b/parsers/src/ecaps2.rs index d5b6531d3282d30c7047d35a9a6245a682bfa73c..5f944d42aac47053e388f4eb79fb14f2d745e51f 100644 --- a/parsers/src/ecaps2.rs +++ b/parsers/src/ecaps2.rs @@ -236,8 +236,7 @@ mod tests { #[test] fn test_xep_ex1() { - let elem: Element = r#" - + let elem: Element = r#" @@ -307,8 +306,7 @@ mod tests { #[test] fn test_xep_ex2() { - let elem: Element = r#" - + let elem: Element = r#" diff --git a/parsers/src/ibr.rs b/parsers/src/ibr.rs index b0a2f85959de80aabb7167b6f0c70194da01c269..71ce324618fc0ca5cd97ba8bc8a44619c7a82d2a 100644 --- a/parsers/src/ibr.rs +++ b/parsers/src/ibr.rs @@ -137,8 +137,7 @@ mod tests { #[test] fn test_ex2() { - let elem: Element = r#" - + let elem: Element = r#" Choose a username and password for use with this service. Please also provide your email address. diff --git a/parsers/src/jingle_ft.rs b/parsers/src/jingle_ft.rs index d44b7e174438514e2d73c44839e3d885fbce9248..491028fb65ebdd72874393668508dd9c208839fc 100644 --- a/parsers/src/jingle_ft.rs +++ b/parsers/src/jingle_ft.rs @@ -354,8 +354,7 @@ mod tests { #[test] fn test_description() { - let elem: Element = r#" - + let elem: Element = r#" text/plain test.txt @@ -387,8 +386,7 @@ mod tests { #[test] fn test_request() { - let elem: Element = r#" - + let elem: Element = r#" w0mcJylzCn+AfvuGdqkty2+KP48= @@ -413,8 +411,7 @@ mod tests { #[test] fn test_descs() { - let elem: Element = r#" - + let elem: Element = r#" text/plain Fichier secret ! @@ -437,8 +434,7 @@ mod tests { Desc(String::from("Fichier secret !")) ); - let elem: Element = r#" - + let elem: Element = r#" text/plain Fichier secret ! diff --git a/parsers/src/jingle_grouping.rs b/parsers/src/jingle_grouping.rs index 01c882d9d867849f59736c3ecc260f93f168ba0d..9b578964b4ad6618b924ebe5f9e325c08302d959 100644 --- a/parsers/src/jingle_grouping.rs +++ b/parsers/src/jingle_grouping.rs @@ -72,8 +72,7 @@ mod tests { #[test] fn parse_group() { - let elem: Element = " - + let elem: Element = " " diff --git a/parsers/src/jingle_ice_udp.rs b/parsers/src/jingle_ice_udp.rs index f3db3aeadb1c7efc840f79912a0991d8999c97d8..fdda570b94a8a34a83137212409fec5412f07f10 100644 --- a/parsers/src/jingle_ice_udp.rs +++ b/parsers/src/jingle_ice_udp.rs @@ -134,8 +134,7 @@ mod tests { #[test] fn test_gajim() { - let elem: Element = " - + let elem: Element = " @@ -164,8 +163,7 @@ mod tests { #[test] fn test_jitsi_meet() { - let elem: Element = " - + let elem: Element = " 97:F2:B5:BE:DB:A6:00:B1:3E:40:B2:41:3C:0D:FC:E0:BD:B2:A0:E8 diff --git a/parsers/src/jingle_raw_udp.rs b/parsers/src/jingle_raw_udp.rs index d40bb68642d75cba023b108e956e2aa103bcc3ba..1e9202c7c2d4c80a272355206c0096ada26dd5fd 100644 --- a/parsers/src/jingle_raw_udp.rs +++ b/parsers/src/jingle_raw_udp.rs @@ -78,8 +78,7 @@ mod tests { #[test] fn example_1() { - let elem: Element = " - + let elem: Element = " diff --git a/parsers/src/jingle_rtp_hdrext.rs b/parsers/src/jingle_rtp_hdrext.rs index 23326649d015c0d0311b98c6a602355a39e00cdc..7aceca5890f0963a9f21a36c7f2ff2839c983a10 100644 --- a/parsers/src/jingle_rtp_hdrext.rs +++ b/parsers/src/jingle_rtp_hdrext.rs @@ -74,8 +74,7 @@ mod tests { #[test] fn parse_exthdr() { - let elem: Element = " - " @@ -124,8 +123,7 @@ mod tests { #[test] fn parse_source_group() { - let elem: Element = " - + let elem: Element = " " diff --git a/parsers/src/legacy_omemo.rs b/parsers/src/legacy_omemo.rs index 4b0eea45e2a6202534b3034919883330f8e7b0d3..ae7ace0a67ba0412d140bef2abf48e1529066081 100644 --- a/parsers/src/legacy_omemo.rs +++ b/parsers/src/legacy_omemo.rs @@ -186,8 +186,7 @@ mod tests { #[test] fn parse_bundle() { - let elem: Element = r#" - + let elem: Element = r#" BYAbACA15bPn95p7RGC2XbgQyly8aRKS4BaJ+hD8Ybhe sIJVNDZi/NgFsry4OBdM+adyGttLEXbUh/h/5dVOZveMgyVoIdgwBUzq8Wgd2xYTQMioNzwYebTX+9p0h9eujA== BQFd2p/Oq97vAAdLKA09DlcSg0x1xWn260p1jaeyIhAZ @@ -254,8 +253,7 @@ mod tests { } #[test] fn parse_device_list() { - let elem: Element = r#" - + let elem: Element = r#" @@ -275,8 +273,7 @@ mod tests { } #[test] fn parse_encrypted() { - let elem: Element = r#" - + let elem: Element = r#"
Mwjp9AESIQVylscLPpj/HlowaTiIsaBj73HCVEllXpVTtMG9EYwRexohBQFd2p/Oq97vAAdLKA09DlcSg0x1xWn260p1jaeyIhAZImIzCiEFhaQ4I+DuQgo6vCLCjHu4uewDZmWHuBl8uJw1IkyZxhUQABgAIjCoEVgVThWlaIlnN3V5Bg1hQX7OD1cvstLD5lH3zZMadL3KeONELESlBbeKmNgcYC/e3HZnbgWzBiic36yNAjAW MwohBTV6dpumL1OxA9MdIFmu2E19+cIWDHWYfhdubvo0hmh6EAAYHCIwNc9/59eeYi8pVZQhMJJMVkKUkFP/yrTfG3o1lfpHGseCqb/JTgtDytQPiYrTpHl2V/mdsM6IPig= diff --git a/parsers/src/mam.rs b/parsers/src/mam.rs index eff0065eb6400182bee80d8d121377610864a590..e36f2153462d36052f94070fd2a1491de7d698fe 100644 --- a/parsers/src/mam.rs +++ b/parsers/src/mam.rs @@ -123,8 +123,7 @@ mod tests { #[test] fn test_result() { #[cfg(not(feature = "component"))] - let elem: Element = r#" - + let elem: Element = r#" @@ -136,8 +135,7 @@ mod tests { .parse() .unwrap(); #[cfg(feature = "component")] - let elem: Element = r#" - + let elem: Element = r#" @@ -151,8 +149,7 @@ mod tests { #[test] fn test_fin() { - let elem: Element = r#" - + let elem: Element = r#" 28482-98726-73623 09af3-cc343-b409f @@ -166,8 +163,7 @@ mod tests { #[test] fn test_query_x() { - let elem: Element = r#" - + let elem: Element = r#" urn:xmpp:mam:2 @@ -185,8 +181,7 @@ mod tests { #[test] fn test_query_x_set() { - let elem: Element = r#" - + let elem: Element = r#" urn:xmpp:mam:2 diff --git a/parsers/src/mam_prefs.rs b/parsers/src/mam_prefs.rs index 04e86632a954d364eefd0ab2de3e5a52022cff26..76bfe8e9cbd40c37fed43a01ee81b74e766d434f 100644 --- a/parsers/src/mam_prefs.rs +++ b/parsers/src/mam_prefs.rs @@ -134,8 +134,7 @@ mod tests { assert!(prefs.always.is_empty()); assert!(prefs.never.is_empty()); - let elem: Element = r#" - + let elem: Element = r#" @@ -149,8 +148,7 @@ mod tests { #[test] fn test_prefs_result() { - let elem: Element = r#" - + let elem: Element = r#" romeo@montague.lit diff --git a/parsers/src/media_element.rs b/parsers/src/media_element.rs index e1d93383693784230ef927c4340eed6fe8c2d3bc..576bf9c17e1a2eac5968b038f69b9b3c5786e369 100644 --- a/parsers/src/media_element.rs +++ b/parsers/src/media_element.rs @@ -174,8 +174,7 @@ mod tests { #[test] fn test_xep_ex1() { - let elem: Element = r#" - + let elem: Element = r#" http://victim.example.com/challenges/speech.wav?F3A6292C @@ -211,8 +210,7 @@ mod tests { #[test] fn test_xep_ex2() { - let elem: Element = r#" - + let elem: Element = r#" [ ... ] " .parse() @@ -179,8 +178,7 @@ mod tests { assert_eq!(history.seconds, None); assert_eq!(history.since, None); - let elem: Element = " - + let elem: Element = " " .parse() diff --git a/parsers/src/muc/user.rs b/parsers/src/muc/user.rs index c608a83eeded430363d64869d1d0cf208555022f..f40521843c528818a01858314f129ee85152c882 100644 --- a/parsers/src/muc/user.rs +++ b/parsers/src/muc/user.rs @@ -239,25 +239,21 @@ mod tests { #[test] fn test_simple() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); MucUser::try_from(elem).unwrap(); } #[test] fn statuses_and_items() { - let elem: Element = " - + let elem: Element = " - - " - .parse() - .unwrap(); + " + .parse() + .unwrap(); let muc_user = MucUser::try_from(elem).unwrap(); assert_eq!(muc_user.status.len(), 2); assert_eq!(muc_user.status[0], Status::AffiliationChange); @@ -269,13 +265,11 @@ mod tests { #[test] fn test_invalid_child() { - let elem: Element = " - + let elem: Element = " - - " - .parse() - .unwrap(); + " + .parse() + .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -286,11 +280,9 @@ mod tests { #[test] fn test_serialise() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let muc = MucUser { status: vec![], items: vec![], @@ -302,11 +294,9 @@ mod tests { #[cfg(not(feature = "disable-validation"))] #[test] fn test_invalid_attribute() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = MucUser::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -317,21 +307,17 @@ mod tests { #[test] fn test_status_simple() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Status::try_from(elem).unwrap(); } #[test] fn test_status_invalid() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -343,13 +329,11 @@ mod tests { #[cfg(not(feature = "disable-validation"))] #[test] fn test_status_invalid_child() { - let elem: Element = " - + let elem: Element = " - - " - .parse() - .unwrap(); + " + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -360,22 +344,18 @@ mod tests { #[test] fn test_status_simple_code() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let status = Status::try_from(elem).unwrap(); assert_eq!(status, Status::Kicked); } #[test] fn test_status_invalid_code() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -386,11 +366,9 @@ mod tests { #[test] fn test_status_invalid_code2() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Status::try_from(elem).unwrap_err(); let error = match error { Error::ParseIntError(error) => error, @@ -401,11 +379,9 @@ mod tests { #[test] fn test_actor_required_attributes() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -416,13 +392,11 @@ mod tests { #[test] fn test_actor_required_attributes2() { - let elem: Element = " - " + .parse() + .unwrap(); let error = Actor::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -433,12 +407,10 @@ mod tests { #[test] fn test_actor_jid() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let actor = Actor::try_from(elem).unwrap(); let jid = match actor { Actor::Jid(jid) => jid, @@ -449,11 +421,9 @@ mod tests { #[test] fn test_actor_nick() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let actor = Actor::try_from(elem).unwrap(); let nick = match actor { Actor::Nick(nick) => nick, @@ -464,35 +434,29 @@ mod tests { #[test] fn test_continue_simple() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); Continue::try_from(elem).unwrap(); } #[test] fn test_continue_thread_attribute() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let continue_ = Continue::try_from(elem).unwrap(); assert_eq!(continue_.thread, Some("foo".to_owned())); } #[test] fn test_continue_invalid() { - let elem: Element = " - + let elem: Element = " - - " - .parse() - .unwrap(); + " + .parse() + .unwrap(); let continue_ = Continue::try_from(elem).unwrap_err(); let message = match continue_ { Error::ParseError(string) => string, @@ -503,8 +467,7 @@ mod tests { #[test] fn test_reason_simple() { - let elem: Element = " - Reason" + let elem: Element = "Reason" .parse() .unwrap(); let elem2 = elem.clone(); @@ -518,11 +481,9 @@ mod tests { #[cfg(not(feature = "disable-validation"))] #[test] fn test_reason_invalid_attribute() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -534,13 +495,11 @@ mod tests { #[cfg(not(feature = "disable-validation"))] #[test] fn test_reason_invalid() { - let elem: Element = " - + let elem: Element = " - - " - .parse() - .unwrap(); + " + .parse() + .unwrap(); let error = Reason::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -552,12 +511,10 @@ mod tests { #[cfg(not(feature = "disable-validation"))] #[test] fn test_item_invalid_attr() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -568,24 +525,20 @@ mod tests { #[test] fn test_item_affiliation_role_attr() { - let elem: Element = " - " + .parse() + .unwrap(); Item::try_from(elem).unwrap(); } #[test] fn test_item_affiliation_role_invalid_attr() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -596,14 +549,12 @@ mod tests { #[test] fn test_item_nick_attr() { - let elem: Element = " - " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { Item { nick, .. } => assert_eq!(nick, Some("foobar".to_owned())), @@ -612,12 +563,10 @@ mod tests { #[test] fn test_item_affiliation_role_invalid_attr2() { - let elem: Element = " - - " - .parse() - .unwrap(); + let elem: Element = "" + .parse() + .unwrap(); let error = Item::try_from(elem).unwrap_err(); let message = match error { Error::ParseError(string) => string, @@ -631,15 +580,13 @@ mod tests { #[test] fn test_item_role_actor_child() { - let elem: Element = " - " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { Item { actor, .. } => assert_eq!(actor, Some(Actor::Nick("foobar".to_owned()))), @@ -648,15 +595,13 @@ mod tests { #[test] fn test_item_role_continue_child() { - let elem: Element = " - " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); let continue_1 = Continue { thread: Some("foobar".to_owned()), @@ -672,15 +617,13 @@ mod tests { #[test] fn test_item_role_reason_child() { - let elem: Element = " - " + .parse() + .unwrap(); let item = Item::try_from(elem).unwrap(); match item { Item { reason, .. } => assert_eq!(reason, Some(Reason("foobar".to_owned()))), diff --git a/parsers/src/roster.rs b/parsers/src/roster.rs index 1695296181314154fea313ae684230c53da8a9b9..a461fcbcb5b0553bbdad6cf816b63d38aa2b4c99 100644 --- a/parsers/src/roster.rs +++ b/parsers/src/roster.rs @@ -142,8 +142,7 @@ mod tests { assert_eq!(roster.ver, Some(String::from("ver9"))); assert!(roster.items.is_empty()); - let elem: Element = r#" - + let elem: Element = r#" @@ -215,16 +214,14 @@ mod tests { assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); - let elem: Element = r#" - + let elem: Element = r#" Servants - -"# - .parse() - .unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); @@ -236,14 +233,12 @@ mod tests { Group::from_str("Servants").unwrap() ); - let elem: Element = r#" - + let elem: Element = r#" - -"# - .parse() - .unwrap(); +"# + .parse() + .unwrap(); let roster = Roster::try_from(elem).unwrap(); assert!(roster.ver.is_none()); assert_eq!(roster.items.len(), 1); diff --git a/parsers/src/sm.rs b/parsers/src/sm.rs index 315761e755e5d3fc217aa9bb6a09d07b673d8a05..04d477e3febbe0ca4104ec4eafee9cb2dc83d741 100644 --- a/parsers/src/sm.rs +++ b/parsers/src/sm.rs @@ -180,7 +180,7 @@ mod tests { #[test] fn a() { - let elem: Element = " for TimeResult { check_no_children!(child, "tzo"); check_no_attributes!(child, "tzo"); // TODO: Add a FromStr implementation to FixedOffset to avoid this hack. - let fake_date = String::from("2019-04-22T11:38:00") + &child.text(); + let fake_date = format!("{}{}", "2019-04-22T11:38:00", child.text()); let date_time = DateTime::from_str(&fake_date)?; tzo = Some(date_time.timezone()); } else if child.is("utc", ns::TIME) { diff --git a/tokio-xmpp/Cargo.toml b/tokio-xmpp/Cargo.toml index a80f7a01e61fc4711de1076861fdd4a22bebb624..4818ce60776ef3d67ef8770ce44001336f21cd18 100644 --- a/tokio-xmpp/Cargo.toml +++ b/tokio-xmpp/Cargo.toml @@ -25,8 +25,9 @@ tokio-stream = { version = "0.1", features = [] } tokio-util = { version = "0.6", features = ["codec"] } trust-dns-proto = "0.20" trust-dns-resolver = "0.20" -xml5ever = "0.16" xmpp-parsers = "0.19" +minidom = "0.14" +rxml = "^0.7.1" webpki-roots = { version = "0.22", optional = true } [build-dependencies] diff --git a/tokio-xmpp/src/client/async_client.rs b/tokio-xmpp/src/client/async_client.rs index bf6cd0e8268596f7626271e7015b6d24b917102e..dc629932ec0c74fa606bc3491fcc9524ce204822 100644 --- a/tokio-xmpp/src/client/async_client.rs +++ b/tokio-xmpp/src/client/async_client.rs @@ -203,30 +203,28 @@ impl Stream for Client { self.poll_next(cx) } ClientState::Disconnected => Poll::Ready(None), - ClientState::Connecting(mut connect) => { - match Pin::new(&mut connect).poll(cx) { - Poll::Ready(Ok(Ok(stream))) => { - let bound_jid = stream.jid.clone(); - self.state = ClientState::Connected(stream); - Poll::Ready(Some(Event::Online { - bound_jid, - resumed: false, - })) - } - Poll::Ready(Ok(Err(e))) => { - self.state = ClientState::Disconnected; - return Poll::Ready(Some(Event::Disconnected(e.into()))); - } - Poll::Ready(Err(e)) => { - self.state = ClientState::Disconnected; - panic!("connect task: {}", e); - } - Poll::Pending => { - self.state = ClientState::Connecting(connect); - Poll::Pending - } + ClientState::Connecting(mut connect) => match Pin::new(&mut connect).poll(cx) { + Poll::Ready(Ok(Ok(stream))) => { + let bound_jid = stream.jid.clone(); + self.state = ClientState::Connected(stream); + Poll::Ready(Some(Event::Online { + bound_jid, + resumed: false, + })) } - } + Poll::Ready(Ok(Err(e))) => { + self.state = ClientState::Disconnected; + return Poll::Ready(Some(Event::Disconnected(e.into()))); + } + Poll::Ready(Err(e)) => { + self.state = ClientState::Disconnected; + panic!("connect task: {}", e); + } + Poll::Pending => { + self.state = ClientState::Connecting(connect); + Poll::Pending + } + }, ClientState::Connected(mut stream) => { // Poll sink match Pin::new(&mut stream).poll_ready(cx) { diff --git a/tokio-xmpp/src/error.rs b/tokio-xmpp/src/error.rs index 0efcd66306fcbee23cf1cdf692b203ca9705294e..0818dab84131d38f2801e45c2958c4080a3f501f 100644 --- a/tokio-xmpp/src/error.rs +++ b/tokio-xmpp/src/error.rs @@ -5,7 +5,6 @@ use std::borrow::Cow; use std::error::Error as StdError; use std::fmt; use std::io::Error as IoError; -use std::str::Utf8Error; #[cfg(feature = "tls-rust")] use tokio_rustls::rustls::client::InvalidDnsNameError; #[cfg(feature = "tls-rust")] @@ -106,44 +105,6 @@ impl From for Error { } } -/// Causes for stream parsing errors -#[derive(Debug)] -pub enum ParserError { - /// Encoding error - Utf8(Utf8Error), - /// XML parse error - Parse(ParseError), - /// Illegal `` - ShortTag, - /// Required by `impl Decoder` - Io(IoError), -} - -impl fmt::Display for ParserError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - ParserError::Utf8(e) => write!(fmt, "UTF-8 error: {}", e), - ParserError::Parse(e) => write!(fmt, "parse error: {}", e), - ParserError::ShortTag => write!(fmt, "short tag"), - ParserError::Io(e) => write!(fmt, "IO error: {}", e), - } - } -} - -impl StdError for ParserError {} - -impl From for ParserError { - fn from(e: IoError) -> Self { - ParserError::Io(e) - } -} - -impl From for Error { - fn from(e: ParserError) -> Self { - ProtocolError::Parser(e).into() - } -} - /// XML parse error wrapper type #[derive(Debug)] pub struct ParseError(pub Cow<'static, str>); @@ -167,7 +128,7 @@ impl fmt::Display for ParseError { #[derive(Debug)] pub enum ProtocolError { /// XML parser error - Parser(ParserError), + Parser(minidom::Error), /// Error with expected stanza schema Parsers(ParsersError), /// No TLS available @@ -205,12 +166,18 @@ impl fmt::Display for ProtocolError { impl StdError for ProtocolError {} -impl From for ProtocolError { - fn from(e: ParserError) -> Self { +impl From for ProtocolError { + fn from(e: minidom::Error) -> Self { ProtocolError::Parser(e) } } +impl From for Error { + fn from(e: minidom::Error) -> Self { + ProtocolError::Parser(e).into() + } +} + impl From for ProtocolError { fn from(e: ParsersError) -> Self { ProtocolError::Parsers(e) diff --git a/tokio-xmpp/src/lib.rs b/tokio-xmpp/src/lib.rs index 05cfc555a272330f42f018735ed4bf3e9c14e8cd..aaf65b24b7e87adfa7ca3790ad1203e289efcade 100644 --- a/tokio-xmpp/src/lib.rs +++ b/tokio-xmpp/src/lib.rs @@ -16,5 +16,5 @@ pub use client::{async_client::Client as AsyncClient, simple_client::Client as S mod component; pub use crate::component::Component; mod error; -pub use crate::error::{AuthError, ConnecterError, Error, ParseError, ParserError, ProtocolError}; +pub use crate::error::{AuthError, ConnecterError, Error, ParseError, ProtocolError}; pub use starttls::starttls; diff --git a/tokio-xmpp/src/xmpp_codec.rs b/tokio-xmpp/src/xmpp_codec.rs index 6c6b92cc2da87d7e83de4915957f642ce74ff38d..028dcbde54d3e93d7f4b8d2d1fdcc9b4d02fd99c 100644 --- a/tokio-xmpp/src/xmpp_codec.rs +++ b/tokio-xmpp/src/xmpp_codec.rs @@ -1,23 +1,16 @@ //! XML stream parser for XMPP -use crate::{ParseError, ParserError}; +use crate::Error; use bytes::{BufMut, BytesMut}; -use log::{debug, error}; +use log::debug; +use minidom::tree_builder::TreeBuilder; +use rxml::{Lexer, PushDriver, RawParser}; use std; -use std::borrow::Cow; -use std::collections::vec_deque::VecDeque; use std::collections::HashMap; use std::default::Default; use std::fmt::Write; use std::io; -use std::iter::FromIterator; -use std::str::from_utf8; -use std::sync::Arc; -use std::sync::Mutex; use tokio_util::codec::{Decoder, Encoder}; -use xml5ever::buffer_queue::BufferQueue; -use xml5ever::interface::Attribute; -use xml5ever::tokenizer::{Tag, TagKind, Token, TokenSink, XmlTokenizer}; use xmpp_parsers::Element; /// Anything that can be sent or received on an XMPP/XML stream @@ -33,175 +26,24 @@ pub enum Packet { StreamEnd, } -type QueueItem = Result; - -/// Parser state -struct ParserSink { - // Ready stanzas, shared with XMPPCodec - queue: Arc>>, - // Parsing stack - stack: Vec, - ns_stack: Vec, String>>, -} - -impl ParserSink { - pub fn new(queue: Arc>>) -> Self { - ParserSink { - queue, - stack: vec![], - ns_stack: vec![], - } - } - - fn push_queue(&self, pkt: Packet) { - self.queue.lock().unwrap().push_back(Ok(pkt)); - } - - fn push_queue_error(&self, e: ParserError) { - self.queue.lock().unwrap().push_back(Err(e)); - } - - /// Lookup XML namespace declaration for given prefix (or no prefix) - fn lookup_ns(&self, prefix: &Option) -> Option<&str> { - for nss in self.ns_stack.iter().rev() { - if let Some(ns) = nss.get(prefix) { - return Some(ns); - } - } - - None - } - - fn handle_start_tag(&mut self, tag: Tag) { - let mut nss = HashMap::new(); - let is_prefix_xmlns = |attr: &Attribute| { - attr.name - .prefix - .as_ref() - .map(|prefix| prefix.eq_str_ignore_ascii_case("xmlns")) - .unwrap_or(false) - }; - for attr in &tag.attrs { - match attr.name.local.as_ref() { - "xmlns" => { - nss.insert(None, attr.value.as_ref().to_owned()); - } - prefix if is_prefix_xmlns(attr) => { - nss.insert(Some(prefix.to_owned()), attr.value.as_ref().to_owned()); - } - _ => (), - } - } - self.ns_stack.push(nss); - - let el = { - let el_ns = self - .lookup_ns(&tag.name.prefix.map(|prefix| prefix.as_ref().to_owned())) - .unwrap(); - let mut el_builder = Element::builder(tag.name.local.as_ref(), el_ns); - for attr in &tag.attrs { - match attr.name.local.as_ref() { - "xmlns" => (), - _ if is_prefix_xmlns(attr) => (), - _ => { - let attr_name = if let Some(ref prefix) = attr.name.prefix { - Cow::Owned(format!("{}:{}", prefix, attr.name.local)) - } else { - Cow::Borrowed(attr.name.local.as_ref()) - }; - el_builder = el_builder.attr(attr_name, attr.value.as_ref()); - } - } - } - el_builder.build() - }; - - if self.stack.is_empty() { - let attrs = HashMap::from_iter(tag.attrs.iter().map(|attr| { - ( - attr.name.local.as_ref().to_owned(), - attr.value.as_ref().to_owned(), - ) - })); - self.push_queue(Packet::StreamStart(attrs)); - } - - self.stack.push(el); - } - - fn handle_end_tag(&mut self) { - let el = self.stack.pop().unwrap(); - self.ns_stack.pop(); - - match self.stack.len() { - // - 0 => self.push_queue(Packet::StreamEnd), - // - 1 => self.push_queue(Packet::Stanza(el)), - len => { - let parent = &mut self.stack[len - 1]; - parent.append_child(el); - } - } - } -} - -impl TokenSink for ParserSink { - fn process_token(&mut self, token: Token) { - match token { - Token::TagToken(tag) => match tag.kind { - TagKind::StartTag => self.handle_start_tag(tag), - TagKind::EndTag => self.handle_end_tag(), - TagKind::EmptyTag => { - self.handle_start_tag(tag); - self.handle_end_tag(); - } - TagKind::ShortTag => self.push_queue_error(ParserError::ShortTag), - }, - Token::CharacterTokens(tendril) => match self.stack.len() { - 0 | 1 => self.push_queue(Packet::Text(tendril.into())), - len => { - let el = &mut self.stack[len - 1]; - el.append_text_node(tendril); - } - }, - Token::EOFToken => self.push_queue(Packet::StreamEnd), - Token::ParseError(s) => { - self.push_queue_error(ParserError::Parse(ParseError(s))); - } - _ => (), - } - } - - // fn end(&mut self) { - // } -} - /// Stateful encoder/decoder for a bytestream from/to XMPP `Packet` pub struct XMPPCodec { /// Outgoing ns: Option, /// Incoming - parser: XmlTokenizer, - /// For handling incoming truncated utf8 - // TODO: optimize using tendrils? - buf: Vec, - /// Shared with ParserSink - queue: Arc>>, + driver: PushDriver, + stanza_builder: TreeBuilder, } impl XMPPCodec { /// Constructor pub fn new() -> Self { - let queue = Arc::new(Mutex::new(VecDeque::new())); - let sink = ParserSink::new(queue.clone()); - // TODO: configure parser? - let parser = XmlTokenizer::new(sink, Default::default()); + let stanza_builder = TreeBuilder::new(); + let driver = PushDriver::wrap(Lexer::new(), RawParser::new()); XMPPCodec { ns: None, - parser, - queue, - buf: vec![], + driver, + stanza_builder, } } } @@ -214,57 +56,53 @@ impl Default for XMPPCodec { impl Decoder for XMPPCodec { type Item = Packet; - type Error = ParserError; + type Error = Error; fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { - let buf1: Box> = if !self.buf.is_empty() && !buf.is_empty() { - let mut prefix = std::mem::replace(&mut self.buf, vec![]); - prefix.extend_from_slice(&buf.split_to(buf.len())); - Box::new(prefix) - } else { - Box::new(buf.split_to(buf.len())) - }; - let buf1 = buf1.as_ref().as_ref(); - match from_utf8(buf1) { - Ok(s) => { - debug!("<< {:?}", s); - if !s.is_empty() { - let mut buffer_queue = BufferQueue::new(); - let tendril = FromIterator::from_iter(s.chars()); - buffer_queue.push_back(tendril); - self.parser.feed(&mut buffer_queue); + loop { + let token = match self.driver.parse(buf, false) { + Ok(Some(token)) => token, + Ok(None) => break, + Err(rxml::Error::IO(e)) if e.kind() == std::io::ErrorKind::WouldBlock => break, + Err(e) => return Err(minidom::Error::from(e).into()), + }; + + let had_stream_root = self.stanza_builder.depth() > 0; + self.stanza_builder.process_event(token)?; + let has_stream_root = self.stanza_builder.depth() > 0; + + if !had_stream_root && has_stream_root { + let root = self.stanza_builder.top().unwrap(); + let attrs = + root.attrs() + .map(|(name, value)| (name.to_owned(), value.to_owned())) + .chain(root.prefixes.declared_prefixes().iter().map( + |(prefix, namespace)| { + ( + prefix + .as_ref() + .map(|prefix| format!("xmlns:{}", prefix)) + .unwrap_or_else(|| "xmlns".to_owned()), + namespace.clone(), + ) + }, + )) + .collect(); + return Ok(Some(Packet::StreamStart(attrs))); + } else if self.stanza_builder.depth() == 1 { + self.driver.release_temporaries(); + + if let Some(stanza) = self.stanza_builder.unshift_child() { + return Ok(Some(Packet::Stanza(stanza))); } - } - // Remedies for truncated utf8 - Err(e) if e.valid_up_to() >= buf1.len() - 3 => { - // Prepare all the valid data - let mut b = BytesMut::with_capacity(e.valid_up_to()); - b.put(&buf1[0..e.valid_up_to()]); + } else if let Some(_) = self.stanza_builder.root.take() { + self.driver.release_temporaries(); - // Retry - let result = self.decode(&mut b); - - // Keep the tail back in - self.buf.extend_from_slice(&buf1[e.valid_up_to()..]); - - return result; - } - Err(e) => { - error!( - "error {} at {}/{} in {:?}", - e, - e.valid_up_to(), - buf1.len(), - buf1 - ); - return Err(ParserError::Utf8(e)); + return Ok(Some(Packet::StreamEnd)); } } - match self.queue.lock().unwrap().pop_front() { - None => Ok(None), - Some(result) => result.map(|pkt| Some(pkt)), - } + Ok(None) } fn decode_eof(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { @@ -392,7 +230,6 @@ mod tests { Ok(Some(Packet::StreamStart(_))) => true, _ => false, }); - b.clear(); b.put_slice(b""); let r = c.decode(&mut b); assert!(match r { @@ -412,7 +249,6 @@ mod tests { _ => false, }); - b.clear(); b.put_slice("ß false, }); - b.clear(); b.put_slice(b">"); let r = c.decode(&mut b); assert!(match r { @@ -440,7 +275,6 @@ mod tests { _ => false, }); - b.clear(); b.put(&b"\xc3"[..]); let r = c.decode(&mut b); assert!(match r { @@ -448,7 +282,6 @@ mod tests { _ => false, }); - b.clear(); b.put(&b"\x9f"[..]); let r = c.decode(&mut b); assert!(match r { @@ -469,7 +302,6 @@ mod tests { _ => false, }); - b.clear(); b.put_slice(b"Test status"); let r = c.decode(&mut b); assert!(match r { @@ -503,8 +335,11 @@ mod tests { block_on(framed.send(Packet::Stanza(stanza))).expect("send"); assert_eq!( framed.get_ref().get_ref(), - &("".to_owned() + &text + "") - .as_bytes() + &format!( + "{}", + text + ) + .as_bytes() ); } @@ -519,7 +354,6 @@ mod tests { _ => false, }); - b.clear(); b.put_slice(b"Foo"); let r = c.decode(&mut b); diff --git a/tokio-xmpp/src/xmpp_stream.rs b/tokio-xmpp/src/xmpp_stream.rs index 54b3821e7311d5eed5e832204c5c94f93dca2219..1d474d7b3246e03a253adbf4b4523534ca98c863 100644 --- a/tokio-xmpp/src/xmpp_stream.rs +++ b/tokio-xmpp/src/xmpp_stream.rs @@ -54,7 +54,7 @@ impl XMPPStream { } /// Send a `` start tag - pub async fn start<'a>(stream: S, jid: Jid, ns: String) -> Result { + pub async fn start(stream: S, jid: Jid, ns: String) -> Result { let xmpp_stream = Framed::new(stream, XMPPCodec::new()); stream_start::start(xmpp_stream, jid, ns).await } @@ -65,7 +65,7 @@ impl XMPPStream { } /// Re-run `start()` - pub async fn restart<'a>(self) -> Result { + pub async fn restart(self) -> Result { let stream = self.stream.into_inner().unwrap().into_inner(); Self::start(stream, self.jid, self.ns).await }