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#"<query xmlns='jabber:iq:register'>
141 <instructions>
142 Choose a username and password for use with this service.
143 Please also provide your email address.
144 </instructions>
145 <username/>
146 <password/>
147 <email/>
148</query>
149"#
150 .parse()
151 .unwrap();
152 let query = Query::try_from(elem).unwrap();
153 assert_eq!(query.registered, false);
154 assert_eq!(query.fields["instructions"], "\n Choose a username and password for use with this service.\n Please also provide your email address.\n ");
155 assert_eq!(query.fields["username"], "");
156 assert_eq!(query.fields["password"], "");
157 assert_eq!(query.fields["email"], "");
158 assert_eq!(query.fields.contains_key("name"), false);
159
160 // FIXME: HashMap doesn’t keep the order right.
161 //let elem2 = query.into();
162 //assert_eq!(elem, elem2);
163 }
164
165 #[test]
166 fn test_ex9() {
167 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>"
168 .parse()
169 .unwrap();
170 let elem1 = elem.clone();
171 let query = Query::try_from(elem).unwrap();
172 assert_eq!(query.registered, false);
173 assert!(!query.fields["instructions"].is_empty());
174 let form = query.form.clone().unwrap();
175 assert!(!form.instructions.unwrap().is_empty());
176 let elem2 = query.into();
177 assert_eq!(elem1, elem2);
178 }
179
180 #[test]
181 fn test_ex10() {
182 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>"
183 .parse()
184 .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_eq!(elem1, elem2);
193 }
194}