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 xso::{AsXml, FromXml};
8
9use crate::data_forms::DataForm;
10use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
11use crate::ns;
12
13/// Entity requests registration fields from host.
14#[derive(FromXml, AsXml, Debug, Clone)]
15#[xml(namespace = ns::REGISTER, name = "query")]
16pub struct FieldsQuery;
17
18impl IqGetPayload for FieldsQuery {}
19
20/// Entity requests cancellation of existing registration.
21#[derive(FromXml, AsXml, Debug, Clone)]
22#[xml(namespace = ns::REGISTER, name = "query")]
23pub struct RemoveQuery {
24 /// Whether to remove that registration.
25 // TODO: we should be able to remove that flag and make this type a zero-sized type.
26 #[xml(flag)]
27 pub remove: bool,
28}
29
30impl IqSetPayload for RemoveQuery {}
31
32/// Query for registering against a service, the legacy way.
33#[derive(FromXml, AsXml, Debug, Clone)]
34#[xml(namespace = ns::REGISTER, name = "query")]
35pub struct LegacyQuery {
36 /// Whether this account is already registered
37 #[xml(flag)]
38 pub registered: bool,
39
40 /// Instructions to be presented to entities implementing this legacy element.
41 #[xml(extract(default, fields(text(type_ = String))))]
42 pub instructions: Option<String>,
43
44 /// Account name associated with the user
45 #[xml(extract(default, fields(text(type_ = String))))]
46 pub username: Option<String>,
47
48 /// Familiar name of the user
49 #[xml(extract(default, fields(text(type_ = String))))]
50 pub nick: Option<String>,
51
52 /// Password or secret for the user
53 #[xml(extract(default, fields(text(type_ = String))))]
54 pub password: Option<String>,
55
56 /// Full name of the user
57 #[xml(extract(default, fields(text(type_ = String))))]
58 pub name: Option<String>,
59
60 /// Given name of the user
61 #[xml(extract(default, fields(text(type_ = String))))]
62 pub first: Option<String>,
63
64 /// Family name of the user
65 #[xml(extract(default, fields(text(type_ = String))))]
66 pub last: Option<String>,
67
68 /// Email address of the user
69 #[xml(extract(default, fields(text(type_ = String))))]
70 pub email: Option<String>,
71
72 /// Street portion of a physical or mailing address
73 #[xml(extract(default, fields(text(type_ = String))))]
74 pub address: Option<String>,
75
76 /// Locality portion of a physical or mailing address
77 #[xml(extract(default, fields(text(type_ = String))))]
78 pub city: Option<String>,
79
80 /// Region portion of a physical or mailing address
81 #[xml(extract(default, fields(text(type_ = String))))]
82 pub state: Option<String>,
83
84 /// Postal code portion of a physical or mailing address
85 #[xml(extract(default, fields(text(type_ = String))))]
86 pub zip: Option<String>,
87
88 /// Telephone number of the user
89 #[xml(extract(default, fields(text(type_ = String))))]
90 pub phone: Option<String>,
91
92 /// URL to web page describing the user
93 #[xml(extract(default, fields(text(type_ = String))))]
94 pub url: Option<String>,
95
96 /// Some date (e.g., birth date, hire date, sign-up date)
97 #[xml(extract(default, fields(text(type_ = String))))]
98 pub date: Option<String>,
99
100 /// Free-form text field (obsolete)
101 #[xml(extract(default, fields(text(type_ = String))))]
102 pub misc: Option<String>,
103
104 /// Free-form text field (obsolete)
105 #[xml(extract(default, fields(text(type_ = String))))]
106 pub text: Option<String>,
107
108 /// Session key for transaction (obsolete)
109 #[xml(extract(default, fields(text(type_ = String))))]
110 pub key: Option<String>,
111}
112
113impl IqSetPayload for LegacyQuery {}
114impl IqResultPayload for LegacyQuery {}
115
116/// Query for registering against a service.
117#[derive(FromXml, AsXml, Debug, Clone)]
118#[xml(namespace = ns::REGISTER, name = "query")]
119pub struct FormQuery {
120 /// Legacy instructions fallback for entities which don’t understand the form.
121 #[xml(extract(default, fields(text(type_ = String))))]
122 pub instructions: Option<String>,
123
124 /// A data form the user must fill before being allowed to register.
125 #[xml(child)]
126 pub form: DataForm,
127 // Not yet implemented.
128 //pub oob: Option<Oob>,
129}
130
131impl IqSetPayload for FormQuery {}
132impl IqResultPayload for FormQuery {}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::data_forms::DataFormType;
138 use minidom::Element;
139
140 #[cfg(target_pointer_width = "32")]
141 #[test]
142 fn test_size() {
143 assert_size!(RemoveQuery, 1);
144 assert_size!(LegacyQuery, 220);
145 assert_size!(FormQuery, 52);
146 }
147
148 #[cfg(target_pointer_width = "64")]
149 #[test]
150 fn test_size() {
151 assert_size!(RemoveQuery, 1);
152 assert_size!(LegacyQuery, 440);
153 assert_size!(FormQuery, 104);
154 }
155
156 #[test]
157 fn test_simple() {
158 let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap();
159 FieldsQuery::try_from(elem).unwrap();
160
161 let elem: Element = "<query xmlns='jabber:iq:register'><remove/></query>"
162 .parse()
163 .unwrap();
164 let query = RemoveQuery::try_from(elem).unwrap();
165 assert_eq!(query.remove, true);
166
167 let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap();
168 let query = LegacyQuery::try_from(elem).unwrap();
169 assert_eq!(query.first, None);
170
171 let elem: Element =
172 "<query xmlns='jabber:iq:register'><x xmlns='jabber:x:data' type='submit'/></query>"
173 .parse()
174 .unwrap();
175 let query = FormQuery::try_from(elem).unwrap();
176 assert!(query.instructions.is_none());
177 assert!(query.form.fields.is_empty());
178 }
179
180 #[test]
181 fn test_ex2() {
182 let elem: Element = r#"<query xmlns='jabber:iq:register'>
183 <instructions>
184 Choose a username and password for use with this service.
185 Please also provide your email address.
186 </instructions>
187 <username/>
188 <password/>
189 <email/>
190</query>
191"#
192 .parse()
193 .unwrap();
194 let query = LegacyQuery::try_from(elem).unwrap();
195 assert_eq!(query.registered, false);
196 assert_eq!(query.instructions.unwrap(), "\n Choose a username and password for use with this service.\n Please also provide your email address.\n ");
197 assert_eq!(query.username.unwrap(), "");
198 assert_eq!(query.password.unwrap(), "");
199 assert_eq!(query.email.unwrap(), "");
200 assert_eq!(query.name, None);
201
202 // FIXME: HashMap doesn’t keep the order right.
203 //let elem2 = query.into();
204 //assert_eq!(elem, elem2);
205 }
206
207 #[test]
208 fn test_ex9() {
209 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>"
210 .parse()
211 .unwrap();
212 let elem1 = elem.clone();
213 let query = FormQuery::try_from(elem).unwrap();
214 assert!(query.instructions.is_some());
215 let form = query.form.clone();
216 assert_eq!(
217 form.instructions.unwrap(),
218 "Please provide the following information to sign up for our special contests!"
219 );
220 assert_eq!(form.type_, DataFormType::Form);
221 let elem2 = query.into();
222 assert_eq!(elem1, elem2);
223 }
224
225 #[test]
226 fn test_ex10() {
227 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>"
228 .parse()
229 .unwrap();
230 let elem1 = elem.clone();
231 let query = FormQuery::try_from(elem).unwrap();
232 let elem2 = query.into();
233 assert_eq!(elem1, elem2);
234 }
235}