iq.rs

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