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