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 IntoAttributeValue for &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 _ctx: &::xso::Context<'_>,
233 ) -> Result<Self::Builder, ::xso::error::FromEventsError> {
234 if qname.0 != crate::ns::DEFAULT_NS || qname.1 != "iq" {
235 return Err(::xso::error::FromEventsError::Mismatch { name: qname, attrs });
236 }
237 Self::Builder::new(qname, attrs)
238 }
239}
240
241impl ::xso::AsXml for Iq {
242 type ItemIter<'x> = ::xso::minidom_compat::AsItemsViaElement<'x>;
243
244 fn as_xml_iter(&self) -> Result<Self::ItemIter<'_>, ::xso::error::Error> {
245 ::xso::minidom_compat::AsItemsViaElement::new(self.clone())
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use crate::disco::DiscoInfoQuery;
253 use crate::stanza_error::{DefinedCondition, ErrorType};
254
255 #[cfg(target_pointer_width = "32")]
256 #[test]
257 fn test_size() {
258 assert_size!(IqType, 108);
259 assert_size!(Iq, 152);
260 }
261
262 #[cfg(target_pointer_width = "64")]
263 #[test]
264 fn test_size() {
265 assert_size!(IqType, 216);
266 assert_size!(Iq, 304);
267 }
268
269 #[test]
270 fn test_require_type() {
271 #[cfg(not(feature = "component"))]
272 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
273 #[cfg(feature = "component")]
274 let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap();
275 let error = Iq::try_from(elem).unwrap_err();
276 let message = match error {
277 FromElementError::Invalid(Error::Other(string)) => string,
278 _ => panic!(),
279 };
280 assert_eq!(message, "Required attribute 'id' missing.");
281
282 #[cfg(not(feature = "component"))]
283 let elem: Element = "<iq xmlns='jabber:client' id='coucou'/>".parse().unwrap();
284 #[cfg(feature = "component")]
285 let elem: Element = "<iq xmlns='jabber:component:accept' id='coucou'/>"
286 .parse()
287 .unwrap();
288 let error = Iq::try_from(elem).unwrap_err();
289 let message = match error {
290 FromElementError::Invalid(Error::Other(string)) => string,
291 _ => panic!(),
292 };
293 assert_eq!(message, "Required attribute 'type' missing.");
294 }
295
296 #[test]
297 fn test_get() {
298 #[cfg(not(feature = "component"))]
299 let elem: Element = "<iq xmlns='jabber:client' type='get' id='foo'>
300 <foo xmlns='bar'/>
301 </iq>"
302 .parse()
303 .unwrap();
304 #[cfg(feature = "component")]
305 let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='foo'>
306 <foo xmlns='bar'/>
307 </iq>"
308 .parse()
309 .unwrap();
310 let iq = Iq::try_from(elem).unwrap();
311 let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
312 assert_eq!(iq.from, None);
313 assert_eq!(iq.to, None);
314 assert_eq!(&iq.id, "foo");
315 assert!(match iq.payload {
316 IqType::Get(element) => element == query,
317 _ => false,
318 });
319 }
320
321 #[test]
322 fn test_set() {
323 #[cfg(not(feature = "component"))]
324 let elem: Element = "<iq xmlns='jabber:client' type='set' id='vcard'>
325 <vCard xmlns='vcard-temp'/>
326 </iq>"
327 .parse()
328 .unwrap();
329 #[cfg(feature = "component")]
330 let elem: Element = "<iq xmlns='jabber:component:accept' type='set' id='vcard'>
331 <vCard xmlns='vcard-temp'/>
332 </iq>"
333 .parse()
334 .unwrap();
335 let iq = Iq::try_from(elem).unwrap();
336 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
337 assert_eq!(iq.from, None);
338 assert_eq!(iq.to, None);
339 assert_eq!(&iq.id, "vcard");
340 assert!(match iq.payload {
341 IqType::Set(element) => element == vcard,
342 _ => false,
343 });
344 }
345
346 #[test]
347 fn test_result_empty() {
348 #[cfg(not(feature = "component"))]
349 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>"
350 .parse()
351 .unwrap();
352 #[cfg(feature = "component")]
353 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
354 .parse()
355 .unwrap();
356 let iq = Iq::try_from(elem).unwrap();
357 assert_eq!(iq.from, None);
358 assert_eq!(iq.to, None);
359 assert_eq!(&iq.id, "res");
360 assert!(match iq.payload {
361 IqType::Result(None) => true,
362 _ => false,
363 });
364 }
365
366 #[test]
367 fn test_result() {
368 #[cfg(not(feature = "component"))]
369 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'>
370 <query xmlns='http://jabber.org/protocol/disco#items'/>
371 </iq>"
372 .parse()
373 .unwrap();
374 #[cfg(feature = "component")]
375 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'>
376 <query xmlns='http://jabber.org/protocol/disco#items'/>
377 </iq>"
378 .parse()
379 .unwrap();
380 let iq = Iq::try_from(elem).unwrap();
381 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>"
382 .parse()
383 .unwrap();
384 assert_eq!(iq.from, None);
385 assert_eq!(iq.to, None);
386 assert_eq!(&iq.id, "res");
387 assert!(match iq.payload {
388 IqType::Result(Some(element)) => element == query,
389 _ => false,
390 });
391 }
392
393 #[test]
394 fn test_error() {
395 #[cfg(not(feature = "component"))]
396 let elem: Element = "<iq xmlns='jabber:client' type='error' id='err1'>
397 <ping xmlns='urn:xmpp:ping'/>
398 <error type='cancel'>
399 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
400 </error>
401 </iq>"
402 .parse()
403 .unwrap();
404 #[cfg(feature = "component")]
405 let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='err1'>
406 <ping xmlns='urn:xmpp:ping'/>
407 <error type='cancel'>
408 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
409 </error>
410 </iq>"
411 .parse()
412 .unwrap();
413 let iq = Iq::try_from(elem).unwrap();
414 assert_eq!(iq.from, None);
415 assert_eq!(iq.to, None);
416 assert_eq!(iq.id, "err1");
417 match iq.payload {
418 IqType::Error(error) => {
419 assert_eq!(error.type_, ErrorType::Cancel);
420 assert_eq!(error.by, None);
421 assert_eq!(
422 error.defined_condition,
423 DefinedCondition::ServiceUnavailable
424 );
425 assert_eq!(error.texts.len(), 0);
426 assert_eq!(error.other, None);
427 }
428 _ => panic!(),
429 }
430 }
431
432 #[test]
433 fn test_children_invalid() {
434 #[cfg(not(feature = "component"))]
435 let elem: Element = "<iq xmlns='jabber:client' type='error' id='error'/>"
436 .parse()
437 .unwrap();
438 #[cfg(feature = "component")]
439 let elem: Element = "<iq xmlns='jabber:component:accept' type='error' id='error'/>"
440 .parse()
441 .unwrap();
442 let error = Iq::try_from(elem).unwrap_err();
443 let message = match error {
444 FromElementError::Invalid(Error::Other(string)) => string,
445 _ => panic!(),
446 };
447 assert_eq!(message, "Wrong number of children in iq element.");
448 }
449
450 #[test]
451 fn test_serialise() {
452 #[cfg(not(feature = "component"))]
453 let elem: Element = "<iq xmlns='jabber:client' type='result' id='res'/>"
454 .parse()
455 .unwrap();
456 #[cfg(feature = "component")]
457 let elem: Element = "<iq xmlns='jabber:component:accept' type='result' id='res'/>"
458 .parse()
459 .unwrap();
460 let iq2 = Iq {
461 from: None,
462 to: None,
463 id: String::from("res"),
464 payload: IqType::Result(None),
465 };
466 let elem2 = iq2.into();
467 assert_eq!(elem, elem2);
468 }
469
470 #[test]
471 fn test_disco() {
472 #[cfg(not(feature = "component"))]
473 let elem: Element = "<iq xmlns='jabber:client' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
474 #[cfg(feature = "component")]
475 let elem: Element = "<iq xmlns='jabber:component:accept' type='get' id='disco'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
476 let iq = Iq::try_from(elem).unwrap();
477 let disco_info = match iq.payload {
478 IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(),
479 _ => panic!(),
480 };
481 assert!(disco_info.node.is_none());
482 }
483}