Skip to main content

xmpp_parsers/
ibr.rs

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}