message.rs

  1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use std::convert::TryFrom;
  8use std::str::FromStr;
  9use std::collections::BTreeMap;
 10
 11use minidom::{Element, IntoAttributeValue};
 12
 13use jid::Jid;
 14
 15use error::Error;
 16
 17use ns;
 18
 19use stanza_error::StanzaError;
 20use chatstates::ChatState;
 21use receipts::Receipt;
 22use delay::Delay;
 23use attention::Attention;
 24use message_correct::Replace;
 25use eme::ExplicitMessageEncryption;
 26
 27/// Lists every known payload of a `<message/>`.
 28#[derive(Debug, Clone)]
 29pub enum MessagePayload {
 30    StanzaError(StanzaError),
 31    ChatState(ChatState),
 32    Receipt(Receipt),
 33    Delay(Delay),
 34    Attention(Attention),
 35    MessageCorrect(Replace),
 36    ExplicitMessageEncryption(ExplicitMessageEncryption),
 37}
 38
 39#[derive(Debug, Clone, PartialEq)]
 40pub enum MessageType {
 41    Chat,
 42    Error,
 43    Groupchat,
 44    Headline,
 45    Normal,
 46}
 47
 48impl Default for MessageType {
 49    fn default() -> MessageType {
 50        MessageType::Normal
 51    }
 52}
 53
 54impl FromStr for MessageType {
 55    type Err = Error;
 56
 57    fn from_str(s: &str) -> Result<MessageType, Error> {
 58        Ok(match s {
 59            "chat" => MessageType::Chat,
 60            "error" => MessageType::Error,
 61            "groupchat" => MessageType::Groupchat,
 62            "headline" => MessageType::Headline,
 63            "normal" => MessageType::Normal,
 64
 65            _ => return Err(Error::ParseError("Invalid 'type' attribute on message element.")),
 66        })
 67    }
 68}
 69
 70impl IntoAttributeValue for MessageType {
 71    fn into_attribute_value(self) -> Option<String> {
 72        Some(match self {
 73            MessageType::Chat => "chat",
 74            MessageType::Error => "error",
 75            MessageType::Groupchat => "groupchat",
 76            MessageType::Headline => "headline",
 77            MessageType::Normal => "normal",
 78        }.to_owned())
 79    }
 80}
 81
 82#[derive(Debug, Clone)]
 83pub enum MessagePayloadType {
 84    XML(Element),
 85    Parsed(MessagePayload),
 86}
 87
 88type Lang = String;
 89type Body = String;
 90type Subject = String;
 91type Thread = String;
 92
 93#[derive(Debug, Clone)]
 94pub struct Message {
 95    pub from: Option<Jid>,
 96    pub to: Option<Jid>,
 97    pub id: Option<String>,
 98    pub type_: MessageType,
 99    pub bodies: BTreeMap<Lang, Body>,
100    pub subjects: BTreeMap<Lang, Subject>,
101    pub thread: Option<Thread>,
102    pub payloads: Vec<MessagePayloadType>,
103}
104
105impl<'a> TryFrom<&'a Element> for Message {
106    type Error = Error;
107
108    fn try_from(root: &'a Element) -> Result<Message, Error> {
109        if !root.is("message", ns::JABBER_CLIENT) {
110            return Err(Error::ParseError("This is not a message element."));
111        }
112        let from = root.attr("from")
113            .and_then(|value| value.parse().ok());
114        let to = root.attr("to")
115            .and_then(|value| value.parse().ok());
116        let id = root.attr("id")
117            .and_then(|value| value.parse().ok());
118        let type_ = match root.attr("type") {
119            Some(type_) => type_.parse()?,
120            None => Default::default(),
121        };
122        let mut bodies = BTreeMap::new();
123        let mut subjects = BTreeMap::new();
124        let mut thread = None;
125        let mut payloads = vec!();
126        for elem in root.children() {
127            if elem.is("body", ns::JABBER_CLIENT) {
128                for _ in elem.children() {
129                    return Err(Error::ParseError("Unknown child in body element."));
130                }
131                let lang = elem.attr("xml:lang").unwrap_or("").to_owned();
132                if bodies.insert(lang, elem.text()).is_some() {
133                    return Err(Error::ParseError("Body element present twice for the same xml:lang."));
134                }
135            } else if elem.is("subject", ns::JABBER_CLIENT) {
136                for _ in elem.children() {
137                    return Err(Error::ParseError("Unknown child in subject element."));
138                }
139                let lang = elem.attr("xml:lang").unwrap_or("").to_owned();
140                if subjects.insert(lang, elem.text()).is_some() {
141                    return Err(Error::ParseError("Subject element present twice for the same xml:lang."));
142                }
143            } else if elem.is("thread", ns::JABBER_CLIENT) {
144                if thread.is_some() {
145                    return Err(Error::ParseError("Thread element present twice."));
146                }
147                for _ in elem.children() {
148                    return Err(Error::ParseError("Unknown child in thread element."));
149                }
150                thread = Some(elem.text());
151            } else {
152                let payload = if let Ok(stanza_error) = StanzaError::try_from(elem) {
153                    Some(MessagePayload::StanzaError(stanza_error))
154                } else if let Ok(chatstate) = ChatState::try_from(elem) {
155                    Some(MessagePayload::ChatState(chatstate))
156                } else if let Ok(receipt) = Receipt::try_from(elem) {
157                    Some(MessagePayload::Receipt(receipt))
158                } else if let Ok(delay) = Delay::try_from(elem) {
159                    Some(MessagePayload::Delay(delay))
160                } else if let Ok(attention) = Attention::try_from(elem) {
161                    Some(MessagePayload::Attention(attention))
162                } else if let Ok(replace) = Replace::try_from(elem) {
163                    Some(MessagePayload::MessageCorrect(replace))
164                } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) {
165                    Some(MessagePayload::ExplicitMessageEncryption(eme))
166                } else {
167                    None
168                };
169                payloads.push(match payload {
170                    Some(payload) => MessagePayloadType::Parsed(payload),
171                    None => MessagePayloadType::XML(elem.clone()),
172                });
173            }
174        }
175        Ok(Message {
176            from: from,
177            to: to,
178            id: id,
179            type_: type_,
180            bodies: bodies,
181            subjects: subjects,
182            thread: thread,
183            payloads: payloads,
184        })
185    }
186}
187
188impl<'a> Into<Element> for &'a MessagePayload {
189    fn into(self) -> Element {
190        match *self {
191            MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(),
192            MessagePayload::Attention(ref attention) => attention.into(),
193            MessagePayload::ChatState(ref chatstate) => chatstate.into(),
194            MessagePayload::Receipt(ref receipt) => receipt.into(),
195            MessagePayload::Delay(ref delay) => delay.into(),
196            MessagePayload::MessageCorrect(ref replace) => replace.into(),
197            MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(),
198        }
199    }
200}
201
202impl<'a> Into<Element> for &'a Message {
203    fn into(self) -> Element {
204        let mut stanza = Element::builder("message")
205                                 .ns(ns::JABBER_CLIENT)
206                                 .attr("from", self.from.clone().and_then(|value| Some(String::from(value))))
207                                 .attr("to", self.to.clone().and_then(|value| Some(String::from(value))))
208                                 .attr("id", self.id.clone())
209                                 .attr("type", self.type_.clone())
210                                 .append(self.subjects.iter()
211                                                    .map(|(lang, subject)| {
212                                                         Element::builder("subject")
213                                                                 .ns(ns::JABBER_CLIENT)
214                                                                 .attr("xml:lang", match lang.as_ref() {
215                                                                      "" => None,
216                                                                      lang => Some(lang),
217                                                                  })
218                                                                 .append(subject.clone())
219                                                                 .build() })
220                                                    .collect::<Vec<_>>())
221                                 .append(self.bodies.iter()
222                                                    .map(|(lang, body)| {
223                                                         Element::builder("body")
224                                                                 .ns(ns::JABBER_CLIENT)
225                                                                 .attr("xml:lang", match lang.as_ref() {
226                                                                      "" => None,
227                                                                      lang => Some(lang),
228                                                                  })
229                                                                 .append(body.clone())
230                                                                 .build() })
231                                                    .collect::<Vec<_>>())
232                                 .build();
233        for child in self.payloads.clone() {
234            let elem = match child {
235                MessagePayloadType::XML(elem) => elem,
236                MessagePayloadType::Parsed(payload) => (&payload).into(),
237            };
238            stanza.append_child(elem);
239        }
240        stanza
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    #[test]
249    fn test_simple() {
250        let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
251        let message = Message::try_from(&elem).unwrap();
252        assert_eq!(message.from, None);
253        assert_eq!(message.to, None);
254        assert_eq!(message.id, None);
255        assert_eq!(message.type_, MessageType::Normal);
256        assert!(message.payloads.is_empty());
257    }
258
259    #[test]
260    fn test_serialise() {
261        let elem: Element = "<message xmlns='jabber:client' type='normal'/>".parse().unwrap();
262        let message = Message {
263            from: None,
264            to: None,
265            id: None,
266            type_: MessageType::Normal,
267            bodies: BTreeMap::new(),
268            subjects: BTreeMap::new(),
269            thread: None,
270            payloads: vec!(),
271        };
272        let elem2 = (&message).into();
273        assert_eq!(elem, elem2);
274    }
275
276    #[test]
277    fn test_body() {
278        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
279        let message = Message::try_from(&elem).unwrap();
280        assert_eq!(message.bodies[""], "Hello world!");
281
282        let elem2 = (&message).into();
283        assert_eq!(elem, elem2);
284    }
285
286    #[test]
287    fn test_serialise_body() {
288        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
289        let mut bodies = BTreeMap::new();
290        bodies.insert(String::from(""), String::from("Hello world!"));
291        let message = Message {
292            from: None,
293            to: Some(Jid::from_str("coucou@example.org").unwrap()),
294            id: None,
295            type_: MessageType::Chat,
296            bodies: bodies,
297            subjects: BTreeMap::new(),
298            thread: None,
299            payloads: vec!(),
300        };
301        let elem2 = (&message).into();
302        assert_eq!(elem, elem2);
303    }
304
305    #[test]
306    fn test_subject() {
307        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><subject>Hello world!</subject></message>".parse().unwrap();
308        let message = Message::try_from(&elem).unwrap();
309        assert_eq!(message.subjects[""], "Hello world!");
310
311        let elem2 = (&message).into();
312        assert_eq!(elem, elem2);
313    }
314
315    #[test]
316    fn test_attention() {
317        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><attention xmlns='urn:xmpp:attention:0'/></message>".parse().unwrap();
318        let message = Message::try_from(&elem).unwrap();
319        let elem2 = (&message).into();
320        assert_eq!(elem, elem2);
321    }
322}