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