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