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, IntoElements, IntoAttributeValue, ElementEmitter};
 11
 12use jid::Jid;
 13
 14use error::Error;
 15
 16use ns;
 17
 18use body;
 19use stanza_error;
 20use chatstates;
 21use receipts::Receipt;
 22use delay;
 23use attention::Attention;
 24use message_correct::Replace;
 25use eme;
 26
 27/// Lists every known payload of a `<message/>`.
 28#[derive(Debug, Clone)]
 29pub enum MessagePayload {
 30    Body(body::Body),
 31    StanzaError(stanza_error::StanzaError),
 32    ChatState(chatstates::ChatState),
 33    Receipt(Receipt),
 34    Delay(delay::Delay),
 35    Attention(Attention),
 36    MessageCorrect(Replace),
 37    ExplicitMessageEncryption(eme::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
 98pub fn parse_message(root: &Element) -> Result<Message, Error> {
 99    if !root.is("message", ns::JABBER_CLIENT) {
100        return Err(Error::ParseError("This is not a message element."));
101    }
102    let from = root.attr("from")
103        .and_then(|value| value.parse().ok());
104    let to = root.attr("to")
105        .and_then(|value| value.parse().ok());
106    let id = root.attr("id")
107        .and_then(|value| value.parse().ok());
108    let type_ = match root.attr("type") {
109        Some(type_) => type_.parse()?,
110        None => Default::default(),
111    };
112    let mut payloads = vec!();
113    for elem in root.children() {
114        let payload = if let Ok(body) = body::parse_body(elem) {
115            Some(MessagePayload::Body(body))
116        } else if let Ok(stanza_error) = stanza_error::parse_stanza_error(elem) {
117            Some(MessagePayload::StanzaError(stanza_error))
118        } else if let Ok(chatstate) = chatstates::parse_chatstate(elem) {
119            Some(MessagePayload::ChatState(chatstate))
120        } else if let Ok(receipt) = Receipt::try_from(elem) {
121            Some(MessagePayload::Receipt(receipt))
122        } else if let Ok(delay) = delay::parse_delay(elem) {
123            Some(MessagePayload::Delay(delay))
124        } else if let Ok(attention) = Attention::try_from(elem) {
125            Some(MessagePayload::Attention(attention))
126        } else if let Ok(replace) = Replace::try_from(elem) {
127            Some(MessagePayload::MessageCorrect(replace))
128        } else if let Ok(eme) = eme::parse_explicit_message_encryption(elem) {
129            Some(MessagePayload::ExplicitMessageEncryption(eme))
130        } else {
131            None
132        };
133        payloads.push(match payload {
134            Some(payload) => MessagePayloadType::Parsed(payload),
135            None => MessagePayloadType::XML(elem.clone()),
136        });
137    }
138    Ok(Message {
139        from: from,
140        to: to,
141        id: id,
142        type_: type_,
143        payloads: payloads,
144    })
145}
146
147pub fn serialise_payload(payload: &MessagePayload) -> Element {
148    match *payload {
149        MessagePayload::Body(ref body) => body::serialise(body),
150        MessagePayload::StanzaError(ref stanza_error) => stanza_error::serialise(stanza_error),
151        MessagePayload::Attention(ref attention) => attention.into(),
152        MessagePayload::ChatState(ref chatstate) => chatstates::serialise(chatstate),
153        MessagePayload::Receipt(ref receipt) => receipt.into(),
154        MessagePayload::Delay(ref delay) => delay::serialise(delay),
155        MessagePayload::MessageCorrect(ref replace) => replace.into(),
156        MessagePayload::ExplicitMessageEncryption(ref eme) => eme::serialise(eme),
157    }
158}
159
160pub fn serialise(message: &Message) -> Element {
161    let mut stanza = Element::builder("message")
162                             .ns(ns::JABBER_CLIENT)
163                             .attr("from", message.from.clone().and_then(|value| Some(String::from(value))))
164                             .attr("to", message.to.clone().and_then(|value| Some(String::from(value))))
165                             .attr("id", message.id.clone())
166                             .attr("type", message.type_.clone())
167                             .build();
168    for child in message.payloads.clone() {
169        let elem = match child {
170            MessagePayloadType::XML(elem) => elem,
171            MessagePayloadType::Parsed(payload) => serialise_payload(&payload),
172        };
173        stanza.append_child(elem);
174    }
175    stanza
176}
177
178impl IntoElements for Message {
179    fn into_elements(self, emitter: &mut ElementEmitter) {
180        let elem = serialise(&self);
181        emitter.append_child(elem);
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use std::str::FromStr;
188    use minidom::Element;
189    use jid::Jid;
190    use message;
191
192    #[test]
193    fn test_simple() {
194        let elem: Element = "<message xmlns='jabber:client'/>".parse().unwrap();
195        let message = message::parse_message(&elem).unwrap();
196        assert_eq!(message.from, None);
197        assert_eq!(message.to, None);
198        assert_eq!(message.id, None);
199        assert_eq!(message.type_, message::MessageType::Normal);
200        assert!(message.payloads.is_empty());
201    }
202
203    #[test]
204    fn test_serialise() {
205        let elem: Element = "<message xmlns='jabber:client' type='normal'/>".parse().unwrap();
206        let message = message::Message {
207            from: None,
208            to: None,
209            id: None,
210            type_: message::MessageType::Normal,
211            payloads: vec!(),
212        };
213        let elem2 = message::serialise(&message);
214        assert_eq!(elem, elem2);
215    }
216
217    #[test]
218    fn test_body() {
219        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
220        message::parse_message(&elem).unwrap();
221    }
222
223    #[test]
224    fn test_serialise_body() {
225        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><body>Hello world!</body></message>".parse().unwrap();
226        let message = message::Message {
227            from: None,
228            to: Some(Jid::from_str("coucou@example.org").unwrap()),
229            id: None,
230            type_: message::MessageType::Chat,
231            payloads: vec!(
232                message::MessagePayloadType::Parsed(message::MessagePayload::Body("Hello world!".to_owned())),
233            ),
234        };
235        let elem2 = message::serialise(&message);
236        assert_eq!(elem, elem2);
237    }
238
239    #[test]
240    fn test_attention() {
241        let elem: Element = "<message xmlns='jabber:client' to='coucou@example.org' type='chat'><attention xmlns='urn:xmpp:attention:0'/></message>".parse().unwrap();
242        let message = message::parse_message(&elem).unwrap();
243        let elem2 = message::serialise(&message);
244        assert_eq!(elem, elem2);
245    }
246}