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 crate::util::helpers::Base64;
  8use crate::iq::IqSetPayload;
  9
 10generate_id!(
 11    /// An identifier matching a stream.
 12    StreamId
 13);
 14
 15generate_attribute!(
 16/// Which stanza type to use to exchange data.
 17Stanza, "stanza", {
 18    /// `<iq/>` gives a feedback on whether the chunk has been received or not,
 19    /// which is useful in the case the recipient might not receive them in a
 20    /// timely manner, or to do your own throttling based on the results.
 21    Iq => "iq",
 22
 23    /// `<message/>` can be faster, since it doesn’t require any feedback, but in
 24    /// practice it will be throttled by the servers on the way.
 25    Message => "message",
 26}, Default = Iq);
 27
 28generate_element!(
 29/// Starts an In-Band Bytestream session with the given parameters.
 30Open, "open", IBB,
 31attributes: [
 32    /// Maximum size in bytes for each chunk.
 33    block_size: Required<u16> = "block-size",
 34
 35    /// The identifier to be used to create a stream.
 36    sid: Required<StreamId> = "sid",
 37
 38    /// Which stanza type to use to exchange data.
 39    stanza: Default<Stanza> = "stanza",
 40]);
 41
 42impl IqSetPayload for Open {}
 43
 44generate_element!(
 45/// Exchange a chunk of data in an open stream.
 46Data, "data", IBB,
 47    attributes: [
 48        /// Sequence number of this chunk, must wraparound after 65535.
 49        seq: Required<u16> = "seq",
 50
 51        /// The identifier of the stream on which data is being exchanged.
 52        sid: Required<StreamId> = "sid"
 53    ],
 54    text: (
 55        /// Vector of bytes to be exchanged.
 56        data: Base64<Vec<u8>>
 57    )
 58);
 59
 60impl IqSetPayload for Data {}
 61
 62generate_element!(
 63/// Close an open stream.
 64Close, "close", IBB,
 65attributes: [
 66    /// The identifier of the stream to be closed.
 67    sid: Required<StreamId> = "sid",
 68]);
 69
 70impl IqSetPayload for Close {}
 71
 72#[cfg(test)]
 73mod tests {
 74    use super::*;
 75    use crate::util::error::Error;
 76    use minidom::Element;
 77    use std::error::Error as StdError;
 78    use std::convert::TryFrom;
 79
 80    #[cfg(target_pointer_width = "32")]
 81    #[test]
 82    fn test_size() {
 83        assert_size!(StreamId, 12);
 84        assert_size!(Stanza, 1);
 85        assert_size!(Open, 16);
 86        assert_size!(Data, 28);
 87        assert_size!(Close, 12);
 88    }
 89
 90    #[cfg(target_pointer_width = "64")]
 91    #[test]
 92    fn test_size() {
 93        assert_size!(StreamId, 24);
 94        assert_size!(Stanza, 1);
 95        assert_size!(Open, 32);
 96        assert_size!(Data, 56);
 97        assert_size!(Close, 24);
 98    }
 99
100    #[test]
101    fn test_simple() {
102        let sid = StreamId(String::from("coucou"));
103
104        let elem: Element =
105            "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>"
106                .parse()
107                .unwrap();
108        let open = Open::try_from(elem).unwrap();
109        assert_eq!(open.block_size, 3);
110        assert_eq!(open.sid, sid);
111        assert_eq!(open.stanza, Stanza::Iq);
112
113        let elem: Element =
114            "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>"
115                .parse()
116                .unwrap();
117        let data = Data::try_from(elem).unwrap();
118        assert_eq!(data.seq, 0);
119        assert_eq!(data.sid, sid);
120        assert_eq!(data.data, vec!(0, 0, 0));
121
122        let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>"
123            .parse()
124            .unwrap();
125        let close = Close::try_from(elem).unwrap();
126        assert_eq!(close.sid, sid);
127    }
128
129    #[test]
130    fn test_invalid() {
131        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>"
132            .parse()
133            .unwrap();
134        let error = Open::try_from(elem).unwrap_err();
135        let message = match error {
136            Error::ParseError(string) => string,
137            _ => panic!(),
138        };
139        assert_eq!(message, "Required attribute 'block-size' missing.");
140
141        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>"
142            .parse()
143            .unwrap();
144        let error = Open::try_from(elem).unwrap_err();
145        let message = match error {
146            Error::ParseIntError(error) => error,
147            _ => panic!(),
148        };
149        assert_eq!(message.description(), "invalid digit found in string");
150
151        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>"
152            .parse()
153            .unwrap();
154        let error = Open::try_from(elem).unwrap_err();
155        let message = match error {
156            Error::ParseError(error) => error,
157            _ => panic!(),
158        };
159        assert_eq!(message, "Required attribute 'sid' missing.");
160    }
161
162    #[test]
163    fn test_invalid_stanza() {
164        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
165        let error = Open::try_from(elem).unwrap_err();
166        let message = match error {
167            Error::ParseError(string) => string,
168            _ => panic!(),
169        };
170        assert_eq!(message, "Unknown value for 'stanza' attribute.");
171    }
172}