Add an In-Band Bytestream parser.

Emmanuel Gil Peyrot created

Change summary

src/ibb.rs | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/lib.rs |   1 
src/ns.rs  |   1 
3 files changed, 124 insertions(+)

Detailed changes

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<Stanza, Error> {
+        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<u8>),
+    Close(String),
+}
+
+fn optional_attr<T: FromStr>(root: &Element, attr: &str) -> Option<T> {
+    root.attr(attr)
+        .and_then(|value| value.parse().ok())
+}
+
+fn required_attr<T: FromStr>(root: &Element, attr: &str, err: Error) -> Result<T, Error> {
+    optional_attr(root, attr).ok_or(err)
+}
+
+pub fn parse_ibb(root: &Element) -> Result<IBB, Error> {
+    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 = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou'/>".parse().unwrap();
+        ibb::parse_ibb(&elem).unwrap();
+    }
+
+    #[test]
+    fn test_invalid() {
+        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>".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 = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>".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 = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>".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 = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".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.");
+    }
+}

src/lib.rs 🔗

@@ -10,3 +10,4 @@ pub mod ecaps2;
 pub mod jingle;
 pub mod ping;
 pub mod chatstates;
+pub mod ibb;

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";