diff --git a/src/lib.rs b/src/lib.rs index 19689510..e2a656b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,9 @@ pub mod hashes; /// XEP-0308: Last Message Correction pub mod message_correct; +/// XEP-0359: Unique and Stable Stanza IDs +pub mod stanza_id; + /// XEP-0380: Explicit Message Encryption pub mod eme; diff --git a/src/ns.rs b/src/ns.rs index 415858c9..a3484637 100644 --- a/src/ns.rs +++ b/src/ns.rs @@ -57,6 +57,9 @@ pub const HASH_ALGO_BLAKE2B_512: &'static str = "urn:xmpp:hash-function-text-nam /// XEP-0308: Last Message Correction pub const MESSAGE_CORRECT: &'static str = "urn:xmpp:message-correct:0"; +/// XEP-0359: Unique and Stable Stanza IDs +pub const SID: &'static str = "urn:xmpp:sid:0"; + /// XEP-0380: Explicit Message Encryption pub const EME: &'static str = "urn:xmpp:eme:0"; diff --git a/src/stanza_id.rs b/src/stanza_id.rs new file mode 100644 index 00000000..14d842b7 --- /dev/null +++ b/src/stanza_id.rs @@ -0,0 +1,129 @@ +use minidom::Element; +use jid::Jid; + +use error::Error; + +use ns; + +#[derive(Debug, Clone)] +pub enum StanzaId { + StanzaId { + id: String, + by: Jid, + }, + OriginId { + id: String, + }, +} + +pub fn parse_stanza_id(root: &Element) -> Result { + let is_stanza_id = root.is("stanza-id", ns::SID); + if !is_stanza_id && !root.is("origin-id", ns::SID) { + return Err(Error::ParseError("This is not a stanza-id or origin-id element.")); + } + for _ in root.children() { + return Err(Error::ParseError("Unknown child in stanza-id or origin-id element.")); + } + let id = match root.attr("id") { + Some(id) => id.to_owned(), + None => return Err(Error::ParseError("No 'id' attribute present in stanza-id or origin-id.")), + }; + Ok(if is_stanza_id { + let by = match root.attr("by") { + Some(by) => by.parse().unwrap(), + None => return Err(Error::ParseError("No 'by' attribute present in stanza-id.")), + }; + StanzaId::StanzaId { id, by } + } else { + StanzaId::OriginId { id } + }) +} + +pub fn serialise(stanza_id: &StanzaId) -> Element { + match *stanza_id { + StanzaId::StanzaId { ref id, ref by } => { + Element::builder("stanza-id") + .ns(ns::SID) + .attr("id", id.clone()) + .attr("by", String::from(by.clone())) + .build() + }, + StanzaId::OriginId { ref id } => { + Element::builder("origin-id") + .ns(ns::SID) + .attr("id", id.clone()) + .build() + }, + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use minidom::Element; + use jid::Jid; + use error::Error; + use stanza_id; + + #[test] + fn test_simple() { + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); + if let stanza_id::StanzaId::StanzaId { id, by } = stanza_id { + assert_eq!(id, String::from("coucou")); + assert_eq!(by, Jid::from_str("coucou@coucou").unwrap()); + } else { + panic!(); + } + + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::parse_stanza_id(&elem).unwrap(); + if let stanza_id::StanzaId::OriginId { id } = stanza_id { + assert_eq!(id, String::from("coucou")); + } else { + panic!(); + } + } + + #[test] + fn test_invalid_child() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "Unknown child in stanza-id or origin-id element."); + } + + #[test] + fn test_invalid_id() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'id' attribute present in stanza-id or origin-id."); + } + + #[test] + fn test_invalid_by() { + let elem: Element = "".parse().unwrap(); + let error = stanza_id::parse_stanza_id(&elem).unwrap_err(); + let message = match error { + Error::ParseError(string) => string, + _ => panic!(), + }; + assert_eq!(message, "No 'by' attribute present in stanza-id."); + } + + #[test] + fn test_serialise() { + let elem: Element = "".parse().unwrap(); + let stanza_id = stanza_id::StanzaId::StanzaId { id: String::from("coucou"), by: Jid::from_str("coucou@coucou").unwrap() }; + let elem2 = stanza_id::serialise(&stanza_id); + assert_eq!(elem, elem2); + } +}