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