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