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}