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::str::FromStr;
8
9use minidom::{Element, IntoElements, IntoAttributeValue};
10use minidom::convert::ElementEmitter;
11
12use jid::Jid;
13
14use error::Error;
15
16use ns;
17
18use delay;
19use ecaps2;
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum Show {
23 Away,
24 Chat,
25 Dnd,
26 Xa,
27}
28
29impl IntoElements for Show {
30 fn into_elements(self, emitter: &mut ElementEmitter) {
31 let elem = Element::builder(match self {
32 Show::Away => "away",
33 Show::Chat => "chat",
34 Show::Dnd => "dnd",
35 Show::Xa => "xa",
36 }).build();
37 emitter.append_child(elem);
38 }
39}
40
41pub type Status = String;
42
43/// Lists every known payload of a `<presence/>`.
44#[derive(Debug, Clone)]
45pub enum PresencePayload {
46 Show(Show),
47 Status(Status),
48 Delay(delay::Delay),
49 ECaps2(ecaps2::ECaps2),
50}
51
52#[derive(Debug, Clone, PartialEq)]
53pub enum PresenceType {
54 /// This value is not an acceptable 'type' attribute, it is only used
55 /// internally to signal the absence of 'type'.
56 Available,
57 Error,
58 Probe,
59 Subscribe,
60 Subscribed,
61 Unavailable,
62 Unsubscribe,
63 Unsubscribed,
64}
65
66impl Default for PresenceType {
67 fn default() -> PresenceType {
68 PresenceType::Available
69 }
70}
71
72impl FromStr for PresenceType {
73 type Err = Error;
74
75 fn from_str(s: &str) -> Result<PresenceType, Error> {
76 Ok(match s {
77 "error" => PresenceType::Error,
78 "probe" => PresenceType::Probe,
79 "subscribe" => PresenceType::Subscribe,
80 "subscribed" => PresenceType::Subscribed,
81 "unavailable" => PresenceType::Unavailable,
82 "unsubscribe" => PresenceType::Unsubscribe,
83 "unsubscribed" => PresenceType::Unsubscribed,
84
85 _ => return Err(Error::ParseError("Invalid 'type' attribute on presence element.")),
86 })
87 }
88}
89
90impl IntoAttributeValue for PresenceType {
91 fn into_attribute_value(self) -> Option<String> {
92 Some(match self {
93 PresenceType::Available => return None,
94
95 PresenceType::Error => "error",
96 PresenceType::Probe => "probe",
97 PresenceType::Subscribe => "subscribe",
98 PresenceType::Subscribed => "subscribed",
99 PresenceType::Unavailable => "unavailable",
100 PresenceType::Unsubscribe => "unsubscribe",
101 PresenceType::Unsubscribed => "unsubscribed",
102 }.to_owned())
103 }
104}
105
106#[derive(Debug, Clone)]
107pub enum PresencePayloadType {
108 XML(Element),
109 Parsed(PresencePayload),
110}
111
112#[derive(Debug, Clone)]
113pub struct Presence {
114 pub from: Option<Jid>,
115 pub to: Option<Jid>,
116 pub id: Option<String>,
117 pub type_: PresenceType,
118 pub payloads: Vec<PresencePayloadType>,
119}
120
121pub fn parse_presence(root: &Element) -> Result<Presence, Error> {
122 if !root.is("presence", ns::JABBER_CLIENT) {
123 return Err(Error::ParseError("This is not a presence element."));
124 }
125 let from = root.attr("from")
126 .and_then(|value| value.parse().ok());
127 let to = root.attr("to")
128 .and_then(|value| value.parse().ok());
129 let id = root.attr("id")
130 .and_then(|value| value.parse().ok());
131 let type_ = match root.attr("type") {
132 Some(type_) => type_.parse()?,
133 None => Default::default(),
134 };
135 let mut payloads = vec!();
136 for elem in root.children() {
137 if elem.is("show", ns::JABBER_CLIENT) {
138 for _ in elem.children() {
139 return Err(Error::ParseError("Unknown child in show element."));
140 }
141 let payload = PresencePayload::Show(match elem.text().as_ref() {
142 "away" => Show::Away,
143 "chat" => Show::Chat,
144 "dnd" => Show::Dnd,
145 "xa" => Show::Xa,
146
147 _ => return Err(Error::ParseError("Invalid value for show.")),
148 });
149 payloads.push(PresencePayloadType::Parsed(payload));
150 } else if elem.is("status", ns::JABBER_CLIENT) {
151 for _ in elem.children() {
152 return Err(Error::ParseError("Unknown child in status element."));
153 }
154 let payload = PresencePayload::Status(elem.text());
155 payloads.push(PresencePayloadType::Parsed(payload));
156 } else {
157 let payload = if let Ok(delay) = delay::parse_delay(elem) {
158 Some(PresencePayload::Delay(delay))
159 } else if let Ok(ecaps2) = ecaps2::parse_ecaps2(elem) {
160 Some(PresencePayload::ECaps2(ecaps2))
161 } else {
162 None
163 };
164 payloads.push(match payload {
165 Some(payload) => PresencePayloadType::Parsed(payload),
166 None => PresencePayloadType::XML(elem.clone()),
167 });
168 }
169 }
170 Ok(Presence {
171 from: from,
172 to: to,
173 id: id,
174 type_: type_,
175 payloads: payloads,
176 })
177}
178
179pub fn serialise_payload(payload: &PresencePayload) -> Element {
180 match *payload {
181 PresencePayload::Show(ref show) => {
182 Element::builder("status")
183 .ns(ns::JABBER_CLIENT)
184 .append(show.to_owned())
185 .build()
186 },
187 PresencePayload::Status(ref status) => {
188 Element::builder("status")
189 .ns(ns::JABBER_CLIENT)
190 .append(status.to_owned())
191 .build()
192 },
193 PresencePayload::Delay(ref delay) => delay::serialise(delay),
194 PresencePayload::ECaps2(ref ecaps2) => ecaps2::serialise(ecaps2),
195 }
196}
197
198pub fn serialise(presence: &Presence) -> Element {
199 let mut stanza = Element::builder("presence")
200 .ns(ns::JABBER_CLIENT)
201 .attr("from", presence.from.clone().and_then(|value| Some(String::from(value))))
202 .attr("to", presence.to.clone().and_then(|value| Some(String::from(value))))
203 .attr("id", presence.id.clone())
204 .attr("type", presence.type_.clone())
205 .build();
206 for child in presence.payloads.clone() {
207 let elem = match child {
208 PresencePayloadType::XML(elem) => elem,
209 PresencePayloadType::Parsed(payload) => serialise_payload(&payload),
210 };
211 stanza.append_child(elem);
212 }
213 stanza
214}
215
216#[cfg(test)]
217mod tests {
218 use minidom::Element;
219 use error::Error;
220 use presence;
221 use ns;
222
223 #[test]
224 fn test_simple() {
225 let elem: Element = "<presence xmlns='jabber:client'/>".parse().unwrap();
226 let presence = presence::parse_presence(&elem).unwrap();
227 assert_eq!(presence.from, None);
228 assert_eq!(presence.to, None);
229 assert_eq!(presence.id, None);
230 assert_eq!(presence.type_, presence::PresenceType::Available);
231 assert!(presence.payloads.is_empty());
232 }
233
234 #[test]
235 fn test_serialise() {
236 let elem: Element = "<presence xmlns='jabber:client' type='unavailable'/>".parse().unwrap();
237 let presence = presence::Presence {
238 from: None,
239 to: None,
240 id: None,
241 type_: presence::PresenceType::Unavailable,
242 payloads: vec!(),
243 };
244 let elem2 = presence::serialise(&presence);
245 assert_eq!(elem, elem2);
246 }
247
248 #[test]
249 fn test_show() {
250 let elem: Element = "<presence xmlns='jabber:client'><show>chat</show></presence>".parse().unwrap();
251 let presence = presence::parse_presence(&elem).unwrap();
252 assert_eq!(presence.payloads.len(), 1);
253 match presence.payloads[0] {
254 presence::PresencePayloadType::Parsed(presence::PresencePayload::Show(ref show)) => {
255 assert_eq!(*show, presence::Show::Chat);
256 },
257 _ => panic!("Failed to parse show presence."),
258 }
259 }
260
261 #[test]
262 fn test_missing_show_value() {
263 // "online" used to be a pretty common mistake.
264 let elem: Element = "<presence xmlns='jabber:client'><show/></presence>".parse().unwrap();
265 let error = presence::parse_presence(&elem).unwrap_err();
266 let message = match error {
267 Error::ParseError(string) => string,
268 _ => panic!(),
269 };
270 assert_eq!(message, "Invalid value for show.");
271 }
272
273 #[test]
274 fn test_invalid_show() {
275 // "online" used to be a pretty common mistake.
276 let elem: Element = "<presence xmlns='jabber:client'><show>online</show></presence>".parse().unwrap();
277 let error = presence::parse_presence(&elem).unwrap_err();
278 let message = match error {
279 Error::ParseError(string) => string,
280 _ => panic!(),
281 };
282 assert_eq!(message, "Invalid value for show.");
283 }
284
285 #[test]
286 fn test_status() {
287 let elem: Element = "<presence xmlns='jabber:client'><status xmlns='jabber:client'/></presence>".parse().unwrap();
288 let presence = presence::parse_presence(&elem).unwrap();
289 assert_eq!(presence.payloads.len(), 1);
290 match presence.payloads[0] {
291 presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(ref status)) => {
292 assert_eq!(*status, presence::Status::from(""));
293 },
294 _ => panic!("Failed to parse status presence."),
295 }
296 }
297
298 #[test]
299 fn test_unknown_child() {
300 let elem: Element = "<presence xmlns='jabber:client'><test xmlns='invalid'/></presence>".parse().unwrap();
301 let presence = presence::parse_presence(&elem).unwrap();
302 if let presence::PresencePayloadType::XML(ref payload) = presence.payloads[0] {
303 assert!(payload.is("test", "invalid"));
304 } else {
305 panic!("Did successfully parse an invalid element.");
306 }
307 }
308
309 #[test]
310 #[ignore]
311 fn test_invalid_status_child() {
312 let elem: Element = "<presence xmlns='jabber:client'><status xmlns='jabber:client'><coucou/></status></presence>".parse().unwrap();
313 let error = presence::parse_presence(&elem).unwrap_err();
314 let message = match error {
315 Error::ParseError(string) => string,
316 _ => panic!(),
317 };
318 assert_eq!(message, "Unknown child in status element.");
319 }
320
321 #[test]
322 #[ignore]
323 fn test_invalid_attribute() {
324 let elem: Element = "<status xmlns='jabber:client' coucou=''/>".parse().unwrap();
325 let error = presence::parse_presence(&elem).unwrap_err();
326 let message = match error {
327 Error::ParseError(string) => string,
328 _ => panic!(),
329 };
330 assert_eq!(message, "Unknown attribute in status element.");
331 }
332
333 #[test]
334 fn test_serialise_status() {
335 let status = presence::Status::from("Hello world!");
336 let payloads = vec!(presence::PresencePayloadType::Parsed(presence::PresencePayload::Status(status)));
337 let presence = presence::Presence {
338 from: None,
339 to: None,
340 id: None,
341 type_: presence::PresenceType::Unavailable,
342 payloads: payloads,
343 };
344 let elem = presence::serialise(&presence);
345 assert!(elem.is("presence", ns::JABBER_CLIENT));
346 assert!(elem.children().collect::<Vec<_>>()[0].is("status", ns::JABBER_CLIENT));
347 }
348}