1use std::str::FromStr;
2
3use minidom::Element;
4use base64;
5
6use error::Error;
7
8use ns;
9
10#[derive(Debug, Clone)]
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 { block_size: u16, sid: String, stanza: Stanza },
39 Data { seq: u16, sid: String, data: Vec<u8> },
40 Close(String),
41}
42
43fn optional_attr<T: FromStr>(root: &Element, attr: &str) -> Option<T> {
44 root.attr(attr)
45 .and_then(|value| value.parse().ok())
46}
47
48fn required_attr<T: FromStr>(root: &Element, attr: &str, err: Error) -> Result<T, Error> {
49 optional_attr(root, attr).ok_or(err)
50}
51
52pub fn parse_ibb(root: &Element) -> Result<IBB, Error> {
53 if root.is("open", ns::IBB) {
54 let block_size = required_attr(root, "block-size", Error::ParseError("Required attribute 'block-size' missing in open element."))?;
55 let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in open element."))?;
56 let stanza = root.attr("stanza")
57 .and_then(|value| value.parse().ok())
58 .unwrap_or_default();
59 for _ in root.children() {
60 return Err(Error::ParseError("Unknown child in open element."));
61 }
62 Ok(IBB::Open {
63 block_size: block_size,
64 sid: sid,
65 stanza: stanza
66 })
67 } else if root.is("data", ns::IBB) {
68 let seq = required_attr(root, "seq", Error::ParseError("Required attribute 'seq' missing in data element."))?;
69 let sid = required_attr(root, "sid", Error::ParseError("Required attribute 'sid' missing in data element."))?;
70 let data = base64::decode(&root.text())?;
71 for _ in root.children() {
72 return Err(Error::ParseError("Unknown child in data element."));
73 }
74 Ok(IBB::Data {
75 seq: seq,
76 sid: sid,
77 data: data
78 })
79 } else {
80 Err(Error::ParseError("This is not an ibb element."))
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use minidom::Element;
87 use error::Error;
88 use ibb;
89
90 #[test]
91 fn test_simple() {
92 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>".parse().unwrap();
93 ibb::parse_ibb(&elem).unwrap();
94
95 let elem: Element = "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>".parse().unwrap();
96 ibb::parse_ibb(&elem).unwrap();
97 }
98
99 #[test]
100 fn test_invalid() {
101 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>".parse().unwrap();
102 let error = ibb::parse_ibb(&elem).unwrap_err();
103 let message = match error {
104 Error::ParseError(string) => string,
105 _ => panic!(),
106 };
107 assert_eq!(message, "Required attribute 'block-size' missing in open element.");
108
109 // TODO: maybe make a better error message here.
110 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>".parse().unwrap();
111 let error = ibb::parse_ibb(&elem).unwrap_err();
112 let message = match error {
113 Error::ParseError(string) => string,
114 _ => panic!(),
115 };
116 assert_eq!(message, "Required attribute 'block-size' missing in open element.");
117
118 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>".parse().unwrap();
119 let error = ibb::parse_ibb(&elem).unwrap_err();
120 let message = match error {
121 Error::ParseError(string) => string,
122 _ => panic!(),
123 };
124 assert_eq!(message, "Required attribute 'sid' missing in open element.");
125 }
126
127 #[test]
128 #[ignore]
129 fn test_invalid_stanza() {
130 let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
131 let error = ibb::parse_ibb(&elem).unwrap_err();
132 let message = match error {
133 Error::ParseError(string) => string,
134 _ => panic!(),
135 };
136 assert_eq!(message, "Wrong value for 'stanza' attribute in open.");
137 }
138}