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