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 try_from::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 roster::Roster;
21use disco::{DiscoInfoResult, DiscoInfoQuery};
22use ibb::IBB;
23use jingle::Jingle;
24use ping::Ping;
25use mam::{Query as MamQuery, Fin as MamFin, Prefs as MamPrefs};
26
27/// Lists every known payload of an `<iq type='get'/>`.
28#[derive(Debug, Clone)]
29pub enum IqGetPayload {
30 Roster(Roster),
31 DiscoInfo(DiscoInfoQuery),
32 Ping(Ping),
33 MamQuery(MamQuery),
34 MamPrefs(MamPrefs),
35
36 Unknown(Element),
37}
38
39/// Lists every known payload of an `<iq type='set'/>`.
40#[derive(Debug, Clone)]
41pub enum IqSetPayload {
42 Roster(Roster),
43 IBB(IBB),
44 Jingle(Jingle),
45 MamQuery(MamQuery),
46 MamPrefs(MamPrefs),
47
48 Unknown(Element),
49}
50
51/// Lists every known payload of an `<iq type='result'/>`.
52#[derive(Debug, Clone)]
53pub enum IqResultPayload {
54 Roster(Roster),
55 DiscoInfo(DiscoInfoResult),
56 MamQuery(MamQuery),
57 MamFin(MamFin),
58 MamPrefs(MamPrefs),
59
60 Unknown(Element),
61}
62
63impl TryFrom<Element> for IqGetPayload {
64 type Err = Error;
65
66 fn try_from(elem: Element) -> Result<IqGetPayload, Error> {
67 Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) {
68 // RFC-6121
69 ("query", ns::ROSTER) => IqGetPayload::Roster(Roster::try_from(elem)?),
70
71 // XEP-0030
72 ("query", ns::DISCO_INFO) => IqGetPayload::DiscoInfo(DiscoInfoQuery::try_from(elem)?),
73
74 // XEP-0199
75 ("ping", ns::PING) => IqGetPayload::Ping(Ping::try_from(elem)?),
76
77 // XEP-0313
78 ("query", ns::MAM) => IqGetPayload::MamQuery(MamQuery::try_from(elem)?),
79 ("prefs", ns::MAM) => IqGetPayload::MamPrefs(MamPrefs::try_from(elem)?),
80
81 _ => IqGetPayload::Unknown(elem),
82 })
83 }
84}
85
86impl From<IqGetPayload> for Element {
87 fn from(payload: IqGetPayload) -> Element {
88 match payload {
89 IqGetPayload::Roster(roster) => roster.into(),
90 IqGetPayload::DiscoInfo(disco) => disco.into(),
91 IqGetPayload::Ping(ping) => ping.into(),
92 IqGetPayload::MamQuery(query) => query.into(),
93 IqGetPayload::MamPrefs(prefs) => prefs.into(),
94
95 IqGetPayload::Unknown(elem) => elem,
96 }
97 }
98}
99
100impl TryFrom<Element> for IqSetPayload {
101 type Err = Error;
102
103 fn try_from(elem: Element) -> Result<IqSetPayload, Error> {
104 Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) {
105 // RFC-6121
106 ("query", ns::ROSTER) => IqSetPayload::Roster(Roster::try_from(elem)?),
107
108 // XEP-0047
109 ("open", ns::IBB)
110 | ("data", ns::IBB)
111 | ("close", ns::IBB) => IqSetPayload::IBB(IBB::try_from(elem)?),
112
113 // XEP-0166
114 ("jingle", ns::JINGLE) => IqSetPayload::Jingle(Jingle::try_from(elem)?),
115
116 // XEP-0313
117 ("query", ns::MAM) => IqSetPayload::MamQuery(MamQuery::try_from(elem)?),
118 ("prefs", ns::MAM) => IqSetPayload::MamPrefs(MamPrefs::try_from(elem)?),
119
120 _ => IqSetPayload::Unknown(elem),
121 })
122 }
123}
124
125impl From<IqSetPayload> for Element {
126 fn from(payload: IqSetPayload) -> Element {
127 match payload {
128 IqSetPayload::Roster(roster) => roster.into(),
129 IqSetPayload::IBB(ibb) => ibb.into(),
130 IqSetPayload::Jingle(jingle) => jingle.into(),
131 IqSetPayload::MamQuery(query) => query.into(),
132 IqSetPayload::MamPrefs(prefs) => prefs.into(),
133
134 IqSetPayload::Unknown(elem) => elem,
135 }
136 }
137}
138
139impl TryFrom<Element> for IqResultPayload {
140 type Err = Error;
141
142 fn try_from(elem: Element) -> Result<IqResultPayload, Error> {
143 Ok(match (elem.name().as_ref(), elem.ns().unwrap().as_ref()) {
144 // RFC-6121
145 ("query", ns::ROSTER) => IqResultPayload::Roster(Roster::try_from(elem)?),
146
147 // XEP-0030
148 ("query", ns::DISCO_INFO) => IqResultPayload::DiscoInfo(DiscoInfoResult::try_from(elem)?),
149
150 // XEP-0313
151 ("query", ns::MAM) => IqResultPayload::MamQuery(MamQuery::try_from(elem)?),
152 ("fin", ns::MAM) => IqResultPayload::MamFin(MamFin::try_from(elem)?),
153 ("prefs", ns::MAM) => IqResultPayload::MamPrefs(MamPrefs::try_from(elem)?),
154
155 _ => IqResultPayload::Unknown(elem),
156 })
157 }
158}
159
160impl From<IqResultPayload> for Element {
161 fn from(payload: IqResultPayload) -> Element {
162 match payload {
163 IqResultPayload::Roster(roster) => roster.into(),
164 IqResultPayload::DiscoInfo(disco) => disco.into(),
165 IqResultPayload::MamQuery(query) => query.into(),
166 IqResultPayload::MamFin(fin) => fin.into(),
167 IqResultPayload::MamPrefs(prefs) => prefs.into(),
168
169 IqResultPayload::Unknown(elem) => elem,
170 }
171 }
172}
173
174#[derive(Debug, Clone)]
175pub enum IqType {
176 Get(Element),
177 Set(Element),
178 Result(Option<Element>),
179 Error(StanzaError),
180}
181
182impl<'a> IntoAttributeValue for &'a IqType {
183 fn into_attribute_value(self) -> Option<String> {
184 Some(match *self {
185 IqType::Get(_) => "get",
186 IqType::Set(_) => "set",
187 IqType::Result(_) => "result",
188 IqType::Error(_) => "error",
189 }.to_owned())
190 }
191}
192
193#[derive(Debug, Clone)]
194pub struct Iq {
195 pub from: Option<Jid>,
196 pub to: Option<Jid>,
197 pub id: Option<String>,
198 pub payload: IqType,
199}
200
201impl TryFrom<Element> for Iq {
202 type Err = Error;
203
204 fn try_from(root: Element) -> Result<Iq, Error> {
205 if !root.is("iq", ns::JABBER_CLIENT) {
206 return Err(Error::ParseError("This is not an iq element."));
207 }
208 let from = get_attr!(root, "from", optional);
209 let to = get_attr!(root, "to", optional);
210 let id = get_attr!(root, "id", optional);
211 let type_: String = get_attr!(root, "type", required);
212
213 let mut payload = None;
214 let mut error_payload = None;
215 for elem in root.children() {
216 if payload.is_some() {
217 return Err(Error::ParseError("Wrong number of children in iq element."));
218 }
219 if type_ == "error" {
220 if elem.is("error", ns::JABBER_CLIENT) {
221 if error_payload.is_some() {
222 return Err(Error::ParseError("Wrong number of children in iq element."));
223 }
224 error_payload = Some(StanzaError::try_from(elem.clone())?);
225 } else if root.children().count() != 2 {
226 return Err(Error::ParseError("Wrong number of children in iq element."));
227 }
228 } else {
229 payload = Some(elem.clone());
230 }
231 }
232
233 let type_ = if type_ == "get" {
234 if let Some(payload) = payload {
235 IqType::Get(payload)
236 } else {
237 return Err(Error::ParseError("Wrong number of children in iq element."));
238 }
239 } else if type_ == "set" {
240 if let Some(payload) = payload {
241 IqType::Set(payload)
242 } else {
243 return Err(Error::ParseError("Wrong number of children in iq element."));
244 }
245 } else if type_ == "result" {
246 if let Some(payload) = payload {
247 IqType::Result(Some(payload))
248 } else {
249 IqType::Result(None)
250 }
251 } else if type_ == "error" {
252 if let Some(payload) = error_payload {
253 IqType::Error(payload)
254 } else {
255 return Err(Error::ParseError("Wrong number of children in iq element."));
256 }
257 } else {
258 return Err(Error::ParseError("Unknown iq type."));
259 };
260
261 Ok(Iq {
262 from: from,
263 to: to,
264 id: id,
265 payload: type_,
266 })
267 }
268}
269
270impl From<Iq> for Element {
271 fn from(iq: Iq) -> Element {
272 let mut stanza = Element::builder("iq")
273 .ns(ns::JABBER_CLIENT)
274 .attr("from", iq.from.and_then(|value| Some(String::from(value))))
275 .attr("to", iq.to.and_then(|value| Some(String::from(value))))
276 .attr("id", iq.id)
277 .attr("type", &iq.payload)
278 .build();
279 let elem = match iq.payload {
280 IqType::Get(elem)
281 | IqType::Set(elem)
282 | IqType::Result(Some(elem)) => elem,
283 IqType::Error(error) => error.into(),
284 IqType::Result(None) => return stanza,
285 };
286 stanza.append_child(elem);
287 stanza
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294 use stanza_error::{ErrorType, DefinedCondition};
295
296 #[test]
297 fn test_require_type() {
298 let elem: Element = "<iq xmlns='jabber:client'/>".parse().unwrap();
299 let error = Iq::try_from(elem).unwrap_err();
300 let message = match error {
301 Error::ParseError(string) => string,
302 _ => panic!(),
303 };
304 assert_eq!(message, "Required attribute 'type' missing.");
305 }
306
307 #[test]
308 fn test_get() {
309 let elem: Element = "<iq xmlns='jabber:client' type='get'>
310 <foo/>
311 </iq>".parse().unwrap();
312 let iq = Iq::try_from(elem).unwrap();
313 let query: Element = "<foo xmlns='jabber:client'/>".parse().unwrap();
314 assert_eq!(iq.from, None);
315 assert_eq!(iq.to, None);
316 assert_eq!(iq.id, None);
317 assert!(match iq.payload {
318 IqType::Get(element) => element == query,
319 _ => false
320 });
321 }
322
323 #[test]
324 fn test_set() {
325 let elem: Element = "<iq xmlns='jabber:client' type='set'>
326 <vCard xmlns='vcard-temp'/>
327 </iq>".parse().unwrap();
328 let iq = Iq::try_from(elem).unwrap();
329 let vcard: Element = "<vCard xmlns='vcard-temp'/>".parse().unwrap();
330 assert_eq!(iq.from, None);
331 assert_eq!(iq.to, None);
332 assert_eq!(iq.id, None);
333 assert!(match iq.payload {
334 IqType::Set(element) => element == vcard,
335 _ => false
336 });
337 }
338
339 #[test]
340 fn test_result_empty() {
341 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
342 let iq = Iq::try_from(elem).unwrap();
343 assert_eq!(iq.from, None);
344 assert_eq!(iq.to, None);
345 assert_eq!(iq.id, None);
346 assert!(match iq.payload {
347 IqType::Result(None) => true,
348 _ => false,
349 });
350 }
351
352 #[test]
353 fn test_result() {
354 let elem: Element = "<iq xmlns='jabber:client' type='result'>
355 <query xmlns='http://jabber.org/protocol/disco#items'/>
356 </iq>".parse().unwrap();
357 let iq = Iq::try_from(elem).unwrap();
358 let query: Element = "<query xmlns='http://jabber.org/protocol/disco#items'/>".parse().unwrap();
359 assert_eq!(iq.from, None);
360 assert_eq!(iq.to, None);
361 assert_eq!(iq.id, None);
362 assert!(match iq.payload {
363 IqType::Result(Some(element)) => element == query,
364 _ => false,
365 });
366 }
367
368 #[test]
369 fn test_error() {
370 let elem: Element = "<iq xmlns='jabber:client' type='error'>
371 <ping xmlns='urn:xmpp:ping'/>
372 <error type='cancel'>
373 <service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
374 </error>
375 </iq>".parse().unwrap();
376 let iq = Iq::try_from(elem).unwrap();
377 assert_eq!(iq.from, None);
378 assert_eq!(iq.to, None);
379 assert_eq!(iq.id, None);
380 match iq.payload {
381 IqType::Error(error) => {
382 assert_eq!(error.type_, ErrorType::Cancel);
383 assert_eq!(error.by, None);
384 assert_eq!(error.defined_condition, DefinedCondition::ServiceUnavailable);
385 assert_eq!(error.texts.len(), 0);
386 assert_eq!(error.other, None);
387 },
388 _ => panic!(),
389 }
390 }
391
392 #[test]
393 fn test_children_invalid() {
394 let elem: Element = "<iq xmlns='jabber:client' type='error'></iq>".parse().unwrap();
395 let error = Iq::try_from(elem).unwrap_err();
396 let message = match error {
397 Error::ParseError(string) => string,
398 _ => panic!(),
399 };
400 assert_eq!(message, "Wrong number of children in iq element.");
401 }
402
403 #[test]
404 fn test_serialise() {
405 let elem: Element = "<iq xmlns='jabber:client' type='result'/>".parse().unwrap();
406 let iq2 = Iq {
407 from: None,
408 to: None,
409 id: None,
410 payload: IqType::Result(None),
411 };
412 let elem2 = iq2.into();
413 assert_eq!(elem, elem2);
414 }
415
416 #[test]
417 fn test_disco() {
418 let elem: Element = "<iq xmlns='jabber:client' type='get'><query xmlns='http://jabber.org/protocol/disco#info'/></iq>".parse().unwrap();
419 let iq = Iq::try_from(elem).unwrap();
420 let payload = match iq.payload {
421 IqType::Get(payload) => IqGetPayload::try_from(payload).unwrap(),
422 _ => panic!(),
423 };
424 assert!(match payload {
425 IqGetPayload::DiscoInfo(DiscoInfoQuery { .. }) => true,
426 _ => false,
427 });
428 }
429}