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