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