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