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_size() {
231 assert_size!(IqType, 216);
232 assert_size!(Iq, 384);
233 }
234
235 #[test]
236 fn test_require_type() {
237 #[cfg(not(feature = "component"))]
238 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
239 #[cfg(feature = "component")]
240 let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap();
241 let error = Iq::try_from(elem).unwrap_err();
242 let message = match error {
243 Error::ParseError(string) => string,
244 _ => panic!(),
245 };
246 assert_eq!(message, "Required attribute 'type' missing.");
247 }
248
249 #[test]
250 fn test_get() {
251 #[cfg(not(feature = "component"))]
252 let elem: Element = "<iq xmlns='jabber:client' type='get'>
253 <foo xmlns='bar'/>
254 </iq>".parse().unwrap();
255 #[cfg(feature = "component")]
256 let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
257 <foo xmlns='bar'/>
258 </iq>".parse().unwrap();
259 let iq = Iq::try_from(elem).unwrap();
260 let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
261 assert_eq!(iq.from, None);
262 assert_eq!(iq.to, None);
263 assert_eq!(iq.id, None);
264 assert!(match iq.payload {
265 IqType::Get(element) => element.compare_to(&query),
266 _ => false
267 });
268 }
269
270 #[test]
271 fn test_set() {
272 #[cfg(not(feature = "component"))]
273 let elem: Element = "<iq xmlns='jabber:client' type='set'>
274 <vCard xmlns='vcard-temp'/>
275 </iq>".parse().unwrap();
276 #[cfg(feature = "component")]
277 let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
278 <vCard xmlns='vcard-temp'/>
279 </iq>".parse().unwrap();
280 let iq = Iq::try_from(elem).unwrap();
281 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
282 assert_eq!(iq.from, None);
283 assert_eq!(iq.to, None);
284 assert_eq!(iq.id, None);
285 assert!(match iq.payload {
286 IqType::Set(element) => element.compare_to(&vcard),
287 _ => false
288 });
289 }
290
291 #[test]
292 fn test_result_empty() {
293 #[cfg(not(feature = "component"))]
294 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
295 #[cfg(feature = "component")]
296 let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
297 let iq = Iq::try_from(elem).unwrap();
298 assert_eq!(iq.from, None);
299 assert_eq!(iq.to, None);
300 assert_eq!(iq.id, None);
301 assert!(match iq.payload {
302 IqType::Result(None) => true,
303 _ => false,
304 });
305 }
306
307 #[test]
308 fn test_result() {
309 #[cfg(not(feature = "component"))]
310 let elem: Element = "<iq xmlns='jabber:client' type='result'>
311 <query xmlns='http://jabber.org/protocol/disco#items'/>
312 </iq>".parse().unwrap();
313 #[cfg(feature = "component")]
314 let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
315 <query xmlns='http://jabber.org/protocol/disco#items'/>
316 </iq>".parse().unwrap();
317 let iq = Iq::try_from(elem).unwrap();
318 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
319 assert_eq!(iq.from, None);
320 assert_eq!(iq.to, None);
321 assert_eq!(iq.id, None);
322 assert!(match iq.payload {
323 IqType::Result(Some(element)) => element.compare_to(&query),
324 _ => false,
325 });
326 }
327
328 #[test]
329 fn test_error() {
330 #[cfg(not(feature = "component"))]
331 let elem: Element = "<iq xmlns='jabber:client' type='error'>
332 <ping xmlns='urn:xmpp:ping'/>
333 <error type='cancel'>
334 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
335 </error>
336 </iq>".parse().unwrap();
337 #[cfg(feature = "component")]
338 let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
339 <ping xmlns='urn:xmpp:ping'/>
340 <error type='cancel'>
341 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
342 </error>
343 </iq>".parse().unwrap();
344 let iq = Iq::try_from(elem).unwrap();
345 assert_eq!(iq.from, None);
346 assert_eq!(iq.to, None);
347 assert_eq!(iq.id, None);
348 match iq.payload {
349 IqType::Error(error) => {
350 assert_eq!(error.type_, ErrorType::Cancel);
351 assert_eq!(error.by, None);
352 assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
353 assert_eq!(error.texts.len(), 0);
354 assert_eq!(error.other, None);
355 },
356 _ => panic!(),
357 }
358 }
359
360 #[test]
361 fn test_children_invalid() {
362 #[cfg(not(feature = "component"))]
363 let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
364 #[cfg(feature = "component")]
365 let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>".parse().unwrap();
366 let error = Iq::try_from(elem).unwrap_err();
367 let message = match error {
368 Error::ParseError(string) => string,
369 _ => panic!(),
370 };
371 assert_eq!(message, "Wrong number of children in iq element.");
372 }
373
374 #[test]
375 fn test_serialise() {
376 #[cfg(not(feature = "component"))]
377 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
378 #[cfg(feature = "component")]
379 let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
380 let iq2 = Iq {
381 from: None,
382 to: None,
383 id: None,
384 payload: IqType::Result(None),
385 };
386 let elem2 = iq2.into();
387 assert_eq!(elem, elem2);
388 }
389
390 #[test]
391 fn test_disco() {
392 #[cfg(not(feature = "component"))]
393 let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
394 #[cfg(feature = "component")]
395 let elem: Element = "<iq xmlns='jabber:component:accept' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
396 let iq = Iq::try_from(elem).unwrap();
397 let disco_info = match iq.payload {
398 IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(),
399 _ => panic!(),
400 };
401 assert!(disco_info.node.is_none());
402 }
403}