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