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