1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use std::collections::HashMap;
8use try_from::TryFrom;
9
10use minidom::Element;
11
12use error::Error;
13
14use iq::{IqGetPayload, IqSetPayload, IqResultPayload};
15use data_forms::DataForm;
16
17use ns;
18
19#[derive(Debug, Clone)]
20pub struct Query {
21 pub fields: HashMap<String, String>,
22 pub registered: bool,
23 pub remove: bool,
24 pub form: Option<DataForm>,
25 // Not yet implemented.
26 //pub oob: Option<Oob>,
27}
28
29impl IqGetPayload for Query {}
30impl IqSetPayload for Query {}
31impl IqResultPayload for Query {}
32
33impl TryFrom<Element> for Query {
34 type Err = Error;
35
36 fn try_from(elem: Element) -> Result<Query, Error> {
37 check_self!(elem, "query", REGISTER, "IBR query");
38 let mut query = Query {
39 registered: false,
40 fields: HashMap::new(),
41 remove: false,
42 form: None,
43 };
44 for child in elem.children() {
45 let namespace = child.ns().unwrap();
46 if namespace == ns::REGISTER {
47 let name = child.name();
48 let fields = vec!["address", "city", "date", "email", "first", "instructions",
49 "key", "last", "misc", "name", "nick", "password", "phone",
50 "state", "text", "url", "username", "zip"];
51 if fields.binary_search(&name).is_ok() {
52 query.fields.insert(name.to_owned(), child.text());
53 } else if name == "registered" {
54 query.registered = true;
55 } else if name == "remove" {
56 query.remove = true;
57 } else {
58 return Err(Error::ParseError("Wrong field in ibr element."));
59 }
60 } else if child.is("x", ns::DATA_FORMS) {
61 query.form = Some(DataForm::try_from(child.clone())?);
62 } else {
63 return Err(Error::ParseError("Unknown child in ibr element."));
64 }
65 }
66 Ok(query)
67 }
68}
69
70impl From<Query> for Element {
71 fn from(query: Query) -> Element {
72 Element::builder("query")
73 .ns(ns::REGISTER)
74 .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None })
75 .append(query.fields.into_iter().map(|(name, value)| {
76 Element::builder(name).ns(ns::REGISTER).append(value)
77 }).collect::<Vec<_>>())
78 .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None })
79 .append(query.form)
80 .build()
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use compare_elements::NamespaceAwareCompare;
88
89 #[test]
90 fn test_simple() {
91 let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap();
92 Query::try_from(elem).unwrap();
93 }
94
95 #[test]
96 fn test_ex2() {
97 let elem: Element = r#"
98<query xmlns='jabber:iq:register'>
99 <instructions>
100 Choose a username and password for use with this service.
101 Please also provide your email address.
102 </instructions>
103 <username/>
104 <password/>
105 <email/>
106</query>
107"#.parse().unwrap();
108 let query = Query::try_from(elem).unwrap();
109 assert_eq!(query.registered, false);
110 assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n ");
111 assert_eq!(query.fields["username"], "");
112 assert_eq!(query.fields["password"], "");
113 assert_eq!(query.fields["email"], "");
114 assert_eq!(query.fields.contains_key("name"), false);
115
116 // FIXME: HashMap doesn’t keep the order right.
117 //let elem2 = query.into();
118 //assert_eq!(elem, elem2);
119 }
120
121 #[test]
122 fn test_ex9() {
123 let elem: Element = r#"
124<query xmlns='jabber:iq:register'>
125 <instructions>
126 Use the enclosed form to register. If your Jabber client does not
127 support Data Forms, visit http://www.shakespeare.lit/contests.php
128 </instructions>
129 <x xmlns='jabber:x:data' type='form'>
130 <title>Contest Registration</title>
131 <instructions>
132 Please provide the following information
133 to sign up for our special contests!
134 </instructions>
135 <field type='hidden' var='FORM_TYPE'>
136 <value>jabber:iq:register</value>
137 </field>
138 <field label='Given Name' var='first'>
139 <required/>
140 </field>
141 <field label='Family Name' var='last'>
142 <required/>
143 </field>
144 <field label='Email Address' var='email'>
145 <required/>
146 </field>
147 <field type='list-single' label='Gender' var='x-gender'>
148 <option label='Male'><value>M</value></option>
149 <option label='Female'><value>F</value></option>
150 </field>
151 </x>
152</query>
153"#.parse().unwrap();
154 let elem1 = elem.clone();
155 let query = Query::try_from(elem).unwrap();
156 assert_eq!(query.registered, false);
157 assert!(!query.fields["instructions"].is_empty());
158 let form = query.form.clone().unwrap();
159 assert!(!form.instructions.unwrap().is_empty());
160 assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok());
161 assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok());
162 assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err());
163 let elem2 = query.into();
164 assert!(elem1.compare_to(&elem2));
165 }
166
167 #[test]
168 fn test_ex10() {
169 let elem: Element = r#"
170<query xmlns='jabber:iq:register'>
171 <x xmlns='jabber:x:data' type='submit'>
172 <field type='hidden' var='FORM_TYPE'>
173 <value>jabber:iq:register</value>
174 </field>
175 <field label='Given Name' var='first'>
176 <value>Juliet</value>
177 </field>
178 <field label='Family Name' var='last'>
179 <value>Capulet</value>
180 </field>
181 <field label='Email Address' var='email'>
182 <value>juliet@capulet.com</value>
183 </field>
184 <field type='list-single' label='Gender' var='x-gender'>
185 <value>F</value>
186 </field>
187 </x>
188</query>
189"#.parse().unwrap();
190 let elem1 = elem.clone();
191 let query = Query::try_from(elem).unwrap();
192 assert_eq!(query.registered, false);
193 for _ in &query.fields {
194 panic!();
195 }
196 let elem2 = query.into();
197 assert!(elem1.compare_to(&elem2));
198 }
199}