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