1use std::str::FromStr;
2
3use minidom::Element;
4use base64;
5
6use error::Error;
7
8use ns;
9
10#[derive(Debug, Clone, PartialEq)]
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 Ok(match s {
27 "iq" => Stanza::Iq,
28 "message" => Stanza::Message,
29
30 _ => return Err(Error::ParseError("Invalid 'stanza' attribute.")),
31 })
32 }
33}
34
35#[derive(Debug, Clone)]
36pub enum IBB {
37 Open {
38 block_size: u16,
39 sid: String,
40 stanza: Stanza,
41 },
42 Data {
43 seq: u16,
44 sid: String,
45 data: Vec<u8>,
46 },
47 Close {
48 sid: String,
49 },
50}
51
52fn required_attr<T: FromStr>(root: &Element, attr: &str, err: Error) -> Result<T, Error> {
53 root.attr(attr)
54 .and_then(|value| value.parse().ok())
55 .ok_or(err)
56}
57
58pub fn parse_ibb(root: &Element) -> Result<IBB, Error> {
59 if root.is("open", ns::IBB) {
60 for _ in root.children() {
61 return Err(Error::ParseError("Unknown child in open element."));
62 }
63 let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?;
64 let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?;
65 let stanza = match root.attr("stanza") {
66 Some(stanza) => stanza.parse()?,
67 None => Default::default(),
68 };
69 Ok(IBB::Open {
70 block_size: block_size,
71 sid: sid,
72 stanza: stanza
73 })
74 } else if root.is("data", ns::IBB) {
75 for _ in root.children() {
76 return Err(Error::ParseError("Unknown child in data element."));
77 }
78 let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?;
79 let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?;
80 let data = base64::decode(&root.text())?;
81 Ok(IBB::Data {
82 seq: seq,
83 sid: sid,
84 data: data
85 })
86 } else if root.is("close", ns::IBB) {
87 let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?;
88 for _ in root.children() {
89 return Err(Error::ParseError("Unknown child in close element."));
90 }
91 Ok(IBB::Close {
92 sid: sid,
93 })
94 } else {
95 Err(Error::ParseError("This is not an ibb element."))
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use minidom::Element;
102 use error::Error;
103 use ibb;
104
105 #[test]
106 fn test_simple() {
107 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>".parse().unwrap();
108 let open = ibb::parse_ibb(&elem).unwrap();
109 match open {
110 ibb::IBB::Open { block_size, sid, stanza } => {
111 assert_eq!(block_size, 3);
112 assert_eq!(sid, "coucou");
113 assert_eq!(stanza, ibb::Stanza::Iq);
114 },
115 _ => panic!(),
116 }
117
118 let elem: Element = "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>".parse().unwrap();
119 let data = ibb::parse_ibb(&elem).unwrap();
120 match data {
121 ibb::IBB::Data { seq, sid, data } => {
122 assert_eq!(seq, 0);
123 assert_eq!(sid, "coucou");
124 assert_eq!(data, vec!(0, 0, 0));
125 },
126 _ => panic!(),
127 }
128
129 let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>".parse().unwrap();
130 let close = ibb::parse_ibb(&elem).unwrap();
131 match close {
132 ibb::IBB::Close { sid } => {
133 assert_eq!(sid, "coucou");
134 },
135 _ => panic!(),
136 }
137 }
138
139 #[test]
140 fn test_invalid() {
141 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>".parse().unwrap();
142 let error = ibb::parse_ibb(&elem).unwrap_err();
143 let message = match error {
144 Error::ParseError(string) => string,
145 _ => panic!(),
146 };
147 assert_eq!(message, "Required attribute 'block-size' missing in open element.");
148
149 // TODO: maybe make a better error message here.
150 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>".parse().unwrap();
151 let error = ibb::parse_ibb(&elem).unwrap_err();
152 let message = match error {
153 Error::ParseError(string) => string,
154 _ => panic!(),
155 };
156 assert_eq!(message, "Required attribute 'block-size' missing in open element.");
157
158 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>".parse().unwrap();
159 let error = ibb::parse_ibb(&elem).unwrap_err();
160 let message = match error {
161 Error::ParseError(string) => string,
162 _ => panic!(),
163 };
164 assert_eq!(message, "Required attribute 'sid' missing in open element.");
165 }
166
167 #[test]
168 fn test_invalid_stanza() {
169 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
170 let error = ibb::parse_ibb(&elem).unwrap_err();
171 let message = match error {
172 Error::ParseError(string) => string,
173 _ => panic!(),
174 };
175 assert_eq!(message, "Invalid 'stanza' attribute.");
176 }
177}