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