From 4f31727a1ad47bc461c009caf81b47c04d7b56c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 19 Apr 2017 21:52:14 +0100 Subject: [PATCH] Add an In-Band Bytestream parser. --- src/ibb.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/ns.rs | 1 + 3 files changed, 124 insertions(+) create mode 100644 src/ibb.rs diff --git a/src/ibb.rs b/src/ibb.rs new file mode 100644 index 00000000..1868772e --- /dev/null +++ b/src/ibb.rs @@ -0,0 +1,122 @@ +use std::str::FromStr; + +use minidom::Element; + +use error::Error; + +use ns::IBB_NS; + +#[derive(Debug)] +pub enum Stanza { + Iq, + Message, +} + +impl Default for Stanza { + fn default() -> Stanza { + Stanza::Iq + } +} + +impl FromStr for Stanza { + type Err = Error; + + fn from_str(s: &str) -> Result { + if s == "iq" { + Ok(Stanza::Iq) + } else if s == "message" { + Ok(Stanza::Message) + } else { + Err(Error::ParseError("Unknown 'stanza' attribute.")) + } + } +} + +#[derive(Debug)] +pub enum IBB { + Open { block_size: u16, sid: String, stanza: Stanza }, + Data(u16, String, Vec), + Close(String), +} + +fn optional_attr(root: &Element, attr: &str) -> Option { + root.attr(attr) + .and_then(|value| value.parse().ok()) +} + +fn required_attr(root: &Element, attr: &str, err: Error) -> Result { + optional_attr(root, attr).ok_or(err) +} + +pub fn parse_ibb(root: &Element) -> Result { + if root.is("open", IBB_NS) { + let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?; + let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?; + let stanza = root.attr("stanza") + .and_then(|value| value.parse().ok()) + .unwrap_or(Default::default()); + for _ in root.children() { + return Err(Error::ParseError("Unknown child in open element.")); + } + Ok(IBB::Open { + block_size: block_size, + sid: sid, + stanza: stanza + }) + } else { + Err(Error::ParseError("Unknown ibb element.")) + } +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use ibb; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + ibb::parse_ibb(&elem).unwrap(); + } + + #[test] + fn test_invalid() { + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in open element."); + + // TODO: maybe make a better error message here. + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'block-size' missing in open element."); + + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Required attribute 'sid' missing in open element."); + } + + #[test] + #[ignore] + fn test_invalid_stanza() { + let elem: Element = "".parse().unwrap(); + let error = ibb::parse_ibb(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong value for 'stanza' attribute in open."); + } +} diff --git a/src/lib.rs b/src/lib.rs index 62996e9c..f7c964b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,3 +10,4 @@ pub mod ecaps2; pub mod jingle; pub mod ping; pub mod chatstates; +pub mod ibb; diff --git a/src/ns.rs b/src/ns.rs index 84ca86e9..24b81133 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -4,3 +4,4 @@ pub const MEDIA_ELEMENT_NS: &'static str = "urn:xmpp:media-element"; pub const JINGLE_NS: &'static str = "urn:xmpp:jingle:1"; pub const PING_NS: &'static str = "urn:xmpp:ping"; pub const CHATSTATES_NS: &'static str = "http://jabber.org/protocol/chatstates"; +pub const IBB_NS: &'static str = "http://jabber.org/protocol/ibb";