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::util::error::Error;
9use crate::ns;
10use crate::stanza_error::StanzaError;
11use jid::Jid;
12use minidom::Element;
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)]
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)]
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 `<iq/>` stanza containing a result.
94 pub fn from_result<S: Into<String>>(id: S, payload: Option<impl IqResultPayload>) -> Iq {
95 Iq {
96 from: None,
97 to: None,
98 id: id.into(),
99 payload: IqType::Result(payload.map(Into::into)),
100 }
101 }
102
103 /// Creates an `<iq/>` stanza containing an error.
104 pub fn from_error<S: Into<String>>(id: S, payload: StanzaError) -> Iq {
105 Iq {
106 from: None,
107 to: None,
108 id: id.into(),
109 payload: IqType::Error(payload),
110 }
111 }
112
113 /// Sets the recipient of this stanza.
114 pub fn with_to(mut self, to: Jid) -> Iq {
115 self.to = Some(to);
116 self
117 }
118
119 /// Sets the emitter of this stanza.
120 pub fn with_from(mut self, from: Jid) -> Iq {
121 self.from = Some(from);
122 self
123 }
124
125 /// Sets the id of this stanza, in order to later match its response.
126 pub fn with_id(mut self, id: String) -> Iq {
127 self.id = id;
128 self
129 }
130}
131
132impl TryFrom<Element> for Iq {
133 type Error = Error;
134
135 fn try_from(root: Element) -> Result<Iq, Error> {
136 check_self!(root, "iq", DEFAULT_NS);
137 let from = get_attr!(root, "from", Option);
138 let to = get_attr!(root, "to", Option);
139 let id = get_attr!(root, "id", Required);
140 let type_: String = get_attr!(root, "type", Required);
141
142 let mut payload = None;
143 let mut error_payload = None;
144 for elem in root.children() {
145 if payload.is_some() {
146 return Err(Error::ParseError("Wrong number of children in iq element."));
147 }
148 if type_ == "error" {
149 if elem.is("error", ns::DEFAULT_NS) {
150 if error_payload.is_some() {
151 return Err(Error::ParseError("Wrong number of children in iq element."));
152 }
153 error_payload = Some(StanzaError::try_from(elem.clone())?);
154 } else if root.children().count() != 2 {
155 return Err(Error::ParseError("Wrong number of children in iq element."));
156 }
157 } else {
158 payload = Some(elem.clone());
159 }
160 }
161
162 let type_ = if type_ == "get" {
163 if let Some(payload) = payload {
164 IqType::Get(payload)
165 } else {
166 return Err(Error::ParseError("Wrong number of children in iq element."));
167 }
168 } else if type_ == "set" {
169 if let Some(payload) = payload {
170 IqType::Set(payload)
171 } else {
172 return Err(Error::ParseError("Wrong number of children in iq element."));
173 }
174 } else if type_ == "result" {
175 if let Some(payload) = payload {
176 IqType::Result(Some(payload))
177 } else {
178 IqType::Result(None)
179 }
180 } else if type_ == "error" {
181 if let Some(payload) = error_payload {
182 IqType::Error(payload)
183 } else {
184 return Err(Error::ParseError("Wrong number of children in iq element."));
185 }
186 } else {
187 return Err(Error::ParseError("Unknown iq type."));
188 };
189
190 Ok(Iq {
191 from,
192 to,
193 id,
194 payload: type_,
195 })
196 }
197}
198
199impl From<Iq> for Element {
200 fn from(iq: Iq) -> Element {
201 let mut stanza = Element::builder("iq")
202 .ns(ns::DEFAULT_NS)
203 .attr("from", iq.from)
204 .attr("to", iq.to)
205 .attr("id", iq.id)
206 .attr("type", &iq.payload)
207 .build();
208 let elem = match iq.payload {
209 IqType::Get(elem) | IqType::Set(elem) | IqType::Result(Some(elem)) => elem,
210 IqType::Error(error) => error.into(),
211 IqType::Result(None) => return stanza,
212 };
213 stanza.append_child(elem);
214 stanza
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::util::compare_elements::NamespaceAwareCompare;
222 use crate::disco::DiscoInfoQuery;
223 use crate::stanza_error::{DefinedCondition, ErrorType};
224
225 #[cfg(target_pointer_width = "32")]
226 #[test]
227 fn test_size() {
228 assert_size!(IqType, 112);
229 assert_size!(Iq, 204);
230 }
231
232 #[cfg(target_pointer_width = "64")]
233 #[test]
234 fn test_size() {
235 assert_size!(IqType, 224);
236 assert_size!(Iq, 408);
237 }
238
239 #[test]
240 fn test_require_type() {
241 #[cfg(not(feature = "component"))]
242 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
243 #[cfg(feature = "component")]
244 let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap();
245 let error = Iq::try_from(elem).unwrap_err();
246 let message = match error {
247 Error::ParseError(string) => string,
248 _ => panic!(),
249 };
250 assert_eq!(message, "Required attribute 'id' missing.");
251
252 #[cfg(not(feature = "component"))]
253 let elem: Element = "<iq xmlns='jabber:client' id='coucou'/>".parse().unwrap();
254 #[cfg(feature = "component")]
255 let elem: Element = "<iq xmlns='jabber:component:accept' id='coucou'/>".parse().unwrap();
256 let error = Iq::try_from(elem).unwrap_err();
257 let message = match error {
258 Error::ParseError(string) => string,
259 _ => panic!(),
260 };
261 assert_eq!(message, "Required attribute 'type' missing.");
262 }
263
264 #[test]
265 fn test_get() {
266 #[cfg(not(feature = "component"))]
267 let elem: Element = "<iq xmlns='jabber:client' type='get' id='foo'>
268 <foo xmlns='bar'/>
269 </iq>"
270 .parse()
271 .unwrap();
272 #[cfg(feature = "component")]
273 let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='foo'>
274 <foo xmlns='bar'/>
275 </iq>"
276 .parse()
277 .unwrap();
278 let iq = Iq::try_from(elem).unwrap();
279 let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
280 assert_eq!(iq.from, None);
281 assert_eq!(iq.to, None);
282 assert_eq!(&iq.id, "foo");
283 assert!(match iq.payload {
284 IqType::Get(element) => element.compare_to(&query),
285 _ => false,
286 });
287 }
288
289 #[test]
290 fn test_set() {
291 #[cfg(not(feature = "component"))]
292 let elem: Element = "<iq xmlns='jabber:client' type='set' id='vcard'>
293 <vCard xmlns='vcard-temp'/>
294 </iq>"
295 .parse()
296 .unwrap();
297 #[cfg(feature = "component")]
298 let elem: Element = "<iq xmlns='jabber:component:accept' type='set' id='vcard'>
299 <vCard xmlns='vcard-temp'/>
300 </iq>"
301 .parse()
302 .unwrap();
303 let iq = Iq::try_from(elem).unwrap();
304 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
305 assert_eq!(iq.from, None);
306 assert_eq!(iq.to, None);
307 assert_eq!(&iq.id, "vcard");
308 assert!(match iq.payload {
309 IqType::Set(element) => element.compare_to(&vcard),
310 _ => false,
311 });
312 }
313
314 #[test]
315 fn test_result_empty() {
316 #[cfg(not(feature = "component"))]
317 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>".parse().unwrap();
318 #[cfg(feature = "component")]
319 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
320 .parse()
321 .unwrap();
322 let iq = Iq::try_from(elem).unwrap();
323 assert_eq!(iq.from, None);
324 assert_eq!(iq.to, None);
325 assert_eq!(&iq.id, "res");
326 assert!(match iq.payload {
327 IqType::Result(None) => true,
328 _ => false,
329 });
330 }
331
332 #[test]
333 fn test_result() {
334 #[cfg(not(feature = "component"))]
335 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'>
336 <query xmlns='http://jabber.org/protocol/disco#items'/>
337 </iq>"
338 .parse()
339 .unwrap();
340 #[cfg(feature = "component")]
341 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'>
342 <query xmlns='http://jabber.org/protocol/disco#items'/>
343 </iq>"
344 .parse()
345 .unwrap();
346 let iq = Iq::try_from(elem).unwrap();
347 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
348 .parse()
349 .unwrap();
350 assert_eq!(iq.from, None);
351 assert_eq!(iq.to, None);
352 assert_eq!(&iq.id, "res");
353 assert!(match iq.payload {
354 IqType::Result(Some(element)) => element.compare_to(&query),
355 _ => false,
356 });
357 }
358
359 #[test]
360 fn test_error() {
361 #[cfg(not(feature = "component"))]
362 let elem: Element = "<iq xmlns='jabber:client' type='error' id='err1'>
363 <ping xmlns='urn:xmpp:ping'/>
364 <error type='cancel'>
365 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
366 </error>
367 </iq>"
368 .parse()
369 .unwrap();
370 #[cfg(feature = "component")]
371 let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='err1'>
372 <ping xmlns='urn:xmpp:ping'/>
373 <error type='cancel'>
374 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
375 </error>
376 </iq>"
377 .parse()
378 .unwrap();
379 let iq = Iq::try_from(elem).unwrap();
380 assert_eq!(iq.from, None);
381 assert_eq!(iq.to, None);
382 assert_eq!(iq.id, "err1");
383 match iq.payload {
384 IqType::Error(error) => {
385 assert_eq!(error.type_, ErrorType::Cancel);
386 assert_eq!(error.by, None);
387 assert_eq!(
388 error.defined_condition,
389 DefinedCondition::ServiceUnavailable
390 );
391 assert_eq!(error.texts.len(), 0);
392 assert_eq!(error.other, None);
393 }
394 _ => panic!(),
395 }
396 }
397
398 #[test]
399 fn test_children_invalid() {
400 #[cfg(not(feature = "component"))]
401 let elem: Element = "<iq xmlns='jabber:client' type='error' id='error'/>"
402 .parse()
403 .unwrap();
404 #[cfg(feature = "component")]
405 let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='error'/>"
406 .parse()
407 .unwrap();
408 let error = Iq::try_from(elem).unwrap_err();
409 let message = match error {
410 Error::ParseError(string) => string,
411 _ => panic!(),
412 };
413 assert_eq!(message, "Wrong number of children in iq element.");
414 }
415
416 #[test]
417 fn test_serialise() {
418 #[cfg(not(feature = "component"))]
419 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>".parse().unwrap();
420 #[cfg(feature = "component")]
421 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
422 .parse()
423 .unwrap();
424 let iq2 = Iq {
425 from: None,
426 to: None,
427 id: String::from("res"),
428 payload: IqType::Result(None),
429 };
430 let elem2 = iq2.into();
431 assert_eq!(elem, elem2);
432 }
433
434 #[test]
435 fn test_disco() {
436 #[cfg(not(feature = "component"))]
437 let elem: Element = "<iq xmlns='jabber:client' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
438 #[cfg(feature = "component")]
439 let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
440 let iq = Iq::try_from(elem).unwrap();
441 let disco_info = match iq.payload {
442 IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(),
443 _ => panic!(),
444 };
445 assert!(disco_info.node.is_none());
446 }
447}