ibb.rs

  1use std::str::FromStr;
  2
  3use minidom::Element;
  4use base64;
  5
  6use error::Error;
  7
  8use ns;
  9
 10#[derive(Debug, Clone)]
 11pub enum Stanza {
 12    Iq,
 13    Message,
 14}
 15
 16impl Default for Stanza {
 17    fn default() -> Stanza {
 18        Stanza::Iq
 19    }
 20}
 21
 22impl FromStr for Stanza {
 23    type Err = Error;
 24
 25    fn from_str(s: &str) -> Result<Stanza, Error> {
 26        if s == "iq" {
 27            Ok(Stanza::Iq)
 28        } else if s == "message" {
 29            Ok(Stanza::Message)
 30        } else {
 31            Err(Error::ParseError("Unknown 'stanza' attribute."))
 32        }
 33    }
 34}
 35
 36#[derive(Debug, Clone)]
 37pub enum IBB {
 38    Open { block_size: u16, sid: String, stanza: Stanza },
 39    Data { seq: u16, sid: String, data: Vec<u8> },
 40    Close(String),
 41}
 42
 43fn optional_attr<T: FromStr>(root: &Element, attr: &str) -> Option<T> {
 44    root.attr(attr)
 45        .and_then(|value| value.parse().ok())
 46}
 47
 48fn required_attr<T: FromStr>(root: &Element, attr: &str, err: Error) -> Result<T, Error> {
 49    optional_attr(root, attr).ok_or(err)
 50}
 51
 52pub fn parse_ibb(root: &Element) -> Result<IBB, Error> {
 53    if root.is("open", ns::IBB) {
 54        let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?;
 55        let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?;
 56        let stanza = root.attr("stanza")
 57                         .and_then(|value| value.parse().ok())
 58                         .unwrap_or_default();
 59        for _ in root.children() {
 60            return Err(Error::ParseError("Unknown child in open element."));
 61        }
 62        Ok(IBB::Open {
 63            block_size: block_size,
 64            sid: sid,
 65            stanza: stanza
 66        })
 67    } else if root.is("data", ns::IBB) {
 68        let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?;
 69        let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?;
 70        let data = base64::decode(&root.text())?;
 71        for _ in root.children() {
 72            return Err(Error::ParseError("Unknown child in data element."));
 73        }
 74        Ok(IBB::Data {
 75            seq: seq,
 76            sid: sid,
 77            data: data
 78        })
 79    } else {
 80        Err(Error::ParseError("This is not an ibb element."))
 81    }
 82}
 83
 84#[cfg(test)]
 85mod tests {
 86    use minidom::Element;
 87    use error::Error;
 88    use ibb;
 89
 90    #[test]
 91    fn test_simple() {
 92        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>".parse().unwrap();
 93        ibb::parse_ibb(&elem).unwrap();
 94
 95        let elem: Element = "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>".parse().unwrap();
 96        ibb::parse_ibb(&elem).unwrap();
 97    }
 98
 99    #[test]
100    fn test_invalid() {
101        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>".parse().unwrap();
102        let error = ibb::parse_ibb(&elem).unwrap_err();
103        let message = match error {
104            Error::ParseError(string) => string,
105            _ => panic!(),
106        };
107        assert_eq!(message, "Required attribute 'block-size' missing in open element.");
108
109        // TODO: maybe make a better error message here.
110        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>".parse().unwrap();
111        let error = ibb::parse_ibb(&elem).unwrap_err();
112        let message = match error {
113            Error::ParseError(string) => string,
114            _ => panic!(),
115        };
116        assert_eq!(message, "Required attribute 'block-size' missing in open element.");
117
118        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>".parse().unwrap();
119        let error = ibb::parse_ibb(&elem).unwrap_err();
120        let message = match error {
121            Error::ParseError(string) => string,
122            _ => panic!(),
123        };
124        assert_eq!(message, "Required attribute 'sid' missing in open element.");
125    }
126
127    #[test]
128    #[ignore]
129    fn test_invalid_stanza() {
130        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
131        let error = ibb::parse_ibb(&elem).unwrap_err();
132        let message = match error {
133            Error::ParseError(string) => string,
134            _ => panic!(),
135        };
136        assert_eq!(message, "Wrong value for 'stanza' attribute in open.");
137    }
138}