1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2// Copyright (c) 2017 Maxime “pep” Buquet <pep+code@bouah.net>
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8use try_from::TryFrom;
9
10use minidom::Element;
11use minidom::IntoAttributeValue;
12
13use jid::Jid;
14
15use error::Error;
16
17use ns;
18
19use stanza_error::StanzaError;
20
21/// Should be implemented on every known payload of an `<iq type='get'/>`.
22pub trait IqGetPayload: TryFrom<Element> + Into<Element> {}
23
24/// Should be implemented on every known payload of an `<iq type='set'/>`.
25pub trait IqSetPayload: TryFrom<Element> + Into<Element> {}
26
27/// Should be implemented on every known payload of an `<iq type='result'/>`.
28pub trait IqResultPayload: TryFrom<Element> + Into<Element> {}
29
30/// Represents one of the four possible iq types.
31#[derive(Debug, Clone)]
32pub enum IqType {
33 /// This is a request for accessing some data.
34 Get(Element),
35
36 /// This is a request for modifying some data.
37 Set(Element),
38
39 /// This is a result containing some data.
40 Result(Option<Element>),
41
42 /// A get or set request failed.
43 Error(StanzaError),
44}
45
46impl<'a> IntoAttributeValue for &'a IqType {
47 fn into_attribute_value(self) -> Option<String> {
48 Some(match *self {
49 IqType::Get(_) => "get",
50 IqType::Set(_) => "set",
51 IqType::Result(_) => "result",
52 IqType::Error(_) => "error",
53 }.to_owned())
54 }
55}
56
57/// The main structure representing the `<iq/>` stanza.
58#[derive(Debug, Clone)]
59pub struct Iq {
60 /// The JID emitting this stanza.
61 pub from: Option<Jid>,
62
63 /// The recipient of this stanza.
64 pub to: Option<Jid>,
65
66 /// The @id attribute of this stanza, which is required in order to match a
67 /// request with its result/error.
68 pub id: Option<String>,
69
70 /// The payload content of this stanza.
71 pub payload: IqType,
72}
73
74impl Iq {
75 /// Creates an `<iq/>` stanza containing a get request.
76 pub fn from_get(payload: impl IqGetPayload) -> Iq {
77 Iq {
78 from: None,
79 to: None,
80 id: None,
81 payload: IqType::Get(payload.into()),
82 }
83 }
84
85 /// Creates an `<iq/>` stanza containing a set request.
86 pub fn from_set(payload: impl IqSetPayload) -> Iq {
87 Iq {
88 from: None,
89 to: None,
90 id: None,
91 payload: IqType::Set(payload.into()),
92 }
93 }
94
95 /// Creates an `<iq/>` stanza containing a result.
96 pub fn from_result(payload: Option<impl IqResultPayload>) -> Iq {
97 Iq {
98 from: None,
99 to: None,
100 id: None,
101 payload: IqType::Result(payload.map(|payload| payload.into())),
102 }
103 }
104
105 /// Creates an `<iq/>` stanza containing an error.
106 pub fn from_error(payload: StanzaError) -> Iq {
107 Iq {
108 from: None,
109 to: None,
110 id: None,
111 payload: IqType::Error(payload),
112 }
113 }
114
115 /// Sets the recipient of this stanza.
116 pub fn with_to(mut self, to: Jid) -> Iq {
117 self.to = Some(to);
118 self
119 }
120
121 /// Sets the emitter of this stanza.
122 pub fn with_from(mut self, from: Jid) -> Iq {
123 self.from = Some(from);
124 self
125 }
126
127 /// Sets the id of this stanza, in order to later match its response.
128 pub fn with_id(mut self, id: String) -> Iq {
129 self.id = Some(id);
130 self
131 }
132}
133
134impl TryFrom<Element> for Iq {
135 type Err = Error;
136
137 fn try_from(root: Element) -> Result<Iq, Error> {
138 check_self!(root, "iq", DEFAULT_NS);
139 let from = get_attr!(root, "from", optional);
140 let to = get_attr!(root, "to", optional);
141 let id = get_attr!(root, "id", optional);
142 let type_: String = get_attr!(root, "type", required);
143
144 let mut payload = None;
145 let mut error_payload = None;
146 for elem in root.children() {
147 if payload.is_some() {
148 return Err(Error::ParseError("Wrong number of children in iq element."));
149 }
150 if type_ == "error" {
151 if elem.is("error", ns::DEFAULT_NS) {
152 if error_payload.is_some() {
153 return Err(Error::ParseError("Wrong number of children in iq element."));
154 }
155 error_payload = Some(StanzaError::try_from(elem.clone())?);
156 } else if root.children().count() != 2 {
157 return Err(Error::ParseError("Wrong number of children in iq element."));
158 }
159 } else {
160 payload = Some(elem.clone());
161 }
162 }
163
164 let type_ = if type_ == "get" {
165 if let Some(payload) = payload {
166 IqType::Get(payload)
167 } else {
168 return Err(Error::ParseError("Wrong number of children in iq element."));
169 }
170 } else if type_ == "set" {
171 if let Some(payload) = payload {
172 IqType::Set(payload)
173 } else {
174 return Err(Error::ParseError("Wrong number of children in iq element."));
175 }
176 } else if type_ == "result" {
177 if let Some(payload) = payload {
178 IqType::Result(Some(payload))
179 } else {
180 IqType::Result(None)
181 }
182 } else if type_ == "error" {
183 if let Some(payload) = error_payload {
184 IqType::Error(payload)
185 } else {
186 return Err(Error::ParseError("Wrong number of children in iq element."));
187 }
188 } else {
189 return Err(Error::ParseError("Unknown iq type."));
190 };
191
192 Ok(Iq {
193 from: from,
194 to: to,
195 id: id,
196 payload: type_,
197 })
198 }
199}
200
201impl From<Iq> for Element {
202 fn from(iq: Iq) -> Element {
203 let mut stanza = Element::builder("iq")
204 .ns(ns::DEFAULT_NS)
205 .attr("from", iq.from)
206 .attr("to", iq.to)
207 .attr("id", iq.id)
208 .attr("type", &iq.payload)
209 .build();
210 let elem = match iq.payload {
211 IqType::Get(elem)
212 | IqType::Set(elem)
213 | IqType::Result(Some(elem)) => elem,
214 IqType::Error(error) => error.into(),
215 IqType::Result(None) => return stanza,
216 };
217 stanza.append_child(elem);
218 stanza
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use stanza_error::{ErrorType, DefinedCondition};
226 use compare_elements::NamespaceAwareCompare;
227 use disco::DiscoInfoQuery;
228
229 #[test]
230 fn test_require_type() {
231 #[cfg(not(feature = "component"))]
232 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
233 #[cfg(feature = "component")]
234 let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap();
235 let error = Iq::try_from(elem).unwrap_err();
236 let message = match error {
237 Error::ParseError(string) => string,
238 _ => panic!(),
239 };
240 assert_eq!(message, "Required attribute 'type' missing.");
241 }
242
243 #[test]
244 fn test_get() {
245 #[cfg(not(feature = "component"))]
246 let elem: Element = "<iq xmlns='jabber:client' type='get'>
247 <foo xmlns='bar'/>
248 </iq>".parse().unwrap();
249 #[cfg(feature = "component")]
250 let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
251 <foo xmlns='bar'/>
252 </iq>".parse().unwrap();
253 let iq = Iq::try_from(elem).unwrap();
254 let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
255 assert_eq!(iq.from, None);
256 assert_eq!(iq.to, None);
257 assert_eq!(iq.id, None);
258 assert!(match iq.payload {
259 IqType::Get(element) => element.compare_to(&query),
260 _ => false
261 });
262 }
263
264 #[test]
265 fn test_set() {
266 #[cfg(not(feature = "component"))]
267 let elem: Element = "<iq xmlns='jabber:client' type='set'>
268 <vCard xmlns='vcard-temp'/>
269 </iq>".parse().unwrap();
270 #[cfg(feature = "component")]
271 let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
272 <vCard xmlns='vcard-temp'/>
273 </iq>".parse().unwrap();
274 let iq = Iq::try_from(elem).unwrap();
275 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
276 assert_eq!(iq.from, None);
277 assert_eq!(iq.to, None);
278 assert_eq!(iq.id, None);
279 assert!(match iq.payload {
280 IqType::Set(element) => element.compare_to(&vcard),
281 _ => false
282 });
283 }
284
285 #[test]
286 fn test_result_empty() {
287 #[cfg(not(feature = "component"))]
288 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
289 #[cfg(feature = "component")]
290 let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
291 let iq = Iq::try_from(elem).unwrap();
292 assert_eq!(iq.from, None);
293 assert_eq!(iq.to, None);
294 assert_eq!(iq.id, None);
295 assert!(match iq.payload {
296 IqType::Result(None) => true,
297 _ => false,
298 });
299 }
300
301 #[test]
302 fn test_result() {
303 #[cfg(not(feature = "component"))]
304 let elem: Element = "<iq xmlns='jabber:client' type='result'>
305 <query xmlns='http://jabber.org/protocol/disco#items'/>
306 </iq>".parse().unwrap();
307 #[cfg(feature = "component")]
308 let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
309 <query xmlns='http://jabber.org/protocol/disco#items'/>
310 </iq>".parse().unwrap();
311 let iq = Iq::try_from(elem).unwrap();
312 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
313 assert_eq!(iq.from, None);
314 assert_eq!(iq.to, None);
315 assert_eq!(iq.id, None);
316 assert!(match iq.payload {
317 IqType::Result(Some(element)) => element.compare_to(&query),
318 _ => false,
319 });
320 }
321
322 #[test]
323 fn test_error() {
324 #[cfg(not(feature = "component"))]
325 let elem: Element = "<iq xmlns='jabber:client' type='error'>
326 <ping xmlns='urn:xmpp:ping'/>
327 <error type='cancel'>
328 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
329 </error>
330 </iq>".parse().unwrap();
331 #[cfg(feature = "component")]
332 let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
333 <ping xmlns='urn:xmpp:ping'/>
334 <error type='cancel'>
335 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
336 </error>
337 </iq>".parse().unwrap();
338 let iq = Iq::try_from(elem).unwrap();
339 assert_eq!(iq.from, None);
340 assert_eq!(iq.to, None);
341 assert_eq!(iq.id, None);
342 match iq.payload {
343 IqType::Error(error) => {
344 assert_eq!(error.type_, ErrorType::Cancel);
345 assert_eq!(error.by, None);
346 assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
347 assert_eq!(error.texts.len(), 0);
348 assert_eq!(error.other, None);
349 },
350 _ => panic!(),
351 }
352 }
353
354 #[test]
355 fn test_children_invalid() {
356 #[cfg(not(feature = "component"))]
357 let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
358 #[cfg(feature = "component")]
359 let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>".parse().unwrap();
360 let error = Iq::try_from(elem).unwrap_err();
361 let message = match error {
362 Error::ParseError(string) => string,
363 _ => panic!(),
364 };
365 assert_eq!(message, "Wrong number of children in iq element.");
366 }
367
368 #[test]
369 fn test_serialise() {
370 #[cfg(not(feature = "component"))]
371 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
372 #[cfg(feature = "component")]
373 let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
374 let iq2 = Iq {
375 from: None,
376 to: None,
377 id: None,
378 payload: IqType::Result(None),
379 };
380 let elem2 = iq2.into();
381 assert_eq!(elem, elem2);
382 }
383
384 #[test]
385 fn test_disco() {
386 #[cfg(not(feature = "component"))]
387 let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
388 #[cfg(feature = "component")]
389 let elem: Element = "<iq xmlns='jabber:component:accept' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
390 let iq = Iq::try_from(elem).unwrap();
391 let disco_info = match iq.payload {
392 IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(),
393 _ => panic!(),
394 };
395 assert!(disco_info.node.is_none());
396 }
397}