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 std::collections::HashMap;
  8use try_from::TryFrom;
  9
 10use minidom::Element;
 11
 12use crate::error::Error;
 13
 14use crate::iq::{IqGetPayload, IqSetPayload, IqResultPayload};
 15use crate::data_forms::DataForm;
 16
 17use crate::ns;
 18
 19/// Query for registering against a service.
 20#[derive(Debug, Clone)]
 21pub struct Query {
 22    /// Deprecated fixed list of possible fields to fill before the user can
 23    /// register.
 24    pub fields: HashMap<String, String>,
 25
 26    /// Whether this account is already registered.
 27    pub registered: bool,
 28
 29    /// Whether to remove this account.
 30    pub remove: bool,
 31
 32    /// A data form the user must fill before being allowed to register.
 33    pub form: Option<DataForm>,
 34
 35    // Not yet implemented.
 36    //pub oob: Option<Oob>,
 37}
 38
 39impl IqGetPayload for Query {}
 40impl IqSetPayload for Query {}
 41impl IqResultPayload for Query {}
 42
 43impl TryFrom<Element> for Query {
 44    type Err = Error;
 45
 46    fn try_from(elem: Element) -> Result<Query, Error> {
 47        check_self!(elem, "query", REGISTER, "IBR query");
 48        let mut query = Query {
 49            registered: false,
 50            fields: HashMap::new(),
 51            remove: false,
 52            form: None,
 53        };
 54        for child in elem.children() {
 55            let namespace = child.ns().unwrap();
 56            if namespace == ns::REGISTER {
 57                let name = child.name();
 58                let fields = vec!["address", "city", "date", "email", "first", "instructions",
 59                                  "key", "last", "misc", "name", "nick", "password", "phone",
 60                                  "state", "text", "url", "username", "zip"];
 61                if fields.binary_search(&name).is_ok() {
 62                    query.fields.insert(name.to_owned(), child.text());
 63                } else if name == "registered" {
 64                    query.registered = true;
 65                } else if name == "remove" {
 66                    query.remove = true;
 67                } else {
 68                    return Err(Error::ParseError("Wrong field in ibr element."));
 69                }
 70            } else if child.is("x", ns::DATA_FORMS) {
 71                query.form = Some(DataForm::try_from(child.clone())?);
 72            } else {
 73                return Err(Error::ParseError("Unknown child in ibr element."));
 74            }
 75        }
 76        Ok(query)
 77    }
 78}
 79
 80impl From<Query> for Element {
 81    fn from(query: Query) -> Element {
 82        Element::builder("query")
 83                .ns(ns::REGISTER)
 84                .append(if query.registered { Some(Element::builder("registered").ns(ns::REGISTER)) } else { None })
 85                .append(query.fields.into_iter().map(|(name, value)| {
 86                     Element::builder(name).ns(ns::REGISTER).append(value)
 87                 }).collect::<Vec<_>>())
 88                .append(if query.remove { Some(Element::builder("remove").ns(ns::REGISTER)) } else { None })
 89                .append(query.form)
 90                .build()
 91    }
 92}
 93
 94#[cfg(test)]
 95mod tests {
 96    use super::*;
 97    use crate::compare_elements::NamespaceAwareCompare;
 98
 99    #[cfg(target_pointer_width = "32")]
100    #[test]
101    fn test_size() {
102        assert_size!(Query, 88);
103    }
104
105    #[cfg(target_pointer_width = "64")]
106    #[test]
107    fn test_size() {
108        assert_size!(Query, 152);
109    }
110
111    #[test]
112    fn test_simple() {
113        let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap();
114        Query::try_from(elem).unwrap();
115    }
116
117    #[test]
118    fn test_ex2() {
119        let elem: Element = r#"
120<query xmlns='jabber:iq:register'>
121  <instructions>
122    Choose a username and password for use with this service.
123    Please also provide your email address.
124  </instructions>
125  <username/>
126  <password/>
127  <email/>
128</query>
129"#.parse().unwrap();
130        let query = Query::try_from(elem).unwrap();
131        assert_eq!(query.registered, false);
132        assert_eq!(query.fields["instructions"], "\n    Choose a username and password for use with this service.\n    Please also provide your email address.\n  ");
133        assert_eq!(query.fields["username"], "");
134        assert_eq!(query.fields["password"], "");
135        assert_eq!(query.fields["email"], "");
136        assert_eq!(query.fields.contains_key("name"), false);
137
138        // FIXME: HashMap doesn’t keep the order right.
139        //let elem2 = query.into();
140        //assert_eq!(elem, elem2);
141    }
142
143    #[test]
144    fn test_ex9() {
145        let elem: Element = r#"
146<query xmlns='jabber:iq:register'>
147  <instructions>
148    Use the enclosed form to register. If your Jabber client does not
149    support Data Forms, visit http://www.shakespeare.lit/contests.php
150  </instructions>
151  <x xmlns='jabber:x:data' type='form'>
152    <title>Contest Registration</title>
153    <instructions>
154      Please provide the following information
155      to sign up for our special contests!
156    </instructions>
157    <field type='hidden' var='FORM_TYPE'>
158      <value>jabber:iq:register</value>
159    </field>
160    <field label='Given Name' var='first'>
161      <required/>
162    </field>
163    <field label='Family Name' var='last'>
164      <required/>
165    </field>
166    <field label='Email Address' var='email'>
167      <required/>
168    </field>
169    <field type='list-single' label='Gender' var='x-gender'>
170      <option label='Male'><value>M</value></option>
171      <option label='Female'><value>F</value></option>
172    </field>
173  </x>
174</query>
175"#.parse().unwrap();
176        let elem1 = elem.clone();
177        let query = Query::try_from(elem).unwrap();
178        assert_eq!(query.registered, false);
179        assert!(!query.fields["instructions"].is_empty());
180        let form = query.form.clone().unwrap();
181        assert!(!form.instructions.unwrap().is_empty());
182        assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok());
183        assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok());
184        assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err());
185        let elem2 = query.into();
186        assert!(elem1.compare_to(&elem2));
187    }
188
189    #[test]
190    fn test_ex10() {
191        let elem: Element = r#"
192<query xmlns='jabber:iq:register'>
193  <x xmlns='jabber:x:data' type='submit'>
194    <field type='hidden' var='FORM_TYPE'>
195      <value>jabber:iq:register</value>
196    </field>
197    <field label='Given Name' var='first'>
198      <value>Juliet</value>
199    </field>
200    <field label='Family Name' var='last'>
201      <value>Capulet</value>
202    </field>
203    <field label='Email Address' var='email'>
204      <value>juliet@capulet.com</value>
205    </field>
206    <field type='list-single' label='Gender' var='x-gender'>
207      <value>F</value>
208    </field>
209  </x>
210</query>
211"#.parse().unwrap();
212        let elem1 = elem.clone();
213        let query = Query::try_from(elem).unwrap();
214        assert_eq!(query.registered, false);
215        for _ in &query.fields {
216            panic!();
217        }
218        let elem2 = query.into();
219        assert!(elem1.compare_to(&elem2));
220    }
221}