make it work with minidom

This commit is contained in:
Astro 2017-07-18 20:12:17 +02:00
parent 36d7ab474e
commit b944ca4ac7

View file

@ -9,8 +9,9 @@ use std::io::{Error, ErrorKind};
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::vec_deque::VecDeque; use std::collections::vec_deque::VecDeque;
use tokio_io::codec::{Encoder, Decoder}; use tokio_io::codec::{Encoder, Decoder};
use minidom::Element; use minidom::{Element, Node};
use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind}; use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind};
use xml5ever::interface::Attribute;
use bytes::*; use bytes::*;
// const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/"; // const NS_XMLNS: &'static str = "http://www.w3.org/2000/xmlns/";
@ -25,10 +26,11 @@ pub enum Packet {
} }
struct ParserSink { struct ParserSink {
// Ready stanzas // Ready stanzas, shared with XMPPCodec
queue: Rc<RefCell<VecDeque<Packet>>>, queue: Rc<RefCell<VecDeque<Packet>>>,
// Parsing stack // Parsing stack
stack: Vec<Element>, stack: Vec<Element>,
ns_stack: Vec<HashMap<Option<String>, String>>,
} }
impl ParserSink { impl ParserSink {
@ -36,21 +38,79 @@ impl ParserSink {
ParserSink { ParserSink {
queue, queue,
stack: vec![], stack: vec![],
ns_stack: vec![],
} }
} }
fn push_queue(&self, pkt: Packet) { fn push_queue(&self, pkt: Packet) {
println!("push: {:?}", pkt);
self.queue.borrow_mut().push_back(pkt); self.queue.borrow_mut().push_back(pkt);
} }
fn lookup_ns(&self, prefix: &Option<String>) -> Option<&str> {
for nss in self.ns_stack.iter().rev() {
match nss.get(prefix) {
Some(ns) => return Some(ns),
None => (),
}
}
None
}
fn handle_start_tag(&mut self, tag: Tag) { fn handle_start_tag(&mut self, tag: Tag) {
let el = tag_to_element(&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());
match self.lookup_ns(&tag.name.prefix.map(|prefix| prefix.as_ref().to_owned())) {
Some(el_ns) => el_builder = el_builder.ns(el_ns),
None => (),
}
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(
el.attrs()
.map(|(name, value)| (name.to_owned(), value.to_owned()))
);
self.push_queue(Packet::StreamStart(attrs));
}
self.stack.push(el); self.stack.push(el);
} }
fn handle_end_tag(&mut self) { fn handle_end_tag(&mut self) {
let el = self.stack.pop().unwrap(); let el = self.stack.pop().unwrap();
self.ns_stack.pop();
match self.stack.len() { match self.stack.len() {
// </stream:stream> // </stream:stream>
0 => 0 =>
@ -66,22 +126,8 @@ impl ParserSink {
} }
} }
fn tag_to_element(tag: &Tag) -> Element {
let el_builder = Element::builder(tag.name.local.as_ref())
.ns(tag.name.ns.as_ref());
let el_builder = tag.attrs.iter().fold(
el_builder,
|el_builder, attr| el_builder.attr(
attr.name.local.as_ref(),
attr.value.as_ref()
)
);
el_builder.build()
}
impl TokenSink for ParserSink { impl TokenSink for ParserSink {
fn process_token(&mut self, token: Token) { fn process_token(&mut self, token: Token) {
println!("Token: {:?}", token);
match token { match token {
Token::TagToken(tag) => match tag.kind { Token::TagToken(tag) => match tag.kind {
TagKind::StartTag => TagKind::StartTag =>
@ -119,9 +165,14 @@ impl TokenSink for ParserSink {
} }
pub struct XMPPCodec { pub struct XMPPCodec {
/// Outgoing
ns: Option<String>,
/// Incoming
parser: XmlTokenizer<ParserSink>, parser: XmlTokenizer<ParserSink>,
// For handling truncated utf8 /// For handling incoming truncated utf8
// TODO: optimize using tendrils?
buf: Vec<u8>, buf: Vec<u8>,
/// Shared with ParserSink
queue: Rc<RefCell<VecDeque<Packet>>>, queue: Rc<RefCell<VecDeque<Packet>>>,
} }
@ -132,6 +183,7 @@ impl XMPPCodec {
// TODO: configure parser? // TODO: configure parser?
let parser = XmlTokenizer::new(sink, Default::default()); let parser = XmlTokenizer::new(sink, Default::default());
XMPPCodec { XMPPCodec {
ns: None,
parser, parser,
queue, queue,
buf: vec![], buf: vec![],
@ -144,7 +196,6 @@ impl Decoder for XMPPCodec {
type Error = Error; type Error = Error;
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> { fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
println!("decode {} bytes", buf.len());
let buf1: Box<AsRef<[u8]>> = let buf1: Box<AsRef<[u8]>> =
if self.buf.len() > 0 && buf.len() > 0 { if self.buf.len() > 0 && buf.len() > 0 {
let mut prefix = std::mem::replace(&mut self.buf, vec![]); let mut prefix = std::mem::replace(&mut self.buf, vec![]);
@ -196,32 +247,39 @@ impl Encoder for XMPPCodec {
type Error = Error; type Error = Error;
fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> { fn encode(&mut self, item: Self::Item, dst: &mut BytesMut) -> Result<(), Self::Error> {
println!("encode {:?}", item);
match item { match item {
Packet::StreamStart(start_attrs) => { Packet::StreamStart(start_attrs) => {
let mut buf = String::new(); let mut buf = String::new();
write!(buf, "<stream:stream").unwrap(); write!(buf, "<stream:stream").unwrap();
for (ref name, ref value) in &start_attrs { for (name, value) in start_attrs.into_iter() {
write!(buf, " {}=\"{}\"", escape(&name), escape(&value)) write!(buf, " {}=\"{}\"", escape(&name), escape(&value))
.unwrap(); .unwrap();
if name == "xmlns" {
self.ns = Some(value);
}
} }
write!(buf, ">\n").unwrap(); write!(buf, ">\n").unwrap();
print!(">> {}", buf); print!(">> {}", buf);
write!(dst, "{}", buf) write!(dst, "{}", buf)
.map_err(|_| Error::from(ErrorKind::InvalidInput)) .map_err(|e| Error::new(ErrorKind::InvalidInput, e))
}, },
Packet::Stanza(stanza) => { Packet::Stanza(stanza) => {
println!(">> {:?}", stanza); let root_ns = self.ns.as_ref().map(|s| s.as_ref());
let mut root_ns = None; // TODO write_element(&stanza, dst, root_ns)
stanza.write_to_inner(&mut dst.clone().writer(), &mut root_ns) .and_then(|_| {
.map_err(|_| Error::from(ErrorKind::InvalidInput)) println!(">> {:?}", dst);
Ok(())
})
.map_err(|e| Error::new(ErrorKind::InvalidInput, format!("{}", e)))
}, },
Packet::Text(text) => { Packet::Text(text) => {
let escaped = escape(&text); write_text(&text, dst)
println!(">> {}", escaped); .and_then(|_| {
write!(dst, "{}", escaped) println!(">> {:?}", dst);
.map_err(|_| Error::from(ErrorKind::InvalidInput)) Ok(())
})
.map_err(|e| Error::new(ErrorKind::InvalidInput, format!("{}", e)))
}, },
// TODO: Implement all // TODO: Implement all
_ => Ok(()) _ => Ok(())
@ -229,6 +287,45 @@ impl Encoder for XMPPCodec {
} }
} }
pub fn write_text<W: Write>(text: &str, writer: &mut W) -> Result<(), std::fmt::Error> {
write!(writer, "{}", text)
}
// TODO: escape everything?
pub fn write_element<W: Write>(el: &Element, writer: &mut W, parent_ns: Option<&str>) -> Result<(), std::fmt::Error> {
write!(writer, "<")?;
write!(writer, "{}", el.name())?;
if let Some(ref ns) = el.ns() {
if parent_ns.map(|s| s.as_ref()) != el.ns() {
write!(writer, " xmlns=\"{}\"", ns)?;
}
}
for (key, value) in el.attrs() {
write!(writer, " {}=\"{}\"", key, value)?;
}
if ! el.nodes().any(|_| true) {
write!(writer, " />")?;
return Ok(())
}
write!(writer, ">")?;
for node in el.nodes() {
match node {
&Node::Element(ref child) =>
write_element(child, writer, el.ns())?,
&Node::Text(ref text) =>
write_text(text, writer)?,
}
}
write!(writer, "</{}>", el.name())?;
Ok(())
}
/// Copied from RustyXML for now /// Copied from RustyXML for now
pub fn escape(input: &str) -> String { pub fn escape(input: &str) -> String {
let mut result = String::with_capacity(input.len()); let mut result = String::with_capacity(input.len());