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
 85    #[test]
 86    fn test_simple() {
 87        let elem: Element = "<query xmlns='jabber:iq:register'/>".parse().unwrap();
 88        Query::try_from(elem).unwrap();
 89    }
 90
 91    #[test]
 92    fn test_ex2() {
 93        let elem: Element = r#"
 94<query xmlns='jabber:iq:register'>
 95  <instructions>
 96    Choose a username and password for use with this service.
 97    Please also provide your email address.
 98  </instructions>
 99  <username/>
100  <password/>
101  <email/>
102</query>
103"#.parse().unwrap();
104        let query = Query::try_from(elem).unwrap();
105        assert_eq!(query.registered, false);
106        assert_eq!(query.fields["instructions"], "\n    Choose a username and password for use with this service.\n    Please also provide your email address.\n  ");
107        assert_eq!(query.fields["username"], "");
108        assert_eq!(query.fields["password"], "");
109        assert_eq!(query.fields["email"], "");
110        assert_eq!(query.fields.contains_key("name"), false);
111
112        // FIXME: HashMap doesn’t keep the order right.
113        //let elem2 = query.into();
114        //assert_eq!(elem, elem2);
115    }
116
117    #[test]
118    fn test_ex9() {
119        let elem: Element = r#"
120<query xmlns='jabber:iq:register'>
121  <instructions>
122    Use the enclosed form to register. If your Jabber client does not
123    support Data Forms, visit http://www.shakespeare.lit/contests.php
124  </instructions>
125  <x xmlns='jabber:x:data' type='form'>
126    <title>Contest Registration</title>
127    <instructions>
128      Please provide the following information
129      to sign up for our special contests!
130    </instructions>
131    <field type='hidden' var='FORM_TYPE'>
132      <value>jabber:iq:register</value>
133    </field>
134    <field label='Given Name' var='first'>
135      <required/>
136    </field>
137    <field label='Family Name' var='last'>
138      <required/>
139    </field>
140    <field label='Email Address' var='email'>
141      <required/>
142    </field>
143    <field type='list-single' label='Gender' var='x-gender'>
144      <option label='Male'><value>M</value></option>
145      <option label='Female'><value>F</value></option>
146    </field>
147  </x>
148</query>
149"#.parse().unwrap();
150        let elem1 = elem.clone();
151        let query = Query::try_from(elem).unwrap();
152        assert_eq!(query.registered, false);
153        assert!(!query.fields["instructions"].is_empty());
154        let form = query.form.clone().unwrap();
155        assert!(!form.instructions.unwrap().is_empty());
156        assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("first"))).is_ok());
157        assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("x-gender"))).is_ok());
158        assert!(form.fields.binary_search_by(|field| field.var.cmp(&String::from("coucou"))).is_err());
159        let elem2 = query.into();
160        assert_eq!(elem1, elem2);
161    }
162
163    #[test]
164    fn test_ex10() {
165        let elem: Element = r#"
166<query xmlns='jabber:iq:register'>
167  <x xmlns='jabber:x:data' type='submit'>
168    <field type='hidden' var='FORM_TYPE'>
169      <value>jabber:iq:register</value>
170    </field>
171    <field label='Given Name' var='first'>
172      <value>Juliet</value>
173    </field>
174    <field label='Family Name' var='last'>
175      <value>Capulet</value>
176    </field>
177    <field label='Email Address' var='email'>
178      <value>juliet@capulet.com</value>
179    </field>
180    <field type='list-single' label='Gender' var='x-gender'>
181      <value>F</value>
182    </field>
183  </x>
184</query>
185"#.parse().unwrap();
186        let elem1 = elem.clone();
187        let query = Query::try_from(elem).unwrap();
188        assert_eq!(query.registered, false);
189        for _ in &query.fields {
190            panic!();
191        }
192        let elem2 = query.into();
193        assert_eq!(elem1, elem2);
194    }
195}