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