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}