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