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 std::convert::TryFrom;
  9
 10use minidom::Element;
 11use minidom::IntoAttributeValue;
 12
 13use jid::Jid;
 14
 15use error::Error;
 16
 17use ns;
 18
 19use stanza_error::StanzaError;
 20use disco::Disco;
 21use ibb::IBB;
 22use jingle::Jingle;
 23use ping::Ping;
 24use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs};
 25
 26/// Lists every known payload of a `<iq/>`.
 27#[derive(Debug, Clone)]
 28pub enum IqPayload {
 29    Disco(Disco),
 30    IBB(IBB),
 31    Jingle(Jingle),
 32    Ping(Ping),
 33    MamQuery(MamQuery),
 34    MamFin(MamFin),
 35    MamPrefs(MamPrefs),
 36
 37    Unknown(Element),
 38}
 39
 40impl TryFrom<Element> for IqPayload {
 41    type Error = Error;
 42
 43    fn try_from(elem: Element) -> Result<IqPayload, Error> {
 44        Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) {
 45            // XEP-0030
 46            ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?),
 47
 48            // XEP-0047
 49            ("open", ns::IBB)
 50          | ("data", ns::IBB)
 51          | ("close", ns::IBB) => IqPayload::IBB(IBB::try_from(elem)?),
 52
 53            // XEP-0166
 54            ("jingle", ns::JINGLE) => IqPayload::Jingle(Jingle::try_from(elem)?),
 55
 56            // XEP-0199
 57            ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?),
 58
 59            // XEP-0313
 60            ("query", ns::MAM) => IqPayload::MamQuery(MamQuery::try_from(elem)?),
 61            ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?),
 62            ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?),
 63
 64            _ => IqPayload::Unknown(elem),
 65        })
 66    }
 67}
 68
 69#[derive(Debug, Clone)]
 70pub enum IqType {
 71    Get(Element),
 72    Set(Element),
 73    Result(Option<Element>),
 74    Error(StanzaError),
 75}
 76
 77impl<'a> IntoAttributeValue for &'a IqType {
 78    fn into_attribute_value(self) -> Option<String> {
 79        Some(match *self {
 80            IqType::Get(_) => "get",
 81            IqType::Set(_) => "set",
 82            IqType::Result(_) => "result",
 83            IqType::Error(_) => "error",
 84        }.to_owned())
 85    }
 86}
 87
 88#[derive(Debug, Clone)]
 89pub struct Iq {
 90    pub from: Option<Jid>,
 91    pub to: Option<Jid>,
 92    pub id: Option<String>,
 93    pub payload: IqType,
 94}
 95
 96impl TryFrom<Element> for Iq {
 97    type Error = Error;
 98
 99    fn try_from(root: Element) -> Result<Iq, Error> {
100        if !root.is("iq", ns::JABBER_CLIENT) {
101            return Err(Error::ParseError("This is not an iq element."));
102        }
103        let from = get_attr!(root, "from", optional);
104        let to = get_attr!(root, "to", optional);
105        let id = get_attr!(root, "id", optional);
106        let type_: String = get_attr!(root, "type", required);
107
108        let mut payload = None;
109        let mut error_payload = None;
110        for elem in root.children() {
111            if payload.is_some() {
112                return Err(Error::ParseError("Wrong number of children in iq element."));
113            }
114            if type_ == "error" {
115                if elem.is("error", ns::JABBER_CLIENT) {
116                    if error_payload.is_some() {
117                        return Err(Error::ParseError("Wrong number of children in iq element."));
118                    }
119                    error_payload = Some(StanzaError::try_from(elem.clone())?);
120                } else if root.children().collect::<Vec<_>>().len() != 2 {
121                    return Err(Error::ParseError("Wrong number of children in iq element."));
122                }
123            } else {
124                payload = Some(elem.clone());
125            }
126        }
127
128        let type_ = if type_ == "get" {
129            if let Some(payload) = payload {
130                IqType::Get(payload)
131            } else {
132                return Err(Error::ParseError("Wrong number of children in iq element."));
133            }
134        } else if type_ == "set" {
135            if let Some(payload) = payload {
136                IqType::Set(payload)
137            } else {
138                return Err(Error::ParseError("Wrong number of children in iq element."));
139            }
140        } else if type_ == "result" {
141            if let Some(payload) = payload {
142                IqType::Result(Some(payload))
143            } else {
144                IqType::Result(None)
145            }
146        } else if type_ == "error" {
147            if let Some(payload) = error_payload {
148                IqType::Error(payload)
149            } else {
150                return Err(Error::ParseError("Wrong number of children in iq element."));
151            }
152        } else {
153            return Err(Error::ParseError("Unknown iq type."));
154        };
155
156        Ok(Iq {
157            from: from,
158            to: to,
159            id: id,
160            payload: type_,
161        })
162    }
163}
164
165impl Into<Element> for IqPayload {
166    fn into(self) -> Element {
167        match self {
168            IqPayload::Disco(disco) => disco.into(),
169            IqPayload::IBB(ibb) => ibb.into(),
170            IqPayload::Jingle(jingle) => jingle.into(),
171            IqPayload::Ping(ping) => ping.into(),
172            IqPayload::MamQuery(query) => query.into(),
173            IqPayload::MamFin(fin) => fin.into(),
174            IqPayload::MamPrefs(prefs) => prefs.into(),
175
176            IqPayload::Unknown(elem) => elem,
177        }
178    }
179}
180
181impl Into<Element> for Iq {
182    fn into(self) -> Element {
183        let mut stanza = Element::builder("iq")
184                                 .ns(ns::JABBER_CLIENT)
185                                 .attr("from", self.from.and_then(|value| Some(String::from(value))))
186                                 .attr("to", self.to.and_then(|value| Some(String::from(value))))
187                                 .attr("id", self.id)
188                                 .attr("type", &self.payload)
189                                 .build();
190        let elem = match self.payload {
191            IqType::Get(elem)
192          | IqType::Set(elem)
193          | IqType::Result(Some(elem)) => elem,
194            IqType::Error(error) => error.into(),
195            IqType::Result(None) => return stanza,
196        };
197        stanza.append_child(elem);
198        stanza
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use stanza_error::{ErrorType, DefinedCondition};
206
207    #[test]
208    fn test_require_type() {
209        let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
210        let error = Iq::try_from(elem).unwrap_err();
211        let message = match error {
212            Error::ParseError(string) => string,
213            _ => panic!(),
214        };
215        assert_eq!(message, "Required attribute 'type' missing.");
216    }
217
218    #[test]
219    fn test_get() {
220        let elem: Element = "<iq xmlns='jabber:client' type='get'>
221            <foo/>
222        </iq>".parse().unwrap();
223        let iq = Iq::try_from(elem).unwrap();
224        let query: Element = "<foo xmlns='jabber:client'/>".parse().unwrap();
225        assert_eq!(iq.from, None);
226        assert_eq!(iq.to, None);
227        assert_eq!(iq.id, None);
228        assert!(match iq.payload {
229            IqType::Get(element) => element == query,
230            _ => false
231        });
232    }
233
234    #[test]
235    fn test_set() {
236        let elem: Element = "<iq xmlns='jabber:client' type='set'>
237            <vCard xmlns='vcard-temp'/>
238        </iq>".parse().unwrap();
239        let iq = Iq::try_from(elem).unwrap();
240        let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
241        assert_eq!(iq.from, None);
242        assert_eq!(iq.to, None);
243        assert_eq!(iq.id, None);
244        assert!(match iq.payload {
245            IqType::Set(element) => element == vcard,
246            _ => false
247        });
248    }
249
250    #[test]
251    fn test_result_empty() {
252        let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
253        let iq = Iq::try_from(elem).unwrap();
254        assert_eq!(iq.from, None);
255        assert_eq!(iq.to, None);
256        assert_eq!(iq.id, None);
257        assert!(match iq.payload {
258            IqType::Result(None) => true,
259            _ => false,
260        });
261    }
262
263    #[test]
264    fn test_result() {
265        let elem: Element = "<iq xmlns='jabber:client' type='result'>
266            <query xmlns='http://jabber.org/protocol/disco#items'/>
267        </iq>".parse().unwrap();
268        let iq = Iq::try_from(elem).unwrap();
269        let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
270        assert_eq!(iq.from, None);
271        assert_eq!(iq.to, None);
272        assert_eq!(iq.id, None);
273        assert!(match iq.payload {
274            IqType::Result(Some(element)) => element == query,
275            _ => false,
276        });
277    }
278
279    #[test]
280    fn test_error() {
281        let elem: Element = "<iq xmlns='jabber:client' type='error'>
282            <ping xmlns='urn:xmpp:ping'/>
283            <error type='cancel'>
284                <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
285            </error>
286        </iq>".parse().unwrap();
287        let iq = Iq::try_from(elem).unwrap();
288        assert_eq!(iq.from, None);
289        assert_eq!(iq.to, None);
290        assert_eq!(iq.id, None);
291        match iq.payload {
292            IqType::Error(error) => {
293                assert_eq!(error.type_, ErrorType::Cancel);
294                assert_eq!(error.by, None);
295                assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
296                assert_eq!(error.texts.len(), 0);
297                assert_eq!(error.other, None);
298            },
299            _ => panic!(),
300        }
301    }
302
303    #[test]
304    fn test_children_invalid() {
305        let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
306        let error = Iq::try_from(elem).unwrap_err();
307        let message = match error {
308            Error::ParseError(string) => string,
309            _ => panic!(),
310        };
311        assert_eq!(message, "Wrong number of children in iq element.");
312    }
313
314    #[test]
315    fn test_serialise() {
316        let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
317        let iq2 = Iq {
318            from: None,
319            to: None,
320            id: None,
321            payload: IqType::Result(None),
322        };
323        let elem2 = iq2.into();
324        assert_eq!(elem, elem2);
325    }
326
327    #[test]
328    fn test_disco() {
329        let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
330        let iq = Iq::try_from(elem).unwrap();
331        let payload = match iq.payload {
332            IqType::Get(payload) => IqPayload::try_from(payload).unwrap(),
333            _ => panic!(),
334        };
335        assert!(match payload {
336            IqPayload::Disco(Disco { .. }) => true,
337            _ => false,
338        });
339    }
340}