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