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}