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