//! XML stream parser for XMPP
use crate::{ParseError, ParserError};
use bytes::{BufMut, BytesMut};
use log::{debug, error};
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
#[derive(Debug, Clone, PartialEq, Eq)]
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,
}
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>>,
}
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());
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