1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2// Copyright (c) 2017 Maxime “pep” Buquet <pep+code@bouah.net>
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8use std::convert::TryFrom;
9
10use minidom::Element;
11use minidom::IntoAttributeValue;
12
13use jid::Jid;
14
15use error::Error;
16
17use ns;
18
19use stanza_error::StanzaError;
20use disco::Disco;
21use ibb::IBB;
22use jingle::Jingle;
23use ping::Ping;
24use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs};
25
26/// Lists every known payload of a `<iq/>`.
27#[derive(Debug, Clone)]
28pub enum IqPayload {
29 Disco(Disco),
30 IBB(IBB),
31 Jingle(Jingle),
32 Ping(Ping),
33 MamQuery(MamQuery),
34 MamFin(MamFin),
35 MamPrefs(MamPrefs),
36
37 Unknown(Element),
38}
39
40impl TryFrom<Element> for IqPayload {
41 type Error = Error;
42
43 fn try_from(elem: Element) -> Result<IqPayload, Error> {
44 Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) {
45 // XEP-0030
46 ("query", ns::DISCO_INFO) => IqPayload::Disco(Disco::try_from(elem)?),
47
48 // XEP-0047
49 ("open", ns::IBB)
50 | ("data", ns::IBB)
51 | ("close", ns::IBB) => IqPayload::IBB(IBB::try_from(elem)?),
52
53 // XEP-0166
54 ("jingle", ns::JINGLE) => IqPayload::Jingle(Jingle::try_from(elem)?),
55
56 // XEP-0199
57 ("ping", ns::PING) => IqPayload::Ping(Ping::try_from(elem)?),
58
59 // XEP-0313
60 ("query", ns::MAM) => IqPayload::MamQuery(MamQuery::try_from(elem)?),
61 ("fin", ns::MAM) => IqPayload::MamFin(MamFin::try_from(elem)?),
62 ("prefs", ns::MAM) => IqPayload::MamPrefs(MamPrefs::try_from(elem)?),
63
64 _ => IqPayload::Unknown(elem),
65 })
66 }
67}
68
69#[derive(Debug, Clone)]
70pub enum IqType {
71 Get(Element),
72 Set(Element),
73 Result(Option<Element>),
74 Error(StanzaError),
75}
76
77impl<'a> IntoAttributeValue for &'a IqType {
78 fn into_attribute_value(self) -> Option<String> {
79 Some(match *self {
80 IqType::Get(_) => "get",
81 IqType::Set(_) => "set",
82 IqType::Result(_) => "result",
83 IqType::Error(_) => "error",
84 }.to_owned())
85 }
86}
87
88#[derive(Debug, Clone)]
89pub struct Iq {
90 pub from: Option<Jid>,
91 pub to: Option<Jid>,
92 pub id: Option<String>,
93 pub payload: IqType,
94}
95
96impl TryFrom<Element> for Iq {
97 type Error = Error;
98
99 fn try_from(root: Element) -> Result<Iq, Error> {
100 if !root.is("iq", ns::JABBER_CLIENT) {
101 return Err(Error::ParseError("This is not an iq element."));
102 }
103 let from = get_attr!(root, "from", optional);
104 let to = get_attr!(root, "to", optional);
105 let id = get_attr!(root, "id", optional);
106 let type_: String = get_attr!(root, "type", required);
107
108 let mut payload = None;
109 let mut error_payload = None;
110 for elem in root.children() {
111 if payload.is_some() {
112 return Err(Error::ParseError("Wrong number of children in iq element."));
113 }
114 if type_ == "error" {
115 if elem.is("error", ns::JABBER_CLIENT) {
116 if error_payload.is_some() {
117 return Err(Error::ParseError("Wrong number of children in iq element."));
118 }
119 error_payload = Some(StanzaError::try_from(elem.clone())?);
120 } else if root.children().collect::<Vec<_>>().len() != 2 {
121 return Err(Error::ParseError("Wrong number of children in iq element."));
122 }
123 } else {
124 payload = Some(elem.clone());
125 }
126 }
127
128 let type_ = if type_ == "get" {
129 if let Some(payload) = payload {
130 IqType::Get(payload)
131 } else {
132 return Err(Error::ParseError("Wrong number of children in iq element."));
133 }
134 } else if type_ == "set" {
135 if let Some(payload) = payload {
136 IqType::Set(payload)
137 } else {
138 return Err(Error::ParseError("Wrong number of children in iq element."));
139 }
140 } else if type_ == "result" {
141 if let Some(payload) = payload {
142 IqType::Result(Some(payload))
143 } else {
144 IqType::Result(None)
145 }
146 } else if type_ == "error" {
147 if let Some(payload) = error_payload {
148 IqType::Error(payload)
149 } else {
150 return Err(Error::ParseError("Wrong number of children in iq element."));
151 }
152 } else {
153 return Err(Error::ParseError("Unknown iq type."));
154 };
155
156 Ok(Iq {
157 from: from,
158 to: to,
159 id: id,
160 payload: type_,
161 })
162 }
163}
164
165impl Into<Element> for IqPayload {
166 fn into(self) -> Element {
167 match self {
168 IqPayload::Disco(disco) => disco.into(),
169 IqPayload::IBB(ibb) => ibb.into(),
170 IqPayload::Jingle(jingle) => jingle.into(),
171 IqPayload::Ping(ping) => ping.into(),
172 IqPayload::MamQuery(query) => query.into(),
173 IqPayload::MamFin(fin) => fin.into(),
174 IqPayload::MamPrefs(prefs) => prefs.into(),
175
176 IqPayload::Unknown(elem) => elem,
177 }
178 }
179}
180
181impl Into<Element> for Iq {
182 fn into(self) -> Element {
183 let mut stanza = Element::builder("iq")
184 .ns(ns::JABBER_CLIENT)
185 .attr("from", self.from.and_then(|value| Some(String::from(value))))
186 .attr("to", self.to.and_then(|value| Some(String::from(value))))
187 .attr("id", self.id)
188 .attr("type", &self.payload)
189 .build();
190 let elem = match self.payload {
191 IqType::Get(elem)
192 | IqType::Set(elem)
193 | IqType::Result(Some(elem)) => elem,
194 IqType::Error(error) => error.into(),
195 IqType::Result(None) => return stanza,
196 };
197 stanza.append_child(elem);
198 stanza
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use stanza_error::{ErrorType, DefinedCondition};
206
207 #[test]
208 fn test_require_type() {
209 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
210 let error = Iq::try_from(elem).unwrap_err();
211 let message = match error {
212 Error::ParseError(string) => string,
213 _ => panic!(),
214 };
215 assert_eq!(message, "Required attribute 'type' missing.");
216 }
217
218 #[test]
219 fn test_get() {
220 let elem: Element = "<iq xmlns='jabber:client' type='get'>
221 <foo/>
222 </iq>".parse().unwrap();
223 let iq = Iq::try_from(elem).unwrap();
224 let query: Element = "<foo xmlns='jabber:client'/>".parse().unwrap();
225 assert_eq!(iq.from, None);
226 assert_eq!(iq.to, None);
227 assert_eq!(iq.id, None);
228 assert!(match iq.payload {
229 IqType::Get(element) => element == query,
230 _ => false
231 });
232 }
233
234 #[test]
235 fn test_set() {
236 let elem: Element = "<iq xmlns='jabber:client' type='set'>
237 <vCard xmlns='vcard-temp'/>
238 </iq>".parse().unwrap();
239 let iq = Iq::try_from(elem).unwrap();
240 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
241 assert_eq!(iq.from, None);
242 assert_eq!(iq.to, None);
243 assert_eq!(iq.id, None);
244 assert!(match iq.payload {
245 IqType::Set(element) => element == vcard,
246 _ => false
247 });
248 }
249
250 #[test]
251 fn test_result_empty() {
252 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
253 let iq = Iq::try_from(elem).unwrap();
254 assert_eq!(iq.from, None);
255 assert_eq!(iq.to, None);
256 assert_eq!(iq.id, None);
257 assert!(match iq.payload {
258 IqType::Result(None) => true,
259 _ => false,
260 });
261 }
262
263 #[test]
264 fn test_result() {
265 let elem: Element = "<iq xmlns='jabber:client' type='result'>
266 <query xmlns='http://jabber.org/protocol/disco#items'/>
267 </iq>".parse().unwrap();
268 let iq = Iq::try_from(elem).unwrap();
269 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
270 assert_eq!(iq.from, None);
271 assert_eq!(iq.to, None);
272 assert_eq!(iq.id, None);
273 assert!(match iq.payload {
274 IqType::Result(Some(element)) => element == query,
275 _ => false,
276 });
277 }
278
279 #[test]
280 fn test_error() {
281 let elem: Element = "<iq xmlns='jabber:client' type='error'>
282 <ping xmlns='urn:xmpp:ping'/>
283 <error type='cancel'>
284 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
285 </error>
286 </iq>".parse().unwrap();
287 let iq = Iq::try_from(elem).unwrap();
288 assert_eq!(iq.from, None);
289 assert_eq!(iq.to, None);
290 assert_eq!(iq.id, None);
291 match iq.payload {
292 IqType::Error(error) => {
293 assert_eq!(error.type_, ErrorType::Cancel);
294 assert_eq!(error.by, None);
295 assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
296 assert_eq!(error.texts.len(), 0);
297 assert_eq!(error.other, None);
298 },
299 _ => panic!(),
300 }
301 }
302
303 #[test]
304 fn test_children_invalid() {
305 let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
306 let error = Iq::try_from(elem).unwrap_err();
307 let message = match error {
308 Error::ParseError(string) => string,
309 _ => panic!(),
310 };
311 assert_eq!(message, "Wrong number of children in iq element.");
312 }
313
314 #[test]
315 fn test_serialise() {
316 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
317 let iq2 = Iq {
318 from: None,
319 to: None,
320 id: None,
321 payload: IqType::Result(None),
322 };
323 let elem2 = iq2.into();
324 assert_eq!(elem, elem2);
325 }
326
327 #[test]
328 fn test_disco() {
329 let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
330 let iq = Iq::try_from(elem).unwrap();
331 let payload = match iq.payload {
332 IqType::Get(payload) => IqPayload::try_from(payload).unwrap(),
333 _ => panic!(),
334 };
335 assert!(match payload {
336 IqPayload::Disco(Disco { .. }) => true,
337 _ => false,
338 });
339 }
340}