iq.rs

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