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