iq.rs

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