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