//! XML stream parser for XMPP
use std;
use std::default::Default;
use std::iter::FromIterator;
use std::cell::RefCell;
use std::rc::Rc;
use std::fmt::Write;
use std::str::{from_utf8, Utf8Error};
use std::io;
use std::collections::HashMap;
use std::collections::vec_deque::VecDeque;
use std::error::Error as StdError;
use std::fmt;
use std::borrow::Cow;
use tokio_codec::{Encoder, Decoder};
use minidom::Element;
use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind};
use xml5ever::interface::Attribute;
use bytes::{BytesMut, BufMut};
use quick_xml::Writer as EventWriter;
/// Anything that can be sent or received on an XMPP/XML stream
#[derive(Debug)]
pub enum Packet {
/// `` start tag
StreamStart(HashMap),
/// A complete stanza or nonza
Stanza(Element),
/// Plain text (think whitespace keep-alive)
Text(String),
/// `` closing tag
StreamEnd,
}
/// Causes for stream parsing errors
#[derive(Debug)]
pub enum ParserError {
/// Encoding error
Utf8(Utf8Error),
/// XML parse error
Parse(Cow<'static, str>),
/// Illegal `>`
ShortTag,
/// Required by `impl Decoder`
IO(io::Error),
}
impl From for ParserError {
fn from(e: io::Error) -> Self {
ParserError::IO(e)
}
}
impl StdError for ParserError {
fn description(&self) -> &str {
match *self {
ParserError::Utf8(ref ue) => ue.description(),
ParserError::Parse(ref pe) => pe,
ParserError::ShortTag => "short tag",
ParserError::IO(ref ie) => ie.description(),
}
}
fn cause(&self) -> Option<&StdError> {
match *self {
ParserError::Utf8(ref ue) => ue.cause(),
ParserError::IO(ref ie) => ie.cause(),
_ => None,
}
}
}
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParserError::Utf8(ref ue) => write!(f, "{}", ue),
ParserError::Parse(ref pe) => write!(f, "{}", pe),
ParserError::ShortTag => write!(f, "Short tag"),
ParserError::IO(ref ie) => write!(f, "{}", ie),
}
}
}
type QueueItem = Result;
/// Parser state
struct ParserSink {
// Ready stanzas, shared with XMPPCodec
queue: Rc>>,
// Parsing stack
stack: Vec,
ns_stack: Vec, String>>,
}
impl ParserSink {
pub fn new(queue: Rc>>) -> Self {
ParserSink {
queue,
stack: vec![],
ns_stack: vec![],
}
}
fn push_queue(&self, pkt: Packet) {
self.queue.borrow_mut().push_back(Ok(pkt));
}
fn push_queue_error(&self, e: ParserError) {
self.queue.borrow_mut().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 mut el_builder = Element::builder(tag.name.local.as_ref());
if let Some(el_ns) = self.lookup_ns(
&tag.name.prefix.map(|prefix|
prefix.as_ref().to_owned())
) {
el_builder = el_builder.ns(el_ns);
}
for attr in &tag.attrs {
match attr.name.local.as_ref() {
"xmlns" => (),
_ if is_prefix_xmlns(attr) => (),
_ => {
el_builder = el_builder.attr(
attr.name.local.as_ref(),
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) => {
// println!("ParseError: {:?}", s);
self.push_queue_error(ParserError::Parse(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: Rc>>,
}
impl XMPPCodec {
/// Constructor
pub fn new() -> Self {
let queue = Rc::new(RefCell::new(VecDeque::new()));
let sink = ParserSink::new(queue.clone());
// TODO: configure parser?
let parser = XmlTokenizer::new(sink, Default::default());
XMPPCodec {
ns: None,
parser,
queue,
buf: vec![],
}
}
}
impl Default for XMPPCodec {
fn default() -> Self {
Self::new()
}
}
impl Decoder for XMPPCodec {
type Item = Packet;
type Error = ParserError;
fn decode(&mut self, buf: &mut BytesMut) -> Result