diff --git a/src/iq.rs b/src/iq.rs new file mode 100644 index 00000000..afdb463f --- /dev/null +++ b/src/iq.rs @@ -0,0 +1,257 @@ +use minidom::Element; +use minidom::IntoAttributeValue; + +use jid::Jid; + +use error::Error; + +use ns; + +/// Lists every known payload of a ``. +#[derive(Debug, Clone)] +pub enum IqPayload { +} + +#[derive(Debug, Clone)] +pub enum IqPayloadType { + XML(Element), + Parsed(IqPayload), +} + +#[derive(Debug, Clone)] +pub enum IqType { + Get(IqPayloadType), + Set(IqPayloadType), + Result(Option), + Error(IqPayloadType), +} + +impl IntoAttributeValue for IqType { + fn into_attribute_value(self) -> Option { + Some(match self { + IqType::Get(_) => "get", + IqType::Set(_) => "set", + IqType::Result(_) => "result", + IqType::Error(_) => "error", + }.to_owned()) + } +} + +#[derive(Debug, Clone)] +pub struct Iq { + pub from: Option, + pub to: Option, + pub id: Option, + pub payload: IqType, +} + +pub fn parse_iq(root: &Element) -> Result { + if !root.is("iq", ns::JABBER_CLIENT) { + return Err(Error::ParseError("This is not an iq element.")); + } + let from = root.attr("from") + .and_then(|value| value.parse().ok()); + let to = root.attr("to") + .and_then(|value| value.parse().ok()); + let id = root.attr("id") + .and_then(|value| value.parse().ok()); + let type_ = match root.attr("type") { + Some(type_) => type_, + None => return Err(Error::ParseError("Iq element requires a 'type' attribute.")), + }; + + let mut payload = None; + for elem in root.children() { + if payload.is_some() { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + if type_ == "error" { + if elem.is("error", ns::JABBER_CLIENT) { + payload = Some(elem); + } else if root.children().collect::>().len() != 2 { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else { + payload = Some(elem); + } + } + + let type_ = if type_ == "get" { + if let Some(payload) = payload.clone() { + IqType::Get(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "set" { + if let Some(payload) = payload.clone() { + IqType::Set(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else if type_ == "result" { + if let Some(payload) = payload.clone() { + IqType::Result(Some(IqPayloadType::XML(payload.clone()))) + } else { + IqType::Result(None) + } + } else if type_ == "error" { + if let Some(payload) = payload.clone() { + IqType::Error(IqPayloadType::XML(payload.clone())) + } else { + return Err(Error::ParseError("Wrong number of children in iq element.")); + } + } else { + panic!() + }; + + Ok(Iq { + from: from, + to: to, + id: id, + payload: type_, + }) +} + +pub fn serialise(iq: &Iq) -> Element { + let mut stanza = Element::builder("iq") + .ns(ns::JABBER_CLIENT) + .attr("from", iq.from.clone().and_then(|value| Some(String::from(value)))) + .attr("to", iq.to.clone().and_then(|value| Some(String::from(value)))) + .attr("id", iq.id.clone()) + .attr("type", iq.payload.clone()) + .build(); + let elem = match iq.payload.clone() { + IqType::Get(IqPayloadType::XML(elem)) => elem, + IqType::Set(IqPayloadType::XML(elem)) => elem, + IqType::Result(None) => return stanza, + IqType::Result(Some(IqPayloadType::XML(elem))) => elem, + IqType::Error(IqPayloadType::XML(elem)) => elem, + _ => panic!(), + }; + stanza.append_child(elem); + stanza +} + +#[cfg(test)] +mod tests { + use minidom::Element; + use error::Error; + use iq; + + #[test] + fn test_require_type() { + let elem: Element = "".parse().unwrap(); + let error = iq::parse_iq(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Iq element requires a 'type' attribute."); + } + + #[test] + fn test_get() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let query: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Get(iq::IqPayloadType::XML(element)) => element == query, + _ => false + }); + } + + #[test] + fn test_set() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let vcard: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Set(iq::IqPayloadType::XML(element)) => element == vcard, + _ => false + }); + } + + #[test] + fn test_result_empty() { + let elem: Element = "".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Result(None) => true, + _ => false, + }); + } + + #[test] + fn test_result() { + let elem: Element = " + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let query: Element = "".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Result(Some(iq::IqPayloadType::XML(element))) => element == query, + _ => false, + }); + } + + #[test] + fn test_error() { + let elem: Element = " + + + + + ".parse().unwrap(); + let iq = iq::parse_iq(&elem).unwrap(); + let error: Element = " + + ".parse().unwrap(); + assert_eq!(iq.from, None); + assert_eq!(iq.to, None); + assert_eq!(iq.id, None); + assert!(match iq.payload { + iq::IqType::Error(iq::IqPayloadType::XML(element)) => element == error, + _ => false, + }); + } + + #[test] + fn test_children_invalid() { + let elem: Element = "".parse().unwrap(); + let error = iq::parse_iq(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Wrong number of children in iq element."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let iq2 = iq::Iq { + from: None, + to: None, + id: None, + payload: iq::IqType::Result(None), + }; + let elem2 = iq::serialise(&iq2); + assert_eq!(elem, elem2); + } +} diff --git a/src/lib.rs b/src/lib.rs index 2536aaa4..a21168fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,8 @@ pub mod ns; pub mod message; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod presence; +/// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core +pub mod iq; /// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence pub mod body;