ibb.rs

  1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use try_from::TryFrom;
  8use std::str::FromStr;
  9
 10use minidom::{Element, IntoAttributeValue};
 11use base64;
 12
 13use error::Error;
 14
 15use ns;
 16
 17generate_attribute!(Stanza, "stanza", {
 18    Iq => "iq",
 19    Message => "message",
 20}, Default = Iq);
 21
 22generate_element_with_only_attributes!(Open, "open", ns::IBB, [
 23    block_size: u16 = "block-size" => required,
 24    sid: String = "sid" => required,
 25    stanza: Stanza = "stanza" => default,
 26]);
 27
 28#[derive(Debug, Clone)]
 29pub struct Data {
 30    pub seq: u16,
 31    pub sid: String,
 32    pub data: Vec<u8>,
 33}
 34
 35impl TryFrom<Element> for Data {
 36    type Err = Error;
 37
 38    fn try_from(elem: Element) -> Result<Data, Error> {
 39        if !elem.is("data", ns::IBB) {
 40            return Err(Error::ParseError("This is not a data element."));
 41        }
 42        for _ in elem.children() {
 43            return Err(Error::ParseError("Unknown child in data element."));
 44        }
 45        Ok(Data {
 46            seq: get_attr!(elem, "seq", required),
 47            sid: get_attr!(elem, "sid", required),
 48            data: base64::decode(&elem.text())?,
 49        })
 50    }
 51}
 52
 53impl From<Data> for Element {
 54    fn from(data: Data) -> Element {
 55        Element::builder("data")
 56                .ns(ns::IBB)
 57                .attr("seq", data.seq)
 58                .attr("sid", data.sid)
 59                .append(base64::encode(&data.data))
 60                .build()
 61    }
 62}
 63
 64generate_element_with_only_attributes!(Close, "close", ns::IBB, [
 65    sid: String = "sid" => required,
 66]);
 67
 68#[cfg(test)]
 69mod tests {
 70    use super::*;
 71    use std::error::Error as StdError;
 72
 73    #[test]
 74    fn test_simple() {
 75        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>".parse().unwrap();
 76        let open = Open::try_from(elem).unwrap();
 77        assert_eq!(open.block_size, 3);
 78        assert_eq!(open.sid, "coucou");
 79        assert_eq!(open.stanza, Stanza::Iq);
 80
 81        let elem: Element = "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>".parse().unwrap();
 82        let data = Data::try_from(elem).unwrap();
 83        assert_eq!(data.seq, 0);
 84        assert_eq!(data.sid, "coucou");
 85        assert_eq!(data.data, vec!(0, 0, 0));
 86
 87        let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>".parse().unwrap();
 88        let close = Close::try_from(elem).unwrap();
 89        assert_eq!(close.sid, "coucou");
 90    }
 91
 92    #[test]
 93    fn test_invalid() {
 94        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>".parse().unwrap();
 95        let error = Open::try_from(elem).unwrap_err();
 96        let message = match error {
 97            Error::ParseError(string) => string,
 98            _ => panic!(),
 99        };
100        assert_eq!(message, "Required attribute 'block-size' missing.");
101
102        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>".parse().unwrap();
103        let error = Open::try_from(elem).unwrap_err();
104        let message = match error {
105            Error::ParseIntError(error) => error,
106            _ => panic!(),
107        };
108        assert_eq!(message.description(), "invalid digit found in string");
109
110        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>".parse().unwrap();
111        let error = Open::try_from(elem).unwrap_err();
112        let message = match error {
113            Error::ParseError(error) => error,
114            _ => panic!(),
115        };
116        assert_eq!(message, "Required attribute 'sid' missing.");
117    }
118
119    #[test]
120    fn test_invalid_stanza() {
121        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
122        let error = Open::try_from(elem).unwrap_err();
123        let message = match error {
124            Error::ParseError(string) => string,
125            _ => panic!(),
126        };
127        assert_eq!(message, "Unknown value for 'stanza' attribute.");
128    }
129}