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