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::util::error::Error;
  9use crate::iq::{IqGetPayload, IqResultPayload, IqSetPayload};
 10use crate::ns;
 11use minidom::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().unwrap();
 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")
 95            .ns(ns::REGISTER)
 96            .append(if query.registered {
 97                Some(Element::builder("registered").ns(ns::REGISTER))
 98            } else {
 99                None
100            })
101            .append(
102                query
103                    .fields
104                    .into_iter()
105                    .map(|(name, value)| Element::builder(name).ns(ns::REGISTER).append(value))
106                    .collect::<Vec<_>>(),
107            )
108            .append(if query.remove {
109                Some(Element::builder("remove").ns(ns::REGISTER))
110            } else {
111                None
112            })
113            .append(query.form)
114            .build()
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use crate::util::compare_elements::NamespaceAwareCompare;
122
123    // TODO: These size tests are sensible to the size of HashMap, which recently grew of two
124    // pointers and is thus different on stable and nightly.  Let’s wait for this issue before
125    // attempting a fix:
126    // https://github.com/rust-lang/hashbrown/issues/69
127
128    #[cfg(target_pointer_width = "32")]
129    #[test]
130    #[ignore]
131    fn test_size() {
132        assert_size!(Query, 88);
133    }
134
135    #[cfg(target_pointer_width = "64")]
136    #[test]
137    #[ignore]
138    fn test_size() {
139        assert_size!(Query, 152);
140    }
141
142    #[test]
143    fn test_simple() {
144        let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap();
145        Query::try_from(elem).unwrap();
146    }
147
148    #[test]
149    fn test_ex2() {
150        let elem: Element = r#"
151<query xmlns='jabber:iq:register'>
152  <instructions>
153    Choose a username and password for use with this service.
154    Please also provide your email address.
155  </instructions>
156  <username/>
157  <password/>
158  <email/>
159</query>
160"#
161        .parse()
162        .unwrap();
163        let query = Query::try_from(elem).unwrap();
164        assert_eq!(query.registered, false);
165        assert_eq!(query.fields["instructions"], "\n    Choose a username and password for use with this service.\n    Please also provide your email address.\n  ");
166        assert_eq!(query.fields["username"], "");
167        assert_eq!(query.fields["password"], "");
168        assert_eq!(query.fields["email"], "");
169        assert_eq!(query.fields.contains_key("name"), false);
170
171        // FIXME: HashMap doesn’t keep the order right.
172        //let elem2 = query.into();
173        //assert_eq!(elem, elem2);
174    }
175
176    #[test]
177    fn test_ex9() {
178        let elem: Element = r#"
179<query xmlns='jabber:iq:register'>
180  <instructions>
181    Use the enclosed form to register. If your Jabber client does not
182    support Data Forms, visit http://www.shakespeare.lit/contests.php
183  </instructions>
184  <x xmlns='jabber:x:data' type='form'>
185    <title>Contest Registration</title>
186    <instructions>
187      Please provide the following information
188      to sign up for our special contests!
189    </instructions>
190    <field type='hidden' var='FORM_TYPE'>
191      <value>jabber:iq:register</value>
192    </field>
193    <field label='Given Name' var='first'>
194      <required/>
195    </field>
196    <field label='Family Name' var='last'>
197      <required/>
198    </field>
199    <field label='Email Address' var='email'>
200      <required/>
201    </field>
202    <field type='list-single' label='Gender' var='x-gender'>
203      <option label='Male'><value>M</value></option>
204      <option label='Female'><value>F</value></option>
205    </field>
206  </x>
207</query>
208"#
209        .parse()
210        .unwrap();
211        let elem1 = elem.clone();
212        let query = Query::try_from(elem).unwrap();
213        assert_eq!(query.registered, false);
214        assert!(!query.fields["instructions"].is_empty());
215        let form = query.form.clone().unwrap();
216        assert!(!form.instructions.unwrap().is_empty());
217        let elem2 = query.into();
218        assert!(elem1.compare_to(&elem2));
219    }
220
221    #[test]
222    fn test_ex10() {
223        let elem: Element = r#"
224<query xmlns='jabber:iq:register'>
225  <x xmlns='jabber:x:data' type='submit'>
226    <field type='hidden' var='FORM_TYPE'>
227      <value>jabber:iq:register</value>
228    </field>
229    <field label='Given Name' var='first'>
230      <value>Juliet</value>
231    </field>
232    <field label='Family Name' var='last'>
233      <value>Capulet</value>
234    </field>
235    <field label='Email Address' var='email'>
236      <value>juliet@capulet.com</value>
237    </field>
238    <field type='list-single' label='Gender' var='x-gender'>
239      <value>F</value>
240    </field>
241  </x>
242</query>
243"#
244        .parse()
245        .unwrap();
246        let elem1 = elem.clone();
247        let query = Query::try_from(elem).unwrap();
248        assert_eq!(query.registered, false);
249        for _ in &query.fields {
250            panic!();
251        }
252        let elem2 = query.into();
253        assert!(elem1.compare_to(&elem2));
254    }
255}