1use minidom::Element;
2use minidom::IntoAttributeValue;
3
4use jid::Jid;
5
6use error::Error;
7
8use ns;
9
10use disco;
11use ibb;
12use jingle;
13use ping;
14
15/// Lists every known payload of a `<iq/>`.
16#[derive(Debug, Clone)]
17pub enum IqPayload {
18 Disco(disco::Disco),
19 IBB(ibb::IBB),
20 Jingle(jingle::Jingle),
21 Ping(ping::Ping),
22}
23
24#[derive(Debug, Clone)]
25pub enum IqPayloadType {
26 XML(Element),
27 Parsed(IqPayload),
28}
29
30#[derive(Debug, Clone)]
31pub enum IqType {
32 Get(IqPayloadType),
33 Set(IqPayloadType),
34 Result(Option<IqPayloadType>),
35 Error(IqPayloadType),
36}
37
38impl IntoAttributeValue for IqType {
39 fn into_attribute_value(self) -> Option<String> {
40 Some(match self {
41 IqType::Get(_) => "get",
42 IqType::Set(_) => "set",
43 IqType::Result(_) => "result",
44 IqType::Error(_) => "error",
45 }.to_owned())
46 }
47}
48
49#[derive(Debug, Clone)]
50pub struct Iq {
51 pub from: Option<Jid>,
52 pub to: Option<Jid>,
53 pub id: Option<String>,
54 pub payload: IqType,
55}
56
57pub fn parse_iq(root: &Element) -> Result<Iq, Error> {
58 if !root.is("iq", ns::JABBER_CLIENT) {
59 return Err(Error::ParseError("This is not an iq element."));
60 }
61 let from = root.attr("from")
62 .and_then(|value| value.parse().ok());
63 let to = root.attr("to")
64 .and_then(|value| value.parse().ok());
65 let id = root.attr("id")
66 .and_then(|value| value.parse().ok());
67 let type_ = match root.attr("type") {
68 Some(type_) => type_,
69 None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")),
70 };
71
72 let mut payload = None;
73 for elem in root.children() {
74 if payload.is_some() {
75 return Err(Error::ParseError("Wrong number of children in iq element."));
76 }
77 if type_ == "error" {
78 if elem.is("error", ns::JABBER_CLIENT) {
79 payload = Some(IqPayloadType::XML(elem.clone()));
80 } else if root.children().collect::<Vec<_>>().len() != 2 {
81 return Err(Error::ParseError("Wrong number of children in iq element."));
82 }
83 } else {
84 let parsed_payload = if let Ok(disco) = disco::parse_disco(elem) {
85 Some(IqPayload::Disco(disco))
86 } else if let Ok(ibb) = ibb::parse_ibb(elem) {
87 Some(IqPayload::IBB(ibb))
88 } else if let Ok(jingle) = jingle::parse_jingle(elem) {
89 Some(IqPayload::Jingle(jingle))
90 } else if let Ok(ping) = ping::parse_ping(elem) {
91 Some(IqPayload::Ping(ping))
92 } else {
93 None
94 };
95
96 payload = match parsed_payload {
97 Some(payload) => Some(IqPayloadType::Parsed(payload)),
98 None => Some(IqPayloadType::XML(elem.clone())),
99 };
100 }
101 }
102
103 let type_ = if type_ == "get" {
104 if let Some(payload) = payload.clone() {
105 IqType::Get(payload.clone())
106 } else {
107 return Err(Error::ParseError("Wrong number of children in iq element."));
108 }
109 } else if type_ == "set" {
110 if let Some(payload) = payload.clone() {
111 IqType::Set(payload.clone())
112 } else {
113 return Err(Error::ParseError("Wrong number of children in iq element."));
114 }
115 } else if type_ == "result" {
116 if let Some(payload) = payload.clone() {
117 IqType::Result(Some(payload.clone()))
118 } else {
119 IqType::Result(None)
120 }
121 } else if type_ == "error" {
122 if let Some(payload) = payload.clone() {
123 IqType::Error(payload.clone())
124 } else {
125 return Err(Error::ParseError("Wrong number of children in iq element."));
126 }
127 } else {
128 panic!()
129 };
130
131 Ok(Iq {
132 from: from,
133 to: to,
134 id: id,
135 payload: type_,
136 })
137}
138
139pub fn serialise_payload(payload: &IqPayload) -> Element {
140 match *payload {
141 IqPayload::Disco(ref disco) => disco::serialise_disco(disco),
142 IqPayload::IBB(ref ibb) => ibb::serialise(ibb),
143 IqPayload::Jingle(ref jingle) => jingle::serialise(jingle),
144 IqPayload::Ping(_) => ping::serialise_ping(),
145 }
146}
147
148pub fn serialise(iq: &Iq) -> Element {
149 let mut stanza = Element::builder("iq")
150 .ns(ns::JABBER_CLIENT)
151 .attr("from", iq.from.clone().and_then(|value| Some(String::from(value))))
152 .attr("to", iq.to.clone().and_then(|value| Some(String::from(value))))
153 .attr("id", iq.id.clone())
154 .attr("type", iq.payload.clone())
155 .build();
156 let elem = match iq.payload.clone() {
157 IqType::Get(IqPayloadType::XML(elem))
158 | IqType::Set(IqPayloadType::XML(elem))
159 | IqType::Result(Some(IqPayloadType::XML(elem)))
160 | IqType::Error(IqPayloadType::XML(elem)) => elem,
161 IqType::Get(IqPayloadType::Parsed(payload))
162 | IqType::Set(IqPayloadType::Parsed(payload))
163 | IqType::Result(Some(IqPayloadType::Parsed(payload))) => serialise_payload(&payload),
164 IqType::Result(None) => return stanza,
165 _ => panic!(),
166 };
167 stanza.append_child(elem);
168 stanza
169}
170
171#[cfg(test)]
172mod tests {
173 use minidom::Element;
174 use error::Error;
175 use iq;
176 use disco;
177
178 #[test]
179 fn test_require_type() {
180 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
181 let error = iq::parse_iq(&elem).unwrap_err();
182 let message = match error {
183 Error::ParseError(string) => string,
184 _ => panic!(),
185 };
186 assert_eq!(message, "Iq element requires a 'type' attribute.");
187 }
188
189 #[test]
190 fn test_get() {
191 let elem: Element = "<iq xmlns='jabber:client' type='get'>
192 <foo/>
193 </iq>".parse().unwrap();
194 let iq = iq::parse_iq(&elem).unwrap();
195 let query: Element = "<foo xmlns='jabber:client'/>".parse().unwrap();
196 assert_eq!(iq.from, None);
197 assert_eq!(iq.to, None);
198 assert_eq!(iq.id, None);
199 assert!(match iq.payload {
200 iq::IqType::Get(iq::IqPayloadType::XML(element)) => element == query,
201 _ => false
202 });
203 }
204
205 #[test]
206 fn test_set() {
207 let elem: Element = "<iq xmlns='jabber:client' type='set'>
208 <vCard xmlns='vcard-temp'/>
209 </iq>".parse().unwrap();
210 let iq = iq::parse_iq(&elem).unwrap();
211 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
212 assert_eq!(iq.from, None);
213 assert_eq!(iq.to, None);
214 assert_eq!(iq.id, None);
215 assert!(match iq.payload {
216 iq::IqType::Set(iq::IqPayloadType::XML(element)) => element == vcard,
217 _ => false
218 });
219 }
220
221 #[test]
222 fn test_result_empty() {
223 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
224 let iq = iq::parse_iq(&elem).unwrap();
225 assert_eq!(iq.from, None);
226 assert_eq!(iq.to, None);
227 assert_eq!(iq.id, None);
228 assert!(match iq.payload {
229 iq::IqType::Result(None) => true,
230 _ => false,
231 });
232 }
233
234 #[test]
235 fn test_result() {
236 let elem: Element = "<iq xmlns='jabber:client' type='result'>
237 <query xmlns='http://jabber.org/protocol/disco#items'/>
238 </iq>".parse().unwrap();
239 let iq = iq::parse_iq(&elem).unwrap();
240 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
241 assert_eq!(iq.from, None);
242 assert_eq!(iq.to, None);
243 assert_eq!(iq.id, None);
244 assert!(match iq.payload {
245 iq::IqType::Result(Some(iq::IqPayloadType::XML(element))) => element == query,
246 _ => false,
247 });
248 }
249
250 #[test]
251 fn test_error() {
252 let elem: Element = "<iq xmlns='jabber:client' type='error'>
253 <ping xmlns='urn:xmpp:ping'/>
254 <error type='cancel'>
255 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
256 </error>
257 </iq>".parse().unwrap();
258 let iq = iq::parse_iq(&elem).unwrap();
259 let error: Element = "<error xmlns='jabber:client' type='cancel'>
260 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
261 </error>".parse().unwrap();
262 assert_eq!(iq.from, None);
263 assert_eq!(iq.to, None);
264 assert_eq!(iq.id, None);
265 assert!(match iq.payload {
266 iq::IqType::Error(iq::IqPayloadType::XML(element)) => element == error,
267 _ => false,
268 });
269 }
270
271 #[test]
272 fn test_children_invalid() {
273 let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
274 let error = iq::parse_iq(&elem).unwrap_err();
275 let message = match error {
276 Error::ParseError(string) => string,
277 _ => panic!(),
278 };
279 assert_eq!(message, "Wrong number of children in iq element.");
280 }
281
282 #[test]
283 fn test_serialise() {
284 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
285 let iq2 = iq::Iq {
286 from: None,
287 to: None,
288 id: None,
289 payload: iq::IqType::Result(None),
290 };
291 let elem2 = iq::serialise(&iq2);
292 assert_eq!(elem, elem2);
293 }
294
295 #[test]
296 fn test_disco() {
297 let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
298 let iq = iq::parse_iq(&elem).unwrap();
299 assert!(match iq.payload {
300 iq::IqType::Get(iq::IqPayloadType::Parsed(iq::IqPayload::Disco(disco::Disco { .. }))) => true,
301 _ => false,
302 });
303 }
304}