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;
  9
 10use minidom::{Element, IntoAttributeValue};
 11
 12use jid::Jid;
 13
 14use error::Error;
 15
 16use ns;
 17
 18use body;
 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    Body(body::Body),
 31    StanzaError(StanzaError),
 32    ChatState(ChatState),
 33    Receipt(Receipt),
 34    Delay(Delay),
 35    Attention(Attention),
 36    MessageCorrect(Replace),
 37    ExplicitMessageEncryption(ExplicitMessageEncryption),
 38}
 39
 40#[derive(Debug, Clone, PartialEq)]
 41pub enum MessageType {
 42    Chat,
 43    Error,
 44    Groupchat,
 45    Headline,
 46    Normal,
 47}
 48
 49impl Default for MessageType {
 50    fn default() -> MessageType {
 51        MessageType::Normal
 52    }
 53}
 54
 55impl FromStr for MessageType {
 56    type Err = Error;
 57
 58    fn from_str(s: &str) -> Result<MessageType, Error> {
 59        Ok(match s {
 60            "chat" => MessageType::Chat,
 61            "error" => MessageType::Error,
 62            "groupchat" => MessageType::Groupchat,
 63            "headline" => MessageType::Headline,
 64            "normal" => MessageType::Normal,
 65
 66            _ => return Err(Error::ParseError("Invalid 'type' attribute on message element.")),
 67        })
 68    }
 69}
 70
 71impl IntoAttributeValue for MessageType {
 72    fn into_attribute_value(self) -> Option<String> {
 73        Some(match self {
 74            MessageType::Chat => "chat",
 75            MessageType::Error => "error",
 76            MessageType::Groupchat => "groupchat",
 77            MessageType::Headline => "headline",
 78            MessageType::Normal => "normal",
 79        }.to_owned())
 80    }
 81}
 82
 83#[derive(Debug, Clone)]
 84pub enum MessagePayloadType {
 85    XML(Element),
 86    Parsed(MessagePayload),
 87}
 88
 89#[derive(Debug, Clone)]
 90pub struct Message {
 91    pub from: Option<Jid>,
 92    pub to: Option<Jid>,
 93    pub id: Option<String>,
 94    pub type_: MessageType,
 95    pub payloads: Vec<MessagePayloadType>,
 96}
 97
 98impl<'a> TryFrom<&'a Element> for Message {
 99    type Error = Error;
100
101    fn try_from(root: &'a Element) -> Result<Message, Error> {
102        if !root.is("message", ns::JABBER_CLIENT) {
103            return Err(Error::ParseError("This is not a message element."));
104        }
105        let from = root.attr("from")
106            .and_then(|value| value.parse().ok());
107        let to = root.attr("to")
108            .and_then(|value| value.parse().ok());
109        let id = root.attr("id")
110            .and_then(|value| value.parse().ok());
111        let type_ = match root.attr("type") {
112            Some(type_) => type_.parse()?,
113            None => Default::default(),
114        };
115        let mut payloads = vec!();
116        for elem in root.children() {
117            let payload = if let Ok(body) = body::parse_body(elem) {
118                Some(MessagePayload::Body(body))
119            } else if let Ok(stanza_error) = StanzaError::try_from(elem) {
120                Some(MessagePayload::StanzaError(stanza_error))
121            } else if let Ok(chatstate) = ChatState::try_from(elem) {
122                Some(MessagePayload::ChatState(chatstate))
123            } else if let Ok(receipt) = Receipt::try_from(elem) {
124                Some(MessagePayload::Receipt(receipt))
125            } else if let Ok(delay) = Delay::try_from(elem) {
126                Some(MessagePayload::Delay(delay))
127            } else if let Ok(attention) = Attention::try_from(elem) {
128                Some(MessagePayload::Attention(attention))
129            } else if let Ok(replace) = Replace::try_from(elem) {
130                Some(MessagePayload::MessageCorrect(replace))
131            } else if let Ok(eme) = ExplicitMessageEncryption::try_from(elem) {
132                Some(MessagePayload::ExplicitMessageEncryption(eme))
133            } else {
134                None
135            };
136            payloads.push(match payload {
137                Some(payload) => MessagePayloadType::Parsed(payload),
138                None => MessagePayloadType::XML(elem.clone()),
139            });
140        }
141        Ok(Message {
142            from: from,
143            to: to,
144            id: id,
145            type_: type_,
146            payloads: payloads,
147        })
148    }
149}
150
151impl<'a> Into<Element> for &'a MessagePayload {
152    fn into(self) -> Element {
153        match *self {
154            MessagePayload::Body(ref body) => body::serialise(body),
155            MessagePayload::StanzaError(ref stanza_error) => stanza_error.into(),
156            MessagePayload::Attention(ref attention) => attention.into(),
157            MessagePayload::ChatState(ref chatstate) => chatstate.into(),
158            MessagePayload::Receipt(ref receipt) => receipt.into(),
159            MessagePayload::Delay(ref delay) => delay.into(),
160            MessagePayload::MessageCorrect(ref replace) => replace.into(),
161            MessagePayload::ExplicitMessageEncryption(ref eme) => eme.into(),
162        }
163    }
164}
165
166impl<'a> Into<Element> for &'a Message {
167    fn into(self) -> Element {
168        let mut stanza = Element::builder("message")
169                                 .ns(ns::JABBER_CLIENT)
170                                 .attr("from", self.from.clone().and_then(|value| Some(String::from(value))))
171                                 .attr("to", self.to.clone().and_then(|value| Some(String::from(value))))
172                                 .attr("id", self.id.clone())
173                                 .attr("type", self.type_.clone())
174                                 .build();
175        for child in self.payloads.clone() {
176            let elem = match child {
177                MessagePayloadType::XML(elem) => elem,
178                MessagePayloadType::Parsed(payload) => (&payload).into(),
179            };
180            stanza.append_child(elem);
181        }
182        stanza
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn test_simple() {
192        let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
193        let message = Message::try_from(&elem).unwrap();
194        assert_eq!(message.from, None);
195        assert_eq!(message.to, None);
196        assert_eq!(message.id, None);
197        assert_eq!(message.type_, MessageType::Normal);
198        assert!(message.payloads.is_empty());
199    }
200
201    #[test]
202    fn test_serialise() {
203        let elem: Element = "<message xmlns='jabber:client' type='normal'/>".parse().unwrap();
204        let message = Message {
205            from: None,
206            to: None,
207            id: None,
208            type_: MessageType::Normal,
209            payloads: vec!(),
210        };
211        let elem2 = (&message).into();
212        assert_eq!(elem, elem2);
213    }
214
215    #[test]
216    fn test_body() {
217        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
218        Message::try_from(&elem).unwrap();
219    }
220
221    #[test]
222    fn test_serialise_body() {
223        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
224        let message = Message {
225            from: None,
226            to: Some(Jid::from_str("coucou@example.org").unwrap()),
227            id: None,
228            type_: MessageType::Chat,
229            payloads: vec!(
230                MessagePayloadType::Parsed(MessagePayload::Body("Hello world!".to_owned())),
231            ),
232        };
233        let elem2 = (&message).into();
234        assert_eq!(elem, elem2);
235    }
236
237    #[test]
238    fn test_attention() {
239        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><attention xmlns='urn:xmpp:attention:0'/></message>".parse().unwrap();
240        let message = Message::try_from(&elem).unwrap();
241        let elem2 = (&message).into();
242        assert_eq!(elem, elem2);
243    }
244}