From 414210796507c03c71ded2dfda77a8a60da25a48 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Sat, 6 May 2017 21:38:23 +0100 Subject: [PATCH] message: Merge body in this module, and make it support xml:lang. --- src/body.rs | 86 -------------------------------------------------- src/lib.rs | 3 -- src/message.rs | 81 +++++++++++++++++++++++++++++++---------------- 3 files changed, 53 insertions(+), 117 deletions(-) delete mode 100644 src/body.rs diff --git a/src/body.rs b/src/body.rs deleted file mode 100644 index 8f23b56..0000000 --- a/src/body.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2017 Emmanuel Gil Peyrot -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use minidom::Element; - -use error::Error; - -use ns; - -pub type Body = String; - -pub fn parse_body(root: &Element) -> Result { - // TODO: also support components and servers. - if !root.is("body", ns::JABBER_CLIENT) { - return Err(Error::ParseError("This is not a body element.")); - } - for _ in root.children() { - return Err(Error::ParseError("Unknown child in body element.")); - } - Ok(root.text()) -} - -pub fn serialise(body: &Body) -> Element { - Element::builder("body") - .ns(ns::JABBER_CLIENT) - .append(body.to_owned()) - .build() -} - -#[cfg(test)] -mod tests { - use minidom::Element; - use error::Error; - use body; - use ns; - - #[test] - fn test_simple() { - let elem: Element = "".parse().unwrap(); - body::parse_body(&elem).unwrap(); - } - - #[test] - fn test_invalid() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "This is not a body element."); - } - - #[test] - fn test_invalid_child() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown child in body element."); - } - - #[test] - #[ignore] - fn test_invalid_attribute() { - let elem: Element = "".parse().unwrap(); - let error = body::parse_body(&elem).unwrap_err(); - let message = match error { - Error::ParseError(string) => string, - _ => panic!(), - }; - assert_eq!(message, "Unknown attribute in body element."); - } - - #[test] - fn test_serialise() { - let body = body::Body::from("Hello world!"); - let elem = body::serialise(&body); - assert!(elem.is("body", ns::JABBER_CLIENT)); - } -} diff --git a/src/lib.rs b/src/lib.rs index e3fb535..67c0629 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,9 +37,6 @@ pub mod iq; /// RFC 6120: Extensible Messaging and Presence Protocol (XMPP): Core pub mod stanza_error; -/// RFC 6121: Extensible Messaging and Presence Protocol (XMPP): Instant Messaging and Presence -pub mod body; - /// XEP-0004: Data Forms pub mod data_forms; diff --git a/src/message.rs b/src/message.rs index 51baf9b..c54b0ab 100644 --- a/src/message.rs +++ b/src/message.rs @@ -6,6 +6,7 @@ use std::convert::TryFrom; use std::str::FromStr; +use std::collections::BTreeMap; use minidom::{Element, IntoAttributeValue}; @@ -15,7 +16,6 @@ use error::Error; use ns; -use body; use stanza_error::StanzaError; use chatstates::ChatState; use receipts::Receipt; @@ -27,7 +27,6 @@ use eme::ExplicitMessageEncryption; /// Lists every known payload of a ``. #[derive(Debug, Clone)] pub enum MessagePayload { - Body(body::Body), StanzaError(StanzaError), ChatState(ChatState), Receipt(Receipt), @@ -86,12 +85,16 @@ pub enum MessagePayloadType { Parsed(MessagePayload), } +type Lang = String; +type Body = String; + #[derive(Debug, Clone)] pub struct Message { pub from: Option, pub to: Option, pub id: Option, pub type_: MessageType, + pub bodies: BTreeMap, pub payloads: Vec, } @@ -112,37 +115,47 @@ impl<'a> TryFrom<&'a Element> for Message { Some(type_) => type_.parse()?, None => Default::default(), }; + let mut bodies = BTreeMap::new(); let mut payloads = vec!(); for elem in root.children() { - let payload = if let Ok(body) = body::parse_body(elem) { - Some(MessagePayload::Body(body)) - } else if let Ok(stanza_error) = StanzaError::try_from(elem) { - Some(MessagePayload::StanzaError(stanza_error)) - } else if let Ok(chatstate) = ChatState::try_from(elem) { - Some(MessagePayload::ChatState(chatstate)) - } else if let Ok(receipt) = Receipt::try_from(elem) { - Some(MessagePayload::Receipt(receipt)) - } else if let Ok(delay) = Delay::try_from(elem) { - Some(MessagePayload::Delay(delay)) - } else if let Ok(attention) = Attention::try_from(elem) { - Some(MessagePayload::Attention(attention)) - } else if let Ok(replace) = Replace::try_from(elem) { - Some(MessagePayload::MessageCorrect(replace)) - } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { - Some(MessagePayload::ExplicitMessageEncryption(eme)) + if elem.is("body", ns::JABBER_CLIENT) { + for _ in elem.children() { + return Err(Error::ParseError("Unknown child in body element.")); + } + let lang = elem.attr("xml:lang").unwrap_or("").to_owned(); + if let Some(_) = bodies.insert(lang, elem.text()) { + return Err(Error::ParseError("Body element present twice for the same xml:lang.")); + } } else { - None - }; - payloads.push(match payload { - Some(payload) => MessagePayloadType::Parsed(payload), - None => MessagePayloadType::XML(elem.clone()), - }); + let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) { + Some(MessagePayload::StanzaError(stanza_error)) + } else if let Ok(chatstate) = ChatState::try_from(elem) { + Some(MessagePayload::ChatState(chatstate)) + } else if let Ok(receipt) = Receipt::try_from(elem) { + Some(MessagePayload::Receipt(receipt)) + } else if let Ok(delay) = Delay::try_from(elem) { + Some(MessagePayload::Delay(delay)) + } else if let Ok(attention) = Attention::try_from(elem) { + Some(MessagePayload::Attention(attention)) + } else if let Ok(replace) = Replace::try_from(elem) { + Some(MessagePayload::MessageCorrect(replace)) + } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) { + Some(MessagePayload::ExplicitMessageEncryption(eme)) + } else { + None + }; + payloads.push(match payload { + Some(payload) => MessagePayloadType::Parsed(payload), + None => MessagePayloadType::XML(elem.clone()), + }); + } } Ok(Message { from: from, to: to, id: id, type_: type_, + bodies: BTreeMap::new(), payloads: payloads, }) } @@ -151,7 +164,6 @@ impl<'a> TryFrom<&'a Element> for Message { impl<'a> Into for &'a MessagePayload { fn into(self) -> Element { match *self { - MessagePayload::Body(ref body) => body::serialise(body), MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(), MessagePayload::Attention(ref attention) => attention.into(), MessagePayload::ChatState(ref chatstate) => chatstate.into(), @@ -171,6 +183,17 @@ impl<'a> Into for &'a Message { .attr("to", self.to.clone().and_then(|value| Some(String::from(value)))) .attr("id", self.id.clone()) .attr("type", self.type_.clone()) + .append(self.bodies.iter() + .map(|(lang, body)| { + Element::builder("body") + .ns(ns::JABBER_CLIENT) + .attr("xml:lang", match lang.as_ref() { + "" => None, + lang => Some(lang), + }) + .append(body.clone()) + .build() }) + .collect::>()) .build(); for child in self.payloads.clone() { let elem = match child { @@ -206,6 +229,7 @@ mod tests { to: None, id: None, type_: MessageType::Normal, + bodies: BTreeMap::new(), payloads: vec!(), }; let elem2 = (&message).into(); @@ -221,14 +245,15 @@ mod tests { #[test] fn test_serialise_body() { let elem: Element = "Hello world!".parse().unwrap(); + let mut bodies = BTreeMap::new(); + bodies.insert(String::from(""), String::from("Hello world!")); let message = Message { from: None, to: Some(Jid::from_str("coucou@example.org").unwrap()), id: None, type_: MessageType::Chat, - payloads: vec!( - MessagePayloadType::Parsed(MessagePayload::Body("Hello world!".to_owned())), - ), + bodies: bodies, + payloads: vec!(), }; let elem2 = (&message).into(); assert_eq!(elem, elem2);