iq.rs

  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#[derive(Debug, Clone)]
 31pub enum IqType {
 32    Get(Element),
 33    Set(Element),
 34    Result(Option<Element>),
 35    Error(StanzaError),
 36}
 37
 38impl<'a> IntoAttributeValue for &'a IqType {
 39    fn into_attribute_value(self) -> Option<String> {
 40        Some(match *self {
 41            IqType::Get(_) => "get",
 42            IqType::Set(_) => "set",
 43            IqType::Result(_) => "result",
 44            IqType::Error(_) => "error",
 45        }.to_owned())
 46    }
 47}
 48
 49/// The main structure representing the `<iq/>` stanza.
 50#[derive(Debug, Clone)]
 51pub struct Iq {
 52    pub from: Option<Jid>,
 53    pub to: Option<Jid>,
 54    pub id: Option<String>,
 55    pub payload: IqType,
 56}
 57
 58impl Iq {
 59    pub fn from_get(payload: impl IqGetPayload) -> Iq {
 60        Iq {
 61            from: None,
 62            to: None,
 63            id: None,
 64            payload: IqType::Get(payload.into()),
 65        }
 66    }
 67
 68    pub fn from_set(payload: impl IqSetPayload) -> Iq {
 69        Iq {
 70            from: None,
 71            to: None,
 72            id: None,
 73            payload: IqType::Set(payload.into()),
 74        }
 75    }
 76
 77    pub fn from_result(payload: Option<impl IqResultPayload>) -> Iq {
 78        Iq {
 79            from: None,
 80            to: None,
 81            id: None,
 82            payload: IqType::Result(payload.map(|payload| payload.into())),
 83        }
 84    }
 85
 86    pub fn from_error(payload: StanzaError) -> Iq {
 87        Iq {
 88            from: None,
 89            to: None,
 90            id: None,
 91            payload: IqType::Error(payload),
 92        }
 93    }
 94
 95    pub fn with_to(mut self, to: Jid) -> Iq {
 96        self.to = Some(to);
 97        self
 98    }
 99
100    pub fn with_from(mut self, from: Jid) -> Iq {
101        self.from = Some(from);
102        self
103    }
104
105    pub fn with_id(mut self, id: String) -> Iq {
106        self.id = Some(id);
107        self
108    }
109}
110
111impl TryFrom<Element> for Iq {
112    type Err = Error;
113
114    fn try_from(root: Element) -> Result<Iq, Error> {
115        check_self!(root, "iq", DEFAULT_NS);
116        let from = get_attr!(root, "from", optional);
117        let to = get_attr!(root, "to", optional);
118        let id = get_attr!(root, "id", optional);
119        let type_: String = get_attr!(root, "type", required);
120
121        let mut payload = None;
122        let mut error_payload = None;
123        for elem in root.children() {
124            if payload.is_some() {
125                return Err(Error::ParseError("Wrong number of children in iq element."));
126            }
127            if type_ == "error" {
128                if elem.is("error", ns::DEFAULT_NS) {
129                    if error_payload.is_some() {
130                        return Err(Error::ParseError("Wrong number of children in iq element."));
131                    }
132                    error_payload = Some(StanzaError::try_from(elem.clone())?);
133                } else if root.children().count() != 2 {
134                    return Err(Error::ParseError("Wrong number of children in iq element."));
135                }
136            } else {
137                payload = Some(elem.clone());
138            }
139        }
140
141        let type_ = if type_ == "get" {
142            if let Some(payload) = payload {
143                IqType::Get(payload)
144            } else {
145                return Err(Error::ParseError("Wrong number of children in iq element."));
146            }
147        } else if type_ == "set" {
148            if let Some(payload) = payload {
149                IqType::Set(payload)
150            } else {
151                return Err(Error::ParseError("Wrong number of children in iq element."));
152            }
153        } else if type_ == "result" {
154            if let Some(payload) = payload {
155                IqType::Result(Some(payload))
156            } else {
157                IqType::Result(None)
158            }
159        } else if type_ == "error" {
160            if let Some(payload) = error_payload {
161                IqType::Error(payload)
162            } else {
163                return Err(Error::ParseError("Wrong number of children in iq element."));
164            }
165        } else {
166            return Err(Error::ParseError("Unknown iq type."));
167        };
168
169        Ok(Iq {
170            from: from,
171            to: to,
172            id: id,
173            payload: type_,
174        })
175    }
176}
177
178impl From<Iq> for Element {
179    fn from(iq: Iq) -> Element {
180        let mut stanza = Element::builder("iq")
181                                 .ns(ns::DEFAULT_NS)
182                                 .attr("from", iq.from)
183                                 .attr("to", iq.to)
184                                 .attr("id", iq.id)
185                                 .attr("type", &iq.payload)
186                                 .build();
187        let elem = match iq.payload {
188            IqType::Get(elem)
189          | IqType::Set(elem)
190          | IqType::Result(Some(elem)) => elem,
191            IqType::Error(error) => error.into(),
192            IqType::Result(None) => return stanza,
193        };
194        stanza.append_child(elem);
195        stanza
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202    use stanza_error::{ErrorType, DefinedCondition};
203    use compare_elements::NamespaceAwareCompare;
204    use disco::DiscoInfoQuery;
205
206    #[test]
207    fn test_require_type() {
208        #[cfg(not(feature = "component"))]
209        let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
210        #[cfg(feature = "component")]
211        let elem: Element = "<iq xmlns='jabber:component:accept'/>".parse().unwrap();
212        let error = Iq::try_from(elem).unwrap_err();
213        let message = match error {
214            Error::ParseError(string) => string,
215            _ => panic!(),
216        };
217        assert_eq!(message, "Required attribute 'type' missing.");
218    }
219
220    #[test]
221    fn test_get() {
222        #[cfg(not(feature = "component"))]
223        let elem: Element = "<iq xmlns='jabber:client' type='get'>
224            <foo xmlns='bar'/>
225        </iq>".parse().unwrap();
226        #[cfg(feature = "component")]
227        let elem: Element = "<iq xmlns='jabber:component:accept' type='get'>
228            <foo xmlns='bar'/>
229        </iq>".parse().unwrap();
230        let iq = Iq::try_from(elem).unwrap();
231        let query: Element = "<foo xmlns='bar'/>".parse().unwrap();
232        assert_eq!(iq.from, None);
233        assert_eq!(iq.to, None);
234        assert_eq!(iq.id, None);
235        assert!(match iq.payload {
236            IqType::Get(element) => element.compare_to(&query),
237            _ => false
238        });
239    }
240
241    #[test]
242    fn test_set() {
243        #[cfg(not(feature = "component"))]
244        let elem: Element = "<iq xmlns='jabber:client' type='set'>
245            <vCard xmlns='vcard-temp'/>
246        </iq>".parse().unwrap();
247        #[cfg(feature = "component")]
248        let elem: Element = "<iq xmlns='jabber:component:accept' type='set'>
249            <vCard xmlns='vcard-temp'/>
250        </iq>".parse().unwrap();
251        let iq = Iq::try_from(elem).unwrap();
252        let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
253        assert_eq!(iq.from, None);
254        assert_eq!(iq.to, None);
255        assert_eq!(iq.id, None);
256        assert!(match iq.payload {
257            IqType::Set(element) => element.compare_to(&vcard),
258            _ => false
259        });
260    }
261
262    #[test]
263    fn test_result_empty() {
264        #[cfg(not(feature = "component"))]
265        let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
266        #[cfg(feature = "component")]
267        let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
268        let iq = Iq::try_from(elem).unwrap();
269        assert_eq!(iq.from, None);
270        assert_eq!(iq.to, None);
271        assert_eq!(iq.id, None);
272        assert!(match iq.payload {
273            IqType::Result(None) => true,
274            _ => false,
275        });
276    }
277
278    #[test]
279    fn test_result() {
280        #[cfg(not(feature = "component"))]
281        let elem: Element = "<iq xmlns='jabber:client' type='result'>
282            <query xmlns='http://jabber.org/protocol/disco#items'/>
283        </iq>".parse().unwrap();
284        #[cfg(feature = "component")]
285        let elem: Element = "<iq xmlns='jabber:component:accept' type='result'>
286            <query xmlns='http://jabber.org/protocol/disco#items'/>
287        </iq>".parse().unwrap();
288        let iq = Iq::try_from(elem).unwrap();
289        let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
290        assert_eq!(iq.from, None);
291        assert_eq!(iq.to, None);
292        assert_eq!(iq.id, None);
293        assert!(match iq.payload {
294            IqType::Result(Some(element)) => element.compare_to(&query),
295            _ => false,
296        });
297    }
298
299    #[test]
300    fn test_error() {
301        #[cfg(not(feature = "component"))]
302        let elem: Element = "<iq xmlns='jabber:client' type='error'>
303            <ping xmlns='urn:xmpp:ping'/>
304            <error type='cancel'>
305                <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
306            </error>
307        </iq>".parse().unwrap();
308        #[cfg(feature = "component")]
309        let elem: Element = "<iq xmlns='jabber:component:accept' type='error'>
310            <ping xmlns='urn:xmpp:ping'/>
311            <error type='cancel'>
312                <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
313            </error>
314        </iq>".parse().unwrap();
315        let iq = Iq::try_from(elem).unwrap();
316        assert_eq!(iq.from, None);
317        assert_eq!(iq.to, None);
318        assert_eq!(iq.id, None);
319        match iq.payload {
320            IqType::Error(error) => {
321                assert_eq!(error.type_, ErrorType::Cancel);
322                assert_eq!(error.by, None);
323                assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
324                assert_eq!(error.texts.len(), 0);
325                assert_eq!(error.other, None);
326            },
327            _ => panic!(),
328        }
329    }
330
331    #[test]
332    fn test_children_invalid() {
333        #[cfg(not(feature = "component"))]
334        let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
335        #[cfg(feature = "component")]
336        let elem: Element = "<iq xmlns='jabber:component:accept' type='error'></iq>".parse().unwrap();
337        let error = Iq::try_from(elem).unwrap_err();
338        let message = match error {
339            Error::ParseError(string) => string,
340            _ => panic!(),
341        };
342        assert_eq!(message, "Wrong number of children in iq element.");
343    }
344
345    #[test]
346    fn test_serialise() {
347        #[cfg(not(feature = "component"))]
348        let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
349        #[cfg(feature = "component")]
350        let elem: Element = "<iq xmlns='jabber:component:accept' type='result'/>".parse().unwrap();
351        let iq2 = Iq {
352            from: None,
353            to: None,
354            id: None,
355            payload: IqType::Result(None),
356        };
357        let elem2 = iq2.into();
358        assert_eq!(elem, elem2);
359    }
360
361    #[test]
362    fn test_disco() {
363        #[cfg(not(feature = "component"))]
364        let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
365        #[cfg(feature = "component")]
366        let elem: Element = "<iq xmlns='jabber:component:accept' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
367        let iq = Iq::try_from(elem).unwrap();
368        let disco_info = match iq.payload {
369            IqType::Get(payload) => DiscoInfoQuery::try_from(payload).unwrap(),
370            _ => panic!(),
371        };
372        assert!(disco_info.node.is_none());
373    }
374}